-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtruss.js
250 lines (242 loc) · 10.3 KB
/
truss.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
// TODO: add defined method in addition/replacement to known
// TODO: determine if getters/setters are doing too much
// TODO: clean up all the error throwing (e.g., should addMember be checking if member is already added?)
// TODO: consider using unit vectors instead of angles
// TODO: correct everything incorrectly using for-loops
// Question: should calling code check conditions, or should library check conditions and throw errors?\\
// Should there be an addMember function for truss?
// Should there be an addJoint function for member?
// TODO: consider chaining
// TODO: performance sucks, since javascript object does not support objects for keys
// TODO: consider adding CAS
(function() {
var root = this;
var t = {};
t.truss = {
get joints() {return this._joints = this._joints || [];},
addJoint: function(joint) {
var added = false;
if (!t.joint.isPrototypeOf(joint)) throw "joint of truss must be joint";
if (!_.contains(this.joints, joint)) {
this.joints.push(joint);
added = true;
}
return added;
},
known: function() {
var known = false;
if (uknowns.length === 0) known = true;
return known;
},
knowns: function() {
return _.reduce(
this.joints,
function(memo, joint) {
return _.union(memo, joint.knowns());
},
[]
);
},
unknowns: function() {
return _.reduce(
this.joints,
function(memo, joint) {
return _.union(memo, joint.unknowns());
},
[]
);
},
// Solve truss
solve: function() {
var columnMap = {}; // Map: matrix column -> unknown member/force
var rowMap = {}; // Map: matrix row -> joint
// Fill columnMap
var columnCount = 0;
_.each(this.unknowns(), function(unknown) {columnMap[++columnCount] = unknown;});
// Fill rowMap
var rowCount = 0;
_.each(this.joints, function(joint) {rowMap[++rowCount, rowCount++] = joint;});
var A = Matrix.Zero(rowCount, columnCount); // Form initial matrix
var b = Vector.Zero(rowCount); // Form initial solution vector
// Fill A and b
_.each(rowMap, function(joint, row) {
row = row - 1; // Math indices to array indices
_.each(columnMap, function(unknown, column) { // Fill A with unknows
column = column - 1; // Math indices to array indices
if (_.contains(joint.unknowns(), unknown)) {
//console.log(unknown.angle);
A.elements[row][column] = Math.cos(unknown.getAngle(joint)); // x
A.elements[row + 1][column] = Math.sin(unknown.getAngle(joint)); // y
}
});
_.each(joint.knowns(), function(known) { // Fill b with knowns
b.elements[row] += -known.forceOn(joint).x(); // x
b.elements[row + 1] += -known.forceOn(joint).y(); // y
});
});
console.log(A.elements);
console.log(b.elements);
// Solve for magnitudes of unknowns
var sol = A.inv().multiply(b);
sol.each(function(mag, index) {
columnMap[index].magnitude = mag;
});
console.log(sol.elements);
}
};
t.joint = {
get x() {return this._x;},
set x(val) {if (!_.isFinite(val)) throw "x-value of joint must be finite number"; this._x = val;},
get y() {return this._y;},
set y(val) {if (!_.isFinite(val)) throw "y-value of joint must be finite number"; this._y = val;},
// Attached members
get members() {return this._members = this._members || [];},
// External forces
get exForces() {return this._forces = this._forces || [];},
addMember: function(mem) {
var added = false;
if (!t.member.isPrototypeOf(mem)) throw "member of joint must be member";
if (!_.contains(this.members, mem)) {
this.members.push(mem);
added = true;
}
return added;
},
addExForce: function(exForce) {
var added = false;
if (!t.force.isPrototypeOf(exForce)) throw "exForce of joint must be force";
if (!_.contains(this.exForces, exForce)) {
this.exForces.push(exForce);
added = true;
}
return added;
},
// Should be "defined" not known
known: function() {
var known = true;
if (this.x === undefined || this.y === undefined) known = false;
return known;
},
// Collect known forces
knowns: function() {
return _.filter(
_.union(this.members, this.exForces),
function(elem) {return elem.known();}
);
},
unknowns: function() {
return _.filter(
_.union(this.members, this.exForces),
function(elem) {return !elem.known();}
);
}
};
t.member = {
get magnitude() {return this._magnitude;}, // Magnitude of internal force
set magnitude(val) {
if (!_.isFinite(val)) throw "Magnitude of member must be finite number";
if (val < 0) {
this._magnitude = -val;
this.compression = !this.compression;
} else this._magnitude = val;
},
// Members in tension by default
get compression() {if (this._compression === undefined) return false; else return this._compression},
set compression(val) {
if (!_.isBoolean(val)) throw "Compression of member must be boolean";
if (this.magnitude === undefined) {throw "Set magnitude before compression";}
this._compression = val;
},
get firJoint() {return this._firJoint;},
set firJoint(val) {if (!t.joint.isPrototypeOf(val)) throw "firJoint of member must be joint"; this._firJoint = val;},
get secJoint() {return this._secJoint;},
set secJoint(val) {if (!t.joint.isPrototypeOf(val)) throw "secJoint of member must be joint"; this._secJoint = val;},
known: function() {
var known = true;
if (this.magnitude === undefined) known = false;
return known;
},
isAttached: function(joint) {
var attached = false;
if (joint === this.firJoint || joint === this.secJoint) attached = true;
return attached;
},
otherJoint: function(joint) {
var otherJoint;
if (!this.isAttached(joint)) throw "Joint not attached to member";
if (joint === this.firJoint) otherJoint = this.secJoint;
else otherJoint = this.firJoint;
return otherJoint;
},
length: function() {
var xDis = this.firJoint.x - this.secJoint.x;
var yDis = this.firJoint.y - this.secJoint.y;
return Math.sqrt(Math.pow(xDis, 2), Math.pow(yDis, 2));
},
// Geometric angle from joint, does not consider internal force
getAngle: function(fromJoint) {
if (!this.isAttached(fromJoint)) throw "Joint not attached to member";
var xToOtherJoint = this.otherJoint(fromJoint).x - fromJoint.x;
var yToOtherJoint = this.otherJoint(fromJoint).y - fromJoint.y;
return Math.atan2(yToOtherJoint, xToOtherJoint);
},
forceOn: function(joint) {
var force = Object.create(t.force);
force.magnitude = this.magnitude;
var angle = this.getAngle(joint);
// Consider angle of internal force
if (this.compression === true) force.angle = angle;
else {
// TODO: is there a smarter way than this? Is this necessary?
if (angle < -Math.PI) force.angle = angle + 2 * Math.PI;
else if (angle > Math.PI) force.angle = angle - 2 * Math.PI;
}
var attachedJoint = joint;
force.forceOn = function(joint) {if (joint !== attachedJoint) throw "Force does not act on given joint"; return this;};
return force;
}
};
t.force = {
get magnitude() {return this._magnitude;},
set magnitude(val) {
if (!_.isFinite(val)) throw "Magnitude of force must be finite number";
if (val < 0) {
this._magnitude = -val;
angle += Math.PI;
} else this._magnitude = val;
},
get angle() {return this._angle;},
set angle(val) {
if (!_.isFinite(val)) throw "Angle of force must be finite number";
// TODO: is there a smarter way than this? Is this necessary?
if (val < -Math.PI) this._angle = val + 2 * Math.PI;
else if (val > Math.PI) this._angle = val - 2 * Math.PI;
else this._angle = val;
},
getAngle: function() { // Quack, quack
return this.angle;
},
// get joint() {return joint;},
// set joint(val) {if (!t.joint.isPrototypeOf(val)) throw "Joint of force must be joint"; joint = val;}
known: function() {
var known = true;
if (this.magnitude === undefined || this.angle === undefined) known = false;
return known;
},
x: function() {
return Math.cos(this.angle) * this.magnitude;
},
y: function() {
return Math.sin(this.angle) * this.magnitude;
},
forceOn: function(joint) { // Quack, quack
// TODO: enforce that force must be connected to joint to be known, added to joint, etc with joint property
// if (joint !== this.joint) throw "Force only acts on its joint";
if (!_.contains(joint.exForces, this)) throw "Force does not act on given joint";
return this;
}
};
// Brace is a kind of force
t.brace = Object.create(t.force);
root.t = t;
}).call(this);