@ue-too/dynamics 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/index.js +2 -0
  2. package/index.js.map +10 -1
  3. package/package.json +3 -3
package/index.js CHANGED
@@ -1,3 +1,5 @@
1
1
  import{PointCal as X}from"@ue-too/math";var r={category:1,mask:65535,group:0};function o(k,Y){if(k.group!==0&&k.group===Y.group)return k.group>0;return(k.category&Y.mask)!==0&&(Y.category&k.mask)!==0}var H0={STATIC:1,DYNAMIC:2,PLAYER:4,ENEMY:8,PROJECTILE:16,SENSOR:32,PICKUP:64,PLATFORM:128};class n{_center;_mass=50;_linearVelocity;_angularVelocity;_orientationAngle=0;linearAcceleartion;force;isStaticBody=!1;_staticFrictionCoeff=0.3;dynamicFrictionCoeff=0.3;frictionEnabled=!1;isMovingStaticBody=!1;angularDampingFactor=0.005;collisionFilter={...r};isSleeping=!1;sleepThreshold=0.01;sleepTime=0.5;timeAtRest=0;constructor(k,Y=0,z=50,w=!1,$=!1){this._center=k,this._orientationAngle=Y,this._mass=z,this.isStaticBody=w,this.frictionEnabled=$,this.force={x:0,y:0},this.linearAcceleartion={x:0,y:0},this._linearVelocity={x:0,y:0},this._angularVelocity=0}move(k){if(!this.isStatic())this._center=X.addVector(this._center,k)}rotateRadians(k){this._orientationAngle+=k}getCenter(){return this._center}getOrientationAngle(){return this._orientationAngle}get angularVelocity(){return this._angularVelocity}set angularVelocity(k){this._angularVelocity=k}get orientationAngle(){return this._orientationAngle}isStatic(){return this.isStaticBody}isMovingStatic(){return this.isMovingStaticBody}setLinearVelocity(k){this._linearVelocity=k}setMovingStatic(k){this.isMovingStaticBody=k}setOrientationAngle(k){this._orientationAngle=k}applyForce(k){if(X.magnitude(this.force)!==0)this.force=X.addVector(this.force,k);else this.force=k}applyForceInOrientation(k){let Y;if(typeof k==="number")Y=X.rotatePoint({x:k,y:0},this._orientationAngle);else Y=X.rotatePoint(k,this._orientationAngle);this.applyForce(Y)}step(k){if(this.frictionEnabled)if(this.isStatic()||this.linearVelocity.x==0&&this.linearVelocity.y==0&&X.magnitude(X.subVector({x:this.force.x,y:this.force.y},{x:0,y:0}))>=0&&X.magnitude({x:this.force.x,y:this.force.y})<this.staticFrictionCoeff*this.mass*9.81)if(this.force.z!=null)this.force={x:0,y:0,z:this.force.z};else this.force={x:0,y:0};else{let G=X.multiplyVectorByScalar(X.unitVector({x:this._linearVelocity.x,y:this._linearVelocity.y}),-1),J=X.multiplyVectorByScalar(G,this.dynamicFrictionCoeff*this.mass*9.81);this.force=X.addVector(this.force,J)}let Y=this._angularVelocity!=0?this._angularVelocity>0?-this.angularDampingFactor:this.angularDampingFactor:0;if(Math.abs(this._angularVelocity)<Math.abs(Y))this._angularVelocity=0;else this._angularVelocity+=Y;if(this._orientationAngle+=this._angularVelocity*k,X.magnitude({x:this._linearVelocity.x,y:this._linearVelocity.y})<X.magnitude(X.divideVectorByScalar(X.multiplyVectorByScalar(this.force,k),this.mass)))if(this._linearVelocity.z!=null)this._linearVelocity={x:0,y:0,z:this._linearVelocity.z};else this._linearVelocity={x:0,y:0};let z=-9.81*this._mass;this.force=X.addVector(this.force,{x:0,y:0,z});let w=X.divideVectorByScalar(X.multiplyVectorByScalar(this.force,k),this.mass);this._linearVelocity=X.addVector(this._linearVelocity,w);let $=X.multiplyVectorByScalar(this._linearVelocity,k);if(this._center=X.addVector(this._center,$),this._center.z!=null&&this._center.z<0)this._center.z=0;this.force={x:0,y:0}}get center(){return this._center}set center(k){this._center=k}get linearVelocity(){return this._linearVelocity}set linearVelocity(k){this._linearVelocity=k}get mass(){return this._mass}get staticFrictionCoeff(){return this._staticFrictionCoeff}set staticFrictionCoeff(k){this._staticFrictionCoeff=k}setSleeping(k){if(k&&!this.isSleeping)this.isSleeping=!0,this._linearVelocity={x:0,y:0},this._angularVelocity=0;else if(!k&&this.isSleeping)this.isSleeping=!1,this.timeAtRest=0}updateSleeping(k){if(this.isStatic()||this.isMovingStatic())return;let Y=X.magnitude(this._linearVelocity),z=Math.abs(this._angularVelocity);if(Y<this.sleepThreshold&&z<this.sleepThreshold){if(this.timeAtRest+=k,this.timeAtRest>=this.sleepTime)this.setSleeping(!0)}else if(this.timeAtRest=0,this.isSleeping)this.setSleeping(!1)}}class G0{_circle;_context;collisionFilter={...r};isSleeping=!1;sleepThreshold=0.01;sleepTime=0.5;timeAtRest=0;constructor(k={x:0,y:0},Y,z,w=0,$=50,G=!1,J=!0){this._circle=new Y0(k,Y,w,$,G,J),this._context=z}draw(k){k.beginPath(),k.arc(this._circle.center.x,this._circle.center.y,this._circle.radius,0,2*Math.PI),k.stroke()}step(k){this._circle.step(k),this.draw(this._context)}isStatic(){return this._circle.isStatic()}isMovingStatic(){return this._circle.isMovingStatic()}getMinMaxProjection(k){return this._circle.getMinMaxProjection(k)}getCollisionAxes(k){return this._circle.getCollisionAxes(k)}applyForce(k){this._circle.applyForce(k)}get AABB(){return this._circle.AABB}getMass(){return this._circle.mass}applyForceInOrientation(k){this._circle.applyForceInOrientation(k)}move(k){this._circle.move(k)}getSignificantVertices(k){return this._circle.getSignificantVertices(k)}get center(){return this._circle.center}set center(k){this._circle.center=k}get linearVelocity(){return this._circle.linearVelocity}set linearVelocity(k){this._circle.linearVelocity=k}get orientationAngle(){return this._circle.orientationAngle}significantVertex(k){return this._circle.significantVertex(k)}set angularVelocity(k){this._circle.angularVelocity=k}get angularVelocity(){return this._circle.angularVelocity}get mass(){return this._circle.mass}getNormalOfSignificantFace(k){return this._circle.getNormalOfSignificantFace(k)}get staticFrictionCoeff(){return this._circle.staticFrictionCoeff}set staticFrictionCoeff(k){this._circle.staticFrictionCoeff=k}getAdjacentFaces(k){return this._circle.getAdjacentFaces(k)}get momentOfInertia(){return this._circle.momentOfInertia}setSleeping(k){if(k&&!this.isSleeping)this.isSleeping=!0,this._circle.linearVelocity={x:0,y:0},this._circle.angularVelocity=0;else if(!k&&this.isSleeping)this.isSleeping=!1,this.timeAtRest=0}updateSleeping(k){if(this._circle.isStatic()||this._circle.isMovingStatic())return;let Y=X.magnitude(this._circle.linearVelocity),z=Math.abs(this._circle.angularVelocity);if(Y<this.sleepThreshold&&z<this.sleepThreshold){if(this.timeAtRest+=k,this.timeAtRest>=this.sleepTime)this.setSleeping(!0)}else if(this.timeAtRest=0,this.isSleeping)this.setSleeping(!1)}}class J0{_polygon;_context;collisionFilter={...r};isSleeping=!1;sleepThreshold=0.01;sleepTime=0.5;timeAtRest=0;constructor(k={x:0,y:0},Y,z,w=0,$=50,G=!1,J=!0){this._polygon=new k0(k,Y,w,$,G,J),this._context=z}draw(k){k.beginPath();let Y=this._polygon.getVerticesAbsCoord();k.moveTo(Y[0].x,Y[0].y),Y.forEach((z)=>{k.lineTo(z.x,z.y)}),k.lineTo(Y[0].x,Y[0].y),k.stroke()}step(k){this._polygon.step(k),this.draw(this._context)}isStatic(){return this._polygon.isStatic()}isMovingStatic(){return this._polygon.isMovingStatic()}getMinMaxProjection(k){return this._polygon.getMinMaxProjection(k)}getCollisionAxes(k){return this._polygon.getCollisionAxes(k)}applyForce(k){this._polygon.applyForce(k)}applyForceInOrientation(k){this._polygon.applyForceInOrientation(k)}setLinearVelocity(k){this._polygon.setLinearVelocity(k)}move(k){this._polygon.move(k)}get center(){return this._polygon.center}set center(k){this._polygon.center=k}get linearVelocity(){return this._polygon.linearVelocity}set linearVelocity(k){this._polygon.linearVelocity=k}get angularVelocity(){return this._polygon.angularVelocity}set angularVelocity(k){this._polygon.angularVelocity=k}get orientationAngle(){return this._polygon.orientationAngle}significantVertex(k){return this._polygon.significantVertex(k)}getSignificantVertices(k){return this._polygon.getSignificantVertices(k)}get AABB(){return this._polygon.AABB}get mass(){return this._polygon.mass}get staticFrictionCoeff(){return this._polygon.staticFrictionCoeff}set staticFrictionCoeff(k){this._polygon.staticFrictionCoeff=k}get momentOfInertia(){return this._polygon.momentOfInertia}getNormalOfSignificantFace(k){return this._polygon.getNormalOfSignificantFace(k)}getAdjacentFaces(k){return this._polygon.getAdjacentFaces(k)}setSleeping(k){if(k&&!this.isSleeping)this.isSleeping=!0,this._polygon.linearVelocity={x:0,y:0},this._polygon.angularVelocity=0;else if(!k&&this.isSleeping)this.isSleeping=!1,this.timeAtRest=0}updateSleeping(k){if(this._polygon.isStatic()||this._polygon.isMovingStatic())return;let Y=X.magnitude(this._polygon.linearVelocity),z=Math.abs(this._polygon.angularVelocity);if(Y<this.sleepThreshold&&z<this.sleepThreshold){if(this.timeAtRest+=k,this.timeAtRest>=this.sleepTime)this.setSleeping(!0)}else if(this.timeAtRest=0,this.isSleeping)this.setSleeping(!1)}}class k0 extends n{vertices;_momentOfInertia;constructor(k={x:0,y:0},Y,z=0,w=50,$=!1,G=!0){super(k,z,w,$,G);this.vertices=Y,this.step=this.step.bind(this);let J=this.vertices.reduce((Q,U,R)=>{let W=R<this.vertices.length-1?R+1:0,S=this.vertices[W],K=X.crossProduct(S,U);return Q+X.magnitude(K)*(X.dotProduct(U,U)+X.dotProduct(U,S)+X.dotProduct(S,S))},0),Z=this.vertices.reduce((Q,U,R)=>{return Q+X.magnitude(X.crossProduct(this.vertices[R<this.vertices.length-1?R+1:0],U))},0);this._momentOfInertia=this._mass*J/(6*Z)}getVerticesAbsCoord(){return this.vertices.map((k)=>{return X.addVector(this._center,X.rotatePoint(k,this._orientationAngle))})}getCollisionAxes(k){return this.getVerticesAbsCoord().map((Y,z,w)=>{let $=X.subVector(Y,w[w.length-1]);if(z>0)$=X.subVector(Y,w[z-1]);return X.unitVector(X.rotatePoint($,Math.PI/2))})}getMinMaxProjection(k){let z=this.getVerticesAbsCoord().map((w)=>{return X.dotProduct(w,k)});return{min:Math.min(...z),max:Math.max(...z)}}get AABB(){let k=this.getVerticesAbsCoord(),Y=k.map((w)=>w.x),z=k.map((w)=>w.y);return{min:{x:Math.min(...Y),y:Math.min(...z)},max:{x:Math.max(...Y),y:Math.max(...z)}}}significantVertex(k){let Y=this.getVerticesAbsCoord(),z=Y.map(($)=>X.dotProduct($,k)),w=z.indexOf(Math.max(...z));return Y[w]}getSignificantVertices(k){let Y=this.getVerticesAbsCoord(),z=Y.map((W)=>X.dotProduct(W,k)),w=z.indexOf(Math.max(...z)),$=Y[w],G=w>0?w-1:Y.length-1,J=w<Y.length-1?w+1:0,Z=Y[G],Q=Y[J],U=X.dotProduct(Z,k),R=X.dotProduct(Q,k);if(U>R)return[Z,$];else return[$,Q]}getNormalOfSignificantFace(k){let Y=this.getSignificantVertices(k),z=X.unitVectorFromA2B(Y[0],Y[1]);return X.rotatePoint(z,-Math.PI/2)}getAdjacentFaces(k){let Y=this.getVerticesAbsCoord(),z=Y.map((K)=>X.dotProduct(K,k)),w=z.indexOf(Math.max(...z)),$=Y[w],G=w>0?w-1:Y.length-1,J=w<Y.length-1?w+1:0,Z=Y[G],Q=Y[J],U=X.dotProduct(Z,k),R=X.dotProduct(Q,k),W=[],S=[];if(U>R){W.push(Z,$),S.push({startPoint:{coord:Z,index:G},endPoint:{coord:$,index:w}}),S.unshift({startPoint:{coord:$,index:w},endPoint:{coord:Q,index:J}});let K=G>0?G-1:Y.length-1;S.unshift({startPoint:{coord:Y[K],index:K},endPoint:{coord:Z,index:G}})}else{W.push($,Q),S.push({startPoint:{coord:$,index:w},endPoint:{coord:Q,index:J}});let K=J<Y.length-1?J+1:0;S.unshift({startPoint:{coord:Q,index:J},endPoint:{coord:Y[K],index:K}}),S.unshift({startPoint:{coord:Z,index:G},endPoint:{coord:$,index:w}})}return S}get momentOfInertia(){return this._momentOfInertia}}class Y0 extends n{_radius;_momentOfInertia;constructor(k={x:0,y:0},Y,z=0,w=50,$=!1,G=!0){super(k,z,w,$,G);this._radius=Y,this.step=this.step.bind(this),this._momentOfInertia=this._mass*this._radius*this._radius/2}getMinMaxProjection(k){let Y=X.addVector(this._center,X.multiplyVectorByScalar(k,this._radius)),z=X.addVector(this._center,X.multiplyVectorByScalar(k,-this._radius));return{min:X.dotProduct(z,k),max:X.dotProduct(Y,k)}}getCollisionAxes(k){return[X.unitVector(X.subVector(k.center,this._center))]}get AABB(){return{min:X.subVector(this._center,{x:this._radius,y:this._radius}),max:X.addVector(this._center,{x:this._radius,y:this._radius})}}significantVertex(k){return X.addVector(this._center,X.multiplyVectorByScalar(k,this._radius))}get radius(){return this._radius}getSignificantVertices(k){return[X.addVector(this._center,X.multiplyVectorByScalar(k,this._radius))]}getNormalOfSignificantFace(k){return X.unitVector(k)}getAdjacentFaces(k){return[]}get momentOfInertia(){return this._momentOfInertia}}class j{bottomLeft;width;height;constructor(k,Y,z){this.bottomLeft=k,this.width=Y,this.height=z}getWidth(){return this.width}getHeight(){return this.height}getbottomLeft(){return this.bottomLeft}}class M{MAX_OBJECTS=10;MAX_LEVELS=5;level;objects=[];nodes=[];bounds;constructor(k,Y){this.level=k,this.objects=[],this.bounds=Y,this.nodes=[void 0,void 0,void 0,void 0]}draw(k){k.beginPath(),k.rect(this.bounds.getbottomLeft().x,this.bounds.getbottomLeft().y,this.bounds.getWidth(),this.bounds.getHeight()),k.stroke();for(let Y=0;Y<this.nodes.length;Y++){let z=this.nodes[Y];if(z!=null)z.draw(k)}}clear(){this.objects=[];for(let k=0;k<this.nodes.length;k++){let Y=this.nodes[k];if(Y!=null)Y.clear(),Y=void 0}}split(){let k=this.bounds.getWidth()/2,Y=this.bounds.getHeight()/2,z=this.bounds.getbottomLeft();this.nodes[0]=new M(this.level+1,new j({x:z.x,y:z.y},k,Y)),this.nodes[1]=new M(this.level+1,new j({x:z.x,y:z.y+Y},k,Y)),this.nodes[2]=new M(this.level+1,new j({x:z.x+k,y:z.y+Y},k,Y)),this.nodes[3]=new M(this.level+1,new j({x:z.x+k,y:z.y},k,Y))}getIndex(k){let Y={x:this.bounds.getbottomLeft().x+this.bounds.getWidth()/2,y:this.bounds.getbottomLeft().y+this.bounds.getHeight()/2},z=k.AABB,w=z.max.y<Y.y&&z.min.y>this.bounds.getbottomLeft().y,$=z.max.x<Y.x&&z.min.x>this.bounds.getbottomLeft().x,G=z.max.x>Y.x&&z.min.x>Y.x,J=z.max.y>Y.y&&z.min.y>Y.y;if(w&&$)return 0;else if(w&&G)return 3;else if(J&&$)return 1;else if(J&&G)return 2;return-1}insert(k){let Y=this.nodes[0];if(Y!=null){let z=this.getIndex(k);if(z!==-1){Y=this.nodes[z],Y?.insert(k);return}}if(this.objects.push(k),this.objects.length>this.MAX_OBJECTS&&this.level<this.MAX_LEVELS){if(this.nodes[0]==null||this.nodes[0]==null)this.split();let z=0;while(z<this.objects.length){let w=this.getIndex(this.objects[z]),$=this.nodes[w];if(w!=-1&&$!==void 0){let G=this.objects[z];this.objects.splice(z,1),$.insert(G)}else z++}}}retrieve(k){let Y=this.getIndex(k),z=[],w=this.nodes[Y];if(Y!==-1&&w!==void 0)z.push(...w.retrieve(k));return z.push(...this.objects),z}}class t{parent=null;children=[null,null];aabb={min:{x:0,y:0},max:{x:0,y:0}};object=null;height=0;constructor(k){if(k)this.setLeaf(k)}isLeaf(){return this.children[0]===null}setLeaf(k){this.object=k,this.updateAABB(),this.children=[null,null],this.height=0}setBranch(k,Y){k.parent=this,Y.parent=this,this.children=[k,Y],this.object=null,this.updateAABB(),this.height=1+Math.max(k.height,Y.height)}updateAABB(k=0.1){if(this.isLeaf()&&this.object){let Y=this.object.AABB;this.aabb={min:{x:Y.min.x-k,y:Y.min.y-k},max:{x:Y.max.x+k,y:Y.max.y+k}}}else if(this.children[0]&&this.children[1]){let Y=this.children[0].aabb,z=this.children[1].aabb;this.aabb={min:{x:Math.min(Y.min.x,z.min.x),y:Math.min(Y.min.y,z.min.y)},max:{x:Math.max(Y.max.x,z.max.x),y:Math.max(Y.max.y,z.max.y)}}}}getSibling(){if(!this.parent)return null;return this.parent.children[0]===this?this.parent.children[1]:this.parent.children[0]}getBalance(){if(this.isLeaf())return 0;let k=this.children[0]?this.children[0].height:0,Y=this.children[1]?this.children[1].height:0;return k-Y}}class u{xEndpoints=[];objects=new Map;nextId=0;clear(){this.xEndpoints=[],this.objects.clear(),this.nextId=0}insert(k){let Y=this.nextId++,z={value:k.AABB.min.x,isMin:!0,object:k,id:Y},w={value:k.AABB.max.x,isMin:!1,object:k,id:Y};this.objects.set(k,{minEndpoint:z,maxEndpoint:w}),this.insertEndpointSorted(z),this.insertEndpointSorted(w)}update(k){let Y=this.objects.get(k);if(!Y)return;let z=k.AABB.min.x,w=k.AABB.max.x;if(Y.minEndpoint.value!==z)this.removeEndpoint(Y.minEndpoint),Y.minEndpoint.value=z,this.insertEndpointSorted(Y.minEndpoint);if(Y.maxEndpoint.value!==w)this.removeEndpoint(Y.maxEndpoint),Y.maxEndpoint.value=w,this.insertEndpointSorted(Y.maxEndpoint)}remove(k){let Y=this.objects.get(k);if(!Y)return;this.removeEndpoint(Y.minEndpoint),this.removeEndpoint(Y.maxEndpoint),this.objects.delete(k)}retrieve(k){let Y=[],z=k.AABB.min.x,w=k.AABB.max.x;for(let $ of this.xEndpoints){if($.value>w)break;if($.isMin&&$.object!==k){let G=this.objects.get($.object)?.maxEndpoint.value;if(G!==void 0&&G>=z){if(this.aabbIntersects($.object.AABB,k.AABB))Y.push($.object)}}}return Y}findAllOverlaps(){let k=[],Y=new Set;for(let z of this.xEndpoints)if(z.isMin){for(let w of Y)if(this.aabbIntersects(z.object.AABB,w.AABB))k.push({a:z.object,b:w});Y.add(z.object)}else Y.delete(z.object);return k}insertEndpointSorted(k){let Y=0,z=this.xEndpoints.length;while(Y<z){let w=Math.floor((Y+z)/2),$=this.xEndpoints[w];if($.value<k.value||$.value===k.value&&$.isMin&&!k.isMin)Y=w+1;else z=w}this.xEndpoints.splice(Y,0,k)}removeEndpoint(k){let Y=this.xEndpoints.indexOf(k);if(Y!==-1)this.xEndpoints.splice(Y,1)}aabbIntersects(k,Y){return!(k.max.x<Y.min.x||k.min.x>Y.max.x||k.max.y<Y.min.y||k.min.y>Y.max.y)}draw(k){k.strokeStyle="orange",k.lineWidth=1,k.globalAlpha=0.3;for(let Y of this.xEndpoints)k.beginPath(),k.moveTo(Y.value,-1000),k.lineTo(Y.value,1000),k.stroke(),k.fillStyle=Y.isMin?"green":"red",k.fillText(Y.isMin?"min":"max",Y.value,-950);k.globalAlpha=1}getStats(){return{endpointCount:this.xEndpoints.length,objectCount:this.objects.size}}}class C{root=null;nodeCount=0;margin=0.1;clear(){this.root=null,this.nodeCount=0}insert(k){let Y=new t(k);this.insertNode(Y)}insertNode(k){if(this.nodeCount++,!this.root){this.root=k;return}let Y=this.findBestSibling(k),z=Y.parent,w=new t;if(w.parent=z,w.setBranch(Y,k),z)if(z.children[0]===Y)z.children[0]=w;else z.children[1]=w;else this.root=w;let $=w.parent;while($)$.updateAABB(this.margin),$.height=1+Math.max($.children[0]?$.children[0].height:0,$.children[1]?$.children[1].height:0),$=this.balance($),$=$.parent}findBestSibling(k){let Y=this.root;while(!Y.isLeaf()){let z=Y.children[0],w=Y.children[1],$=this.getArea(Y.aabb),G=this.combineAABB(Y.aabb,k.aabb),J=this.getArea(G),Z=2*J,Q=2*(J-$),U,R=this.combineAABB(k.aabb,z.aabb);if(z.isLeaf())U=this.getArea(R)+Q;else{let K=this.getArea(z.aabb);U=this.getArea(R)-K+Q}let W,S=this.combineAABB(k.aabb,w.aabb);if(w.isLeaf())W=this.getArea(S)+Q;else{let K=this.getArea(w.aabb);W=this.getArea(S)-K+Q}if(Z<U&&Z<W)break;Y=U<W?z:w}return Y}balance(k){if(k.isLeaf())return k;let Y=k.getBalance();if(Y<-1)return this.rotateLeft(k);else if(Y>1)return this.rotateRight(k);return k}rotateLeft(k){let Y=k.children[1],z=Y.children[0],w=Y.children[1];if(Y.children[0]=k,Y.parent=k.parent,k.parent=Y,Y.parent)if(Y.parent.children[0]===k)Y.parent.children[0]=Y;else Y.parent.children[1]=Y;else this.root=Y;if(z.height>w.height)Y.children[1]=z,k.children[1]=w,w.parent=k,k.updateAABB(this.margin),Y.updateAABB(this.margin),k.height=1+Math.max(k.children[0].height,k.children[1].height),Y.height=1+Math.max(k.height,z.height);else Y.children[1]=w,k.children[1]=z,z.parent=k,k.updateAABB(this.margin),Y.updateAABB(this.margin),k.height=1+Math.max(k.children[0].height,k.children[1].height),Y.height=1+Math.max(k.height,w.height);return Y}rotateRight(k){let Y=k.children[0],z=Y.children[0],w=Y.children[1];if(Y.children[1]=k,Y.parent=k.parent,k.parent=Y,Y.parent)if(Y.parent.children[0]===k)Y.parent.children[0]=Y;else Y.parent.children[1]=Y;else this.root=Y;if(z.height>w.height)Y.children[0]=z,k.children[0]=w,w.parent=k,k.updateAABB(this.margin),Y.updateAABB(this.margin),k.height=1+Math.max(k.children[0].height,k.children[1].height),Y.height=1+Math.max(z.height,k.height);else Y.children[0]=w,k.children[0]=z,z.parent=k,k.updateAABB(this.margin),Y.updateAABB(this.margin),k.height=1+Math.max(k.children[0].height,k.children[1].height),Y.height=1+Math.max(w.height,k.height);return Y}retrieve(k){let Y=[];if(!this.root)return Y;let z=[this.root];while(z.length>0){let w=z.pop();if(this.aabbIntersects(w.aabb,k.AABB))if(w.isLeaf()){if(w.object&&w.object!==k)Y.push(w.object)}else{if(w.children[0])z.push(w.children[0]);if(w.children[1])z.push(w.children[1])}}return Y}draw(k){if(!this.root)return;this.drawNode(k,this.root,0)}drawNode(k,Y,z){let{min:w,max:$}=Y.aabb,G=["red","blue","green","orange","purple","brown"];if(k.strokeStyle=G[z%G.length],k.lineWidth=Math.max(1,3-z),k.beginPath(),k.rect(w.x,w.y,$.x-w.x,$.y-w.y),k.stroke(),!Y.isLeaf()){if(Y.children[0])this.drawNode(k,Y.children[0],z+1);if(Y.children[1])this.drawNode(k,Y.children[1],z+1)}}getArea(k){return(k.max.x-k.min.x)*(k.max.y-k.min.y)}combineAABB(k,Y){return{min:{x:Math.min(k.min.x,Y.min.x),y:Math.min(k.min.y,Y.min.y)},max:{x:Math.max(k.max.x,Y.max.x),y:Math.max(k.max.y,Y.max.y)}}}aabbIntersects(k,Y){return!(k.max.x<Y.min.x||k.min.x>Y.max.x||k.max.y<Y.min.y||k.min.y>Y.max.y)}getStats(){return{nodeCount:this.nodeCount,height:this.root?this.root.height:0}}}import{PointCal as H}from"@ue-too/math";function Z0(k,Y,z){if(k.isStatic()&&Y.isStatic())return;let w=0.4,$=k.isStatic()||k.isMovingStatic()?0:1/k.mass,G=Y.isStatic()||Y.isMovingStatic()?0:1/Y.mass,J=H.subVector(k.linearVelocity,Y.linearVelocity),Z=-(1+w)*H.dotProduct(J,z);Z/=$+G;let Q=H.multiplyVectorByScalar(z,Z*$),U=H.multiplyVectorByScalar(z,Z*G);k.linearVelocity=H.addVector(k.linearVelocity,Q),Y.linearVelocity=H.subVector(Y.linearVelocity,U)}function z0(k,Y,z){if(k.isStatic()&&Y.isStatic())return;let w=0.4,$=k.isStatic()||k.isMovingStatic()?0:1/k.mass,G=Y.isStatic()||Y.isMovingStatic()?0:1/Y.mass,J=k.isStatic()||k.isMovingStatic()?0:1/k.momentOfInertia,Z=Y.isStatic()||Y.isMovingStatic()?0:1/Y.momentOfInertia,Q=[];for(let U=0;U<z.contactPoints.length;U++){let R=z.contactPoints[U],W=H.subVector(R,k.center),S=H.subVector(R,Y.center),K={x:-W.y,y:W.x},_={x:-S.y,y:S.x},E=H.multiplyVectorByScalar(K,k.angularVelocity),V=H.multiplyVectorByScalar(_,Y.angularVelocity),L=H.subVector(H.addVector(k.linearVelocity,E),H.addVector(Y.linearVelocity,V)),F=H.dotProduct(L,z.normal),B=H.dotProduct(K,z.normal),O=H.dotProduct(_,z.normal),D=$+G+B*B*J+O*O*Z,g=-(1+w)*F;g/=D,g/=z.contactPoints.length,Q.push(H.multiplyVectorByScalar(z.normal,g))}Q.forEach((U,R)=>{let W=H.multiplyVectorByScalar(U,$),S=H.multiplyVectorByScalar(U,G);k.linearVelocity=H.addVector(k.linearVelocity,W);let K=H.crossProduct(H.subVector(z.contactPoints[R],k.center),U).z;K=K==null?0:K;let _=H.crossProduct(H.subVector(z.contactPoints[R],Y.center),U).z;_=_==null?0:_,k.angularVelocity+=K*J,Y.angularVelocity-=_*Z,Y.linearVelocity=H.subVector(Y.linearVelocity,S)})}function c(k,Y){if(k.min.x<=Y.max.x&&Y.min.x<=k.max.x&&(k.min.y<=Y.max.y&&Y.min.y<=k.max.y))return!0;return!1}function i(k,Y){let z=[],w=k.getCollisionAxes(Y),$=Y.getCollisionAxes(k);z.push(...w),z.push(...$);let G=!0,J=Number.MAX_VALUE,Z=z[0];if(z.forEach((Q)=>{let U=k.getMinMaxProjection(Q),R=Y.getMinMaxProjection(Q);if(U.min>=R.max||R.min>=U.max)G=!1;else{let W=Math.abs(Math.min(U.max,R.max)-Math.max(R.min,U.min));if(W<J){if(J=W,Z=Q,U.max<R.max)Z=H.multiplyVectorByScalar(Z,-1)}}}),G)return{collision:G,depth:J,normal:Z};else return{collision:!1,depth:void 0,normal:void 0}}function N0(k,Y,z){if(!z)return[];let w=[];return Y.forEach(($)=>{let{bodyA:G,bodyB:J}=$;if(G==J)return;let Z=G.center.z==null?0:G.center.z,Q=J.center.z==null?0:J.center.z;if(Math.abs(Z-Q)>0.5)return;let{collision:U,depth:R,normal:W}=i(G,J);if(U&&W!==void 0&&R!==void 0){let S=H.multiplyVectorByScalar(W,R/2),K=H.multiplyVectorByScalar(W,-R/2);if(!G.isStatic())G.move(S);if(!J.isStatic())J.move(K);if(G.isStatic())J.move(K);if(J.isStatic())G.move(S);let _=G.getNormalOfSignificantFace(H.multiplyVectorByScalar(W,-1)),E=J.getNormalOfSignificantFace(W),V=G.getSignificantVertices(H.multiplyVectorByScalar(W,-1)),L=J.getSignificantVertices(W),F=Math.abs(H.dotProduct(_,H.multiplyVectorByScalar(W,-1))),B=Math.abs(H.dotProduct(E,W));if(L.length==1||V.length==1)if(L.length==1)w.push(L[0]);else w.push(V[0]);else if(F>B){let O=G.getAdjacentFaces(H.multiplyVectorByScalar(W,-1)),D=[...L];for(let f=0;f<O.length-1;f++){let N=O[f].startPoint.coord,T=O[f].endPoint.coord,m=H.subVector(T,N),x=H.subVector(D[0],N),d=H.subVector(D[1],N),A=H.angleFromA2B(m,x)>=0,y=H.angleFromA2B(m,d)>=0;if((A?1:0)^(y?1:0)){let p=H.getLineIntersection(N,T,D[0],D[1]);if(p.intersects&&p.intersection!==void 0)if(A)D[1]=p.intersection;else D[0]=p.intersection}}let g=O[O.length-1],I=g.startPoint.coord,h=g.endPoint.coord,v=H.subVector(h,I),P=H.subVector(D[0],I),b=H.subVector(D[1],I),l=H.angleFromA2B(v,P)>=0,a=H.angleFromA2B(v,b)>=0;if(l)w.push(D[0]);if(a)w.push(D[1])}else{let O=J.getAdjacentFaces(W),D=[...V];if(D.length==0)console.log("warning");let g=0;for(let N=0;N<O.length-1;N++){let T=O[N].startPoint.coord,m=O[N].endPoint.coord,x=H.subVector(m,T),d=H.subVector(D[0],T),A=H.subVector(D[1],T),y=H.angleFromA2B(x,d)>=0,p=H.angleFromA2B(x,A)>=0;if((y?1:0)^(p?1:0)){g+=1;let s=H.getLineIntersection(T,m,D[0],D[1]);if(s.intersects&&s.intersection!==void 0)if(y)D[1]=s.intersection;else D[0]=s.intersection}}let I=O[O.length-1],h=I.startPoint.coord,v=I.endPoint.coord,P=H.subVector(v,h),b=H.subVector(D[0],h),l=H.subVector(D[1],h),a=H.angleFromA2B(P,b)>=0,f=H.angleFromA2B(P,l)>=0;if(a)w.push(D[0]);if(f)w.push(D[1])}if(z)z0(G,J,{normal:W,contactPoints:w})}}),w}function g0(k,Y,z){if(!z)return;Y.forEach((w)=>{let $=k[w.bodyAIndex],G=k[w.bodyBIndex],{collision:J,depth:Z,normal:Q}=i($,G);if(J&&Q!==void 0&&Z!==void 0){let U=H.multiplyVectorByScalar(Q,Z/2),R=H.multiplyVectorByScalar(Q,-Z/2);if(!$.isStatic())$.move(U);if(!G.isStatic())G.move(R);if($.isStatic())G.move(R);if(G.isStatic())$.move(U);if(z)Z0($,G,Q)}})}function M0(k,Y){let z=[];for(let w=0;w<=Y.length-1;w++){let $=k.retrieve(Y[w]);for(let G=0;G<=$.length-1;G++){let J=Y[w],Z=$[G];if(J.isStatic()&&Z.isStatic())continue;if(!c(J.AABB,Z.AABB))continue;z.push({bodyA:J,bodyB:Z})}}return z}function T0(k,Y){let z=[];for(let w=0;w<=Y.length-1;w++){let $=k.retrieve(Y[w]);for(let G=0;G<=$.length-1;G++){let J=Y[w],Z=$[G];if(J.isStatic()&&Z.isStatic())continue;if(!c(J.AABB,Z.AABB))continue;z.push({bodyA:J,bodyB:Z})}}return z}function j0(k,Y){let z=[];for(let w=0;w<=Y.length-1;w++){let $=k.retrieve(Y[w]);for(let G=0;G<=$.length-1;G++){let J=Y[w],Z=$[G];if(J.isStatic()&&Z.isStatic())continue;if(!c(J.AABB,Z.AABB))continue;z.push({bodyAIndex:w,bodyBIndex:G})}}return z}function w0(k,Y){let z=[];for(let w=0;w<=Y.length-1;w++){let $=k.retrieve(Y[w]);for(let G=0;G<=$.length-1;G++){let J=Y[w],Z=$[G];if(J.isStatic()&&Z.isStatic())continue;if(!o(J.collisionFilter,Z.collisionFilter))continue;if(!c(J.AABB,Z.AABB))continue;z.push({bodyA:J,bodyB:Z})}}return z}function $0(k,Y,z){let w=[],$=[];return Y.forEach((G)=>{let{bodyA:J,bodyB:Z}=G,{collision:Q,depth:U,normal:R}=i(J,Z);if(Q&&R!==void 0&&U!==void 0){let W=[],S={x:(J.center.x+Z.center.x)/2,y:(J.center.y+Z.center.y)/2};W.push(S);let K={bodyA:J,bodyB:Z,contactPoints:W,normal:R,depth:U};if($.push(K),w.push(...W),z){let _=H.multiplyVectorByScalar(R,U/2),E=H.multiplyVectorByScalar(R,-U/2);if(!J.isStatic())J.move(_);if(!Z.isStatic())Z.move(E);if(J.isStatic())Z.move(E);if(Z.isStatic())J.move(_);z0(J,Z,{normal:R,contactPoints:W})}}}),{contactPoints:w,collisions:$}}class e{pairs=new Map;frameNumber=0;maxPairAge=10;constructor(){}getPairId(k,Y){let z=this.getBodyId(k),w=this.getBodyId(Y);return z<w?`${z}:${w}`:`${w}:${z}`}getBodyId(k){return k.toString()}updatePairs(k){this.frameNumber++;let Y={created:[],updated:[],removed:[]};this.pairs.forEach((w)=>{w.isActive=!1});for(let w of k){let $=this.getPairId(w.bodyA,w.bodyB),G=this.pairs.get($);if(G)G.isActive=!0,G.frameUpdated=this.frameNumber,G.contactPoints=w.contactPoints||[],G.normal=w.normal,G.depth=w.depth,Y.updated.push(G);else{let J={bodyA:w.bodyA,bodyB:w.bodyB,id:$,isActive:!0,contactPoints:w.contactPoints||[],normal:w.normal,depth:w.depth,frameCreated:this.frameNumber,frameUpdated:this.frameNumber};this.pairs.set($,J),Y.created.push(J)}}let z=[];return this.pairs.forEach((w,$)=>{if(!w.isActive&&this.frameNumber-w.frameUpdated>this.maxPairAge)z.push($),Y.removed.push(w)}),z.forEach((w)=>{this.pairs.delete(w)}),Y}getActivePairs(){return Array.from(this.pairs.values()).filter((k)=>k.isActive)}getPair(k,Y){let z=this.getPairId(k,Y);return this.pairs.get(z)}clear(){this.pairs.clear(),this.frameNumber=0}getStats(){return{totalPairs:this.pairs.size,activePairs:this.getActivePairs().length,frameNumber:this.frameNumber}}}class X0{rigidBodyList;rigidBodyMap;_resolveCollision;maxTransWidth;maxTransHeight;bound;spatialIndex;spatialIndexType;constraints;pinJoints=[];pairManager;enableSleeping=!0;_context=null;constructor(k,Y,z="dynamictree"){if(this.maxTransHeight=Y,this.maxTransWidth=k,this.spatialIndexType=z,this.bound=new j({x:-this.maxTransWidth,y:-this.maxTransHeight},2*this.maxTransWidth,2*this.maxTransHeight),z==="dynamictree")this.spatialIndex=new C;else if(z==="sap")this.spatialIndex=new u;else this.spatialIndex=new M(0,this.bound);this.rigidBodyList=[],this.rigidBodyMap=new Map,this._resolveCollision=!0,this.constraints=[],this.pairManager=new e}addRigidBody(k,Y){if(this.rigidBodyList.push(Y),this.rigidBodyMap.set(k,Y),this.spatialIndexType==="sap")this.spatialIndex.insert(Y)}removeRigidBody(k){if(this.rigidBodyMap.has(k)){let Y=this.rigidBodyMap.get(k);if(this.rigidBodyMap.delete(k),Y&&this.spatialIndexType==="sap"&&this.spatialIndex instanceof u)this.spatialIndex.remove(Y);let z=this.rigidBodyList.findIndex((w)=>w===Y);if(z!==-1)this.rigidBodyList.splice(z,1)}}step(k){if(this.enableSleeping)this.getRigidBodyList().forEach((Y)=>{Y.updateSleeping(k)});if(this._resolveCollision){let Y=this.resolveCollisionPhase()}this.constraints.forEach((Y)=>Y.enforce(k)),this.getRigidBodyList().forEach((Y)=>{if(!Y.isSleeping)Y.step(k)})}resolveCollisionPhase(){let k=[];if(this.spatialIndexType==="sap")this.rigidBodyMap.forEach(($)=>{if(!this.enableSleeping||!$.isSleeping){if(k.push($),this.spatialIndex instanceof u)this.spatialIndex.update($)}});else this.spatialIndex.clear(),this.rigidBodyMap.forEach(($)=>{if(!this.enableSleeping||!$.isSleeping)k.push($),this.spatialIndex.insert($)});let Y=w0(this.spatialIndex,k),z=$0(k,Y,this._resolveCollision),w=this.pairManager.updatePairs(z.collisions);if(this.enableSleeping)w.created.forEach(($)=>{if($.bodyA.isSleeping)$.bodyA.setSleeping(!1);if($.bodyB.isSleeping)$.bodyB.setSleeping(!1)});return z.contactPoints}get resolveCollision(){return this._resolveCollision}set resolveCollision(k){this._resolveCollision=k}getRigidBodyList(){let k=[];return this.rigidBodyMap.forEach((Y)=>{k.push(Y)}),k}getRigidBodyMap(){return this.rigidBodyMap}setMaxTransHeight(k){this.maxTransHeight=k}setMaxTransWidth(k){this.maxTransWidth=k}addConstraint(k){this.constraints.push(k)}getConstraints(){return this.constraints}addPinJoint(k,Y,z,w){this.pinJoints.push({bodyA:k,bodyB:Y,anchorA:z,anchorB:w})}get currentSpatialIndexType(){return this.spatialIndexType}setSpatialIndexType(k){if(k===this.spatialIndexType)return;if(this.spatialIndexType=k,k==="dynamictree")this.spatialIndex=new C;else if(k==="sap")this.spatialIndex=new u;else this.spatialIndex=new M(0,this.bound)}getSpatialIndexStats(){if(this.spatialIndex instanceof C)return this.spatialIndex.getStats();else if(this.spatialIndex instanceof u)return{type:this.spatialIndexType,...this.spatialIndex.getStats()};return{type:this.spatialIndexType,objects:this.rigidBodyMap.size}}get sleepingEnabled(){return this.enableSleeping}set sleepingEnabled(k){if(this.enableSleeping=k,!k)this.rigidBodyMap.forEach((Y)=>{if(Y.isSleeping)Y.setSleeping(!1)})}getPairManager(){return this.pairManager}getCollisionStats(){return{...this.pairManager.getStats(),sleepingBodies:Array.from(this.rigidBodyMap.values()).filter((k)=>k.isSleeping).length,totalBodies:this.rigidBodyMap.size}}}import{PointCal as q}from"@ue-too/math";class Q0{anchorA;worldAnchorA;bodyA;constructor(k,Y,z){this.bodyA=k,this.anchorA=Y,this.worldAnchorA=z}enforce(k){this.solveWorldPinJointConstraint(k)}solveWorldPinJointConstraint(k){let Y=this.bodyA,z=this.anchorA,w=this.worldAnchorA,$=q.addVector(Y.center,q.rotatePoint(z,Y.orientationAngle)),G=q.subVector($,w),J=q.subVector($,Y.center),Z=q.addVector(Y.linearVelocity,q.crossProduct({x:0,y:0,z:Y.angularVelocity},J)),Q=Y.isStatic()?0:1/Y.mass,U=Y.isStatic()?0:1/Y.momentOfInertia,R={x:Q+U*J.y*J.y,y:Q+U*J.x*J.x,xy:-U*J.x*J.y},W=1,S={x:-R.x*G.x-R.xy*G.y-1*G.x/k-Z.x,y:-R.xy*G.x-R.y*G.y-1*G.y/k-Z.y};if(!Y.isStatic())Y.linearVelocity.x+=Q*S.x,Y.linearVelocity.y+=Q*S.y,Y.angularVelocity+=U*(J.x*S.y-J.y*S.x)}}class R0{anchorA;anchorB;bodyA;bodyB;constructor(k,Y,z,w){this.bodyA=k,this.bodyB=Y,this.anchorA=z,this.anchorB=w}enforce(k){this.solvePinJointConstraint(k)}solvePinJointConstraint(k){let Y=this.bodyA,z=this.bodyB,w=this.anchorA,$=this.anchorB,G=q.addVector(Y.center,q.rotatePoint(w,Y.orientationAngle)),J=q.addVector(z.center,q.rotatePoint($,z.orientationAngle)),Z=q.subVector(J,G),Q=q.subVector(G,Y.center),U=q.subVector(J,z.center),R=q.subVector(q.addVector(z.linearVelocity,q.crossProduct({x:0,y:0,z:z.angularVelocity},U)),q.addVector(Y.linearVelocity,q.crossProduct({x:0,y:0,z:Y.angularVelocity},Q))),W=Y.isStatic()?0:1/Y.mass,S=z.isStatic()?0:1/z.mass,K=Y.isStatic()?0:1/Y.momentOfInertia,_=z.isStatic()?0:1/z.momentOfInertia,E={x:W+S+K*Q.y*Q.y+_*U.y*U.y,y:W+S+K*Q.x*Q.x+_*U.x*U.x,xy:-K*Q.x*Q.y-_*U.x*U.y},V=1,L={x:-E.x*Z.x-E.xy*Z.y-1*Z.x/k-R.x,y:-E.xy*Z.x-E.y*Z.y-1*Z.y/k-R.y};if(!Y.isStatic())Y.linearVelocity.x-=W*L.x,Y.linearVelocity.y-=W*L.y,Y.angularVelocity-=K*(Q.x*L.y-Q.y*L.x);if(!z.isStatic())z.linearVelocity.x+=S*L.x,z.linearVelocity.y+=S*L.y,z.angularVelocity+=_*(U.x*L.y-U.y*L.x)}}function P0(k,Y){let{bodyA:z,bodyB:w,anchorA:$,anchorB:G}=k,J=q.addVector(z.center,q.rotatePoint($,z.orientationAngle)),Z=q.addVector(w.center,q.rotatePoint(G,w.orientationAngle)),Q=q.subVector(Z,J),U=q.subVector(J,z.center),R=q.subVector(Z,w.center),W=q.subVector(q.addVector(w.linearVelocity,q.crossProduct({x:0,y:0,z:w.angularVelocity},R)),q.addVector(z.linearVelocity,q.crossProduct({x:0,y:0,z:z.angularVelocity},U))),S=z.isStatic()?0:1/z.mass,K=w.isStatic()?0:1/w.mass,_=z.isStatic()?0:1/z.momentOfInertia,E=w.isStatic()?0:1/w.momentOfInertia,V={x:S+K+_*U.y*U.y+E*R.y*R.y,y:S+K+_*U.x*U.x+E*R.x*R.x,xy:-_*U.x*U.y-E*R.x*R.y},L=0.5,F={x:-V.x*Q.x-V.xy*Q.y-0.5*Q.x/Y-W.x,y:-V.xy*Q.x-V.y*Q.y-0.5*Q.y/Y-W.y};if(!z.isStatic())z.linearVelocity.x-=S*F.x,z.linearVelocity.y-=S*F.y,z.angularVelocity-=_*(U.x*F.y-U.y*F.x);if(!w.isStatic())w.linearVelocity.x+=K*F.x,w.linearVelocity.y+=K*F.y,w.angularVelocity+=E*(R.x*F.y-R.y*F.x)}function x0(k,Y){let{body:z,localAnchor:w,worldAnchor:$}=k,G=q.addVector(z.center,q.rotatePoint(w,z.orientationAngle)),J=q.subVector(G,$),Z=q.subVector(G,z.center),Q=q.addVector(z.linearVelocity,q.crossProduct({x:0,y:0,z:z.angularVelocity},Z)),U=z.isStatic()?0:1/z.mass,R=z.isStatic()?0:1/z.momentOfInertia,W={x:U+R*Z.y*Z.y,y:U+R*Z.x*Z.x,xy:-R*Z.x*Z.y},S=0.2,K={x:-W.x*J.x-W.xy*J.y-0.2*J.x/Y-Q.x,y:-W.xy*J.x-W.y*J.y-0.2*J.y/Y-Q.y};if(!z.isStatic())z.linearVelocity.x+=U*K.x,z.linearVelocity.y+=U*K.y,z.angularVelocity+=R*(Z.x*K.y-Z.y*K.x)}export{x0 as solveWorldPinJointConstraint,P0 as solvePinJointConstraint,z0 as resolveCollisionWithRotation,Z0 as resolveCollision,$0 as narrowPhaseWithRigidBodyAndPairs,N0 as narrowPhaseWithRigidBody,g0 as narrowPhase,i as intersects,o as canCollide,w0 as broadPhaseWithSpatialIndexFiltered,T0 as broadPhaseWithSpatialIndex,M0 as broadPhaseWithRigidBodyReturned,j0 as broadPhase,c as aabbIntersects,X0 as World,J0 as VisualPolygonBody,G0 as VisaulCircleBody,u as SweepAndPrune,j as RectangleBound,M as QuadTree,k0 as Polygon,R0 as PinJoint,e as PairManager,Q0 as FixedPinJoint,C as DynamicTree,r as DEFAULT_COLLISION_FILTER,H0 as CollisionCategory,Y0 as Circle,n as BaseRigidBody};
2
2
 
3
3
  //# debugId=ABD8E4A8414E2D7E64756E2164756E21
4
+
5
+ //# sourceMappingURL=index.js.map
package/index.js.map CHANGED
@@ -1,6 +1,15 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/rigidbody.ts", "../src/collision-filter.ts", "../src/quadtree.ts", "../src/dynamic-tree.ts", "../src/collision.ts", "../src/pair-manager.ts", "../src/world.ts", "../src/constraint.ts"],
3
+ "sources": [
4
+ "rigidbody.ts",
5
+ "collision-filter.ts",
6
+ "quadtree.ts",
7
+ "dynamic-tree.ts",
8
+ "collision.ts",
9
+ "pair-manager.ts",
10
+ "world.ts",
11
+ "constraint.ts"
12
+ ],
4
13
  "sourcesContent": [
5
14
  "import { PointCal, Point } from \"@ue-too/math\";\nimport { CollisionFilter, DEFAULT_COLLISION_FILTER } from \"./collision-filter\";\n\n/**\n * Rigid body interface for 2D physics simulation.\n *\n * @remarks\n * Represents a physical object in the physics world with mass, velocity,\n * rotation, and collision properties. Can be either static (immovable) or\n * dynamic (responds to forces).\n *\n * Implemented by {@link Circle} and {@link Polygon} classes.\n *\n * @category Core\n */\nexport interface RigidBody {\n /** Center position in world coordinates */\n center: Point;\n /** Rotation angle in radians */\n orientationAngle: number;\n /** Linear velocity (pixels/second) */\n linearVelocity: Point;\n /** Angular velocity (radians/second) */\n angularVelocity: number;\n /** Axis-Aligned Bounding Box for broad phase collision */\n AABB: {min: Point, max: Point};\n /** Mass in arbitrary units (affects force response) */\n mass: number;\n /** Static friction coefficient (0-1) */\n staticFrictionCoeff: number;\n /** Moment of inertia (rotational mass) */\n momentOfInertia: number;\n\n /** Collision filtering configuration */\n collisionFilter: CollisionFilter;\n \n // Sleeping system\n isSleeping: boolean;\n sleepThreshold: number;\n sleepTime: number;\n timeAtRest: number;\n \n step(deltaTime: number): void;\n isStatic(): boolean;\n isMovingStatic(): boolean;\n getMinMaxProjection(unitvector: Point): {min: number, max: number};\n getCollisionAxes(relativeBody: RigidBody): Point[];\n applyForce(force: Point): void;\n applyForceInOrientation(force: Point): void;\n // applyForceAtPoint(force: Point, point: Point): void;\n move(delta: Point): void;\n significantVertex(collisionNormal: Point): Point;\n getSignificantVertices(collisionNormal: Point): Point[];\n getNormalOfSignificantFace(collisionNormal: Point): Point;\n getAdjacentFaces(collisionNormal: Point): {startPoint: {coord: Point, index: number}, endPoint: {coord: Point, index: number}}[];\n \n // Sleeping methods\n setSleeping(sleeping: boolean): void;\n updateSleeping(deltaTime: number): void;\n}\n\nexport interface VisualComponent{\n draw(ctx: CanvasRenderingContext2D): void;\n}\n\nexport abstract class BaseRigidBody implements RigidBody{\n \n protected _center: Point;\n protected _mass: number = 50;\n protected _linearVelocity: Point;\n protected _angularVelocity: number; // in radians\n protected _orientationAngle: number = 0;\n protected linearAcceleartion: Point;\n protected force: Point;\n protected isStaticBody: boolean = false;\n protected _staticFrictionCoeff: number = 0.3;\n protected dynamicFrictionCoeff: number = 0.3;\n protected frictionEnabled: boolean = false;\n protected isMovingStaticBody: boolean = false;\n protected angularDampingFactor: number = 0.005;\n \n // Collision filtering\n public collisionFilter: CollisionFilter = { ...DEFAULT_COLLISION_FILTER };\n \n // Sleeping system\n public isSleeping: boolean = false;\n public sleepThreshold: number = 0.01; // Velocity threshold to consider sleeping\n public sleepTime: number = 0.5; // Time (seconds) to wait before sleeping\n public timeAtRest: number = 0;\n \n\n constructor(center: Point, _orientationAngle: number = 0, mass: number = 50, isStaticBody: boolean = false, frictionEnabled: boolean = false){\n this._center = center;\n this._orientationAngle = _orientationAngle;\n this._mass = mass;\n this.isStaticBody = isStaticBody;\n this.frictionEnabled = frictionEnabled;\n this.force = {x: 0, y: 0};\n this.linearAcceleartion = {x: 0, y: 0};\n this._linearVelocity = {x: 0, y: 0};\n this._angularVelocity = 0;\n }\n\n move(delta: Point): void {\n if (!this.isStatic()){\n this._center = PointCal.addVector(this._center, delta);\n }\n }\n\n rotateRadians(angle: number): void {\n this._orientationAngle += angle;\n }\n\n getCenter(): Point {\n return this._center;\n }\n\n getOrientationAngle(): number{\n return this._orientationAngle;\n }\n\n get angularVelocity(): number{\n return this._angularVelocity;\n }\n\n set angularVelocity(angularVelocity: number){\n this._angularVelocity = angularVelocity;\n }\n\n get orientationAngle(): number{\n return this._orientationAngle;\n }\n\n isStatic(): boolean{\n return this.isStaticBody;\n }\n\n isMovingStatic(): boolean {\n return this.isMovingStaticBody;\n }\n\n setLinearVelocity(linearVelocity: Point): void {\n this._linearVelocity = linearVelocity;\n }\n\n setMovingStatic(movingStatic: boolean):void {\n this.isMovingStaticBody = movingStatic;\n }\n\n setOrientationAngle(angle: number): void{\n this._orientationAngle = angle;\n }\n\n applyForce(force: Point): void {\n if (PointCal.magnitude(this.force) !== 0){\n this.force = PointCal.addVector(this.force, force);\n } else {\n this.force = force;\n }\n }\n\n applyForceInOrientation(force: Point | number): void {\n let forceTransformed: Point;\n if (typeof force === \"number\") {\n forceTransformed = PointCal.rotatePoint({x: force, y: 0}, this._orientationAngle);\n } else {\n forceTransformed = PointCal.rotatePoint(force, this._orientationAngle);\n }\n this.applyForce(forceTransformed);\n }\n\n step(deltaTime: number): void {\n if (this.frictionEnabled) {\n if (this.isStatic() || \n (this.linearVelocity.x == 0 && \n this.linearVelocity.y == 0 && \n PointCal.magnitude(PointCal.subVector({x: this.force.x, y: this.force.y}, {x: 0, y: 0})) >= 0 && \n PointCal.magnitude({x: this.force.x, y: this.force.y}) < this.staticFrictionCoeff * this.mass * 9.81)\n ) {\n if (this.force.z != undefined) {\n this.force = {x: 0, y: 0, z: this.force.z};\n } else {\n this.force = {x: 0, y: 0};\n }\n // return;\n } else {\n let kineticFrictionDirection = PointCal.multiplyVectorByScalar(PointCal.unitVector({x: this._linearVelocity.x, y: this._linearVelocity.y}), -1);\n let kineticFriction = PointCal.multiplyVectorByScalar(kineticFrictionDirection, this.dynamicFrictionCoeff * this.mass * 9.81);\n this.force = PointCal.addVector(this.force, kineticFriction);\n }\n }\n const angularDamping = this._angularVelocity != 0 ? this._angularVelocity > 0 ? -this.angularDampingFactor : this.angularDampingFactor : 0;\n // console.log(\"angular velocity\", this._angularVelocity);\n // console.log(\"angular damping\", angularDamping);\n if (Math.abs(this._angularVelocity) < Math.abs(angularDamping)) {\n this._angularVelocity = 0;\n } else {\n this._angularVelocity += angularDamping;\n }\n this._orientationAngle += this._angularVelocity * deltaTime;\n if (PointCal.magnitude({x: this._linearVelocity.x, y: this._linearVelocity.y}) < PointCal.magnitude(PointCal.divideVectorByScalar(PointCal.multiplyVectorByScalar(this.force, deltaTime), this.mass))){\n if (this._linearVelocity.z != undefined) {\n this._linearVelocity = {x: 0, y: 0, z: this._linearVelocity.z};\n } else {\n this._linearVelocity = {x: 0, y: 0};\n }\n }\n const gravitationalForce = -9.81 * this._mass;\n this.force = PointCal.addVector(this.force, {x: 0, y: 0, z: gravitationalForce});\n const deltaLinearVelocity = PointCal.divideVectorByScalar(PointCal.multiplyVectorByScalar(this.force, deltaTime), this.mass);\n this._linearVelocity = PointCal.addVector(this._linearVelocity, deltaLinearVelocity);\n const deltaCenter = PointCal.multiplyVectorByScalar(this._linearVelocity, deltaTime);\n // console.log('delta center', deltaCenter);\n this._center = PointCal.addVector(this._center, deltaCenter);\n if (this._center.z != undefined && this._center.z < 0) {\n this._center.z = 0;\n }\n this.force = {x: 0, y: 0};\n }\n\n get center(): Point {\n return this._center;\n }\n\n set center(dest: Point){\n this._center = dest;\n }\n\n get linearVelocity(): Point {\n return this._linearVelocity;\n }\n\n set linearVelocity(dest: Point){\n this._linearVelocity = dest;\n }\n\n get mass(): number{\n return this._mass;\n }\n\n get staticFrictionCoeff(): number{\n return this._staticFrictionCoeff;\n }\n\n set staticFrictionCoeff(coeff: number){\n this._staticFrictionCoeff = coeff;\n }\n\n abstract get momentOfInertia(): number;\n abstract getMinMaxProjection(unitvector: Point): {min: number, max: number};\n abstract getCollisionAxes(relativeBody: RigidBody): Point[];\n abstract get AABB(): {min: Point, max: Point};\n abstract significantVertex(collisionNormal: Point): Point;\n abstract getSignificantVertices(collisionNormal: Point): Point[];\n abstract getNormalOfSignificantFace(collisionNormal: Point): Point;\n abstract getAdjacentFaces(collisionNormal: Point): {startPoint: {coord: Point, index: number}, endPoint: {coord: Point, index: number}}[];\n\n // Sleeping methods\n setSleeping(sleeping: boolean): void {\n if (sleeping && !this.isSleeping) {\n this.isSleeping = true;\n this._linearVelocity = { x: 0, y: 0 };\n this._angularVelocity = 0;\n } else if (!sleeping && this.isSleeping) {\n this.isSleeping = false;\n this.timeAtRest = 0;\n }\n }\n\n updateSleeping(deltaTime: number): void {\n if (this.isStatic() || this.isMovingStatic()) return;\n\n const speed = PointCal.magnitude(this._linearVelocity);\n const angularSpeed = Math.abs(this._angularVelocity);\n\n if (speed < this.sleepThreshold && angularSpeed < this.sleepThreshold) {\n this.timeAtRest += deltaTime;\n if (this.timeAtRest >= this.sleepTime) {\n this.setSleeping(true);\n }\n } else {\n this.timeAtRest = 0;\n if (this.isSleeping) {\n this.setSleeping(false);\n }\n }\n }\n}\n\nexport class VisaulCircleBody implements VisualComponent, RigidBody {\n\n private _circle: Circle;\n private _context: CanvasRenderingContext2D;\n \n // Collision filtering\n public collisionFilter: CollisionFilter = { ...DEFAULT_COLLISION_FILTER };\n \n // Sleeping system\n public isSleeping: boolean = false;\n public sleepThreshold: number = 0.01;\n public sleepTime: number = 0.5;\n public timeAtRest: number = 0;\n\n constructor(center: Point = {x: 0, y: 0}, radius: number, drawingContext: CanvasRenderingContext2D, _orientationAngle: number = 0, mass: number = 50, isStatic: boolean = false, frictionEnabled: boolean = true) {\n this._circle = new Circle(center, radius, _orientationAngle, mass, isStatic, frictionEnabled);\n this._context = drawingContext;\n }\n\n draw(ctx: CanvasRenderingContext2D): void {\n ctx.beginPath();\n ctx.arc(this._circle.center.x, this._circle.center.y, this._circle.radius, 0, 2 * Math.PI);\n ctx.stroke();\n }\n\n step(deltaTime: number): void {\n this._circle.step(deltaTime);\n this.draw(this._context);\n }\n\n isStatic(): boolean {\n return this._circle.isStatic();\n }\n\n isMovingStatic(): boolean {\n return this._circle.isMovingStatic();\n }\n\n getMinMaxProjection(unitvector: Point): { min: number; max: number; } {\n return this._circle.getMinMaxProjection(unitvector);\n }\n\n getCollisionAxes(relativeBody: RigidBody): Point[] {\n return this._circle.getCollisionAxes(relativeBody);\n }\n\n applyForce(force: Point): void {\n this._circle.applyForce(force);\n }\n\n get AABB(): { min: Point; max: Point; } {\n return this._circle.AABB;\n }\n\n getMass(): number {\n return this._circle.mass;\n }\n\n applyForceInOrientation(force: Point): void {\n this._circle.applyForceInOrientation(force);\n }\n\n move(delta: Point): void {\n this._circle.move(delta);\n }\n\n getSignificantVertices(collisionNormal: Point): Point[] {\n return this._circle.getSignificantVertices(collisionNormal);\n }\n\n get center(): Point {\n return this._circle.center;\n }\n\n set center(dest: Point){\n this._circle.center = dest;\n }\n\n get linearVelocity(): Point {\n return this._circle.linearVelocity;\n }\n\n set linearVelocity(dest: Point){\n this._circle.linearVelocity = dest;\n }\n\n get orientationAngle(){\n return this._circle.orientationAngle;\n }\n\n significantVertex(collisionNormal: Point): Point {\n return this._circle.significantVertex(collisionNormal);\n }\n\n set angularVelocity(angularVelocity: number){\n this._circle.angularVelocity = angularVelocity;\n }\n\n get angularVelocity(): number{\n return this._circle.angularVelocity;\n }\n\n get mass(): number{\n return this._circle.mass;\n }\n\n getNormalOfSignificantFace(collisionNormal: Point): Point {\n return this._circle.getNormalOfSignificantFace(collisionNormal);\n }\n\n get staticFrictionCoeff(): number{\n return this._circle.staticFrictionCoeff;\n }\n\n set staticFrictionCoeff(coeff: number){\n this._circle.staticFrictionCoeff = coeff;\n }\n\n getAdjacentFaces(collisionNormal: Point): {startPoint: {coord: Point, index: number}, endPoint: {coord: Point, index: number}}[] {\n return this._circle.getAdjacentFaces(collisionNormal);\n }\n\n get momentOfInertia(): number {\n return this._circle.momentOfInertia;\n }\n\n // Sleeping methods\n setSleeping(sleeping: boolean): void {\n if (sleeping && !this.isSleeping) {\n this.isSleeping = true;\n this._circle.linearVelocity = { x: 0, y: 0 };\n this._circle.angularVelocity = 0;\n } else if (!sleeping && this.isSleeping) {\n this.isSleeping = false;\n this.timeAtRest = 0;\n }\n }\n\n updateSleeping(deltaTime: number): void {\n if (this._circle.isStatic() || this._circle.isMovingStatic()) return;\n\n const speed = PointCal.magnitude(this._circle.linearVelocity);\n const angularSpeed = Math.abs(this._circle.angularVelocity);\n\n if (speed < this.sleepThreshold && angularSpeed < this.sleepThreshold) {\n this.timeAtRest += deltaTime;\n if (this.timeAtRest >= this.sleepTime) {\n this.setSleeping(true);\n }\n } else {\n this.timeAtRest = 0;\n if (this.isSleeping) {\n this.setSleeping(false);\n }\n }\n }\n\n}\n\nexport class VisualPolygonBody implements VisualComponent, RigidBody {\n \n private _polygon: Polygon;\n private _context: CanvasRenderingContext2D;\n \n // Collision filtering\n public collisionFilter: CollisionFilter = { ...DEFAULT_COLLISION_FILTER };\n \n // Sleeping system\n public isSleeping: boolean = false;\n public sleepThreshold: number = 0.01;\n public sleepTime: number = 0.5;\n public timeAtRest: number = 0;\n\n constructor(center: Point = {x: 0, y: 0}, vertices: Point[], drawingContext: CanvasRenderingContext2D, _orientationAngle: number = 0, mass: number = 50, isStatic: boolean = false, frictionEnabled: boolean = true) {\n this._polygon = new Polygon(center, vertices, _orientationAngle, mass, isStatic, frictionEnabled);\n this._context = drawingContext;\n }\n\n draw(ctx: CanvasRenderingContext2D): void {\n ctx.beginPath();\n let vertices = this._polygon.getVerticesAbsCoord();\n ctx.moveTo(vertices[0].x, vertices[0].y);\n vertices.forEach(vertex => {\n ctx.lineTo(vertex.x, vertex.y);\n });\n ctx.lineTo(vertices[0].x, vertices[0].y);\n ctx.stroke();\n // ctx.beginPath();\n // ctx.rect(this._polygon.AABB.min.x, this._polygon.AABB.min.y, this._polygon.AABB.max.x - this._polygon.AABB.min.x, this._polygon.AABB.max.y - this._polygon.AABB.min.y);\n // ctx.stroke();\n }\n\n step(deltaTime: number): void {\n this._polygon.step(deltaTime);\n this.draw(this._context);\n }\n\n isStatic(): boolean {\n return this._polygon.isStatic();\n }\n\n isMovingStatic(): boolean {\n return this._polygon.isMovingStatic();\n }\n\n getMinMaxProjection(unitvector: Point): { min: number; max: number; } {\n return this._polygon.getMinMaxProjection(unitvector);\n }\n\n getCollisionAxes(relativeBody: RigidBody): Point[] {\n return this._polygon.getCollisionAxes(relativeBody);\n }\n\n applyForce(force: Point): void {\n this._polygon.applyForce(force);\n }\n\n applyForceInOrientation(force: Point): void {\n this._polygon.applyForceInOrientation(force);\n }\n\n setLinearVelocity(linearVelocity: Point): void {\n this._polygon.setLinearVelocity(linearVelocity);\n }\n\n move(delta: Point): void {\n this._polygon.move(delta);\n }\n\n get center(): Point {\n return this._polygon.center;\n }\n\n set center(dest: Point){\n this._polygon.center = dest;\n }\n\n get linearVelocity(): Point {\n return this._polygon.linearVelocity;\n }\n\n set linearVelocity(dest: Point){\n this._polygon.linearVelocity = dest;\n }\n\n get angularVelocity(): number{\n return this._polygon.angularVelocity;\n }\n\n set angularVelocity(angularVelocity: number){\n this._polygon.angularVelocity = angularVelocity;\n }\n\n get orientationAngle(): number{\n return this._polygon.orientationAngle;\n }\n\n significantVertex(collisionNormal: Point): Point {\n return this._polygon.significantVertex(collisionNormal);\n }\n\n getSignificantVertices(collisionNormal: Point): Point[] {\n return this._polygon.getSignificantVertices(collisionNormal);\n }\n\n get AABB(): {min: Point, max: Point}{\n return this._polygon.AABB;\n }\n\n get mass(): number{\n return this._polygon.mass;\n }\n \n get staticFrictionCoeff(): number{\n return this._polygon.staticFrictionCoeff;\n }\n\n set staticFrictionCoeff(coeff: number){\n this._polygon.staticFrictionCoeff = coeff;\n }\n\n get momentOfInertia(): number {\n return this._polygon.momentOfInertia;\n }\n\n getNormalOfSignificantFace(collisionNormal: Point): Point {\n return this._polygon.getNormalOfSignificantFace(collisionNormal);\n }\n\n getAdjacentFaces(collisionNormal: Point): {startPoint: {coord: Point, index: number}, endPoint: {coord: Point, index: number}}[] {\n return this._polygon.getAdjacentFaces(collisionNormal);\n }\n\n // Sleeping methods\n setSleeping(sleeping: boolean): void {\n if (sleeping && !this.isSleeping) {\n this.isSleeping = true;\n this._polygon.linearVelocity = { x: 0, y: 0 };\n this._polygon.angularVelocity = 0;\n } else if (!sleeping && this.isSleeping) {\n this.isSleeping = false;\n this.timeAtRest = 0;\n }\n }\n\n updateSleeping(deltaTime: number): void {\n if (this._polygon.isStatic() || this._polygon.isMovingStatic()) return;\n\n const speed = PointCal.magnitude(this._polygon.linearVelocity);\n const angularSpeed = Math.abs(this._polygon.angularVelocity);\n\n if (speed < this.sleepThreshold && angularSpeed < this.sleepThreshold) {\n this.timeAtRest += deltaTime;\n if (this.timeAtRest >= this.sleepTime) {\n this.setSleeping(true);\n }\n } else {\n this.timeAtRest = 0;\n if (this.isSleeping) {\n this.setSleeping(false);\n }\n }\n }\n}\n\nexport class Polygon extends BaseRigidBody {\n\n private vertices: Point[];\n private _momentOfInertia: number;\n\n constructor(center: Point = {x: 0, y: 0}, vertices: Point[], _orientationAngle: number = 0, mass: number = 50, isStatic: boolean = false, frictionEnabled: boolean = true) {\n super(center, _orientationAngle, mass, isStatic, frictionEnabled);\n this.vertices = vertices;\n this.step = this.step.bind(this);\n\n let numerator = this.vertices.reduce((acc, vertex, index) => {\n let nextPointIndex = index < this.vertices.length - 1 ? index + 1 : 0;\n let nextPoint = this.vertices[nextPointIndex];\n let crossProduct = PointCal.crossProduct(nextPoint, vertex);\n return acc + PointCal.magnitude(crossProduct) * (PointCal.dotProduct(vertex, vertex) + PointCal.dotProduct(vertex, nextPoint) + PointCal.dotProduct(nextPoint, nextPoint));\n }, 0);\n\n let denomiator = this.vertices.reduce((acc, vertex, index) => {\n return acc + PointCal.magnitude(PointCal.crossProduct(this.vertices[index < this.vertices.length - 1 ? index + 1 : 0], vertex));\n }, 0);\n\n \n this._momentOfInertia = this._mass * numerator / (6 * denomiator);\n }\n\n\n getVerticesAbsCoord(): Point[]{\n return this.vertices.map(vertex=>{\n return PointCal.addVector(this._center, PointCal.rotatePoint(vertex, this._orientationAngle));\n });\n }\n\n getCollisionAxes(relativeBody: RigidBody): Point[] {\n return this.getVerticesAbsCoord().map((vertex, index, absVertices)=>{\n let vector = PointCal.subVector(vertex, absVertices[absVertices.length - 1]);\n if (index > 0){\n vector = PointCal.subVector(vertex, absVertices[index - 1]); \n }\n return PointCal.unitVector(PointCal.rotatePoint(vector, Math.PI / 2));\n });\n }\n\n getMinMaxProjection(unitvector: Point): { min: number; max: number; } {\n let vertices = this.getVerticesAbsCoord();\n \n let projections = vertices.map( vertex => {\n return PointCal.dotProduct(vertex, unitvector);\n });\n\n \n return {min: Math.min(...projections), max: Math.max(...projections)};\n }\n\n get AABB(): { min: Point; max: Point; } {\n let points = this.getVerticesAbsCoord();\n let xCoords = points.map(vertex => vertex.x);\n let yCoords = points.map(vertex => vertex.y);\n return {min: {x: Math.min(...xCoords), y: Math.min(...yCoords)}, max: {x: Math.max(...xCoords), y: Math.max(...yCoords)}};\n }\n\n significantVertex(collisionNormal: Point): Point {\n let vertices = this.getVerticesAbsCoord();\n let verticesProjected = vertices.map(vertex => PointCal.dotProduct(vertex, collisionNormal));\n let maxIndex = verticesProjected.indexOf(Math.max(...verticesProjected));\n return vertices[maxIndex];\n }\n\n getSignificantVertices(collisionNormal: Point): Point[]{\n let vertices = this.getVerticesAbsCoord();\n let verticesProjected = vertices.map(vertex => PointCal.dotProduct(vertex, collisionNormal));\n let maxIndex = verticesProjected.indexOf(Math.max(...verticesProjected));\n const tipVertex = vertices[maxIndex];\n let prevPointIndex = maxIndex > 0 ? maxIndex - 1 : vertices.length - 1;\n let nextPointIndex = maxIndex < vertices.length - 1 ? maxIndex + 1 : 0;\n const prevPoint = vertices[prevPointIndex];\n const nextPoint = vertices[nextPointIndex];\n const prevPointProjected = PointCal.dotProduct(prevPoint, collisionNormal);\n const nextPointProjected = PointCal.dotProduct(nextPoint, collisionNormal);\n if (prevPointProjected > nextPointProjected) {\n return [prevPoint, tipVertex];\n } else {\n return [tipVertex, nextPoint];\n }\n }\n\n getNormalOfSignificantFace(collisionNormal: Point): Point{\n const vertices = this.getSignificantVertices(collisionNormal);\n const direction = PointCal.unitVectorFromA2B(vertices[0], vertices[1]);\n return PointCal.rotatePoint(direction, -Math.PI / 2);\n }\n\n getAdjacentFaces(collisionNormal: Point): {startPoint: {coord: Point, index: number}, endPoint: {coord: Point, index: number}}[]{\n let vertices = this.getVerticesAbsCoord();\n let verticesProjected = vertices.map(vertex => PointCal.dotProduct(vertex, collisionNormal));\n let maxIndex = verticesProjected.indexOf(Math.max(...verticesProjected));\n const tipVertex = vertices[maxIndex];\n let prevPointIndex = maxIndex > 0 ? maxIndex - 1 : vertices.length - 1;\n let nextPointIndex = maxIndex < vertices.length - 1 ? maxIndex + 1 : 0;\n const prevPoint = vertices[prevPointIndex];\n const nextPoint = vertices[nextPointIndex];\n const prevPointProjected = PointCal.dotProduct(prevPoint, collisionNormal);\n const nextPointProjected = PointCal.dotProduct(nextPoint, collisionNormal);\n const adjacentFaces: Point[] = [];\n const adjacentFacesWithIndex: {startPoint: {coord: Point, index: number}, endPoint: {coord: Point, index: number}}[] = [];\n if (prevPointProjected > nextPointProjected) {\n adjacentFaces.push(prevPoint, tipVertex);\n adjacentFacesWithIndex.push({startPoint: {coord: prevPoint, index: prevPointIndex}, endPoint: {coord: tipVertex, index: maxIndex}});\n\n // the nextface is the next face\n adjacentFacesWithIndex.unshift({startPoint: {coord: tipVertex, index: maxIndex}, endPoint: {coord: nextPoint, index: nextPointIndex}});\n // need to get the previous face of the previous face\n let prevPrevPointIndex = prevPointIndex > 0 ? prevPointIndex - 1 : vertices.length - 1;\n adjacentFacesWithIndex.unshift({startPoint: {coord: vertices[prevPrevPointIndex], index: prevPrevPointIndex}, endPoint: {coord: prevPoint, index: prevPointIndex}});\n } else {\n adjacentFaces.push(tipVertex, nextPoint);\n adjacentFacesWithIndex.push({startPoint: {coord: tipVertex, index: maxIndex}, endPoint: {coord: nextPoint, index: nextPointIndex}});\n\n // need to get the next face of the next face\n let nextNextPointIndex = nextPointIndex < vertices.length - 1 ? nextPointIndex + 1 : 0;\n adjacentFacesWithIndex.unshift({startPoint: {coord: nextPoint, index: nextPointIndex}, endPoint: {coord: vertices[nextNextPointIndex], index: nextNextPointIndex}})\n\n // the prevoius face is the previous face\n adjacentFacesWithIndex.unshift({startPoint: {coord: prevPoint, index: prevPointIndex}, endPoint: {coord: tipVertex, index: maxIndex}});\n }\n \n return adjacentFacesWithIndex;\n }\n\n get momentOfInertia(): number {\n return this._momentOfInertia;\n }\n\n}\n\nexport class Circle extends BaseRigidBody {\n\n private _radius: number;\n private _momentOfInertia: number;\n\n constructor(center: Point = {x: 0, y: 0}, radius: number, _orientationAngle: number = 0, mass: number = 50, isStatic: boolean = false, frictionEnabled: boolean = true) {\n super(center, _orientationAngle, mass, isStatic, frictionEnabled);\n this._radius = radius;\n this.step = this.step.bind(this);\n this._momentOfInertia = this._mass * this._radius * this._radius / 2;\n }\n\n getMinMaxProjection(unitvector: Point): { min: number; max: number; } {\n let PositiveFurthest = PointCal.addVector(this._center, PointCal.multiplyVectorByScalar(unitvector, this._radius));\n let NegativeFurthest = PointCal.addVector(this._center, PointCal.multiplyVectorByScalar(unitvector, -this._radius));\n return {min: PointCal.dotProduct(NegativeFurthest, unitvector), max: PointCal.dotProduct(PositiveFurthest, unitvector)};\n }\n\n getCollisionAxes(relativeBody: RigidBody): Point[] {\n return [PointCal.unitVector(PointCal.subVector(relativeBody.center, this._center))];\n }\n\n get AABB(): { min: Point; max: Point; } {\n return {min: PointCal.subVector(this._center, {x: this._radius, y: this._radius}), max: PointCal.addVector(this._center, {x: this._radius, y: this._radius})};\n }\n\n significantVertex(collisionNormal: Point): Point {\n return PointCal.addVector(this._center, PointCal.multiplyVectorByScalar(collisionNormal, this._radius));\n }\n\n get radius(): number {\n return this._radius;\n }\n\n getSignificantVertices(collisionNormal: Point): Point[]{\n return [PointCal.addVector(this._center, PointCal.multiplyVectorByScalar(collisionNormal, this._radius))];\n }\n\n getNormalOfSignificantFace(collisionNormal: Point): Point{\n return PointCal.unitVector(collisionNormal);\n }\n\n getAdjacentFaces(collisionNormal: Point): {startPoint: {coord: Point, index: number}, endPoint: {coord: Point, index: number}}[]{\n return [];\n }\n\n get momentOfInertia(): number {\n return this._momentOfInertia;\n }\n}\n",
6
15
  "/**\n * Collision filtering configuration for rigid bodies.\n *\n * @remarks\n * Collision filters use a bitmask system to control which bodies can collide\n * with each other. This is useful for creating layers, groups, and special\n * collision rules in your physics simulation.\n *\n * ### How Filtering Works\n *\n * Two bodies A and B can collide if ALL of these conditions are met:\n * 1. `(A.category & B.mask) !== 0` - A's category matches B's mask\n * 2. `(B.category & A.mask) !== 0` - B's category matches A's mask\n * 3. Group rules are satisfied (see group field)\n *\n * @category Types\n */\nexport interface CollisionFilter {\n /**\n * What category this body belongs to (bitmask).\n *\n * @example\n * ```typescript\n * category: CollisionCategory.PLAYER // 0x0004\n * ```\n */\n category: number;\n\n /**\n * What categories this body can collide with (bitmask).\n *\n * @example\n * ```typescript\n * // Collide with everything except other players\n * mask: ~CollisionCategory.PLAYER & 0xFFFF\n * ```\n */\n mask: number;\n\n /**\n * Collision group for special rules.\n * - 0: No group (use category/mask rules)\n * - Positive: Bodies in same group ALWAYS collide\n * - Negative: Bodies in same group NEVER collide\n *\n * @example\n * ```typescript\n * // Ragdoll parts shouldn't collide with each other\n * group: -1\n *\n * // Team members always collide (for physics interactions)\n * group: 1\n * ```\n */\n group: number;\n}\n\n/**\n * Default collision filter that collides with everything.\n *\n * @remarks\n * Uses category 0x0001 and mask 0xFFFF, meaning it belongs to the first\n * category and can collide with all 16 categories.\n *\n * @category Collision\n */\nexport const DEFAULT_COLLISION_FILTER: CollisionFilter = {\n category: 0x0001,\n mask: 0xFFFF,\n group: 0\n};\n\n/**\n * Determines if two bodies can collide based on their collision filters.\n *\n * @remarks\n * Checks group rules first, then falls back to category/mask matching.\n * This is used internally by the physics engine during broad phase collision detection.\n *\n * @param filterA - Collision filter of first body\n * @param filterB - Collision filter of second body\n * @returns True if the bodies should collide\n *\n * @example\n * ```typescript\n * const player: CollisionFilter = {\n * category: CollisionCategory.PLAYER,\n * mask: 0xFFFF,\n * group: 0\n * };\n *\n * const enemy: CollisionFilter = {\n * category: CollisionCategory.ENEMY,\n * mask: CollisionCategory.PLAYER | CollisionCategory.STATIC,\n * group: 0\n * };\n *\n * console.log(canCollide(player, enemy)); // true\n * ```\n *\n * @category Collision\n */\nexport function canCollide(filterA: CollisionFilter, filterB: CollisionFilter): boolean {\n // If objects are in the same group\n if (filterA.group !== 0 && filterA.group === filterB.group) {\n return filterA.group > 0; // Positive groups collide, negative groups don't\n }\n\n // Check category-mask pairs\n return (filterA.category & filterB.mask) !== 0 && (filterB.category & filterA.mask) !== 0;\n}\n\n/**\n * Predefined collision categories for common game entities.\n *\n * @remarks\n * These are bitmask constants (powers of 2) that can be combined using bitwise OR.\n * You can define up to 16 categories using values from 0x0001 to 0x8000.\n *\n * @example\n * Using predefined categories\n * ```typescript\n * // Player collides with everything except other players\n * player.collisionFilter = {\n * category: CollisionCategory.PLAYER,\n * mask: ~CollisionCategory.PLAYER & 0xFFFF,\n * group: 0\n * };\n *\n * // Projectile only collides with enemies and static objects\n * projectile.collisionFilter = {\n * category: CollisionCategory.PROJECTILE,\n * mask: CollisionCategory.ENEMY | CollisionCategory.STATIC,\n * group: 0\n * };\n * ```\n *\n * @category Collision\n */\nexport const CollisionCategory = {\n STATIC: 0x0001, // Walls, floors, static objects\n DYNAMIC: 0x0002, // Moving objects \n PLAYER: 0x0004, // Player character\n ENEMY: 0x0008, // Enemy entities\n PROJECTILE: 0x0010, // Bullets, projectiles\n SENSOR: 0x0020, // Trigger zones, sensors\n PICKUP: 0x0040, // Items that can be collected\n PLATFORM: 0x0080, // One-way platforms\n} as const;",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ue-too/dynamics",
3
3
  "type": "module",
4
- "version": "0.13.0",
4
+ "version": "0.14.0",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -20,8 +20,8 @@
20
20
  "./package.json": "./package.json"
21
21
  },
22
22
  "dependencies": {
23
- "@ue-too/math": "^0.13.0",
24
- "@ue-too/ecs": "^0.13.0"
23
+ "@ue-too/math": "^0.14.0",
24
+ "@ue-too/ecs": "^0.14.0"
25
25
  },
26
26
  "main": "./index.js",
27
27
  "types": "./index.d.ts",