Skip to content

Commit e682332

Browse files
committed
Resolved merge conflicts for package.json and yarn.lock
2 parents 9c5e04a + ef08557 commit e682332

File tree

10 files changed

+1322
-0
lines changed

10 files changed

+1322
-0
lines changed

modules.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,5 +85,10 @@
8585
},
8686
"remote_execution": {
8787
"tabs": []
88+
},
89+
"physics_2d": {
90+
"tabs": [
91+
"physics_2d"
92+
]
8893
}
8994
}

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@
8585
"@blueprintjs/core": "^4.6.1",
8686
"@blueprintjs/icons": "^4.4.0",
8787
"@blueprintjs/popover2": "^1.4.3",
88+
"@box2d/core": "^0.10.0",
89+
"@box2d/debug-draw": "^0.10.0",
8890
"@jscad/modeling": "^2.9.5",
8991
"@jscad/regl-renderer": "^2.6.1",
9092
"@jscad/stl-serializer": "^2.1.13",
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
/* eslint-disable new-cap */
2+
// We have to disable linting rules since Box2D functions do not
3+
// follow the same guidelines as the rest of the codebase.
4+
5+
import {
6+
type b2Body,
7+
type b2Shape,
8+
type b2Fixture,
9+
b2BodyType,
10+
b2CircleShape,
11+
b2PolygonShape,
12+
b2Vec2,
13+
} from '@box2d/core';
14+
import { type ReplResult } from '../../typings/type_helpers';
15+
16+
import { ACCURACY, type Force, type ForceWithPos } from './types';
17+
import { type PhysicsWorld } from './PhysicsWorld';
18+
19+
export class PhysicsObject implements ReplResult {
20+
private body: b2Body;
21+
private shape: b2Shape;
22+
private fixture: b2Fixture;
23+
private forcesCentered: Force[] = [];
24+
private forcesAtAPoint: ForceWithPos[] = [];
25+
26+
constructor(
27+
position: b2Vec2,
28+
rotation: number,
29+
shape: b2Shape,
30+
isStatic: boolean,
31+
world: PhysicsWorld,
32+
) {
33+
this.body = world.createBody({
34+
type: isStatic ? b2BodyType.b2_staticBody : b2BodyType.b2_dynamicBody,
35+
position,
36+
angle: rotation,
37+
});
38+
this.shape = shape;
39+
40+
this.fixture = this.body.CreateFixture({
41+
shape: this.shape,
42+
density: 1,
43+
friction: 1,
44+
});
45+
}
46+
47+
public getFixture() {
48+
return this.fixture;
49+
}
50+
51+
public getMass() {
52+
return this.body.GetMass();
53+
}
54+
55+
public setDensity(density: number) {
56+
this.fixture.SetDensity(density);
57+
this.body.ResetMassData();
58+
}
59+
60+
public setFriction(friction: number) {
61+
this.fixture.SetFriction(friction);
62+
}
63+
64+
public getPosition() {
65+
return this.body.GetPosition();
66+
}
67+
68+
public setPosition(pos: b2Vec2) {
69+
this.body.SetTransformVec(pos, this.getRotation());
70+
}
71+
72+
public getRotation() {
73+
return this.body.GetAngle();
74+
}
75+
76+
public setRotation(rot: number) {
77+
this.body.SetAngle(rot);
78+
}
79+
80+
public getVelocity() {
81+
return this.body.GetLinearVelocity();
82+
}
83+
84+
public setVelocity(velc: b2Vec2) {
85+
this.body.SetLinearVelocity(velc);
86+
}
87+
88+
public getAngularVelocity() {
89+
return this.body.GetAngularVelocity();
90+
}
91+
92+
public setAngularVelocity(velc: number) {
93+
this.body.SetAngularVelocity(velc);
94+
}
95+
96+
public addForceCentered(force: Force) {
97+
this.forcesCentered.push(force);
98+
}
99+
100+
public addForceAtAPoint(force: Force, pos: b2Vec2) {
101+
this.forcesAtAPoint.push({
102+
force,
103+
pos,
104+
});
105+
}
106+
107+
private applyForcesToCenter(world_time: number) {
108+
this.forcesCentered = this.forcesCentered.filter(
109+
(force: Force) => force.start_time + force.duration > world_time,
110+
);
111+
112+
const resForce = this.forcesCentered
113+
.filter((force: Force) => force.start_time < world_time)
114+
.reduce(
115+
(res: b2Vec2, force: Force) => res.Add(force.direction.Scale(force.magnitude)),
116+
new b2Vec2(),
117+
);
118+
119+
this.body.ApplyForceToCenter(resForce);
120+
}
121+
122+
private applyForcesAtAPoint(world_time: number) {
123+
this.forcesAtAPoint = this.forcesAtAPoint.filter(
124+
(forceWithPos: ForceWithPos) => forceWithPos.force.start_time + forceWithPos.force.duration > world_time,
125+
);
126+
127+
this.forcesAtAPoint.forEach((forceWithPos) => {
128+
const force = forceWithPos.force;
129+
this.body.ApplyForce(
130+
force.direction.Scale(force.magnitude),
131+
forceWithPos.pos,
132+
);
133+
});
134+
}
135+
136+
public applyForces(world_time: number) {
137+
this.applyForcesToCenter(world_time);
138+
this.applyForcesAtAPoint(world_time);
139+
}
140+
141+
public isTouching(obj2: PhysicsObject) {
142+
let ce = this.body.GetContactList();
143+
while (ce !== null) {
144+
if (ce.other === obj2.body && ce.contact.IsTouching()) {
145+
return true;
146+
}
147+
ce = ce.next;
148+
}
149+
return false;
150+
}
151+
152+
public toReplString = () => `
153+
Mass: ${this.getMass()
154+
.toFixed(ACCURACY)}
155+
Position: [${this.getPosition().x.toFixed(
156+
ACCURACY,
157+
)},${this.getPosition().y.toFixed(ACCURACY)}]
158+
Velocity: [${this.getVelocity().x.toFixed(
159+
ACCURACY,
160+
)},${this.getVelocity().y.toFixed(ACCURACY)}]
161+
162+
Rotation: ${this.getRotation()
163+
.toFixed(ACCURACY)}
164+
AngularVelocity: [${this.getAngularVelocity()
165+
.toFixed(ACCURACY)}]`;
166+
167+
public scale_size(scale: number) {
168+
if (this.shape instanceof b2CircleShape) {
169+
this.shape.m_radius *= scale;
170+
} else if (this.shape instanceof b2PolygonShape) {
171+
let centroid: b2Vec2 = this.shape.m_centroid;
172+
let arr: b2Vec2[] = [];
173+
this.shape.m_vertices.forEach((vec) => {
174+
arr.push(
175+
new b2Vec2(
176+
centroid.x + scale * (vec.x - centroid.x),
177+
centroid.y + scale * (vec.y - centroid.y),
178+
),
179+
);
180+
});
181+
this.shape = new b2PolygonShape()
182+
.Set(arr);
183+
}
184+
let f: b2Fixture = this.fixture;
185+
this.body.DestroyFixture(this.fixture);
186+
this.fixture = this.body.CreateFixture({
187+
shape: this.shape,
188+
density: f.GetDensity(),
189+
friction: f.GetFriction(),
190+
});
191+
}
192+
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/* eslint-disable new-cap */
2+
// We have to disable linting rules since Box2D functions do not
3+
// follow the same guidelines as the rest of the codebase.
4+
5+
import {
6+
type b2Body,
7+
type b2Fixture,
8+
type b2BodyDef,
9+
b2BodyType,
10+
b2PolygonShape,
11+
type b2StepConfig,
12+
b2Vec2,
13+
b2World,
14+
b2ContactListener,
15+
type b2Contact,
16+
} from '@box2d/core';
17+
import { type PhysicsObject } from './PhysicsObject';
18+
import { Timer } from './types';
19+
20+
export class PhysicsWorld {
21+
private b2World: b2World;
22+
private physicsObjects: PhysicsObject[];
23+
private timer: Timer;
24+
private touchingObjects: Map<b2Fixture, Map<b2Fixture, number>>;
25+
26+
private iterationsConfig: b2StepConfig = {
27+
velocityIterations: 8,
28+
positionIterations: 3,
29+
};
30+
31+
constructor() {
32+
this.b2World = b2World.Create(new b2Vec2());
33+
this.physicsObjects = [];
34+
this.timer = new Timer();
35+
this.touchingObjects = new Map<b2Fixture, Map<b2Fixture, number>>();
36+
37+
const contactListener: b2ContactListener = new b2ContactListener();
38+
contactListener.BeginContact = (contact: b2Contact) => {
39+
let m = this.touchingObjects.get(contact.GetFixtureA());
40+
if (m === undefined) {
41+
let newMap = new Map<b2Fixture, number>();
42+
newMap.set(contact.GetFixtureB(), this.timer.getTime());
43+
this.touchingObjects.set(contact.GetFixtureA(), newMap);
44+
} else {
45+
m.set(contact.GetFixtureB(), this.timer.getTime());
46+
}
47+
};
48+
contactListener.EndContact = (contact: b2Contact) => {
49+
const contacts = this.touchingObjects.get(contact.GetFixtureA());
50+
if (contacts) {
51+
contacts.delete(contact.GetFixtureB());
52+
}
53+
};
54+
55+
this.b2World.SetContactListener(contactListener);
56+
}
57+
58+
public setGravity(gravity: b2Vec2) {
59+
this.b2World.SetGravity(gravity);
60+
}
61+
62+
public addObject(obj: PhysicsObject) {
63+
this.physicsObjects.push(obj);
64+
return obj;
65+
}
66+
67+
public createBody(bodyDef: b2BodyDef) {
68+
return this.b2World.CreateBody(bodyDef);
69+
}
70+
71+
public makeGround(height: number, friction: number) {
72+
const groundBody: b2Body = this.createBody({
73+
type: b2BodyType.b2_staticBody,
74+
position: new b2Vec2(0, height - 10),
75+
});
76+
const groundShape: b2PolygonShape = new b2PolygonShape()
77+
.SetAsBox(
78+
10000,
79+
10,
80+
);
81+
82+
groundBody.CreateFixture({
83+
shape: groundShape,
84+
density: 1,
85+
friction,
86+
});
87+
}
88+
89+
public update(dt: number) {
90+
for (let obj of this.physicsObjects) {
91+
obj.applyForces(this.timer.getTime());
92+
}
93+
this.b2World.Step(dt, this.iterationsConfig);
94+
this.timer.step(dt);
95+
}
96+
97+
public simulate(total_time: number) {
98+
const dt = 0.01;
99+
for (let i = 0; i < total_time; i += dt) {
100+
this.update(dt);
101+
}
102+
}
103+
104+
public getB2World() {
105+
return this.b2World;
106+
}
107+
108+
public getWorldStatus(): String {
109+
let world_status: String = `
110+
World time: ${this.timer.toString()}
111+
112+
Objects:
113+
`;
114+
this.physicsObjects.forEach((obj) => {
115+
console.log(obj.getMass());
116+
world_status += `
117+
------------------------
118+
${obj.toReplString()}
119+
------------------------
120+
`;
121+
});
122+
return world_status;
123+
}
124+
125+
public findImpact(obj1: PhysicsObject, obj2: PhysicsObject) {
126+
let m = this.touchingObjects.get(obj1.getFixture());
127+
if (m === undefined) {
128+
return -1;
129+
}
130+
let time = m.get(obj2.getFixture());
131+
if (time === undefined) {
132+
return -1;
133+
}
134+
return time;
135+
}
136+
}

0 commit comments

Comments
 (0)