diff --git a/images/bezier.png b/images/bezier.png new file mode 100644 index 0000000000000000000000000000000000000000..b9d4fb929f4ec5ed66b80d64c214c9019d406637 Binary files /dev/null and b/images/bezier.png differ diff --git a/images/quaternions.png b/images/quaternions.png new file mode 100644 index 0000000000000000000000000000000000000000..b6b268b07be162dc7af033a059f86b176c963a55 Binary files /dev/null and b/images/quaternions.png differ diff --git a/images/simulation.png b/images/simulation.png new file mode 100644 index 0000000000000000000000000000000000000000..a423e85bac3e658a403a30a8d1e9df3b652bfaed Binary files /dev/null and b/images/simulation.png differ diff --git a/index.html b/index.html index 632949f5f44cf5304cf74bf337f209aaed5ef1a4..0e617d63b332d759d48dd06a7c73a1fabd1f6168 100644 --- a/index.html +++ b/index.html @@ -15,6 +15,5 @@ <a href="pages/quaternion.html">Quaternions</a> <a href="pages/simulation.html">Cloth Simulation</a> </nav> - </body> </html> diff --git a/src/02-quaternion/DemoQuaternion.ts b/src/02-quaternion/DemoQuaternion.ts index 6751a7af8a54a9b8e3d40da8dfd8f0c49cc07089..16bf30f1e80a4a165301d316928df6f01bc01da4 100644 --- a/src/02-quaternion/DemoQuaternion.ts +++ b/src/02-quaternion/DemoQuaternion.ts @@ -16,9 +16,17 @@ class QuaternionDemo extends PipelineData { light.position.set(1, 1, 1); this.scene.add(light); - const pointLight = new THREE.PointLight(0x6e6e8e, 1); - pointLight.position.set(-1, -1, -1); - this.scene.add(pointLight); + const backLight = new THREE.PointLight(0x6e6e8e, 1); + backLight.position.set(-1, -1, -1); + this.scene.add(backLight); + + const lightA = new THREE.PointLight(0x8eaa8e, 1); + lightA.position.set(2, 2, 0); + this.scene.add(lightA); + + const lightC = new THREE.PointLight(0x8eaa7f, 1); + lightC.position.set(-2, 0, -2); + this.scene.add(lightC); const axes = new Axes(5); this.addRenderable(axes); diff --git a/src/02-quaternion/RotatorHelper.ts b/src/02-quaternion/RotatorHelper.ts index 74e1a4f48930b8a35540d2952c3f057d119f79aa..1dfbc94342e6aeb6ed17fca860518af22913cbd7 100644 --- a/src/02-quaternion/RotatorHelper.ts +++ b/src/02-quaternion/RotatorHelper.ts @@ -26,7 +26,7 @@ class RotationHelper extends Shape implements PipelineObserver, PipelineDraggabl // UNIT SPHERE // The projection of ijk onto the sphere. The fourth dimension is the color. const geometrySphere = new THREE.SphereBufferGeometry(1, 32, 32); - const matSphere = new THREE.MeshBasicMaterial({ color: primeColor, opacity: 0.9, transparent: true }); + const matSphere = new THREE.MeshBasicMaterial({ color: primeColor, opacity: .8, transparent: true }); this._unitSphere = new THREE.Mesh(geometrySphere, matSphere); // POINTS AND LINES @@ -51,6 +51,8 @@ class RotationHelper extends Shape implements PipelineObserver, PipelineDraggabl this._object3d.add(this._qPoints[i].object()); for (let i = 0; i < 5; i++) this._object3d.add(this._qLines[i].object()); + + this._object3d.position.x = 2.5; } update(_deltaTime: number): void { diff --git a/src/03-simulation/DemoSimulation.ts b/src/03-simulation/DemoSimulation.ts index 1f968a8053a15ac536feb88e0996734a24b9f977..94def8985f907c323a5fa87d86078135b8671c37 100644 --- a/src/03-simulation/DemoSimulation.ts +++ b/src/03-simulation/DemoSimulation.ts @@ -1,18 +1,17 @@ -import * as THREE from 'three'; import { PipelineData } from "../core/Pipeline"; -import { Particle } from "./cgPhysix/particle"; -import { SimulationSettings } from './SimulationSettings'; +import { Simulation } from "./Simulation"; +import { SimulationData } from './SimulationData'; class SimulationDemo extends PipelineData { public constructor() { super(); } data(): void { - const sim = new SimulationSettings(); - this.addGUIElement(sim); - - SimulationSettings.generateTextile(); - this.scene.add(SimulationSettings.container); + const data = new SimulationData(); + this.addGUIElement(data); + this.addRenderable(data); + const simulation = new Simulation(); + this.addObserver(simulation); } } diff --git a/src/03-simulation/Simulation.ts b/src/03-simulation/Simulation.ts new file mode 100644 index 0000000000000000000000000000000000000000..4fafe54d74051d2fa01768bf31ca45eab62176af --- /dev/null +++ b/src/03-simulation/Simulation.ts @@ -0,0 +1,72 @@ +import * as THREE from 'three'; +import { PipelineObserver } from "../core/Pipeline"; +import { Particle } from './physics/particle'; +import { SimulationData } from "./SimulationData"; + +export class Simulation implements PipelineObserver { + + public static readonly G = new THREE.Vector3(0, -9.807, 0); + public static current: Particle; + + constructor() { + SimulationData.generateTextile(); + } + + + update(_deltaTime: number): void { + const h = _deltaTime / 100; + for (let t = 0; t < _deltaTime; t += h) { + SimulationData.pList.forEach(pSub => { + pSub.forEach(p => { + if (p.usePhysics) { + Simulation.current = p; + let x = [p.position.x, p.position.y, p.position.z, + p.velocity.x, p.velocity.y, p.velocity.z]; + x = SimulationData.INTEGRATORS[SimulationData.integratorType].step(x, h, Simulation.derivation); + p.setState(x[0], x[1], x[2], x[3], x[4], x[5]); + } + }); + }) + } + } + + + public static derivation(x: Array<number>): Array<number> { + + // 1. create new array to store derivatives + let xDot = new Array<number>(x.length); + + // 2. derivative of position => velocity + xDot[0] = x[3]; + xDot[1] = x[4]; + xDot[2] = x[5]; + + // 3. derivative of velocity => acceleration + + // 3.1 accumulate forces + let totalForce = new THREE.Vector3(0, 0, 0); + totalForce.add(SimulationData.externalForce); + + // 3.2 accumulate spring forces + Simulation.current.references.forEach(spring => { + const force = spring.force(Simulation.current.position); + totalForce.add(force); + }); + + // 3.3 damping force + const dampingForce = Simulation.current.velocity.clone().multiplyScalar(-SimulationData.damping); + totalForce.add(dampingForce); + + // 3.3 acceleration = force / mass + let totalAcceleration = totalForce.divideScalar(Simulation.current.mass); + totalAcceleration.add(Simulation.G); + + // 4. derivative of acceleration => velocity + xDot[3] = totalAcceleration.x; + xDot[4] = totalAcceleration.y; + xDot[5] = totalAcceleration.z; + + return xDot; + } + +} \ No newline at end of file diff --git a/src/03-simulation/SimulationData.ts b/src/03-simulation/SimulationData.ts new file mode 100644 index 0000000000000000000000000000000000000000..f297342ce8494fd3e7207022e6fa29d6bd3805a8 --- /dev/null +++ b/src/03-simulation/SimulationData.ts @@ -0,0 +1,376 @@ +import * as THREE from 'three'; +import { GUI } from 'dat.gui'; +import { picked, PipelineGUI, PipelineRenderable } from '../core/Pipeline'; +import { BendingSpring, ShearSpring, Spring, SpringConstants, StructuralSpring, } from './physics/Springs'; +import { Particle } from './physics/particle'; +import { EulerIntegrator, Integrator, MidpointIntegrator, RungeKuttaIntegrator } from './physics/Integrators'; + +class SimulationData implements PipelineGUI, PipelineRenderable { + + public static container: THREE.Group = new THREE.Group(); + + // cloth settings + public static readonly MAX_TOTAL_MASS = 10; + public static readonly MIN_TOTAL_MASS = 0.1; + + public static readonly MAX_WIDTH = 5; + public static readonly MIN_WIDTH = 0.01; + + public static readonly MAX_HEIGHT = 2; + public static readonly MIN_HEIGHT = 0.01; + + public static readonly MAX_RESOLUTION = 20; + public static readonly MIN_RESOLUTION = 2; + + public static totalMass: number = 1; + public static width: number = .2; + public static height: number = .2; + public static resolution: number = 5; + public static damping: number = .1; + public static externalForce: THREE.Vector3 = new THREE.Vector3(0, 0, 0); + + // particls + public static pList = new Array<Array<Particle>>(); + public static pColorDefault: number = 0xffffff; + public static pColorNoPhysix: number = 0xff00ff; + + // springs + public static readonly MAX_CONSTANT = 10; + public static readonly MIN_CONSTANT = 0.1; + + public static sList = new Array<Spring>(); + public static sConstants: SpringConstants = { structural: 10, shear: 2, bend: 4 }; + public static sColorStructural: number = 0xff0000; + public static sColorShear: number = 0x00ff00; + public static sColorBend: number = 0x0000ff; + + // integrator + public static readonly STEP_SIZE = {Fixed:0, Adaptive:1}; + public static readonly INTEGRATORS: Integrator[] = [ + new EulerIntegrator(), + new MidpointIntegrator(), + new RungeKuttaIntegrator + ] + + public static integratorType: number = 2; + public static stepSize: number = 0; + + public static setEuler = () => { SimulationData.integratorType = 0; } + public static setMidpoint = () => { SimulationData.integratorType = 1; } + public static setRungeKutta = () => { SimulationData.integratorType = 2; } + + + public static generateTextile(): void { + // 1. reset pList + SimulationData.pList = new Array<Array<Particle>>(); + + // 2. generate particles + for (let i = 0; i < SimulationData.resolution; i++) { + SimulationData.pList[i] = new Array<Particle>(); + for (let j = 0; j < SimulationData.resolution; j++) { + + const p = new Particle({ + position: new THREE.Vector3( + SimulationData.width * (i / SimulationData.resolution) - SimulationData.width / 2, + SimulationData.height * (j / SimulationData.resolution) - SimulationData.height / 2, + 0), + velocity: new THREE.Vector3(0, 0, 0), + mass: SimulationData.totalMass / (SimulationData.resolution * SimulationData.resolution), + usePhysics: true, + references: [], + radius: 0, + color: SimulationData.pColorDefault, + wireframe: true + }); + SimulationData.pList[i][j] = p; + } + } + + for (let i = 0; i < SimulationData.resolution; i++) { + SimulationData.pList[i][SimulationData.resolution - 1].usePhysics = false; + } + + // 3. set particle mass and color + SimulationData.changedParticleSize(); + SimulationData.changedParticleColor(); + + // 4. add particles to container + const pLinList = new Array<THREE.Mesh>(); + SimulationData.pList.forEach(pSub => { + pSub.forEach(p => { + pLinList.push(p.mesh); + }); + }); + + // 5. reset sList + SimulationData.sList = new Array<Spring>(); + + // 6. generate springs + for (let i = 0; i < SimulationData.resolution; i++) { + for (let j = 0; j < SimulationData.resolution; j++) { + + // structural springs (horizontal) + if (i < SimulationData.resolution - 1) { + const pA = SimulationData.pList[i][j]; + const pB = SimulationData.pList[i + 1][j]; + + const s = new StructuralSpring({ + positionA: pA.position, + positionB: pB.position, + constants: SimulationData.sConstants, + color: SimulationData.sColorStructural, + }); + SimulationData.sList.push(s); + s.line.renderOrder = 2; + + pA.references.push(s); + pB.references.push(s); + } + + // structural springs (vertical) + if (j < SimulationData.resolution - 1) { + const pA = SimulationData.pList[i][j]; + const pB = SimulationData.pList[i][j + 1]; + + const s = new StructuralSpring({ + positionA: pA.position, + positionB: pB.position, + constants: SimulationData.sConstants, + color: SimulationData.sColorStructural, + }); + SimulationData.sList.push(s); + s.line.renderOrder = 2; + + pA.references.push(s); + pB.references.push(s); + } + + + // shear springs (bottom-left to top-right) + if (i < SimulationData.resolution - 1 && j < SimulationData.resolution - 1) { + const pA = SimulationData.pList[i][j]; + const pB = SimulationData.pList[i + 1][j + 1]; + + const s = new ShearSpring({ + positionA: pA.position, + positionB: pB.position, + constants: SimulationData.sConstants, + color: SimulationData.sColorShear, + }); + SimulationData.sList.push(s); + s.line.renderOrder = 1; + + pA.references.push(s); + pB.references.push(s); + } + + // shear springs (top-left to bottom-right) + if (i < SimulationData.resolution - 1 && j > 0) { + const pA = SimulationData.pList[i][j]; + const pB = SimulationData.pList[i + 1][j - 1]; + + const s = new ShearSpring({ + positionA: pA.position, + positionB: pB.position, + constants: SimulationData.sConstants, + color: SimulationData.sColorShear, + }); + SimulationData.sList.push(s); + s.line.renderOrder = 1; + + pA.references.push(s); + pB.references.push(s); + } + + // bend springs (horizontal) + if (i < SimulationData.resolution - 2) { + const pA = SimulationData.pList[i][j]; + const pB = SimulationData.pList[i + 2][j]; + + const s = new BendingSpring({ + positionA: pA.position, + positionB: pB.position, + constants: SimulationData.sConstants, + color: SimulationData.sColorBend, + }); + SimulationData.sList.push(s); + s.line.renderOrder = 0; + + pA.references.push(s); + pB.references.push(s); + } + + // bend springs (vertical) + if (j < SimulationData.resolution - 2) { + const pA = SimulationData.pList[i][j]; + const pB = SimulationData.pList[i][j + 2]; + + const s = new BendingSpring({ + positionA: pA.position, + positionB: pB.position, + constants: SimulationData.sConstants, + color: SimulationData.sColorBend, + }); + SimulationData.sList.push(s); + s.line.renderOrder = 0; + + pA.references.push(s); + pB.references.push(s); + } + } + } + + // 7. add springs to container + const sLinList = new Array<THREE.Line>(); + SimulationData.sList.forEach(s => { + sLinList.push(s.line); + }); + + // 8. add particles and springs to container + while (SimulationData.container.children.length > 0) { + SimulationData.container.children.pop(); + } + + SimulationData.container.add(...pLinList, ...sLinList); + } + + public static changedParticleSize(): void { + // 1. calculate current mass + const pCount = SimulationData.resolution * SimulationData.resolution; + const pMass = SimulationData.totalMass / pCount; + + // 1.1 update particle mass + SimulationData.pList.forEach(pSub => { + pSub.forEach(p => { + p.mass = pMass; + }); + }); + + // 2. calculate max pSize + const maxSize = Math.min(SimulationData.width, SimulationData.height); + + // 3. normalize pMass + const pMassNorm = (pMass + SimulationData.MIN_TOTAL_MASS) / + (SimulationData.MAX_TOTAL_MASS + SimulationData.MIN_TOTAL_MASS); + + // 4. calculate pSize + const pSize = maxSize * pMassNorm; + + // 5. set pSize + SimulationData.pList.forEach(pSub => { pSub.forEach(p => p.setRadius(pSize)) }); + } + + public static changedDimensions(): void { + // 1. reset positions of particles + for (let i = 0; i < SimulationData.resolution; i++) { + for (let j = 0; j < SimulationData.resolution; j++) { + SimulationData.pList[i][j].setPosition( + SimulationData.width * (i / SimulationData.resolution) - SimulationData.width / 2, + SimulationData.height * (j / SimulationData.resolution) - SimulationData.height / 2, + 0); + } + } + + // 2. set size based on particle mass + SimulationData.changedParticleSize(); + } + + public static changedParticleColor(): void { + // iterate over all particles and set color based on current internal state + SimulationData.pList.forEach(pSub => { + pSub.forEach(p => { + if (p.usePhysics) { + p.setColor(SimulationData.pColorDefault); + } else { + p.setColor(SimulationData.pColorNoPhysix); + } + }) + }); + } + + public static changedSpringColor(): void { + // iterate over all particles and set color based on current internal state + SimulationData.sList.forEach(s => { + if (s instanceof StructuralSpring) { + s.setColor(SimulationData.sColorStructural); + } else if (s instanceof ShearSpring) { + s.setColor(SimulationData.sColorShear); + } else if (s instanceof BendingSpring) { + s.setColor(SimulationData.sColorBend); + } + }); + } + + public static switchParticlePhysics(): void { + // 1. is picked object not null + if (!picked) { + return; + } + + // 2. compare mesh to all particles => find particle and change usePhysics + SimulationData.pList.forEach(pSub => { + pSub.forEach(p => { + if (p.mesh === picked) { + p.usePhysics = !p.usePhysics; + p.setColor(p.usePhysics ? SimulationData.pColorDefault + : SimulationData.pColorNoPhysix); + } + }) + }); + } + + gui(gui: GUI): void { + + + const general = gui.addFolder('General'); + general.add(SimulationData, 'totalMass', SimulationData.MIN_TOTAL_MASS, SimulationData.MAX_TOTAL_MASS) + .step(0.1).name('Total Mass').onChange(SimulationData.changedParticleSize); + general.add(SimulationData, 'width', SimulationData.MIN_WIDTH, SimulationData.MAX_WIDTH) + .step(0.1).name('Width').onChange(SimulationData.changedDimensions); + general.add(SimulationData, 'height', SimulationData.MIN_HEIGHT, SimulationData.MAX_HEIGHT) + .step(0.1).name('Height').onChange(SimulationData.changedDimensions); + general.add(SimulationData, 'resolution', SimulationData.MIN_RESOLUTION, SimulationData.MAX_RESOLUTION) + .step(1).name('Resolution').onChange(SimulationData.generateTextile); + general.open(); + + + const integration = gui.addFolder('Integrator'); + integration.add(SimulationData, 'setEuler').name('Euler').onChange(()=>{console.log(SimulationData.integratorType)}); + integration.add(SimulationData, 'setMidpoint').name('Midpoint').onChange(()=>{console.log(SimulationData.integratorType)}); + integration.add(SimulationData, 'setRungeKutta').name('Runge Kutta (4th order)').onChange(()=>{console.log(SimulationData.integratorType)}); + integration.open(); + + const extForce = general.addFolder('External Force'); + extForce.add(SimulationData.externalForce, 'x',).step(0.01); + extForce.add(SimulationData.externalForce, 'y',).step(0.01); + extForce.add(SimulationData.externalForce, 'z',).step(0.01); + extForce.open(); + + const particles = gui.addFolder('Particles'); + particles.add(SimulationData, 'switchParticlePhysics').name('Switch Particle Physics'); + particles.addColor(SimulationData, 'pColorDefault').name('Default').onChange(SimulationData.changedParticleColor); + particles.addColor(SimulationData, 'pColorNoPhysix').name('No Phyisx').onChange(SimulationData.changedParticleColor); + particles.open(); + + const springs = gui.addFolder('Springs'); + springs.add(SimulationData, 'damping', 0, 1, 0.001).name('Damping Factor'); + springs.add(SimulationData.sConstants, 'structural', SimulationData.MIN_CONSTANT, SimulationData.MAX_CONSTANT) + .step(0.1).name('Structural'); + springs.add(SimulationData.sConstants, 'shear', SimulationData.MIN_CONSTANT, SimulationData.MAX_CONSTANT) + .step(0.1).name('Shear'); + springs.add(SimulationData.sConstants, 'bend', SimulationData.MIN_CONSTANT, SimulationData.MAX_CONSTANT) + .step(0.1).name('Bend'); + springs.addColor(SimulationData, 'sColorStructural').name('Structural').onChange(SimulationData.changedSpringColor); + springs.addColor(SimulationData, 'sColorShear').name('Shear').onChange(SimulationData.changedSpringColor); + springs.addColor(SimulationData, 'sColorBend').name('Bend').onChange(SimulationData.changedSpringColor); + springs.open(); + } + + object(): THREE.Object3D<THREE.Event> { + return SimulationData.container; + } +} + +export { SimulationData }; + + diff --git a/src/03-simulation/SimulationSettings.ts b/src/03-simulation/SimulationSettings.ts deleted file mode 100644 index c4b6d060c25ee9b41bd173993d7967ef9db42ccd..0000000000000000000000000000000000000000 --- a/src/03-simulation/SimulationSettings.ts +++ /dev/null @@ -1,311 +0,0 @@ -import * as THREE from 'three'; -import { GUI } from 'dat.gui'; -import { picked, PipelineGUI } from '../core/Pipeline'; -import { BendingSpring, ShearSpring, Spring, SpringConstants, StructuralSpring, } from './cgPhysix/Springs'; -import { Particle } from './cgPhysix/particle'; - - -class World { - public static readonly G = 9.807; - - public static derivation(x: Array<number>): Array<number> { - - let xDot = new Array<number>(x.length); - - for (let i = 0; i < x.length; i += 6) { - xDot[i] = x[i + 3]; - xDot[i + 1] = x[i + 4]; - xDot[i + 2] = x[i + 5]; - xDot[i + 3] = 0; - xDot[i + 4] = -.01; - xDot[i + 5] = 0; - } - - return xDot; - } - - -} - - -class SimulationSettings implements PipelineGUI { - - public static container: THREE.Group = new THREE.Group(); - - // cloth settings - public static readonly MAX_TOTAL_MASS = 10; - public static readonly MIN_TOTAL_MASS = 0.1; - - public static readonly MAX_WIDTH = 5; - public static readonly MIN_WIDTH = 0.01; - - public static readonly MAX_HEIGHT = 2; - public static readonly MIN_HEIGHT = 0.01; - - public static readonly MAX_RESOLUTION = 20; - public static readonly MIN_RESOLUTION = 2; - - public static totalMass: number = 1; - public static width: number = 1; - public static height: number = 1; - public static resolution: number = 5; - public static externalForce: THREE.Vector3 = new THREE.Vector3(0, 0, 0); - - // particls - public static pList = new Array<Array<Particle>>(); - public static pColorDefault: number = 0xffffff; - public static pColorNoPhysix: number = 0xff00ff; - - // springs - public static readonly MAX_CONSTANT = 10; - public static readonly MIN_CONSTANT = 0.1; - - public static sList = new Array<Spring>(); - public static sConstants: SpringConstants = { structural: 1, shear: 1, bend: 1 }; - public static sColorStructural: number = 0xff0000; - public static sColorShear: number = 0x00ff00; - public static sColorBend: number = 0x0000ff; - - // integrator - public static readonly INTEGRATOR_TYPES = ['Euler', 'Midpoint', 'RungeKutta']; - public static readonly STEP_SIZE = ['Fixed', 'Adaptive']; - - public static integratorType: string = 'Euler'; - public static stepSize: string = 'Fixed'; - - public static generateTextile(): void { - // 1. reset pList - SimulationSettings.pList = new Array<Array<Particle>>(); - - // 2. generate particles - for (let i = 0; i < SimulationSettings.resolution; i++) { - SimulationSettings.pList[i] = new Array<Particle>(); - for (let j = 0; j < SimulationSettings.resolution; j++) { - - const p = new Particle({ - position: new THREE.Vector3( - SimulationSettings.width * (i / SimulationSettings.resolution) - SimulationSettings.width / 2, - SimulationSettings.height * (j / SimulationSettings.resolution) - SimulationSettings.height / 2, - 0), - velocity: new THREE.Vector3(0, 0, 0), - mass: SimulationSettings.totalMass / (SimulationSettings.resolution * SimulationSettings.resolution), - usePhysics: true, - references: [], - radius: 0, - color: SimulationSettings.pColorDefault, - wireframe: true - }); - SimulationSettings.pList[i][j] = p; - } - } - - for (let i = 0; i < SimulationSettings.resolution; i++) { - SimulationSettings.pList[i][SimulationSettings.resolution - 1].usePhysics = false; - } - - // 3. set particle mass and color - SimulationSettings.changedParticleSize(); - SimulationSettings.changedParticleColor(); - - // 4. add particles to container - const pLinList = new Array<THREE.Mesh>(); - SimulationSettings.pList.forEach(pSub => { - pSub.forEach(p => { - pLinList.push(p.mesh); - }); - }); - - // 5. reset sList - SimulationSettings.sList = new Array<Spring>(); - - // 6. generate springs - for (let i = 0; i < SimulationSettings.resolution; i++) { - for (let j = 0; j < SimulationSettings.resolution; j++) { - - // structural springs (horizontal) - if (i < SimulationSettings.resolution - 1) { - const pA = SimulationSettings.pList[i][j]; - const pB = SimulationSettings.pList[i + 1][j]; - - const s = new StructuralSpring({ - positionA: pA.position, - positionB: pB.position, - constants: SimulationSettings.sConstants, - color: SimulationSettings.sColorStructural, - }); - SimulationSettings.sList.push(s); - - pA.references.push(s); - pB.references.push(s); - } - - // structural springs (vertical) - if (j < SimulationSettings.resolution - 1) { - const pA = SimulationSettings.pList[i][j]; - const pB = SimulationSettings.pList[i][j + 1]; - - const s = new StructuralSpring({ - positionA: pA.position, - positionB: pB.position, - constants: SimulationSettings.sConstants, - color: SimulationSettings.sColorStructural, - }); - SimulationSettings.sList.push(s); - - pA.references.push(s); - pB.references.push(s); - } - - - // // shear springs - // if (j < SimulationSettings.resolution - 1) { - // const s = new ShearSpring(SimulationSettings.pList[i][j], SimulationSettings.pList[i][j + 1], SimulationSettings.sConstants.shear); - // SimulationSettings.sList.push(s); - // } - - // // bending springs - // if (i < SimulationSettings.resolution - 1 && j < SimulationSettings.resolution - 1) { - // const s = new BendingSpring(SimulationSettings.pList[i][j], SimulationSettings.pList[i + 1][j + 1], SimulationSettings.sConstants.bend); - // SimulationSettings.sList.push(s); - // } - } - } - - // 7. add springs to container - const sLinList = new Array<THREE.Line>(); - SimulationSettings.sList.forEach(s => { - sLinList.push(s.line); - }); - - // 8. add particles and springs to container - while (SimulationSettings.container.children.length > 0) { - SimulationSettings.container.children.pop(); - } - - SimulationSettings.container.add(...pLinList, ...sLinList); - } - - public static changedParticleSize(): void { - // 1. calculate current mass - const pCount = SimulationSettings.resolution * SimulationSettings.resolution; - const pMass = SimulationSettings.totalMass / pCount; - - // 2. calculate max pSize - const maxSize = Math.min(SimulationSettings.width, SimulationSettings.height); - - // 3. normalize pMass - const pMassNorm = (pMass + SimulationSettings.MIN_TOTAL_MASS) / - (SimulationSettings.MAX_TOTAL_MASS + SimulationSettings.MIN_TOTAL_MASS); - - // 4. calculate pSize - const pSize = maxSize * pMassNorm; - - // 5. set pSize - SimulationSettings.pList.forEach(pSub => { pSub.forEach(p => p.setRadius(pSize)) }); - } - - public static changedDimensions(): void { - // 1. reset positions of particles - for (let i = 0; i < SimulationSettings.resolution; i++) { - for (let j = 0; j < SimulationSettings.resolution; j++) { - SimulationSettings.pList[i][j].setPosition( - SimulationSettings.width * (i / SimulationSettings.resolution) - SimulationSettings.width / 2, - SimulationSettings.height * (j / SimulationSettings.resolution) - SimulationSettings.height / 2, - 0); - } - } - - // 2. set size based on particle mass - SimulationSettings.changedParticleSize(); - } - - public static changedParticleColor(): void { - // iterate over all particles and set color based on current internal state - SimulationSettings.pList.forEach(pSub => { - pSub.forEach(p => { - if (p.usePhysics) { - p.setColor(SimulationSettings.pColorDefault); - } else { - p.setColor(SimulationSettings.pColorNoPhysix); - } - }) - }); - } - - public static changedSpringColor(): void { - // iterate over all particles and set color based on current internal state - SimulationSettings.sList.forEach(s => { - if (s instanceof StructuralSpring) { - s.setColor(SimulationSettings.sColorStructural); - } else if (s instanceof ShearSpring) { - s.setColor(SimulationSettings.sColorShear); - } else if (s instanceof BendingSpring) { - s.setColor(SimulationSettings.sColorBend); - } - }); - } - - public static switchParticlePhysics(): void { - // 1. is picked object not null - if (!picked) { - return; - } - - // 2. compare mesh to all particles => find particle and change usePhysics - SimulationSettings.pList.forEach(pSub => { - pSub.forEach(p => { - if (p.mesh === picked) { - p.usePhysics = !p.usePhysics; - p.setColor(p.usePhysics ? SimulationSettings.pColorDefault - : SimulationSettings.pColorNoPhysix); - } - }) - }); - } - - // callback overrides by other classes - public callbackSpringColorStructural(_value: number): void { } - public callbackSpringColorShear(_value: number): void { } - public callbackSpringColorBend(_value: number): void { } - - gui(gui: GUI): void { - const general = gui.addFolder('General'); - general.add(SimulationSettings, 'totalMass', SimulationSettings.MIN_TOTAL_MASS, SimulationSettings.MAX_TOTAL_MASS) - .step(0.1).name('Total Mass').onChange(SimulationSettings.changedParticleSize); - general.add(SimulationSettings, 'width', SimulationSettings.MIN_WIDTH, SimulationSettings.MAX_WIDTH) - .step(0.1).name('Width').onChange(SimulationSettings.changedDimensions); - general.add(SimulationSettings, 'height', SimulationSettings.MIN_HEIGHT, SimulationSettings.MAX_HEIGHT) - .step(0.1).name('Height').onChange(SimulationSettings.changedDimensions); - general.add(SimulationSettings, 'resolution', SimulationSettings.MIN_RESOLUTION, SimulationSettings.MAX_RESOLUTION) - .step(1).name('Resolution').onChange(SimulationSettings.generateTextile); - general.open(); - - const extForce = general.addFolder('External Force'); - extForce.add(SimulationSettings.externalForce, 'x',).step(0.01); - extForce.add(SimulationSettings.externalForce, 'y',).step(0.01); - extForce.add(SimulationSettings.externalForce, 'z',).step(0.01); - extForce.open(); - - const particles = gui.addFolder('Particles'); - particles.add(SimulationSettings, 'switchParticlePhysics').name('Switch Particle Physics'); - particles.addColor(SimulationSettings, 'pColorDefault').name('Default').onChange(SimulationSettings.changedParticleColor); - particles.addColor(SimulationSettings, 'pColorNoPhysix').name('No Phyisx').onChange(SimulationSettings.changedParticleColor); - particles.open(); - - const springs = gui.addFolder('Springs'); - springs.add(SimulationSettings.sConstants, 'structural', SimulationSettings.MIN_CONSTANT, SimulationSettings.MAX_CONSTANT) - .step(0.1).name('Structural'); - springs.add(SimulationSettings.sConstants, 'shear', SimulationSettings.MIN_CONSTANT, SimulationSettings.MAX_CONSTANT) - .step(0.1).name('Shear'); - springs.add(SimulationSettings.sConstants, 'bend', SimulationSettings.MIN_CONSTANT, SimulationSettings.MAX_CONSTANT) - .step(0.1).name('Bend'); - springs.addColor(SimulationSettings, 'sColorStructural').name('Structural').onChange(SimulationSettings.changedSpringColor); - springs.addColor(SimulationSettings, 'sColorShear').name('Shear').onChange(SimulationSettings.changedSpringColor); - springs.addColor(SimulationSettings, 'sColorBend').name('Bend').onChange(SimulationSettings.changedSpringColor); - springs.open(); - } -} - -export { SimulationSettings }; - - diff --git a/src/03-simulation/physics/Particle.ts b/src/03-simulation/physics/Particle.ts index 6b028deb40e66f86d7c98366c25a1658b68d4acc..acc081fc19d55d2382c9ecdab39a41adf5c72d62 100644 --- a/src/03-simulation/physics/Particle.ts +++ b/src/03-simulation/physics/Particle.ts @@ -47,6 +47,11 @@ export class Particle implements PipelineRenderable { (this.mesh.material as THREE.MeshBasicMaterial).color.set(color); } + public setState(px: number, py: number, pz: number, vx: number, vy: number, vz: number): void { + this.velocity.set(vx, vy, vz); + this.setPosition(px, py, pz); + } + public setPosition(x: number, y: number, z: number): void { this.position.set(x, y, z); this.mesh.position.copy(this.position); @@ -60,4 +65,4 @@ export class Particle implements PipelineRenderable { public object(): THREE.Object3D<THREE.Event> { return this.mesh; } -} \ No newline at end of file +} diff --git a/src/03-simulation/physics/Springs.ts b/src/03-simulation/physics/Springs.ts index e7fccd103b9b7ea594a9cf5112902c3b6e8b7174..0b0d05e1b7a3e12c8b95bf4f00574d45ed27a460 100644 --- a/src/03-simulation/physics/Springs.ts +++ b/src/03-simulation/physics/Springs.ts @@ -33,14 +33,14 @@ export abstract class Spring { abstract get springConstant(): number; - public force(x: number, y: number, z: number): THREE.Vector3 { + public force(p: THREE.Vector3): THREE.Vector3 { let direction = this.positionB.clone().sub(this.positionA); const distance = direction.length(); direction.normalize(); - const sign = distance / Math.abs(distance); - const result = direction.multiplyScalar(this.springConstant * (distance - this.restingLength) * sign); + //const sign = distance / Math.abs(distance); + const result = direction.multiplyScalar(this.springConstant * (distance - this.restingLength)); - if (this.positionA === new THREE.Vector3(x, y, z)) { + if (this.positionA === p) { return result; } diff --git a/src/core/Pipeline.ts b/src/core/Pipeline.ts index 08464fa52948d6ee17563633bc4f2a179eef6ed5..0e1b2c6e5e62a8495c09a1b03660775f40ee2cb1 100644 --- a/src/core/Pipeline.ts +++ b/src/core/Pipeline.ts @@ -188,8 +188,8 @@ class Pipeline { data.observers.forEach(observer => { this.addPipelineObserver(observer); }); data.guiElements.forEach(guiElement => { this.addGUI(guiElement); }); + // picking stuff document.addEventListener( 'mousedown', (event) => { - event.preventDefault(); let mouse3D = new THREE.Vector3( ( event.clientX / window.innerWidth ) * 2 - 1, -( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );