Commit 109d726a authored by amandaghassaei's avatar amandaghassaei
Browse files

working on a rigid solver

parent 2a9f4cde
...@@ -388,6 +388,7 @@ ...@@ -388,6 +388,7 @@
<script type="text/javascript" src="js/3dUI.js"></script> <script type="text/javascript" src="js/3dUI.js"></script>
<script type="text/javascript" src="js/staticSolver.js"></script> <script type="text/javascript" src="js/staticSolver.js"></script>
<script type="text/javascript" src="js/dynamic/dynamicSolver.js"></script> <script type="text/javascript" src="js/dynamic/dynamicSolver.js"></script>
<script type="text/javascript" src="js/rigidSolver.js"></script>
<script type="text/javascript" src="js/pattern.js"></script> <script type="text/javascript" src="js/pattern.js"></script>
<script type="text/javascript" src="js/saveSTL.js"></script> <script type="text/javascript" src="js/saveSTL.js"></script>
<script type="text/javascript" src="js/saveFOLD.js"></script> <script type="text/javascript" src="js/saveFOLD.js"></script>
...@@ -508,6 +509,10 @@ ...@@ -508,6 +509,10 @@
<input name="simType" value="static" data-toggle="radio" class="custom-radio" type="radio"><span class="icons"><span class="icon-unchecked"></span><span class="icon-checked"></span></span> <input name="simType" value="static" data-toggle="radio" class="custom-radio" type="radio"><span class="icons"><span class="icon-unchecked"></span><span class="icon-checked"></span></span>
Compliant Static Simulation <a class="about floatRight" href="#" id="aboutStaticSim"><span class="fui-question-circle"></span></a> Compliant Static Simulation <a class="about floatRight" href="#" id="aboutStaticSim"><span class="fui-question-circle"></span></a>
</label> </label>
<label class="radio">
<input name="simType" value="rigid" data-toggle="radio" class="custom-radio" type="radio"><span class="icons"><span class="icon-unchecked"></span><span class="icon-checked"></span></span>
Rigid Static Simulation <a class="about floatRight" href="#" id="aboutRigidSim"><span class="fui-question-circle"></span></a>
</label>
</div><br/> </div><br/>
<label class="checkbox" for="userInteractionEnabled"> <label class="checkbox" for="userInteractionEnabled">
<input id="userInteractionEnabled" data-toggle="checkbox" class="custom-checkbox layersSelector" type="checkbox"><span class="icons"><span class="icon-unchecked"></span><span class="icon-checked"></span></span> <input id="userInteractionEnabled" data-toggle="checkbox" class="custom-checkbox layersSelector" type="checkbox"><span class="icons"><span class="icon-unchecked"></span><span class="icon-checked"></span></span>
...@@ -937,6 +942,20 @@ ...@@ -937,6 +942,20 @@
</div><!-- /.modal-content --> </div><!-- /.modal-content -->
</div><!-- /.modal-dialog --> </div><!-- /.modal-dialog -->
</div><!-- /.modal --> </div><!-- /.modal -->
<div class="modal fade" id="aboutRigidSimModal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-med">
<div class="modal-content">
<div class="modal-body">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
<p><b>Rigid Static Simulation</b><br/><br/>
....
</p>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<div class="modal fade" id="aboutAxialStrainModal" tabindex="-1" role="dialog"> <div class="modal fade" id="aboutAxialStrainModal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-med"> <div class="modal-dialog modal-med">
<div class="modal-content"> <div class="modal-content">
......
...@@ -123,12 +123,8 @@ function initControls(globals){ ...@@ -123,12 +123,8 @@ function initControls(globals){
setRadio("simType", globals.simType, function(val){ setRadio("simType", globals.simType, function(val){
if (val == "static"){
globals.staticSolver.syncNodesAndEdges();
} else if (val == "dynamic"){
globals.dynamicSolver.syncNodesAndEdges();
}
globals.simType = val; globals.simType = val;
globals.needsSync = true;
}); });
setSliderInput("#axialStiffness", globals.axialStiffness, 10, 40, 1, function(val){ setSliderInput("#axialStiffness", globals.axialStiffness, 10, 40, 1, function(val){
...@@ -252,6 +248,9 @@ function initControls(globals){ ...@@ -252,6 +248,9 @@ function initControls(globals){
setLink("#aboutStaticSim", function(){ setLink("#aboutStaticSim", function(){
$("#aboutStaticSimModal").modal("show"); $("#aboutStaticSimModal").modal("show");
}); });
setLink("#aboutRigidSim", function(){
$("#aboutRigidSimModal").modal("show");
});
setLink("#aboutAxialStrain", function(){ setLink("#aboutAxialStrain", function(){
$("#aboutAxialStrainModal").modal("show"); $("#aboutAxialStrainModal").modal("show");
}); });
......
...@@ -38,7 +38,7 @@ function initDynamicSolver(globals){ ...@@ -38,7 +38,7 @@ function initDynamicSolver(globals){
faces = globals.model.getFaces(); faces = globals.model.getFaces();
creases = globals.model.getCreases(); creases = globals.model.getCreases();
globals.model.sync(); // globals.model.sync();
positions = globals.model.getPositionsArray(); positions = globals.model.getPositionsArray();
colors = globals.model.getColorsArray(); colors = globals.model.getColorsArray();
...@@ -64,49 +64,45 @@ function initDynamicSolver(globals){ ...@@ -64,49 +64,45 @@ function initDynamicSolver(globals){
} }
function solve(_numSteps){ function solve(_numSteps){
if (globals.shouldSyncWithModel){
syncNodesAndEdges(); if (globals.forceHasChanged) {
globals.shouldSyncWithModel = false; updateExternalForces();
} else { globals.forceHasChanged = false;
if (globals.forceHasChanged) { }
updateExternalForces(); if (globals.fixedHasChanged) {
globals.forceHasChanged = false; updateFixed();
} globals.fixedHasChanged = false;
if (globals.fixedHasChanged) { }
updateFixed(); if (globals.nodePositionHasChanged) {
globals.fixedHasChanged = false; updateLastPosition();
} globals.nodePositionHasChanged = false;
if (globals.nodePositionHasChanged) { }
updateLastPosition(); if (globals.creaseMaterialHasChanged) {
globals.nodePositionHasChanged = false; updateCreasesMeta();
} globals.creaseMaterialHasChanged = false;
if (globals.creaseMaterialHasChanged) { }
updateCreasesMeta(); if (globals.materialHasChanged) {
globals.creaseMaterialHasChanged = false; updateMaterials();
} globals.materialHasChanged = false;
if (globals.materialHasChanged) { }
updateMaterials(); if (globals.shouldChangeCreasePercent) {
globals.materialHasChanged = false; setCreasePercent(globals.creasePercent);
} globals.shouldChangeCreasePercent = false;
if (globals.shouldChangeCreasePercent) { }
setCreasePercent(globals.creasePercent); // if (globals.shouldZeroDynamicVelocity){
globals.shouldChangeCreasePercent = false; // globals.gpuMath.step("zeroTexture", [], "u_velocity");
} // globals.gpuMath.step("zeroTexture", [], "u_lastVelocity");
// if (globals.shouldZeroDynamicVelocity){ // globals.shouldZeroDynamicVelocity = false;
// globals.gpuMath.step("zeroTexture", [], "u_velocity"); // }
// globals.gpuMath.step("zeroTexture", [], "u_lastVelocity"); if (globals.shouldCenterGeo){
// globals.shouldZeroDynamicVelocity = false; var avgPosition = getAvgPosition();
// } globals.gpuMath.setProgram("centerTexture");
if (globals.shouldCenterGeo){ globals.gpuMath.setUniformForProgram("centerTexture", "u_center", [avgPosition.x, avgPosition.y, avgPosition.z], "3f");
var avgPosition = getAvgPosition(); globals.gpuMath.step("centerTexture", ["u_lastPosition"], "u_position");
globals.gpuMath.setProgram("centerTexture"); globals.gpuMath.swapTextures("u_position", "u_lastPosition");
globals.gpuMath.setUniformForProgram("centerTexture", "u_center", [avgPosition.x, avgPosition.y, avgPosition.z], "3f"); globals.gpuMath.step("zeroTexture", [], "u_lastVelocity");
globals.gpuMath.step("centerTexture", ["u_lastPosition"], "u_position"); globals.gpuMath.step("zeroTexture", [], "u_velocity");
globals.gpuMath.swapTextures("u_position", "u_lastPosition"); globals.shouldCenterGeo = false;
globals.gpuMath.step("zeroTexture", [], "u_lastVelocity");
globals.gpuMath.step("zeroTexture", [], "u_velocity");
globals.shouldCenterGeo = false;
}
} }
if (_numSteps == undefined) _numSteps = globals.numSteps; if (_numSteps == undefined) _numSteps = globals.numSteps;
......
...@@ -29,10 +29,10 @@ function initGlobals(){ ...@@ -29,10 +29,10 @@ function initGlobals(){
creaseMaterialHasChanged: false, creaseMaterialHasChanged: false,
shouldResetDynamicSim: false, shouldResetDynamicSim: false,
shouldChangeCreasePercent: false, shouldChangeCreasePercent: false,
shouldSyncWithModel: false,
nodePositionHasChanged: false, nodePositionHasChanged: false,
shouldZeroDynamicVelocity: false, shouldZeroDynamicVelocity: false,
shouldCenterGeo: false, shouldCenterGeo: false,
needsSync: false,
//3d vis //3d vis
simType: "dynamic", simType: "dynamic",
......
...@@ -16,6 +16,7 @@ $(function() { ...@@ -16,6 +16,7 @@ $(function() {
globals.model = initModel(globals); globals.model = initModel(globals);
globals.staticSolver = initStaticSolver(globals); globals.staticSolver = initStaticSolver(globals);
globals.dynamicSolver = initDynamicSolver(globals); globals.dynamicSolver = initDynamicSolver(globals);
globals.rigidSolver = initRigidSolver(globals);
globals.pattern = initPattern(globals); globals.pattern = initPattern(globals);
globals.vive = initViveInterface(globals); globals.vive = initViveInterface(globals);
$(".demo[data-url='Tessellations/waterbomb.svg']").click(); $(".demo[data-url='Tessellations/waterbomb.svg']").click();
......
...@@ -98,55 +98,6 @@ function initModel(globals){ ...@@ -98,55 +98,6 @@ function initModel(globals){
return colors; return colors;
} }
// nodes.push(new Node(new THREE.Vector3(0,0,0), nodes.length));
// nodes.push(new Node(new THREE.Vector3(0,0,10), nodes.length));
// nodes.push(new Node(new THREE.Vector3(10,0,0), nodes.length));
// nodes.push(new Node(new THREE.Vector3(0,0,-10), nodes.length));
// nodes.push(new Node(new THREE.Vector3(10,0,-10), nodes.length));
// nodes.push(new Node(new THREE.Vector3(-10,0,0), nodes.length));
// nodes[0].setFixed(true);
// nodes[1].setFixed(true);
// nodes[2].setFixed(true);
// edges.push(new Beam([nodes[1], nodes[0]]));
// edges.push(new Beam([nodes[1], nodes[2]]));
// edges.push(new Beam([nodes[2], nodes[0]]));
// edges.push(new Beam([nodes[3], nodes[0]]));
// edges.push(new Beam([nodes[3], nodes[2]]));
// edges.push(new Beam([nodes[3], nodes[4]]));
// edges.push(new Beam([nodes[2], nodes[4]]));
// edges.push(new Beam([nodes[4], nodes[0]]));
// edges.push(new Beam([nodes[4], nodes[1]]));
// edges.push(new Beam([nodes[3], nodes[4]]));
// faces.push(new THREE.Face3(0,1,2));
// faces.push(new THREE.Face3(0,2,3));
// faces.push(new THREE.Face3(4,3,2));
// faces.push(new THREE.Face3(4,1,0));
// faces.push(new THREE.Face3(3,4,0));
// creases.push(new Crease(edges[2], 0, 1, Math.PI, 1, nodes[1], nodes[3], 0));
// creases.push(new Crease(edges[4], 2, 1, -Math.PI, 1, nodes[4], nodes[0], 1));
// creases.push(new Crease(edges[5], 3, 2, -Math.PI, 1, nodes[3], nodes[1], 1));
// creases.push(new Crease(edges[0], 3, 0, Math.PI, 1, nodes[4], nodes[2], 2));
// var _allNodeObject3Ds = [];
// _.each(nodes, function(node){
// var obj3D = node.getObject3D();
// _allNodeObject3Ds.push(obj3D);
// globals.threeView.sceneAddModel(obj3D);
// });
// allNodeObject3Ds = _allNodeObject3Ds;
// _.each(edges, function(edge){
// globals.threeView.sceneAddModel(edge.getObject3D());
// });
function pause(){ function pause(){
globals.threeView.pauseSimulation(); globals.threeView.pauseSimulation();
} }
...@@ -156,12 +107,23 @@ function initModel(globals){ ...@@ -156,12 +107,23 @@ function initModel(globals){
} }
function reset(){ function reset(){
getSolver().reset(); var solver = getSolver();
if (globals.needsSync){
solver.syncNodesAndEdges();
globals.needsSync = false;
}
solver.reset();
setGeoUpdates(); setGeoUpdates();
} }
function step(numSteps){ function step(numSteps){
getSolver().solve(numSteps); var solver = getSolver();
if (globals.needsSync){
solver.syncNodesAndEdges();
globals.needsSync = false;
}
solver.solve(numSteps);
geometry.attributes.position.needsUpdate = true;
setGeoUpdates(); setGeoUpdates();
} }
...@@ -179,9 +141,7 @@ function initModel(globals){ ...@@ -179,9 +141,7 @@ function initModel(globals){
function startSolver(){ function startSolver(){
globals.threeView.startAnimation(function(){ globals.threeView.startAnimation(function(){
if (!inited) return; if (!inited) return;
getSolver().solve(); step();
geometry.attributes.position.needsUpdate = true;
setGeoUpdates();
}); });
} }
...@@ -203,9 +163,9 @@ function initModel(globals){ ...@@ -203,9 +163,9 @@ function initModel(globals){
for (var i=0;i<_vertices.length;i++){ for (var i=0;i<_vertices.length;i++){
_nodes.push(new Node(_vertices[i].clone(), _nodes.length)); _nodes.push(new Node(_vertices[i].clone(), _nodes.length));
} }
// _nodes[_faces[0].a].setFixed(true); // _nodes[_faces[0][0]].setFixed(true);
// _nodes[_faces[0].b].setFixed(true); // _nodes[_faces[0][1]].setFixed(true);
// _nodes[_faces[0].c].setFixed(true); // _nodes[_faces[0][2]].setFixed(true);
var _edges = []; var _edges = [];
for (var i=0;i<_allEdges.length;i++) { for (var i=0;i<_allEdges.length;i++) {
...@@ -257,7 +217,6 @@ function initModel(globals){ ...@@ -257,7 +217,6 @@ function initModel(globals){
globals.threeView.sceneAddModel(object3D); globals.threeView.sceneAddModel(object3D);
globals.threeView.sceneAddModel(object3D2); globals.threeView.sceneAddModel(object3D2);
globals.shouldSyncWithModel = true;
inited = true; inited = true;
updateEdgeVisibility(); updateEdgeVisibility();
updateMeshVisibility(); updateMeshVisibility();
...@@ -271,14 +230,15 @@ function initModel(globals){ ...@@ -271,14 +230,15 @@ function initModel(globals){
} }
if (!globals.threeView.running()) { if (!globals.threeView.running()) {
getSolver().syncNodesAndEdges();
sync(); sync();
} }
globals.needsSync = true;
} }
function getSolver(){ function getSolver(){
if (globals.simType == "dynamic") return globals.dynamicSolver; if (globals.simType == "dynamic") return globals.dynamicSolver;
return globals.staticSolver; else if (globals.simType == "static") return globals.staticSolver;
return globals.rigidSolver;
} }
function sync(){ function sync(){
......
...@@ -164,6 +164,11 @@ Node.prototype.render = function(position){ ...@@ -164,6 +164,11 @@ Node.prototype.render = function(position){
this.object3D.position.set(position.x, position.y, position.z);//todo need this? this.object3D.position.set(position.x, position.y, position.z);//todo need this?
return position; return position;
}; };
Node.prototype.renderDelta = function(delta){
// if (this.fixed) return;
this.object3D.position.add(delta);
return this.object3D.position;
};
Node.prototype.renderChange = function(change){ Node.prototype.renderChange = function(change){
this.object3D.position.add(change); this.object3D.position.add(change);
......
...@@ -717,6 +717,7 @@ function initPattern(globals){ ...@@ -717,6 +717,7 @@ function initPattern(globals){
if (!containsInnerCrease) { if (!containsInnerCrease) {
polygons.splice(i,1); polygons.splice(i,1);
polygonEdges.splice(i,1); polygonEdges.splice(i,1);
break;//todo only remove once
} }
} }
......
/**
* Created by amandaghassaei on 5/26/17.
*/
function initRigidSolver(){
var nodes;
var edges;
var faces;
var creases;
var positions;
var C, F;
function syncNodesAndEdges(){
nodes = globals.model.getNodes();
edges = globals.model.getEdges();
faces = globals.model.getFaces();
creases = globals.model.getCreases();
positions = globals.model.getPositionsArray();
setUpParams();
}
function solve(){
updateMatrices();
solveStep();
}
function reset(){
}
// function pinv(A) { //for linearly ind rows
// var AT = numeric.transpose(A);
// return numeric.dot(AT, numeric.inv(numeric.dot(AT,A)));
// }
function pinv(A) {
//http://www.numericjs.com/workshop.php?link=b923cdeb84e188a11b44e3e82e44897b3b7da1d6640ddb46ab7330a6625f8e19
var z = numeric.svd(A), foo = z.S[0];
var U = z.U, S = z.S, V = z.V;
var m = A.length, n = A[0].length, tol = Math.max(m,n)*numeric.epsilon*foo,M = S.length;
var i,Sinv = new Array(M);
for(i=M-1;i!==-1;i--) { if(S[i]>tol) Sinv[i] = 1/S[i]; else Sinv[i] = 0; }
return numeric.dot(numeric.dot(V,numeric.diag(Sinv)),numeric.transpose(U))
}
function solveStep(){
//todo add external forces
var X = numeric.dot(numeric.sub(numeric.identity(3*nodes.length),
numeric.dot(numeric.transpose(pinv(numeric.transpose(C))), C)), F);//todo valid psuedoinv?
// var sum = new THREE.Vector3();
// for (var i=0;i<_F.length;i+=3){
// sum.x += _F[i];
// sum.y += _F[i+1];
// sum.z += _F[i+2];
// }
// console.log(sum);
console.log(X);
render(X);
}
function render(X){
for (var i=0;i<nodes.length;i++){
var nodePosition = new THREE.Vector3(X[3*i], X[3*i+1], X[3*i+2]);
var nexPos = nodes[i].renderDelta(nodePosition);
positions[3*i] = nexPos.x;
positions[3*i+1] = nexPos.y;
positions[3*i+2] = nexPos.z;
}
for (var i=0;i<edges.length;i++){
edges[i].render();
}
}
function initEmptyArray(dim1, dim2, dim3){
if (dim2 === undefined) dim2 = 0;
if (dim3 === undefined) dim3 = 0;
var array = [];
for (var i=0;i<dim1;i++){
if (dim2 == 0) array.push(0);
else array.push([]);
for (var j=0;j<dim2;j++){
if (dim3 == 0) array[i].push(0);
else array[i].push([]);
for (var k=0;k<dim3;k++){
array[i][j].push(0);
}
}
}
return array;
}
function setUpParams(){
//C = (edges + creases) x 3nodes
//disp = 1 x 3nodes
C = initEmptyArray(edges.length, 3*nodes.length);//todo change size
F = initEmptyArray(3*nodes.length);
// for (var i=0;i<nodes.length;i++){
// F[3*i] = 0;
// F[3*i+1] = 0;
// F[3*i+2] = 0;
// }
}
function updateMatrices(){
var numNodes = nodes.length;
var numEdges = edges.length;
F = initEmptyArray(3*numNodes);
for (var j=0;j<numEdges;j++){
var edge = edges[j];
var _nodes = edge.nodes;
var edgeVector0 = edge.getVector(_nodes[0]);
var length = edge.getOriginalLength();
var diff = edgeVector0.length() - length;
var rxnForceScale = globals.axialStiffness*diff/length;
edgeVector0.normalize();
var i = _nodes[0].getIndex();
C[j][3*i] = edgeVector0.x;
C[j][3*i+1] = edgeVector0.y;
C[j][3*i+2] = edgeVector0.z;
F[3*i] -= edgeVector0.x*rxnForceScale;
F[3*i+1] -= edgeVector0.y*rxnForceScale;
F[3*i+2] -= edgeVector0.z*rxnForceScale;
i = _nodes[1].getIndex();
C[j][3*i] = -edgeVector0.x;
C[j][3*i+1] = -edgeVector0.y;
C[j][3*i+2] = -edgeVector0.z;
F[3*i] += edgeVector0.x*rxnForceScale;
F[3*i+1] += edgeVector0.y*rxnForceScale;
F[3*i+2] += edgeVector0.z*rxnForceScale;
}
var geometry = globals.model.getGeometry();
var indices = geometry.index.array;
var normals = [];
//compute all normals
var cb = new THREE.Vector3(), ab = new THREE.Vector3();
for (var j=0;j<indices.length;j+=3){
var index = 3*indices[j];
var vA = new THREE.Vector3(positions[index], positions[index+1], positions[index+2]);
index = 3*indices[j+1];
var vB = new THREE.Vector3(positions[index], positions[index+1], positions[index+2]);
index = 3*indices[j+2];
var vC = new THREE.Vector3(positions[index], positions[index+1], positions[index+2]);
cb.subVectors( vC, vB );
ab.subVectors( vA, vB );
cb.cross( ab );
cb.normalize();
normals.push(cb.clone());
}
for (var j=0;j<numFreeCreases;j++){
var crease = creases[freeCreasesMapping[j]];
var normal1 = normals[crease.face1Index];
var normal2 = normals[crease.face2Index];
var dotNormals = normal1.dot(normal2);
if (dotNormals < -1.0) dotNormals = -1.0;
else if (dotNormals > 1.0) dotNormals = 1.0;
var creaseVector = crease.getVector().normalize();
//https://math.stackexchange.com/questions/47059/how-do-i-calculate-a-dihedral-angle-given-cartesian-coordinates
var theta = Math.atan2((normal1.clone().cross(creaseVector)).dot(normal2), dotNormals);
var diff = theta - globals.creasePercent*crease.targetTheta;
var rxnForceScale = crease.getK()*diff;
var partial1, partial2;
if (!crease.node1.fixed){
var i = indicesMapping.indexOf(crease.node1.getIndex());
var dist = crease.getLengthToNode1();
var partial1 = normal1.clone().divideScalar(dist);
// C[j+numFreeEdges][3*i] = partial1.x;
// C[j+numFreeEdges][3*i+1] = partial1.y;
// C[j+numFreeEdges][3*i+2] = partial1.z;
F[3*i] -= partial1.x*rxnForceScale;