From 069705a31e846f8f8e774649ded11f65fe046b7c Mon Sep 17 00:00:00 2001 From: Vincent Loh Date: Mon, 20 May 2024 19:22:57 -0400 Subject: [PATCH] oopsie --- anchor.js | 77 +++++++- craft.js | 456 +++++++++++++++++++++++++++++++++++++++++++++++- craft_models.js | 74 +++++++- engine.js | 1 - index.html | 15 +- main.js | 119 ++++++++++++- mirror.sh | 2 + reactive.js | 93 +++++++++- utils.js | 78 ++++++++- 9 files changed, 907 insertions(+), 8 deletions(-) mode change 120000 => 100644 anchor.js mode change 120000 => 100644 craft.js mode change 120000 => 100644 craft_models.js mode change 120000 => 100644 engine.js mode change 120000 => 100644 index.html mode change 120000 => 100644 main.js create mode 100755 mirror.sh mode change 120000 => 100644 reactive.js mode change 120000 => 100644 utils.js diff --git a/anchor.js b/anchor.js deleted file mode 120000 index 12fa2c9..0000000 --- a/anchor.js +++ /dev/null @@ -1 +0,0 @@ -../phys/anchor.js \ No newline at end of file diff --git a/anchor.js b/anchor.js new file mode 100644 index 0000000..8875209 --- /dev/null +++ b/anchor.js @@ -0,0 +1,76 @@ +class Anchor{ + constructor(opt={}){ + this.p=opt.p||[0,0,0] + this.a=opt.a||[[1,0,0],[0,1,0]] + this.r=opt.r||[[0,0,0],[0,0,0]] + this.v=opt.v||[0,0,0] + this.forces=[[0,0,0],[0,0,0],[0,0,0]] + this.pr={p:[...this.p],a:[[...this.a[0]],[...this.a[1]],vcross(...this.a)]} + } + update(dt=1){ + this.pr={p:[...this.p],a:[[...this.a[0]],[...this.a[1]],vcross(...this.a)]} + for(let i=0;i<2;i++){for(let j=0;j<3;j++){this.r[i][j]+=this.forces[i][j]*dt}} + for(let z=0;z<3;z++){this.v[z]+=this.forces[2][z]*dt} + this.forces=[[0,0,0],[0,0,0],[0,0,0]] + for(let i=0;i<2;i++){for(let j=0;j<3;j++){this.r[i][j]*=cnst.drag.rot;if(i)this.v[j]*=cnst.drag.mov}} + for(let z=0;z<3;z++){this.p[z]+=this.v[z]*dt} + let b=[[...this.a[0]],[...this.a[1]],vcross(...this.a)] + for(let i=0;i<2;i++){for(let j=0;j<3;j++){for(let z=0;z<3;z++){this.a[i][z]+=this.r[i][j]*b[j][z]*dt}}} + + this.a[1]=vnorm(vmultf(vcross(this.a[0],vcross(...this.a)),-1)) + this.a[0]=vnorm(this.a[0]) + } + applyForce(p,d){ + this.forces[0][1]-=cnst.inertia.rot*p[1]*d[0] + this.forces[0][1]+=cnst.inertia.rot*p[0]*d[1] + this.forces[0][2]-=cnst.inertia.rot*p[2]*d[0] + this.forces[0][2]+=cnst.inertia.rot*p[0]*d[2] + this.forces[1][0]-=cnst.inertia.rot*p[1]*d[0] + this.forces[1][0]+=cnst.inertia.rot*p[0]*d[1] + this.forces[1][2]-=cnst.inertia.rot*p[2]*d[1] + this.forces[1][2]+=cnst.inertia.rot*p[1]*d[2] + let a=[...this.a,vcross(...this.a)] + for(let i=0;i<3;i++){for(let z=0;z<3;z++){this.forces[2][i]+=cnst.inertia.mov*d[z]*a[z][i]}} + } + applyWorldForce(p,d){ + this.applyForce(toGrid(p),absolNormal(d)) + } + //clarify: toGrid() is used to convert from world-space to internal grid space, fromGrid() is used to convert from internal grid space back to world space + toGrid(p){ + let a=[p[0]-this.p[0],p[1]-this.p[1],p[2]-this.p[2]] + return [vdot(a,this.a[0]),vdot(a,this.a[1]),vdot(a,vcross(...this.a))] + } + fromGrid(p){ + let a=[...this.p],b=[...this.a,vcross(...this.a)] + for(let z=0;z<3;z++){for(let i=0;i<3;i++){a[i]+=b[z][i]*p[z]}} + return a + } + //clarify: absolNormal() converts a world-space vector to a grid-space vector, for example you can use to apply a constant upwards force independent of rotation + //gridNormal() can be used to display a grid-space vector in world-space + absolNormal(p){ + return [vdot(p,this.a[0]),vdot(p,this.a[1]),vdot(p,vcross(...this.a))] + } + gridNormal(p){ + let a=[0,0,0],b=[...this.a,vcross(...this.a)] + for(let z=0;z<3;z++){for(let i=0;i<3;i++){a[i]+=b[z][i]*p[z]}} + return a + } + + pr_toGrid(p){ + let a=[p[0]-this.pr.p[0],p[1]-this.pr.p[1],p[2]-this.pr.p[2]] + return [vdot(a,this.pr.a[0]),vdot(a,this.pr.a[1]),vdot(a,vcross(...this.pr.a))] + } + pr_fromGrid(p){ + let a=[...this.pr.p],b=[...this.pr.a,vcross(...this.pr.a)] + for(let z=0;z<3;z++){for(let i=0;i<3;i++){a[i]+=b[z][i]*p[z]}} + return a + } + pr_absolNormal(p){ + return [vdot(p,this.pr.a[0]),vdot(p,this.pr.a[1]),vdot(p,vcross(...this.pr.a))] + } + pr_gridNormal(p){ + let a=[0,0,0],b=[...this.pr.a,vcross(...this.pr.a)] + for(let z=0;z<3;z++){for(let i=0;i<3;i++){a[i]+=b[z][i]*p[z]}} + return a + } +} diff --git a/craft.js b/craft.js deleted file mode 120000 index ae0fde1..0000000 --- a/craft.js +++ /dev/null @@ -1 +0,0 @@ -../phys/craft.js \ No newline at end of file diff --git a/craft.js b/craft.js new file mode 100644 index 0000000..a442788 --- /dev/null +++ b/craft.js @@ -0,0 +1,455 @@ +class Craft{ + constructor(opt={}){ + this.fix=opt.fix||[] + this.edges=opt.edges||[] + this.faces=opt.faces||[] + this.joints=opt.joints||[] + this.complexEdges=[] + this.complexFaces=[] + this.p_jt=[[],[],[]] + this.o_jt=[[],[],[]] + this.fc=[] + this.weight=0;this.m_center=[0,0,0] + this.reactive=false + this.reactSetup=opt.reactive||{} + this.reactSetup.generateFriction=this.reactSetup.generateFriction||'vmultf(vadd(this.anchor.pr_fromGrid(this.p_fix[psz[i][2]]),vmultf(this.anchor.fromGrid(psz[i][0]),-1)),1)' + } + addFixture(p,c={},joint=-1){ + if(!(this.joints[joint]||joint<0))return -1 + let y=p,jt=joint,t=0,x=this.joints.length + while(t-1||c>x){ + c++ + this.joints[i].chain.push(z) + z=this.joints[z].joint + } + } + this.m_center=this.weight?vmultf(this.m_center,1/this.weight):[0,0,0] + this.weld() + this.weld() + this.reactive.p_fix=[] + x=this.reactive.fix.length + for(let i=0;i-1&&c-1&&c-1&&c-1&&c{ + let uid=newUID(),models=[[]] + models[0].push(new p5.Geometry( + 1,1, + function createGeometry(){ + for(let x=0;x + + + + + + + + + + + + + diff --git a/main.js b/main.js deleted file mode 120000 index 1d85800..0000000 --- a/main.js +++ /dev/null @@ -1 +0,0 @@ -../phys/main.js \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 0000000..c25ea47 --- /dev/null +++ b/main.js @@ -0,0 +1,118 @@ +let reacts = [], dt = 1, paused = 1; + +function setup(){ + let craftdata = { + fix: [ + [1, 1, 1], // 0 + [1, 1, -1], // 1 + [1, -1, 1], // 2 + [1, -1, -1], // 3 + [-1, 1, 1], // 4 + [-1, 1, -1], // 5 + [-1, -1, 1], // 6 + [-1, -1, -1] // 7 + ], + edges: [ + [0, 1], + [0, 2], + [0, 4], + [1, 3], + [1, 5], + [2, 3], + [2, 6], + [3, 7], + [4, 5], + [4, 6], + [5, 7], + [6, 7] + ], + faces: [ + [0, 1, 2], + [4, 6, 7], + ] + }; + + for(let i=0; i<4; ++i){ + reacts.push(new Craft()); + for(let fix of craftdata.fix) reacts[reacts.length-1].addFixture(fix); + for(let edge of craftdata.edges) reacts[reacts.length-1].addEdge(...edge); + for(let face of craftdata.faces) reacts[reacts.length-1].addFace(...face); + reacts[reacts.length-1].setup(); + reacts[reacts.length-1].reactive.anchor.p[1] = -5 + reacts[reacts.length-1].applyForce([Math.random()*10-5, 0, Math.random()*10-5], + reacts[reacts.length-1].reactive.absolNormal([0, -Math.random()*10-20, 0])); + } + + createCanvas(400, 400, WEBGL); + + lt = Date.now(); +} + +function draw(){ + background(200); + scale(10); + + for(let i=0; i<10; ++i) + point(Math.cos(TWO_PI/10*i)*10, 4, Math.sin(TWO_PI/10*i)*10); + + for(let react of reacts){ + if(!paused){ + react.applyForce([0, 0, 0], react.reactive.absolNormal([0, 8, 0])); + react.collide(1/dt, [0, 4, 0], [[0, 0, 1], [0, 1, 0]]); + react.update(1/dt); + } + + push(); + applyMatrix(mlookAt(react.reactive.anchor.p,react.reactive.anchor.a)); + if(!react.models||!react.models.length) + react.models=genCraftModels(react) + strokeWeight(5); + push(); + translate(...react.p_jt[2]); + fill(100, 100, 100); noStroke(); + model(react.models[0][0]); + noFill(); stroke(255, 255, 0); strokeWeight(1); + model(react.models[0][1]); + pop(); + for(let f=0; f{let x=vadd(i,vmultf(p,-1));return [vdot(x,d[0]),vdot(x,d[1]),vdot(x,vcross(...d))]} + for(let i=0;i0)psz.push([this.fix[i],vmultf(x,1/r),i,d[1],r]) + } + for(let i=0;i0){ + this.anchor.applyForce([0,0,0],this.anchor.absolNormal(vmultf([0,-x[1],0],cnst.rebound/cnst.inertia.mov))) + this.anchor.applyForce([0,0,0],this.anchor.absolNormal(vmultf(this.anchor.v,cnst.grip/cnst.inertia.mov))) + } + } + return collides + } + collidef(g/*generation function for input p, returns {p[3] position d[2][3] anchors}*/){ + let collides=[],psz=[],x,tc=0,getOrient=i=>{let y=g(i);y.d=[vnorm(y.d[0]),vnorm(y.d[1])];x=vadd(i,vmultf(y.p,-1));return [vdot(x,y.d[0]),vdot(x,y.d[1]),vdot(x,vcross(...y.d))]} + for(let i=0;i0)psz.push([this.fix[i],vmultf(x,1/r),i,g(this.anchor.fromGrid(this.fix[i])).d[1],r]) + } + for(let i=0;i0){ + this.anchor.applyForce([0,0,0],this.anchor.absolNormal(vmultf([0,-x[1],0],cnst.rebound/cnst.inertia.mov))) + this.anchor.applyForce([0,0,0],this.anchor.absolNormal(vmultf(this.anchor.v,cnst.grip/cnst.inertia.mov))) + } + } + return collides + } + update(dt=1){ + this.anchor.update(dt) + } + applyForce(p,d){ + this.anchor.applyForce(p,d) + } + applyWorldForce(p,d){ + this.anchor.applyWorldForce(p,d) + } + toGrid(p){ + return this.anchor.toGrid(p) + } + fromGrid(p){ + return this.anchor.fromGrid(p) + } + absolNormal(p){ + return this.anchor.absolNormal(p) + } + gridNormal(p){ + return this.anchor.gridNormal(p) + } + pr_toGrid(p){ + return this.anchor.pr_toGrid(p) + } + pr_fromGrid(p){ + return this.anchor.pr_fromGrid(p) + } + pr_absolNormal(p){ + return this.anchor.pr_absolNormal(p) + } + pr_gridNormal(p){ + return this.anchor.pr_gridNormal(p) + } +} diff --git a/utils.js b/utils.js deleted file mode 120000 index 0154596..0000000 --- a/utils.js +++ /dev/null @@ -1 +0,0 @@ -../phys/utils.js \ No newline at end of file diff --git a/utils.js b/utils.js new file mode 100644 index 0000000..57e581c --- /dev/null +++ b/utils.js @@ -0,0 +1,77 @@ +const cnst={ + inertia:{ + rot:1/1000, + mov:1/1000 + }, + drag:{ + rot:1, + mov:1 + }, + grip:0.7, + rebound:0.7, + slide:0.7 +} +vdot=(a,b)=>a.map((x,i)=>a[i]*b[i]).reduce((m,n)=>m+n) +vcross=(a,b)=>[a[1]*b[2]-a[2]*b[1],a[2]*b[0]-a[0]*b[2],a[0]*b[1]-a[1]*b[0]] +vnorm=a=>{let b=Math.sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]);if(b==0)return [0,0,0];return [a[0]/b,a[1]/b,a[2]/b]} +vadd=(a,b)=>[a[0]+b[0],a[1]+b[1],a[2]+b[2]] +vmult=(a,b)=>[a[0]*b[0],a[1]*b[1],a[2]*b[2]] +vmultf=(a,b)=>[a[0]*b,a[1]*b,a[2]*b] +vmag=a=>Math.sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]) +vsnap=(a,b)=>vadd(a,vmultf(b,-vdot(b,a))) +vtransform=(a,p,v)=>{ + let x=[...p],b=[...a,vcross(...a)] + for(let z=0;z<3;z++){for(let i=0;i<3;i++){x[i]+=b[z][i]*v[z]}} + return x +} +vdetransform=(a,p,v)=>{ + let x=[v[0]-p[0],v[1]-p[1],v[2]-p[2]] + return [vdot(x,a[0]),vdot(x,a[1]),vdot(x,vcross(...a))] +} +pointAt=(p,c)=>{ + rnd.rotateY(-Math.atan2(p[2]-c[2],p[0]-c[0])) + rnd.rotateZ(Math.atan2(p[1]-c[1],Math.sqrt((p[0]-c[0])*(p[0]-c[0])+(p[2]-c[2])*(p[2]-c[2])))) +} +drawScreenAt=(p,c)=>{ + rnd.translate(...p) + pointAt(p,c) +} +build_pointAt=(p,c)=>{ + build_rnd.rotateY(-Math.atan2(p[2]-c[2],p[0]-c[0])) + build_rnd.rotateZ(Math.atan2(p[1]-c[1],Math.sqrt((p[0]-c[0])*(p[0]-c[0])+(p[2]-c[2])*(p[2]-c[2])))) +} +build_drawScreenAt=(p,c)=>{ + build_rnd.translate(...p) + build_pointAt(p,c) +} +let currUID=0 +newUID=_=>{ + currUID++ + return 'UID_'+currUID.toString() +} +keyToCode=(dir,n)=>{ + let mapTable=(`RIGHT 39_LEFT 37_DOWN 40_UP 38_SHIFT 16_SPACE 32_0 48_1 49_2 50_3 51_4 52_5 53_6 54_7 55_8 56_9 57_a 65_b 66_c 67_d 68_e 69_f 70_g 71_h 72_i 73_j 74_k 75_l 76_m 77_n 78_o 79_p 80_q 81_r 82_s 83_t 84_u 85_v 86_w 87_x 88_y 89_z 90`).split('_') + if(dir=='key'){for(let i=0;i{ + let a=[],p=[...p2] + a.push([...a2[0]],[...a2[1]]) + a.push(vcross(...a)) + a[0]=vnorm(a[0]) + a[1]=vnorm(a[1]) + return [...a[0],0,...a[1],0,...a[2],0,...p,1] +} +line3d=(r,a,b,w=0.03)=>{ + r.push() + let x=[vnorm(vadd(a,vmultf(b,-1)))],c=vmultf(vadd(a,b),1/2) + if(x[0][0]>0.9)x.push([0,1,0]) + else x.push([1,0,0]) + x[1]=vnorm(vcross(...x)) + x.push(vnorm(vcross(...x))) + r.applyMatrix([...x[0],0,...x[1],0,...x[2],0,...c,1]) + r.box(vmag(vadd(a,vmultf(b,-1))),w,w) + r.pop() +} +sqrx=a=>a*a