From fc940563bfddb36378178c33fc7c26fc0f007184 Mon Sep 17 00:00:00 2001 From: Siddharth Roy Date: Sun, 17 Apr 2022 12:50:21 +0530 Subject: [PATCH 1/3] Fixed all issues on mobile --- src/main.js | 55 +++++++++++++++++++++++++++++++-------------------- src/style.css | 10 ++++++++++ 2 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/main.js b/src/main.js index a34555e..1f85ca4 100644 --- a/src/main.js +++ b/src/main.js @@ -13,6 +13,7 @@ let panMode = true; let nextColor = getRandomNiceColor(); let nextMass = 10; let bodyToFollow = null; +let isTouchDevice = false; let U = new Universe(100.0); U.addBody(new Body(1000.0, nextColor), 0, 0); @@ -46,6 +47,7 @@ let SCROLL_SENSITIVITY = 0.0005 let mousePos = { x: 0, y: 0 }; // In 2d world let mousePosInViewport = { x: 0, y: 0 }; let launchStart = mousePos; +let launching = false; let infoBar; // The bottom left section @@ -78,18 +80,20 @@ function draw() { updateMousePosition(); - // Draw the preview of body at mouse location - ctx.fillStyle = nextColor - ctx.beginPath(); - ctx.arc(mousePos.x, mousePos.y, mass2radius(nextMass), 0, 2 * Math.PI); - ctx.fill(); - ctx.strokeStyle = nextColor; - - // Draw line of drag when placing - ctx.beginPath(); - ctx.moveTo(launchStart.x, launchStart.y); - ctx.lineTo(mousePos.x, mousePos.y); - ctx.stroke(); + if (launching) { + // Draw the preview of body at mouse location + ctx.fillStyle = nextColor + ctx.beginPath(); + ctx.arc(mousePos.x, mousePos.y, mass2radius(nextMass), 0, 2 * Math.PI); + ctx.fill(); + ctx.strokeStyle = nextColor; + + // Draw line of drag when placing + ctx.beginPath(); + ctx.moveTo(launchStart.x, launchStart.y); + ctx.lineTo(mousePos.x, mousePos.y); + ctx.stroke(); + } requestAnimationFrame(draw); } @@ -118,19 +122,27 @@ function onPointerDown(e) { isDragging = true canvas.style.cursor = 'move'; } else { - launchStart = { ...mousePos }; + updateMousePositionInViewport(e); + updateMousePosition(); + + launchStart = { + x: ((getEventLocation(e).x - window.innerWidth/2)/cameraZoom) - (cameraOffset.x - window.innerWidth/2), + y: ((getEventLocation(e).y - window.innerHeight/2)/cameraZoom) - (cameraOffset.y - window.innerHeight/2), + }; + launching = true; } } function onPointerUp(e) { + console.log(e) isDragging = false initialPinchDistance = null lastZoom = cameraZoom canvas.style.cursor = 'default'; if ((!panMode && e.button !== 2) || (panMode && e.button === 2)) { - let x = (getEventLocation(e).x/cameraZoom - cameraOffset.x); - let y = (getEventLocation(e).y/cameraZoom - cameraOffset.y); + let x = (mousePosInViewport.x/cameraZoom - cameraOffset.x); + let y = (mousePosInViewport.y/cameraZoom - cameraOffset.y); let b = new Body(nextMass, nextColor); let v = new Vector2(x - dragStart.x, y - dragStart.y); v.x = -v.x @@ -142,6 +154,7 @@ function onPointerUp(e) { nextColor = getRandomNiceColor(); launchStart = mousePos; updateInfoBar(); + launching = false; } } @@ -167,7 +180,7 @@ function onPointerMove(e) { } function handleTouch(e, singleTouchHandler) { - if (e.touches.length == 1) { + if (e.touches.length <= 1) { e.button = 0; singleTouchHandler(e) } else if (e.type == "touchmove" && e.touches.length == 2) { @@ -265,12 +278,12 @@ window.addEventListener('load', () => { clearBtn.addEventListener('click', () => { U.clear(); updateInfoBar(); bodyToFollow = null }); // Canvas zoop and pan - canvas.addEventListener('mousedown', onPointerDown) - canvas.addEventListener('touchstart', (e) => handleTouch(e, onPointerDown)) - canvas.addEventListener('mouseup', onPointerUp) - canvas.addEventListener('touchend', (e) => handleTouch(e, onPointerUp)) + canvas.addEventListener('mousedown', (e) => { if (!isTouchDevice) { onPointerDown(e) }}) + canvas.addEventListener('touchstart', (e) => { handleTouch(e, onPointerDown); console.log('touch start'); isTouchDevice = true }) + canvas.addEventListener('mouseup', (e) => { if (!isTouchDevice) { onPointerUp(e) }}) + canvas.addEventListener('touchend', (e) => { handleTouch(e, onPointerUp); console.log('touch end') }) canvas.addEventListener('mousemove', onPointerMove) - canvas.addEventListener('touchmove', (e) => handleTouch(e, onPointerMove)) + canvas.addEventListener('touchmove', (e) => { handleTouch(e, onPointerMove); console.log('touch move') }) canvas.addEventListener( 'wheel', (e) => adjustZoom(e.deltaY*SCROLL_SENSITIVITY)) // Pause Simulation when focuse change diff --git a/src/style.css b/src/style.css index 9480349..d67a2f2 100644 --- a/src/style.css +++ b/src/style.css @@ -109,3 +109,13 @@ input[type="range"]::-moz-range-thumb { font-size: 12px; font-family: monospace; } + +@media screen and (max-width: 500px) { + .info-bar { + bottom: 20px; + } + .links { + left: 5px; + text-align: left; + } +} From 29989e9fe5ec34c03581825be1c86defb0d2378b Mon Sep 17 00:00:00 2001 From: Siddharth Roy Date: Sun, 17 Apr 2022 12:53:30 +0530 Subject: [PATCH 2/3] Removed a uncessary feature from todo because it's too boring --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index cc0faa1..070bf5e 100644 --- a/README.md +++ b/README.md @@ -37,4 +37,3 @@ It's a web-based simulation so no need to install anything, just visit - Add saving and loading feature - Add interesting simulations to load -- Add merge bodies on collition option \ No newline at end of file From eb7d45c35be0feb367208d48860955efbb3ef814 Mon Sep 17 00:00:00 2001 From: Siddharth Roy Date: Mon, 18 Apr 2022 11:06:37 +0530 Subject: [PATCH 3/3] Ready to release --- index.html | 9 ++++++ package.json | 2 +- public/service-worker.js | 2 +- src/Body.js | 4 +-- src/Vector2.js | 7 ++++ src/main.js | 59 +++++++++++++++++++++++++++------- src/patterns.js | 69 ++++++++++++++++++++++++++++++++++++++++ src/style.css | 45 +++++++++++++++++++++++--- 8 files changed, 177 insertions(+), 20 deletions(-) create mode 100644 src/patterns.js diff --git a/index.html b/index.html index 6cfe6ec..cdeb965 100644 --- a/index.html +++ b/index.html @@ -20,6 +20,15 @@ +
+ +
+ + + + +
+
diff --git a/package.json b/package.json index 451f9d0..aa8ffeb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gravity-sandbox", - "version": "1.0.0", + "version": "1.1.0", "description": "A simple, chaotic gravity simulator", "scripts": { "dev": "vite", diff --git a/public/service-worker.js b/public/service-worker.js index 2b3e925..c11120c 100644 --- a/public/service-worker.js +++ b/public/service-worker.js @@ -1,4 +1,4 @@ -const version = '1.0.0'; +const version = '1.1.0'; const contentToCache = [ '/', diff --git a/src/Body.js b/src/Body.js index f3f86cc..182ca31 100644 --- a/src/Body.js +++ b/src/Body.js @@ -1,6 +1,6 @@ import Vector2 from "./Vector2"; +import { getRandomNiceColor } from './niceColors'; -const DEFAULT_COLOR = "#4c4cff"; const TAIL_SIZE = 50; export function mass2radius(mass) { @@ -11,7 +11,7 @@ export default class Body { constructor(mass, color) { this.mass = mass; this.size = mass2radius(mass); - this.color = color != null ? color : DEFAULT_COLOR; + this.color = color != null ? color : getRandomNiceColor(); this.invMass = 1.0 / mass; this.position = new Vector2(0.0, 0.0); this.velocity = new Vector2(0.0, 0.0); diff --git a/src/Vector2.js b/src/Vector2.js index 4fc1688..c23d135 100644 --- a/src/Vector2.js +++ b/src/Vector2.js @@ -56,6 +56,13 @@ export default class Vector2 { this.y /= length; } + rotate(angle) { + const resultX = this.x * Math.cos(angle) - this.y * Math.sin(angle) + const resultY = this.x * Math.sin(angle) + this.y * Math.cos(angle) + this.x = resultX; + this.y = resultY; + } + } diff --git a/src/main.js b/src/main.js index 1f85ca4..a030ee0 100644 --- a/src/main.js +++ b/src/main.js @@ -4,6 +4,9 @@ import Body, { mass2radius } from './Body'; import Universe from './Universe'; import { getRandomNiceColor } from './niceColors'; import Vector2 from './Vector2'; +import { + pattern1, pattern2, pattern3, pattern4 +} from './patterns'; let autoPause = false; let simulationRunning = false; @@ -16,13 +19,7 @@ let bodyToFollow = null; let isTouchDevice = false; let U = new Universe(100.0); -U.addBody(new Body(1000.0, nextColor), 0, 0); -let moon1 = new Body(10); -moon1.velocity.set(0, 20); -U.addBody(moon1, 200, 0); -let moon2 = new Body(10); -moon2.velocity.set(0, -20); -U.addBody(moon2, -200, 0); +pattern1(U); function simulation(context) { let nowTime = (new Date()).getTime(); @@ -53,7 +50,7 @@ let infoBar; // The bottom left section function updateInfoBar() { infoBar.innerText = ` - Looking at : ${(cameraOffset.x+'').slice(0, 6)} ${(cameraOffset.y+'').slice(0, 6)} + Looking at : ${(cameraOffset.x+'').slice(0, 7)} ${(cameraOffset.y+'').slice(0, 7)} Zoom : ${(cameraZoom+'').slice(0, 4)} Bodies: ${U.bodies.length} Speed : ${simulationSpeed} (Less is faster) @@ -134,7 +131,6 @@ function onPointerDown(e) { } function onPointerUp(e) { - console.log(e) isDragging = false initialPinchDistance = null lastZoom = cameraZoom @@ -227,9 +223,48 @@ window.addEventListener('load', () => { let playPauseBtn = document.getElementById('play-pause-btn'); let panAddBtn = document.getElementById('pan-add-btn'); let clearBtn = document.getElementById('clear'); + let massSlider = document.getElementById('mass-slider'); let speedSlider = document.getElementById('speed-slider'); + + let setOrbit = document.getElementById('set-orbit'); + let setGrid = document.getElementById('set-grid'); + let setInfinity = document.getElementById('set-infinity'); + let setCircle = document.getElementById('set-circle'); + + setOrbit.addEventListener('click', () => { + const [zoom, speed] = pattern1(U); + cameraZoom = zoom; + simulationSpeed = speed; + cameraOffset = { x: window.innerWidth/2, y: window.innerHeight/2 } + updateInfoBar(); + }); + + setGrid.addEventListener('click', () => { + const [zoom, speed] = pattern2(U); + cameraZoom = zoom; + simulationSpeed = speed; + updateInfoBar(); + cameraOffset = { x: window.innerWidth/2, y: window.innerHeight/2 } + }); + + setInfinity.addEventListener('click', () => { + const [zoom, speed] = pattern3(U); + cameraZoom = zoom; + simulationSpeed = speed; + updateInfoBar(); + cameraOffset = { x: window.innerWidth/2, y: window.innerHeight/2 } + }); + + setCircle.addEventListener('click', () => { + const [zoom, speed] = pattern4(U); + cameraZoom = zoom; + simulationSpeed = speed; + updateInfoBar(); + cameraOffset = { x: window.innerWidth/2, y: window.innerHeight/2 } + }); + infoBar = document.getElementById('info-bar'); canvas = document.getElementById("canvas"); ctx = canvas.getContext('2d'); @@ -279,11 +314,11 @@ window.addEventListener('load', () => { // Canvas zoop and pan canvas.addEventListener('mousedown', (e) => { if (!isTouchDevice) { onPointerDown(e) }}) - canvas.addEventListener('touchstart', (e) => { handleTouch(e, onPointerDown); console.log('touch start'); isTouchDevice = true }) + canvas.addEventListener('touchstart', (e) => { handleTouch(e, onPointerDown); isTouchDevice = true }) canvas.addEventListener('mouseup', (e) => { if (!isTouchDevice) { onPointerUp(e) }}) - canvas.addEventListener('touchend', (e) => { handleTouch(e, onPointerUp); console.log('touch end') }) + canvas.addEventListener('touchend', (e) => { handleTouch(e, onPointerUp) }) canvas.addEventListener('mousemove', onPointerMove) - canvas.addEventListener('touchmove', (e) => { handleTouch(e, onPointerMove); console.log('touch move') }) + canvas.addEventListener('touchmove', (e) => { handleTouch(e, onPointerMove) }) canvas.addEventListener( 'wheel', (e) => adjustZoom(e.deltaY*SCROLL_SENSITIVITY)) // Pause Simulation when focuse change diff --git a/src/patterns.js b/src/patterns.js new file mode 100644 index 0000000..f0a85ca --- /dev/null +++ b/src/patterns.js @@ -0,0 +1,69 @@ +import Body from './Body'; +import Vector2 from './Vector2'; + +// A planet with two moons +export function pattern1(U) { + U.clear(); + + U.addBody(new Body(1000), 0, 0); + let moon1 = new Body(10); + moon1.velocity.set(0, 20); + U.addBody(moon1, 200, 0); + let moon2 = new Body(10); + moon2.velocity.set(0, -20); + U.addBody(moon2, -200, 0); + + return [1, 44]; +} + +// A grid of planets +export function pattern2(U) { + U.clear(); + + U.addBody(new Body(1000), 0, 400); + U.addBody(new Body(1000), 0, -400); + U.addBody(new Body(1000), 400, 0); + U.addBody(new Body(1000), -400, 0); + + for (let i = -2.5; i <= 2.5; i++) { + for (let j = -2.5; j <= 2.5; j++) { + U.addBody(new Body(10), i * 60, j * 60); + } + } + + return [0.78, 199]; +} + +// INFINITY +export function pattern3(U) { + U.clear(); + + for (let i = 0; i < 360; i += 10) { + const v = new Vector2(-200, 0); + v.x = v.x * Math.cos(i * (Math.PI/180)) - v.y * Math.sin(i * (Math.PI/180)) + v.y = v.x * Math.sin(i * (Math.PI/180)) + v.y * Math.cos(i * (Math.PI/180)) + U.addBody(new Body(10), v.x, v.y); + } + + return [1.58, 69]; +} + +// Circle +export function pattern4(U) { + U.clear(); + + U.addBody(new Body(50), 0, 0); + + for (let i = 0; i < 360; i += 10) { + const p = new Vector2(-200, 0); + const v = new Vector2(0, 5); + p.rotate(i * Math.PI/180); + v.rotate(i * Math.PI/180); + const b = new Body(10); + b.velocity.set(v.x, v.y); + U.addBody(b, p.x, p.y); + } + + return [1.47, 44]; +} + diff --git a/src/style.css b/src/style.css index d67a2f2..b466f39 100644 --- a/src/style.css +++ b/src/style.css @@ -10,7 +10,7 @@ html, body, a, .btn { - color: #c5c5c5; + color: #fff; } #canvas { @@ -23,15 +23,20 @@ a, background: unset; border: none; cursor: pointer; + border: 1px solid #383838; + width: 50px; + height: 50px; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 5px; } .tool-bar { position: fixed; - top: 0; + top: 11px; left: 0; height: 65px; - display: flex; - align-items: center; padding: 0 15px; } @@ -39,6 +44,38 @@ a, height: 40px; } +#box-btn { + position: relative; +} + +#box { + display: none; + position: absolute; + border: 1px solid #383838; + border-bottom: none; + width: 100px; + right: 0px; + top: -1px; + transform: translate(100%); +} + +#box-btn:hover #box { + display: block; +} + +#box button { + background-color: unset; + color: #fff; + border: none; + width: 100%; + text-align: start; + font-size: 12px; + padding: 5px; + padding-left: 10px; + font-family: monospace; + border-bottom: 1px solid #383838; +} + .btn svg { width: 50px; }