diff --git a/css/animation3.scss b/css/animation3.scss index 4cbfa22..03a86e8 100644 --- a/css/animation3.scss +++ b/css/animation3.scss @@ -14,14 +14,17 @@ z-index: 1; &::after { - border: 50px solid transparent; - border-right-color: lightgreen; + border: 50px solid; + border-color: lightgreen transparent transparent turquoise; + border-radius: 50px; content: ' '; height: 0; - left: -30px; + left: -50px; + margin: #{$side / 2} 0 0 #{$side / 2}; opacity: 0.5; position: absolute; - top: -40px; + transform: rotate(45deg); + top: -50px; width: 0; } } @@ -29,7 +32,7 @@ .anim3-movement-circle { border: 2px dotted darkturquoise; position: absolute; - transition: all 0.2s; + transition: left 0.2s, top 0.2s, height 0.2s, width 0.2s; z-index: 0; &:after { @@ -45,3 +48,29 @@ width: 4px; } } + +.anim3-vision-grid { + height: 100px; + // margin-top: 50px; + // margin-left: 50px; + position: absolute; + transform-origin: left top; + width: 100px; + z-index: 99; +} + +.anim3-dot { + $s: 2px; + + background: red; + // border-radius: 2px; + height: $s; + position: absolute; + width: $s; + z-index: 99; + + &.removed { + // background: purple; + background: yellow; + } +} diff --git a/css/style.css b/css/style.css index 1a93f4b..3e076e5 100644 --- a/css/style.css +++ b/css/style.css @@ -58,20 +58,23 @@ body { width: 20px; z-index: 1; } .anim3-particle::after { - border: 50px solid transparent; - border-right-color: lightgreen; + border: 50px solid; + border-color: lightgreen transparent transparent turquoise; + border-radius: 50px; content: ' '; height: 0; - left: -30px; + left: -50px; + margin: 10px 0 0 10px; opacity: 0.5; position: absolute; - top: -40px; + transform: rotate(45deg); + top: -50px; width: 0; } .anim3-movement-circle { border: 2px dotted darkturquoise; position: absolute; - transition: all 0.2s; + transition: left 0.2s, top 0.2s, height 0.2s, width 0.2s; z-index: 0; } .anim3-movement-circle:after { background: darkturquoise; @@ -84,6 +87,22 @@ body { position: absolute; top: 50%; width: 4px; } + +.anim3-vision-grid { + height: 100px; + position: absolute; + transform-origin: left top; + width: 100px; + z-index: 99; } + +.anim3-dot { + background: red; + height: 2px; + position: absolute; + width: 2px; + z-index: 99; } + .anim3-dot.removed { + background: yellow; } .anim3-particle { background: url(../res/seahorse.svg) no-repeat center top #aaa; background-size: 20px 20px; diff --git a/js/animation3.js b/js/animation3.js index 576c6b4..e173d6b 100644 --- a/js/animation3.js +++ b/js/animation3.js @@ -12,33 +12,41 @@ import AnimationBase from './animation0'; import DOM from './dom'; import Store from './store'; -const speed = 2; -const visionRadius = 50; +const speed = 6; const grid = {}; +const t45 = Math.PI / 4; +const t90 = Math.PI / 2; +const t270 = 3 * Math.PI / 2; +const t360 = Math.PI * 2; const movementCircle = document.createElement('div'); movementCircle.className = 'anim3-movement-circle'; -const visionCircle = document.createElement('div'); -// visionCircle.className = 'anim3-vision-circle'; +const visionGrid = document.createElement('div'); +visionGrid.className = 'anim3-vision-grid'; const particle = document.createElement('div'); particle.className = 'anim3-particle'; +const visionGridPoints = calculateVisionGridPoints(); + function move(store) { let { clockwise, interval, - particleX, - particleY, + circleX, + circleY, radius, theta } = store.get(); if (interval <= 0) { - interval = Math.round(Math.random() * 10) + 50; - radius = Math.round(Math.random() * 200) + 50 + const particleX = circleX + radius * Math.cos(theta); + const particleY = circleY - radius * Math.sin(theta); + + interval = Math.round(Math.random() * 10) + 50; + radius = Math.round(Math.random() * 200) + 50; // IF SEEING A WALL: tight 180 // if ( < visionRadius @@ -48,76 +56,163 @@ function move(store) { // state.interval = Math.PI * 20 / speed; // } - detectWall(store); - if (Math.random() < 0.5) { clockwise = !clockwise; theta = (theta > Math.PI ? theta - Math.PI : theta + Math.PI); + radius *= -1; } + + circleX = particleX - radius * Math.cos(theta); + circleY = particleY + radius * Math.sin(theta); } - interval -= 1; + // interval -= 1; - const prevTheta = theta; + // TODO store delta const delta = speed / radius; - theta += (clockwise ? -delta : delta); - - particleX -= radius * Math.cos(theta) - radius * Math.cos(prevTheta); - particleY -= radius * Math.sin(theta) - radius * Math.sin(prevTheta); + theta += (clockwise ? -delta : +delta); + theta %= t360; store.set({ clockwise, interval, - particleX, - particleY, + circleX, + circleY, radius, theta }); + + // detectWall(store); } function detectWall(store) { - const { particleX, particleY, radius, theta } = store.get(); + // const { circleX, circleY, radius, theta } = store.get(); + // const len = visionGridPoints.length; + // + // for (let i = 0; i < len; i++) { + // const { x, y } = visionGridPoints[i]; + // if (grid[x] && grid[x][y] && grid[x][y].type === 'wall') { + // console.warn("Wall detected", x, y); + // return true; + // } + // } + // + // return false; } -function paintMovementCircle(store) { +function transformMovementCircle(store) { // TODO UPDATE ONLY IF THETA CHANGED - MUST HAVE PREVSTATE - const { particleX, particleY, radius, theta } = store.get(); + const { circleX, circleY, radius, theta } = store.get(); - movementCircle.style.left = `${particleX - radius - radius * Math.cos(theta)}px`; - movementCircle.style.top = `${particleY - radius + radius * Math.sin(theta)}px`; + movementCircle.style.left = `${circleX - radius}px`; + movementCircle.style.top = `${circleY - radius}px`; movementCircle.style.height = `${2 * radius}px`; movementCircle.style.width = `${2 * radius}px`; movementCircle.style.borderRadius = `${radius}px`; } -function paintParticle(store) { - const { clockwise, particleX, particleY, theta } = store.get(); - const rad = (clockwise ? theta + Math.PI / 2 : theta - Math.PI / 2); +function transformParticle(store) { + const { clockwise, circleX, circleY, radius, theta } = store.get(); + const rad = clockwise ? Math.PI - theta : t360 - theta; - particle.style.left = `${particleX}px`; - particle.style.top = `${particleY}px`; + particle.style.left = `${circleX + radius * Math.cos(theta)}px`; + particle.style.top = `${circleY - radius * Math.sin(theta)}px`; particle.style.transform = `rotate(${rad}rad)`; } +function transformVisionGrid(store) { + const { clockwise, circleX, circleY, radius, theta } = store.get(); + const particleX = circleX + radius * Math.cos(theta); + const particleY = circleY - radius * Math.sin(theta); + + const r0 = Math.min(theta, theta - Math.PI); + const r1 = Math.max(theta, theta + Math.PI); + + visionGridPoints.forEach(({ x, y, alpha, div }) => { + if (alpha >= 0 && alpha <= r0) { + div.style.display = (clockwise ? 'none' : 'block'); + } else if (alpha >= theta && alpha <= r1) { + div.style.display = (clockwise ? 'none' : 'block'); + } else { + div.style.display = (clockwise ? 'block' : 'none'); + } + + div.style.left = `${x + particleX}px`; + div.style.top = `${-y + particleY}px`; + }); +} + +function calculateVisionGridPoints() { + const visionRadius = 50; + const gridSize = 5; + + const squareGrid = []; + for (let x = -visionRadius; x <= visionRadius; x += gridSize) { + for (let y = -visionRadius; y <= visionRadius; y += gridSize) { + let alpha = Math.atan(y / x); + + if (x === 0 && y === 0) { + alpha = 0; + } else if (x === 0 && y < 0) { + alpha = t270; + } else if (y === 0 && x < 0) { + alpha = Math.PI; + } else if (x === 0 && y > 0) { + alpha = t90; + } else if (x < 0 && y < 0) { + alpha = alpha + Math.PI; + } else if (x <= 0) { + alpha = Math.PI + alpha; + } else if (y < 0) { + alpha = 2 * Math.PI + alpha; + } + + squareGrid.push({ x, y, alpha }); + } + } + + const visionRadiusSquared = Math.pow(visionRadius, 2); + + return squareGrid.reduce((acc, point) => { + if ((Math.pow(point.x, 2) + Math.pow(point.y, 2)) > visionRadiusSquared) { + return acc; + } + + const div = document.createElement('div'); + div.className = 'anim3-dot'; + + acc.push(Object.assign(point, { div })); + return acc; + }, []); +} + function reset() { while (DOM.container.childNodes.length) { DOM.container.removeChild(DOM.container.firstChild); } const store = new Store({ - clockwise: false, + clockwise: true, interval: 10, - particleX: 300, - particleY: 150, + circleX: 300, + circleY: 300, radius: 150, - theta: Math.PI / 2 + theta: 0 }); move(store); + transformParticle(store); + transformMovementCircle(store); + transformVisionGrid(store); + DOM.container.appendChild(particle); DOM.container.appendChild(movementCircle); + visionGridPoints.forEach(point => { + DOM.container.appendChild(point.div); + }); + return store; }; @@ -135,14 +230,12 @@ function init() { } } - const a = Math.round(Math.random() * 180) + 20; - const b = Math.round(Math.random() * 180) + 20; - const stop$ = Rx.Observable.fromEvent(DOM.container, 'stop'); const fps$ = Rx.Observable.interval(1000 / 32) .map(_ => store) - // .take(100) - .takeUntil(stop$); console.error("CLICK TO STOP"); + .take(300) + // .take(0) + // .takeUntil(stop$); console.error("CLICK TO STOP"); const click$ = Rx.Observable.fromEvent(DOM.container, 'click'); click$.subscribe(() => { @@ -150,8 +243,9 @@ function init() { }); fps$.subscribe(move); - fps$.subscribe(paintParticle); - // fps$.subscribe(paintMovementCircle); + fps$.subscribe(transformParticle); + fps$.subscribe(transformMovementCircle); + fps$.subscribe(transformVisionGrid); }; const Animation3 = Object.assign({}, AnimationBase, { init, reset }); diff --git a/js/bundle.js b/js/bundle.js index 73f99f1..be7f603 100644 --- a/js/bundle.js +++ b/js/bundle.js @@ -6379,29 +6379,37 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de // number of frames have emitted. A smoothstep cubic curve was considered, but maintaining consistent entry and exit angles will affect // performance for large particle counts. -var speed = 2; -var visionRadius = 50; +var speed = 6; var grid = {}; +var t45 = Math.PI / 4; +var t90 = Math.PI / 2; +var t270 = 3 * Math.PI / 2; +var t360 = Math.PI * 2; var movementCircle = document.createElement('div'); movementCircle.className = 'anim3-movement-circle'; -var visionCircle = document.createElement('div'); -// visionCircle.className = 'anim3-vision-circle'; +var visionGrid = document.createElement('div'); +visionGrid.className = 'anim3-vision-grid'; var particle = document.createElement('div'); particle.className = 'anim3-particle'; +var visionGridPoints = calculateVisionGridPoints(); + function move(store) { var _store$get = store.get(), clockwise = _store$get.clockwise, interval = _store$get.interval, - particleX = _store$get.particleX, - particleY = _store$get.particleY, + circleX = _store$get.circleX, + circleY = _store$get.circleY, radius = _store$get.radius, theta = _store$get.theta; if (interval <= 0) { + var particleX = circleX + radius * Math.cos(theta); + var particleY = circleY - radius * Math.sin(theta); + interval = Math.round(Math.random() * 10) + 50; radius = Math.round(Math.random() * 200) + 50; @@ -6413,68 +6421,155 @@ function move(store) { // state.interval = Math.PI * 20 / speed; // } - detectWall(store); - if (Math.random() < 0.5) { clockwise = !clockwise; theta = theta > Math.PI ? theta - Math.PI : theta + Math.PI; + radius *= -1; } + + circleX = particleX - radius * Math.cos(theta); + circleY = particleY + radius * Math.sin(theta); } - interval -= 1; + // interval -= 1; - var prevTheta = theta; + // TODO store delta var delta = speed / radius; - theta += clockwise ? -delta : delta; - - particleX -= radius * Math.cos(theta) - radius * Math.cos(prevTheta); - particleY -= radius * Math.sin(theta) - radius * Math.sin(prevTheta); + theta += clockwise ? -delta : +delta; + theta %= t360; store.set({ clockwise: clockwise, interval: interval, - particleX: particleX, - particleY: particleY, + circleX: circleX, + circleY: circleY, radius: radius, theta: theta }); + + // detectWall(store); } function detectWall(store) { + // const { circleX, circleY, radius, theta } = store.get(); + // const len = visionGridPoints.length; + // + // for (let i = 0; i < len; i++) { + // const { x, y } = visionGridPoints[i]; + // if (grid[x] && grid[x][y] && grid[x][y].type === 'wall') { + // console.warn("Wall detected", x, y); + // return true; + // } + // } + // + // return false; +} + +function transformMovementCircle(store) { + // TODO UPDATE ONLY IF THETA CHANGED - MUST HAVE PREVSTATE var _store$get2 = store.get(), - particleX = _store$get2.particleX, - particleY = _store$get2.particleY, + circleX = _store$get2.circleX, + circleY = _store$get2.circleY, radius = _store$get2.radius, theta = _store$get2.theta; + + movementCircle.style.left = circleX - radius + 'px'; + movementCircle.style.top = circleY - radius + 'px'; + movementCircle.style.height = 2 * radius + 'px'; + movementCircle.style.width = 2 * radius + 'px'; + movementCircle.style.borderRadius = radius + 'px'; } -function paintMovementCircle(store) { - // TODO UPDATE ONLY IF THETA CHANGED - MUST HAVE PREVSTATE +function transformParticle(store) { var _store$get3 = store.get(), - particleX = _store$get3.particleX, - particleY = _store$get3.particleY, + clockwise = _store$get3.clockwise, + circleX = _store$get3.circleX, + circleY = _store$get3.circleY, radius = _store$get3.radius, theta = _store$get3.theta; - movementCircle.style.left = particleX - radius - radius * Math.cos(theta) + 'px'; - movementCircle.style.top = particleY - radius + radius * Math.sin(theta) + 'px'; - movementCircle.style.height = 2 * radius + 'px'; - movementCircle.style.width = 2 * radius + 'px'; - movementCircle.style.borderRadius = radius + 'px'; + var rad = clockwise ? Math.PI - theta : t360 - theta; + + particle.style.left = circleX + radius * Math.cos(theta) + 'px'; + particle.style.top = circleY - radius * Math.sin(theta) + 'px'; + particle.style.transform = 'rotate(' + rad + 'rad)'; } -function paintParticle(store) { +function transformVisionGrid(store) { var _store$get4 = store.get(), clockwise = _store$get4.clockwise, - particleX = _store$get4.particleX, - particleY = _store$get4.particleY, + circleX = _store$get4.circleX, + circleY = _store$get4.circleY, + radius = _store$get4.radius, theta = _store$get4.theta; - var rad = clockwise ? theta + Math.PI / 2 : theta - Math.PI / 2; + var particleX = circleX + radius * Math.cos(theta); + var particleY = circleY - radius * Math.sin(theta); - particle.style.left = particleX + 'px'; - particle.style.top = particleY + 'px'; - particle.style.transform = 'rotate(' + rad + 'rad)'; + var r0 = Math.min(theta, theta - Math.PI); + var r1 = Math.max(theta, theta + Math.PI); + + visionGridPoints.forEach(function (_ref) { + var x = _ref.x, + y = _ref.y, + alpha = _ref.alpha, + div = _ref.div; + + if (alpha >= 0 && alpha <= r0) { + div.style.display = clockwise ? 'none' : 'block'; + } else if (alpha >= theta && alpha <= r1) { + div.style.display = clockwise ? 'none' : 'block'; + } else { + div.style.display = clockwise ? 'block' : 'none'; + } + + div.style.left = x + particleX + 'px'; + div.style.top = -y + particleY + 'px'; + }); +} + +function calculateVisionGridPoints() { + var visionRadius = 50; + var gridSize = 5; + + var squareGrid = []; + for (var x = -visionRadius; x <= visionRadius; x += gridSize) { + for (var y = -visionRadius; y <= visionRadius; y += gridSize) { + var alpha = Math.atan(y / x); + + if (x === 0 && y === 0) { + alpha = 0; + } else if (x === 0 && y < 0) { + alpha = t270; + } else if (y === 0 && x < 0) { + alpha = Math.PI; + } else if (x === 0 && y > 0) { + alpha = t90; + } else if (x < 0 && y < 0) { + alpha = alpha + Math.PI; + } else if (x <= 0) { + alpha = Math.PI + alpha; + } else if (y < 0) { + alpha = 2 * Math.PI + alpha; + } + + squareGrid.push({ x: x, y: y, alpha: alpha }); + } + } + + var visionRadiusSquared = Math.pow(visionRadius, 2); + + return squareGrid.reduce(function (acc, point) { + if (Math.pow(point.x, 2) + Math.pow(point.y, 2) > visionRadiusSquared) { + return acc; + } + + var div = document.createElement('div'); + div.className = 'anim3-dot'; + + acc.push(Object.assign(point, { div: div })); + return acc; + }, []); } function reset() { @@ -6483,19 +6578,27 @@ function reset() { } var store = new _store2.default({ - clockwise: false, + clockwise: true, interval: 10, - particleX: 300, - particleY: 150, + circleX: 300, + circleY: 300, radius: 150, - theta: Math.PI / 2 + theta: 0 }); move(store); + transformParticle(store); + transformMovementCircle(store); + transformVisionGrid(store); + _dom2.default.container.appendChild(particle); _dom2.default.container.appendChild(movementCircle); + visionGridPoints.forEach(function (point) { + _dom2.default.container.appendChild(point.div); + }); + return store; }; @@ -6513,15 +6616,12 @@ function init() { } } - var a = Math.round(Math.random() * 180) + 20; - var b = Math.round(Math.random() * 180) + 20; - var stop$ = _rxjs2.default.Observable.fromEvent(_dom2.default.container, 'stop'); var fps$ = _rxjs2.default.Observable.interval(1000 / 32).map(function (_) { return store; - }) - // .take(100) - .takeUntil(stop$);console.error("CLICK TO STOP"); + }).take(300); + // .take(0) + // .takeUntil(stop$); console.error("CLICK TO STOP"); var click$ = _rxjs2.default.Observable.fromEvent(_dom2.default.container, 'click'); click$.subscribe(function () { @@ -6529,8 +6629,9 @@ function init() { }); fps$.subscribe(move); - fps$.subscribe(paintParticle); - // fps$.subscribe(paintMovementCircle); + fps$.subscribe(transformParticle); + fps$.subscribe(transformMovementCircle); + fps$.subscribe(transformVisionGrid); }; var Animation3 = Object.assign({}, _animation2.default, { init: init, reset: reset });