diff --git a/assets/exclamacion_50.stl b/assets/exclamacion_50.stl new file mode 100644 index 0000000..1d4dcca Binary files /dev/null and b/assets/exclamacion_50.stl differ diff --git a/index.html b/index.html index a026ae2..cb9099a 100644 --- a/index.html +++ b/index.html @@ -34,14 +34,31 @@ function help() { '

Desarrollado por:

'); } -function up() { light.shadow.camera.top += 1; light.shadow.camera.updateProjectionMatrix(); helper.update()} +/*function up() { light.shadow.camera.top += 1; light.shadow.camera.updateProjectionMatrix(); helper.update()} function down() { light.shadow.camera.bottom -= 1; light.shadow.camera.updateProjectionMatrix(); helper.update()} function left() { light.shadow.camera.left -= 1; light.shadow.camera.updateProjectionMatrix(); helper.update()} - function right() { light.shadow.camera.right += 1; light.shadow.camera.updateProjectionMatrix(); helper.update()} + function right() { light.shadow.camera.right += 1; light.shadow.camera.updateProjectionMatrix(); helper.update()} */ +function rot(angle) { + document.getElementById("rot0").className = document.getElementById("rot225").className = document.getElementById("rot270").className = document.getElementById("rot315").className = ""; + document.getElementById("rot" + angle).className = "sel"; + ws.setRotation(angle); +} + +document.addEventListener('mousemove', follow, false); +function follow(e) { + follower = document.getElementById("follower"); + status = follower.style.display; + follower.style.display = "block"; + follower.style.left = (e.clientX - follower.clientWidth) + 'px'; + follower.style.top = (e.clientY - follower.clientHeight) + 'px'; + follower.style.display = status; +}
+
+
Resultados
-
¡SIMULAR!
-
+

Rotá tu
dispositivo

diff --git a/js/main.js b/js/main.js index b7e3acb..08c39ee 100644 --- a/js/main.js +++ b/js/main.js @@ -26,8 +26,11 @@ class WindSimulation { this.wm = null; this.simulation = null; + this.simulationPot = null; this.xhr = null; + this.lastFrame = Date.now(); + this.rotation = Math.PI; this.targetRotation = Math.PI; this.dir = 0; @@ -64,18 +67,19 @@ class WindSimulation { for(var i = 0; i < this.mills; i++) { var wm = new WindMill(); wm.position.set(4 + 0.5, i + 5 - Math.ceil(this.mills / 2) + 0.5, 0); - wm.rotation.z = Math.PI; + wm.rotation.z = this.targetRotation; scene.add(wm); this.wms.push(wm); } this.wm = this.wms[0]; } - move(pos, ended=false) { - if(this.simulation) { - scene.remove(this.simulation); - this.simulation = null; - } + move(pos, ended=false, count=2) { + if(this.simulation) + this.resetSimulation(); + + if(! count) + return false; var wm = this.wm; @@ -85,11 +89,22 @@ class WindSimulation { } if(wm == null) return false; - if(pos.x > this.width - 0.5 || pos.x < 0.5 || pos.y > this.height - 0.5 || pos.y < 0.5) return false; + if(pos.x > this.width - 0.5 || pos.x < 0.5 || pos.y > this.height - 0.5 || pos.y < 0.5) { + if(pos.x > this.width - 0.5) + pos.x = this.width - 0.5; + if(pos.x < 0.5) + pos.x = 0.5; + if(pos.y > this.height - 0.5) + pos.y = this.height - 0.5; + if(pos.y < 0.5) + pos.y = 0.5; + return this.move(pos, ended, count - 1); + } for(var i = 0; i < this.wms.length; i++) { if(this.wms[i] == wm) continue; - if(pos.clone().sub(this.wms[i].position).length() < 1) - return false; + if(pos.clone().sub(this.wms[i].position).length() < 1) { + return this.move(this.wms[i].position.clone().add(pos.clone().sub(this.wms[i].position).normalize()), ended, count - 1); + } } wm.position.x = pos.x; wm.position.y = pos.y; @@ -99,11 +114,14 @@ class WindSimulation { animate() { requestAnimationFrame(() => { this.animate(); }); + var now = Date.now(); + var step = (now - this.lastFrame) / 1000; + if(!Screen.isMobile || (Screen.isMobile && screen.clicked)) { const intersects = screen.raycaster.intersectObjects(this.wms); var wm = null; for(var i = 0; i < intersects.length; i++) { - if(intersects[i].object.geometry.constructor.name != "CylinderGeometry") { + if(intersects[i].object.constructor.name != "Spring") { wm = intersects[i].object.parent; break; } @@ -111,22 +129,32 @@ class WindSimulation { if(!screen.clicked || Screen.isMobile) { if(this.wm != wm) { - if(this.wm != null) this.wm.select(false); - if(wm != null) wm.select(true); + if(this.wm != null) { + this.wm.select(false); + document.getElementById("follower").style.display = "none"; + } + if(wm != null) { + wm.select(true); + if(this.simulation) { + var i = this.wms.indexOf(wm); + var follower = document.getElementById("follower"); + follower.innerHTML = "Aerogenerador Nº" + (i + 1) + "
" + (Math.round(this.simulationPot[i] * 100) / 100) + " GWh (" + Math.round(this.simulationPot[i] * 100 / 14.95) + "%)"; + follower.style.display = "block"; + } + } } this.wm = wm; } } for(var i = 0; i < this.wms.length; i++) - this.wms[i].animate(); + this.wms[i].animate(step); - var speed = 0.02; + var speed = 2 * step; if(Math.abs(this.targetRotation - this.rotation) > speed) { var step = speed * Math.sign(this.targetRotation - this.rotation); this.rotation += step; for(var i = 0; i < this.wms.length; i++) { - this.wms[i].animate(); this.wms[i].rotation.z += step; } this.flag.rotation.z += step; @@ -134,14 +162,25 @@ class WindSimulation { this.flag.animate(); + if(window.exclamation) + window.exclamation.rotation.y += 0.02; + + this.lastFrame = now; + renderer.render(scene, camera); } + resetSimulation() { + scene.remove(this.simulation); + this.simulation = null; + this.simulationPot = null; + document.getElementById("follower").style.display = "none"; + document.getElementById("results").style.display = "none"; + } + setRotation(dir) { - if(this.simulation) { - scene.remove(this.simulation); - this.simulation = null; - } + if(this.simulation) + this.resetSimulation(); var rotations = {}; rotations[0] = Math.PI; @@ -153,6 +192,30 @@ class WindSimulation { this.targetRotation = rotations[dir]; } + addWindMill() { + if(this.mills >= 10) return; + + this.mills++; + this.reset(); + } + + removeWindMill() { + if(this.mills <= 1) return; + + this.mills--; + this.reset(); + } + + reset() { + for(var i = 0; i < this.wms.length; i++) + scene.remove(this.wms[i]); + + this.wms = []; + this.rotation = this.targetRotation; + + this.init_windmills(); + } + simulate() { if(this.xhr != null) this.xhr.abort(); @@ -165,7 +228,8 @@ class WindSimulation { if(this.xhr != xhr) return; if (xhr.readyState === 4 && xhr.status === 200) { var json = JSON.parse(xhr.responseText); - this.add_simulation(json.ws); + console.log(json.pot) + this.add_simulation(json.ws, json.pot); this.xhr = null; } }; @@ -176,7 +240,17 @@ class WindSimulation { xhr.send(data); } - add_simulation(ws) { + add_simulation(ws, pot) { + var sum = 0; + for(let i = 0; i < pot.length; i++) { + this.wms[i].lowPower(pot[i] < 2.75/*14.95 * 0.8*/) + sum += pot[i]; + } + + var results = document.getElementById("results") + results.innerHTML = (Math.round(sum * 100) / 100) + " GWh (" + Math.round(sum * 100 / 14.95 / pot.length) + "%)"; + results.style.display = "block"; + var geometry = new THREE.PlaneGeometry(this.width, this.height, 10 * this.width, 10 * this.height); var colors = new three.BufferAttribute(new Float32Array(geometry.attributes.position.count * 4), 4); geometry.setAttribute('color', colors); @@ -190,8 +264,9 @@ class WindSimulation { } if(this.simulation) - scene.remove(this.simulation); + this.resetSimulation(); this.simulation = mesh; + this.simulationPot = pot; scene.add(mesh); } } @@ -200,7 +275,7 @@ var width = 10; var height = 10; var ws = new WindSimulation(width, height, 5); -var screen = new Screen(camera, renderer, new THREE.Vector3(width / 2 + 0.5, height / 2, -0.65)); +var screen = new Screen(camera, renderer, new THREE.Vector3(width / 2 + 0.5, height / 2, -0.3)); screen.setMoveCallBack((pos, end) => { ws.move(pos, end); }); screen.onResize(); @@ -232,6 +307,16 @@ window.light = light; //scene.add( helper); //window.helper = helper; + + + + +window.windmill = WindMill; + + + + + window.ws = ws; window.screen = screen; window.scene = scene; diff --git a/js/screen.js b/js/screen.js index 023d49c..569dc9d 100644 --- a/js/screen.js +++ b/js/screen.js @@ -14,11 +14,11 @@ class Screen { this.pointerScreen = new THREE.Vector2(); this.pointerWorld = new THREE.Vector3(); - this.radius = 24; + this.radius = 26; this.phi = -0.5; this.theta = 0.45; this.z = 15; - this.aspect = 2.13; + this.aspect = 1.94; this.clicked = false; diff --git a/js/spring.js b/js/spring.js index cb92868..b2be7aa 100644 --- a/js/spring.js +++ b/js/spring.js @@ -1,7 +1,7 @@ import * as THREE from 'three'; // https://discourse.threejs.org/t/solved-create-a-spring/45135/9 -class Spring extends THREE.Mesh{ +class Spring extends THREE.Mesh { constructor(radius, turns, segmentsPerTurn, height, growth, material){ let g = new THREE.CylinderGeometry(0.005, 0.005, 1, 16, segmentsPerTurn * turns).translate(0, 0.5, 0).rotateX(Math.PI * 0.5); let initPos = g.attributes.position.clone(); diff --git a/js/windmill.js b/js/windmill.js index 7251fc6..591dd6b 100644 --- a/js/windmill.js +++ b/js/windmill.js @@ -6,6 +6,7 @@ import { Spring } from './spring.js'; class WindMill extends THREE.Group { static geometryBody = null; static geometryBlades = null; + static geometryExclamation = null; static { const loader = new STLLoader(); @@ -16,15 +17,25 @@ class WindMill extends THREE.Group { //loader.load('assets/cuerpo_126m_50.stl', function(geometry) { WindMill.geometryBody = geometry; }); + loader.load('assets/exclamacion_50.stl', function(geometry) { + WindMill.geometryExclamation = geometry; + var colors = new three.BufferAttribute(new Float32Array(geometry.attributes.position.array.length), 3); + for(let i = 0; i < colors.count; i++) + if(Math.abs(geometry.attributes.position.getZ(i)) < 7.4) + colors.setXYZ(i, 0xfa / 0xff, 0xd5 / 0xff, 0x0a / 0xff); + WindMill.geometryExclamation.setAttribute('color', colors); + }); } static isReady() { - return WindMill.geometryBody != null && WindMill.geometryBlades != null; + return WindMill.geometryBody != null && WindMill.geometryBlades != null && WindMill.geometryExclamation != null; } constructor() { super() + this.isLow = false; + this.material = new THREE.MeshStandardMaterial({color: 0xffffff, roughness: 0.15}); this.materialCircle = new THREE.MeshStandardMaterial({color: 0xaa0000}); //this.materialCircle.transparent = true; @@ -84,20 +95,32 @@ class WindMill extends THREE.Group { this.spring1.visible = this.spring2.visible = this.spring3.visible = false; + var material = new THREE.MeshStandardMaterial({color: 0xfad50a, roughness: 0.15, vertexColors: true}); + this.meshExclamation = new THREE.Mesh(WindMill.geometryExclamation, material); + this.meshExclamation.castShadow = true; + //this.meshExclamation.receiveShadow = true; + this.meshExclamation.scale.setScalar(0.002); + this.meshExclamation.rotateX(Math.PI / 2); + this.meshExclamation.position.z = (90 + 126 / 2 + 1) / 126; + this.add(this.meshExclamation); + this.meshExclamation.visible = false; + this.selected = false; } - animate() { - this.meshBlades.rotation.x += 0.1; - this.spring1.rotation.x += 0.1; - this.spring2.rotation.x += 0.1; - this.spring3.rotation.x += 0.1; + animate(step) { + var inc = (this.isLow ? 0.3 : 3) * step; + this.meshBlades.rotation.x += inc; + this.spring1.rotation.x += inc; + this.spring2.rotation.x += inc; + this.spring3.rotation.x += inc; + this.meshExclamation.rotation.y += 4 * step; } select(selected) { if(selected == this.selected) return; this.selected = selected; - this.meshCircle.visible = selected; + this.meshCircle.visible = selected; if(selected) this.material.color.set(0xff0000); @@ -105,6 +128,13 @@ class WindMill extends THREE.Group { this.material.color.set(0xffffff); } + lowPower(isLow) { + if(this.isLow == isLow) return; + this.isLow = isLow; + + this.meshExclamation.visible = isLow; + } + show_springs(value) { this.spring1.visible = this.spring2.visible = this.spring3.visible = value; } diff --git a/server.py b/server.py index 25386f8..961e36e 100644 --- a/server.py +++ b/server.py @@ -9,13 +9,19 @@ import xxx def index(): if request.method != 'POST' or not request.data: return abort(400); + params = json.loads(request.data.decode(encoding='utf-8')) pos = params['pos'] direction = params['dir'] - ws = xxx.run(direction, pos); + + if len(pos) > 10 or direction not in (0, 225, 270, 315): + return abort(400); + + ws, pot = xxx.run(direction, pos); return { 'pos': pos, 'ws': ws, + 'pot': pot, 'dir': direction, } diff --git a/style.css b/style.css index e5fec23..b6f94a8 100644 --- a/style.css +++ b/style.css @@ -1,3 +1,8 @@ +* { + margin: 0; + padding: 0; +} + body { margin: 0; font-family: "Encode Sans", sans-serif; @@ -34,7 +39,7 @@ body { } #popup p + p { - margin-top: -3vh; + margin-top: 1vh; } .l { @@ -58,9 +63,7 @@ body { } #button { - position: absolute; - right: 4vh; - top: 4vh; + display: inline-block; padding: 4vh; background: darkblue; font-weight: 800; @@ -70,24 +73,45 @@ body { background: blue; } -#help { +#right { position: absolute; - right: 4vh; - top: 16vh; + max-width: 75vw; + text-align: right; + right: 1vh; + top: 1vh; font-weight: 800; font-size: .8em; } -#help li { - display: block; - float: left; +#right li { + display: inline-block; border: solid white .5vh; - padding: .1vh 1vh; border-radius: 1vh; cursor: pointer; - margin-left: 1vh; - width: 2.5vh; + margin-bottom: 1vh; + width: 4vh; + height: 4vh; text-align: center; + opacity: 0.5; +} + +#right li.sel, #right li:hover { + opacity: 1; +} + +#follower, #results { + border: solid white 1px; + z-index: 900; + position: absolute; + background: #00000088; + padding: 1vh; + font-size: .5em; + display: none; +} + +#results { + bottom: 1vh; + left: 1vh; } #copyright { diff --git a/xxx.py b/xxx.py index 0a5bd90..bd46890 100644 --- a/xxx.py +++ b/xxx.py @@ -65,15 +65,15 @@ def py_wake_Initial_Cong(D,name,h,U_ref,initial_position=initial_position): method='linear')) - - - - windTurbines = py_wake_Initial_Cong(D, 'NREL_5MW', h, U_ref) +grid = XYGrid(x=np.arange(0, 10.01, 0.1)*D, y=np.arange(0, 10.01, 0.1)*D) def run(direction=0, initial_position=initial_position, U_ref=U_ref): initial_position = np.array(initial_position); - site = UniformSite(p_wd=[1] * 8, + p_wd = [0] * 360 + #p_wd[direction * n // 360] = 1 + p_wd[direction] = 1 + site = UniformSite(p_wd=p_wd, ws=U_ref, initial_position=initial_position*D) #ds = xr.Dataset( @@ -84,7 +84,9 @@ def run(direction=0, initial_position=initial_position, U_ref=U_ref): wt_x, wt_y = site.initial_position.T/D wfm = PropagateDownwind(site, windTurbines, wake_deficitModel=TurboGaussianDeficit()) - grid = XYGrid(x=np.arange(0, 10.01, 0.1)*126, y=np.arange(0, 10.01, 0.1)*126) - xa = wfm(x=wt_x*126, y=wt_y*126, wd=direction, yaw=0).flow_map(grid) + xa = wfm(x=wt_x*D, y=wt_y*D, wd=direction, yaw=0).flow_map(grid) ws = xa.WS_eff - return ws[:,:,0,0].values[:,:,0].tolist() + + aep = wfm(x=wt_x*D, y=wt_y*D).aep() + + return ws[:,:,0,0].values[:,:,0].tolist(), [sum(aep.values[i,:,0]) for i in aep.wt.values]