diff --git a/css/main.css b/css/main.css index 8c7744dd44a4ce0f8eb74c7f88c6e59cd4a8f0c7..cc656e0d2d3d9045828c90061246ae75a0b812cc 100644 --- a/css/main.css +++ b/css/main.css @@ -287,3 +287,8 @@ svg{ text-align: center; margin: 0 18px; } + +#aboutError{ + margin-right: 10px; + color: #34495e; +} diff --git a/index.html b/index.html index b99f719e14e7c78d2aff3143ee299322d1986e0a..b8905a3fe834dd67098c8c53576b5020f63602ce 100644 --- a/index.html +++ b/index.html @@ -102,9 +102,9 @@ return; } - vec3 velocity = texture2D(u_velocity, scaledFragCoord).xyz; - vec3 position = velocity*u_dt + lastPosition; - gl_FragColor = vec4(position, 0.0); + vec4 velocityData = texture2D(u_velocity, scaledFragCoord); + vec3 position = velocityData.xyz*u_dt + lastPosition; + gl_FragColor = vec4(position, velocityData.a);//velocity.a has error info } </script> @@ -146,6 +146,8 @@ vec4 neighborIndices = texture2D(u_meta, scaledFragCoord); vec4 meta = texture2D(u_meta, scaledFragCoord); + float nodeError = 0.0; + for (int j=0;j<100;j++){//for all beams (up to 100, had to put a const int in here) if (j >= int(meta[1])) break; @@ -164,11 +166,13 @@ vec3 nominalDist = neighborOriginalPosition-originalPosition; vec3 deltaP = neighborLastPosition-lastPosition+nominalDist; deltaP -= normalize(deltaP)*beamMeta[2]; + nodeError += length(deltaP)/length(nominalDist); vec3 deltaV = neighborLastVelocity-lastVelocity; vec3 _force = deltaP*beamMeta[0] + deltaV*beamMeta[1]; force += _force; } + nodeError /= meta[1]; for (int j=0;j<100;j++){//for all creases (up to 100, had to put a const int in here) if (j >= int(meta[3])) break; @@ -227,7 +231,7 @@ } vec3 velocity = force*u_dt/mass[0] + lastVelocity; - gl_FragColor = vec4(velocity,0.0); + gl_FragColor = vec4(velocity,nodeError); } </script> @@ -451,6 +455,9 @@ <span class="label-slider">Damping (0-1): </span><div class="flat-slider ui-slider ui-corner-all ui-slider-horizontal ui-widget ui-widget-content"></div> <input value="" placeholder="" class="form-control" type="text"> </div> + <br/><br/> + <b>Error:</b><br/> + <div class="indent"> Average Error (per node): <span id="globalError"></span><a class="floatRight" href="#" id="aboutError"><span class="fui-question-circle"></span></a></div> <div class="extraSpace"></div> </div> @@ -479,6 +486,10 @@ <input name="colorMode" value="normal" data-toggle="radio" class="custom-radio" type="radio"><span class="icons"><span class="icon-unchecked"></span><span class="icon-checked"></span></span> Face Normals Material </label> + <label class="radio"> + <input name="colorMode" value="error" data-toggle="radio" class="custom-radio" type="radio"><span class="icons"><span class="icon-unchecked"></span><span class="icon-checked"></span></span> + Node Error Material + </label> </div> </div><br/> Edges: @@ -592,5 +603,27 @@ </div><!-- /.modal-content --> </div><!-- /.modal-dialog --> </div><!-- /.modal --> +<div class="modal fade" id="aboutErrorModal" 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">×</span> + </button> + <p><b>Simulation Error</b><br/><br/> + "Average Error (per node)" gives a sense of how much the distance constraints in the + origami pattern are being violated. The error at each node is evaluated by averaging the + percent deviation of all its distance constraints with adjacent nodes. This error is + reported as a percent of the total length of the distance constraint to remove scaling effects. + Increasing the "Axial Stiffness" of the simulation will tighten these constraints and + lower the error in the simulation.<br/> + <br/> + To visualize the error of each node graphically, select "Node Error Material" under "Mesh Material" + in the left menu. + </p> + </div> + </div><!-- /.modal-content --> + </div><!-- /.modal-dialog --> +</div><!-- /.modal --> </body> </html> \ No newline at end of file diff --git a/js/controls.js b/js/controls.js index 85cb9ef35880c1ab94b0bace4bff9bf8062f114e..37a8e8c7fac4457ac3d7076f862545f25d2dbd0f 100644 --- a/js/controls.js +++ b/js/controls.js @@ -393,6 +393,10 @@ function initControls(globals){ else $("#meshMaterialOptions").hide(); }); + setLink("#aboutError", function(){ + $("#aboutErrorModal").modal("show"); + }); + function setButtonGroup(id, callback){ $(id+" a").click(function(e){ e.preventDefault(); diff --git a/js/dynamic/dynamicSolver.js b/js/dynamic/dynamicSolver.js index 7f85643e5a4f71c754044d569d3ad9f88521cdfc..71ee2aee6fa82c158aaf1bcc8c913fd0230aa3aa 100644 --- a/js/dynamic/dynamicSolver.js +++ b/js/dynamic/dynamicSolver.js @@ -128,6 +128,8 @@ function initDynamicSolver(globals){ gpuMath.swapTextures("u_position", "u_lastPosition"); } + var $errorOutput = $("#globalError"); + function render(){ // var vectorLength = 2; @@ -148,7 +150,7 @@ function initDynamicSolver(globals){ // console.log("here"); // } - var vectorLength = 3; + var vectorLength = 4; globals.gpuMath.setProgram("packToBytes"); globals.gpuMath.setUniformForProgram("packToBytes", "u_vectorLength", vectorLength, "1f"); globals.gpuMath.setUniformForProgram("packToBytes", "u_floatTextureDim", [textureDim, textureDim], "2f"); @@ -161,11 +163,14 @@ function initDynamicSolver(globals){ var pixels = new Uint8Array(height*textureDim*4*vectorLength); globals.gpuMath.readPixels(0, 0, textureDim * vectorLength, height, pixels); var parsedPixels = new Float32Array(pixels.buffer); + var globalError = 0; for (var i = 0; i < nodes.length; i++) { var rgbaIndex = i * vectorLength; + globalError += parsedPixels[rgbaIndex+3]; var nodePosition = new THREE.Vector3(parsedPixels[rgbaIndex], parsedPixels[rgbaIndex + 1], parsedPixels[rgbaIndex + 2]); nodes[i].render(nodePosition); } + $errorOutput.html((globalError/nodes.length*100).toFixed(7) + " %"); for (var i=0;i<edges.length;i++){ edges[i].render(); } diff --git a/js/model.js b/js/model.js index c4e1e9c63502190994ebefc8f16f4b01348147ec..3e1031860adcb6b63ba48498fa03b47698ae4460 100644 --- a/js/model.js +++ b/js/model.js @@ -5,12 +5,22 @@ //wireframe model and folding structure function initModel(globals){ + var doubleGeoFaces = []; + var faces = []; + var geometry = new THREE.Geometry(); + geometry.dynamic = true; + var material; setMeshMaterial(); - function setMeshMaterial(){ - if (globals.colorMode == "normal"){ + function setMeshMaterial() { + if (globals.colorMode == "normal") { + // geometry.faces = faces; material = new THREE.MeshNormalMaterial({side: THREE.DoubleSide}); + } else if (globals.colorMode == "error"){ + // geometry.faces = faces; + material = new THREE.MeshBasicMaterial({ vertexColors: THREE.VertexColors, side:THREE.DoubleSide}); } else { + // geometry.faces = doubleGeoFaces; material = new THREE.MultiMaterial([ new THREE.MeshLambertMaterial({shading:THREE.FlatShading, color:0xff0000, side:THREE.FrontSide}), new THREE.MeshLambertMaterial({shading:THREE.FlatShading, color:0x0000ff, side:THREE.FrontSide}) @@ -38,14 +48,11 @@ function initModel(globals){ object3D.visible = globals.meshVisible; } - var geometry = new THREE.Geometry(); - geometry.dynamic = true; - var object3D = new THREE.Mesh(geometry, material); - function getGeometry(){ return geometry; } + var object3D = new THREE.Mesh(geometry, material); var allNodeObject3Ds = []; var nodes = []; @@ -74,7 +81,6 @@ function initModel(globals){ // edges.push(new Beam([nodes[4], nodes[1]])); // edges.push(new Beam([nodes[3], nodes[4]])); - var faces = []; // faces.push(new THREE.Face3(0,1,2)); // faces.push(new THREE.Face3(0,2,3)); // faces.push(new THREE.Face3(4,3,2)); @@ -195,14 +201,14 @@ function initModel(globals){ vertices.push(nodes[i].getPosition()); } - var geofaces = faces.slice(); + doubleGeoFaces = faces.slice(); for (var i=0;i<faces.length;i++){ - geofaces[i].materialIndex = 1; - geofaces.push(new THREE.Face3(faces[i].a, faces[i].c, faces[i].b)); + doubleGeoFaces[i].materialIndex = 1; + doubleGeoFaces.push(new THREE.Face3(faces[i].a, faces[i].c, faces[i].b)); } geometry.vertices = vertices; - geometry.faces = geofaces; + geometry.faces = doubleGeoFaces; geometry.verticesNeedUpdate = true; geometry.elementsNeedUpdate = true; geometry.computeFaceNormals();