diff --git a/Scratch.ipynb b/Scratch.ipynb index 61ceee5..b844896 100644 --- a/Scratch.ipynb +++ b/Scratch.ipynb @@ -7,32 +7,50 @@ "outputs": [], "source": [ "import meshutil\n", - "import stl.mesh" + "import stl.mesh\n", + "import numpy" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ - "c = meshutil.cube(open_xz=True)" + "c = meshutil.cube(open_xz=False)\n", + "c1 = meshutil.cube_distort(0.3)\n", + "c1[\"vectors\"] = c1[\"vectors\"]/2 + [0,1,0]\n", + "c2 = meshutil.cube_distort(0.3)\n", + "c2[\"vectors\"] = c2[\"vectors\"]/2 + [0.5,1,0]\n", + "c3 = meshutil.cube_distort(0.3)\n", + "c3[\"vectors\"] = c3[\"vectors\"]/2 + [0,1,0.5]\n", + "c4 = meshutil.cube_distort(0.3)\n", + "c4[\"vectors\"] = c4[\"vectors\"]/2 + [0.5,1,0.5]" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ - "mesh = stl.mesh.Mesh(c)\n", + "d = numpy.concatenate([c,c1,c2,c3,c4])" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "mesh = stl.mesh.Mesh(d)\n", "mesh_fname = \"cube_test.stl\"\n", "mesh.save(mesh_fname)" ] }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -41,15 +59,14 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/home/hodapp/.local/lib/python3.6/site-packages/IPython/core/display.py:694: UserWarning: Consider using IPython.display.IFrame instead\n", - " warnings.warn(\"Consider using IPython.display.IFrame instead\")\n" + "face_normals didn't match triangles, ignoring!\n" ] }, { @@ -247,7 +264,7 @@ "Math.pow(distanceToCamera,2)+\n", "Math.pow(distanceToCamera,2));camera.position.set(len,len,len);controls.update();camera.lookAt(boundingSphere.center);controls.target.set(boundingSphere.center.x,boundingSphere.center.y,boundingSphere.center.z);camera.updateProjectionMatrix();}\n", "function centerControls(obj,camera,controls){const boundingBox=new THREE.Box3().setFromObject(obj);const boundingSphere=new THREE.Sphere();boundingBox.getBoundingSphere((target=boundingSphere));controls.update();controls.target.set(boundingSphere.center.x,boundingSphere.center.y,boundingSphere.center.z);}\n", - "function init(){scene=new THREE.Scene();scene.background=new THREE.Color(0xffffff);tracklight=new THREE.DirectionalLight(0xffffff,1.75);scene.add(tracklight);base64_data="Z2xURgIAAACIBAAAfAMAAEpTT057InNjZW5lIjogMCwgInNjZW5lcyI6IFt7Im5vZGVzIjogWzBdfV0sICJhc3NldCI6IHsidmVyc2lvbiI6ICIyLjAiLCAiZ2VuZXJhdG9yIjogImh0dHBzOi8vZ2l0aHViLmNvbS9taWtlZGgvdHJpbWVzaCJ9LCAiYWNjZXNzb3JzIjogW3siYnVmZmVyVmlldyI6IDAsICJjb21wb25lbnRUeXBlIjogNTEyNSwgImNvdW50IjogMzYsICJtYXgiOiBbN10sICJtaW4iOiBbMF0sICJ0eXBlIjogIlNDQUxBUiJ9LCB7ImJ1ZmZlclZpZXciOiAxLCAiY29tcG9uZW50VHlwZSI6IDUxMjYsICJjb3VudCI6IDgsICJ0eXBlIjogIlZFQzMiLCAiYnl0ZU9mZnNldCI6IDAsICJtYXgiOiBbMS4wLCAxLjAsIDEuMF0sICJtaW4iOiBbMC4wLCAwLjAsIDAuMF19XSwgIm1lc2hlcyI6IFt7Im5hbWUiOiAiY3ViZV90ZXN0LnN0bCIsICJwcmltaXRpdmVzIjogW3siYXR0cmlidXRlcyI6IHsiUE9TSVRJT04iOiAxfSwgImluZGljZXMiOiAwLCAibW9kZSI6IDR9XX1dLCAiY2FtZXJhcyI6IFt7Im5hbWUiOiAiY2FtZXJhXzlFTzZYNyIsICJ0eXBlIjogInBlcnNwZWN0aXZlIiwgInBlcnNwZWN0aXZlIjogeyJhc3BlY3RSYXRpbyI6IDEuMzMzMzMzMzMzMzMzMzMzMywgInlmb3YiOiAwLjc4NTM5ODE2MzM5NzQ0ODMsICJ6bmVhciI6IDAuMDF9fV0sICJub2RlcyI6IFt7Im5hbWUiOiAid29ybGQiLCAiY2hpbGRyZW4iOiBbMV19LCB7Im5hbWUiOiAiY3ViZV90ZXN0LnN0bF9URVhPN0oyTk5QUVAiLCAibWVzaCI6IDB9XSwgImJ1ZmZlcnMiOiBbeyJieXRlTGVuZ3RoIjogMjQwfV0sICJidWZmZXJWaWV3cyI6IFt7ImJ1ZmZlciI6IDAsICJieXRlT2Zmc2V0IjogMCwgImJ5dGVMZW5ndGgiOiAxNDR9LCB7ImJ1ZmZlciI6IDAsICJieXRlT2Zmc2V0IjogMTQ0LCAiYnl0ZUxlbmd0aCI6IDk2fV19ICAg8AAAAEJJTgAAAAAABgAAAAQAAAAAAAAAAgAAAAYAAAAEAAAABwAAAAUAAAAEAAAABgAAAAcAAAAFAAAAAwAAAAEAAAAFAAAABwAAAAMAAAABAAAAAgAAAAAAAAABAAAAAwAAAAIAAAACAAAABwAAAAYAAAACAAAAAwAAAAcAAAAAAAAABAAAAAUAAAAAAAAABQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgD8AAAAAAACAPwAAAAAAAAAAAACAPwAAgD8AAIA/AAAAAAAAAAAAAIA/AAAAAAAAgD8AAIA/AACAPwAAAAAAAIA/AACAPwAAgD8=";;renderer=new THREE.WebGLRenderer({antialias:true});renderer.setPixelRatio(window.devicePixelRatio);renderer.setSize(window.innerWidth,window.innerHeight);document.body.appendChild(renderer.domElement);loader=new THREE.GLTFLoader();loader.load("data:text/plain;base64,"+base64_data,function(gltf){scene.add(gltf.scene);camera=gltf.cameras[0];controls=new THREE.TrackballControls(camera);controls.rotateSpeed=1.0;controls.zoomSpeed=1.2;controls.panSpeed=0.8;controls.noZoom=false;controls.noPan=false;controls.staticMoving=true;controls.dynamicDampingFactor=0.3;controls.keys=[65,83,68];controls.addEventListener("change",render);centerControls(scene,camera,controls);render();window.addEventListener("resize",onWindowResize,false);animate();});}\n", + "function init(){scene=new THREE.Scene();scene.background=new THREE.Color(0xffffff);tracklight=new THREE.DirectionalLight(0xffffff,1.75);scene.add(tracklight);base64_data="Z2xURgIAAAA4BwAAsAMAAEpTT057InNjZW5lIjogMCwgInNjZW5lcyI6IFt7Im5vZGVzIjogWzBdfV0sICJhc3NldCI6IHsidmVyc2lvbiI6ICIyLjAiLCAiZ2VuZXJhdG9yIjogImh0dHBzOi8vZ2l0aHViLmNvbS9taWtlZGgvdHJpbWVzaCJ9LCAiYWNjZXNzb3JzIjogW3siYnVmZmVyVmlldyI6IDAsICJjb21wb25lbnRUeXBlIjogNTEyNSwgImNvdW50IjogMTMyLCAibWF4IjogWzI4XSwgIm1pbiI6IFswXSwgInR5cGUiOiAiU0NBTEFSIn0sIHsiYnVmZmVyVmlldyI6IDEsICJjb21wb25lbnRUeXBlIjogNTEyNiwgImNvdW50IjogMjksICJ0eXBlIjogIlZFQzMiLCAiYnl0ZU9mZnNldCI6IDAsICJtYXgiOiBbMS4wLCAxLjc2MjAyMjQ5NTI2OTc3NTQsIDEuMF0sICJtaW4iOiBbLTAuMTcwMDkxODUyNTQ1NzM4MjIsIDAuMCwgLTAuMTcwMDkxODUyNTQ1NzM4MjJdfV0sICJtZXNoZXMiOiBbeyJuYW1lIjogImN1YmVfdGVzdC5zdGwiLCAicHJpbWl0aXZlcyI6IFt7ImF0dHJpYnV0ZXMiOiB7IlBPU0lUSU9OIjogMX0sICJpbmRpY2VzIjogMCwgIm1vZGUiOiA0fV19XSwgImNhbWVyYXMiOiBbeyJuYW1lIjogImNhbWVyYV9VTk5VSVEiLCAidHlwZSI6ICJwZXJzcGVjdGl2ZSIsICJwZXJzcGVjdGl2ZSI6IHsiYXNwZWN0UmF0aW8iOiAxLjMzMzMzMzMzMzMzMzMzMzMsICJ5Zm92IjogMC43ODUzOTgxNjMzOTc0NDgzLCAiem5lYXIiOiAwLjAxfX1dLCAibm9kZXMiOiBbeyJuYW1lIjogIndvcmxkIiwgImNoaWxkcmVuIjogWzFdfSwgeyJuYW1lIjogImN1YmVfdGVzdC5zdGxfVURNTVhVT0RFUjNSIiwgIm1lc2giOiAwfV0sICJidWZmZXJzIjogW3siYnl0ZUxlbmd0aCI6IDg3Nn1dLCAiYnVmZmVyVmlld3MiOiBbeyJidWZmZXIiOiAwLCAiYnl0ZU9mZnNldCI6IDAsICJieXRlTGVuZ3RoIjogNTI4fSwgeyJidWZmZXIiOiAwLCAiYnl0ZU9mZnNldCI6IDUyOCwgImJ5dGVMZW5ndGgiOiAzNDh9XX0gIGwDAABCSU4AAAAAAAcAAAAFAAAAAAAAAAIAAAAHAAAABQAAAAgAAAAGAAAABQAAAAcAAAAIAAAABgAAAAMAAAABAAAABgAAAAgAAAADAAAAAQAAAAIAAAAAAAAAAQAAAAMAAAACAAAAAgAAAAgAAAAHAAAAAgAAAAMAAAAIAAAAAAAAAAUAAAAGAAAAAAAAAAYAAAABAAAAAgAAAAsAAAASAAAAAgAAAA4AAAALAAAAEgAAABgAAAAUAAAAEgAAAAsAAAAYAAAAFAAAABsAAAAEAAAAFAAAABgAAAAbAAAABAAAAA4AAAACAAAABAAAABsAAAAOAAAAEgAAABYAAAAHAAAAEgAAABkAAAAWAAAABwAAAA0AAAAJAAAABwAAABYAAAANAAAACQAAABAAAAAUAAAACQAAAA0AAAAQAAAAFAAAABkAAAASAAAAFAAAABAAAAAZAAAABAAAAAoAAAAUAAAABAAAAA8AAAAKAAAAFAAAABcAAAATAAAAFAAAAAoAAAAXAAAAEwAAABwAAAADAAAAEwAAABcAAAAcAAAAAwAAAA8AAAAEAAAAAwAAABwAAAAPAAAAFAAAABUAAAAJAAAAFAAAABoAAAAVAAAACQAAAAwAAAAIAAAACQAAABUAAAAMAAAACAAAABEAAAATAAAACAAAAAwAAAARAAAAEwAAABoAAAAUAAAAEwAAABEAAAAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAAAAAAAAAAAAgD8AAIA/AAAAAAAAgD8AAAA/AACAPwAAAAAAAAAAAACAPwAAAAAAAIA/AACAPwAAgD8AAAAAAACAPwAAgD8AAIA/AACAPwAAgD8AAAA/QaGuPiegzj+46ag+QaGuPiegzj+PLC6+GJlRP/SJ4T8YmVE/GJlRP/SJ4T8wMqM+bU4Xvlq2uz9tThe+bU4Xvlq2uz/KWLQ+uOmoPiegzj9Boa4+uOmoPiegzj+gUFc/AAAAPwAAgD8AAAAAAAAAPwAAgD8AAIA/AAAAPwAAgD8AAAA/oFBXPyegzj+46ag+oFBXPyegzj+PLC6+MDKjPvSJ4T8YmVE/MDKjPvSJ4T8wMqM+yli0Plq2uz9tThe+yli0Plq2uz/KWLQ+jywuviegzj9Boa4+jywuviegzj+gUFc/";;renderer=new THREE.WebGLRenderer({antialias:true});renderer.setPixelRatio(window.devicePixelRatio);renderer.setSize(window.innerWidth,window.innerHeight);document.body.appendChild(renderer.domElement);loader=new THREE.GLTFLoader();loader.load("data:text/plain;base64,"+base64_data,function(gltf){scene.add(gltf.scene);camera=gltf.cameras[0];controls=new THREE.TrackballControls(camera);controls.rotateSpeed=1.0;controls.zoomSpeed=1.2;controls.panSpeed=0.8;controls.noZoom=false;controls.noPan=false;controls.staticMoving=true;controls.dynamicDampingFactor=0.3;controls.keys=[65,83,68];controls.addEventListener("change",render);centerControls(scene,camera,controls);render();window.addEventListener("resize",onWindowResize,false);animate();});}\n", "function onWindowResize(){camera.aspect=window.innerWidth/window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth,window.innerHeight);controls.handleResize();render();}\n", "function animate(){requestAnimationFrame(animate);controls.update();}\n", "function render(){tracklight.position.copy(camera.position);renderer.render(scene,camera);}\n", @@ -258,7 +275,7 @@ "" ] }, - "execution_count": 5, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } diff --git a/meshutil.py b/meshutil.py index cd7813d..59346a9 100644 --- a/meshutil.py +++ b/meshutil.py @@ -2,23 +2,56 @@ import stl.mesh import numpy import quaternion +import quat + +# (left/right, bottom/top, back/front) +# I'm using X to the right, Y up, Z inward +# (not that it really matters except for variable names) +lbf = numpy.array([0,0,0]) +rbf = numpy.array([1,0,0]) +ltf = numpy.array([0,1,0]) +rtf = numpy.array([1,1,0]) +lbb = numpy.array([0,0,1]) +rbb = numpy.array([1,0,1]) +ltb = numpy.array([0,1,1]) +rtb = numpy.array([1,1,1]) + def cube(open_xz=False): if open_xz: data = numpy.zeros(8, dtype=stl.mesh.Mesh.dtype) else: data = numpy.zeros(12, dtype=stl.mesh.Mesh.dtype) - v = data["vectors"] - v[0] = [[0,0,0], [1,1,0], [1,0,0]] - v[1] = [[0,0,0], [0,1,0], [1,1,0]] - v[2] = [[1,0,0], [1,1,1], [1,0,1]] - v[3] = [[1,0,0], [1,1,0], [1,1,1]] - v[4] = [[1,0,1], [0,1,1], [0,0,1]] - v[5] = [[1,0,1], [1,1,1], [0,1,1]] - v[6] = [[0,0,1], [0,1,0], [0,0,0]] - v[7] = [[0,0,1], [0,1,1], [0,1,0]] + v = data["vectors"] + v[0] = [lbf, rtf, rbf] + v[1] = [lbf, ltf, rtf] + v[2] = [rbf, rtb, rbb] + v[3] = [rbf, rtf, rtb] + v[4] = [rbb, ltb, lbb] + v[5] = [rbb, rtb, ltb] + v[6] = [lbb, ltf, lbf] + v[7] = [lbb, ltb, ltf] if not open_xz: - v[8] = [[0,1,0], [1,1,1], [1,1,0]] - v[9] = [[0,1,0], [0,1,1], [1,1,1]] - v[10] = [[0,0,0], [1,0,0], [1,0,1]] - v[11] = [[0,0,0], [1,0,1], [0,0,1]] + v[8] = [ltf, rtb, rtf] + v[9] = [ltf, ltb, rtb] + v[10] = [lbf, rbf, rbb] + v[11] = [lbf, rbb, lbb] + return data + +def cube_distort(angle): + data = numpy.zeros(8, dtype=stl.mesh.Mesh.dtype) + v = data["vectors"] + q = quat.rotation_quaternion(numpy.array([-1,0,1]), angle) + ltf2 = quat.conjugate_by(ltf, q)[0,:] + rtf2 = quat.conjugate_by(rtf, q)[0,:] + ltb2 = quat.conjugate_by(ltb, q)[0,:] + rtb2 = quat.conjugate_by(rtb, q)[0,:] + # TODO: Just make these functions work right with single vectors + v[0] = [lbf, rtf2, rbf] + v[1] = [lbf, ltf2, rtf2] + v[2] = [rbf, rtb2, rbb] + v[3] = [rbf, rtf2, rtb2] + v[4] = [rbb, ltb2, lbb] + v[5] = [rbb, rtb2, ltb2] + v[6] = [lbb, ltf2, lbf] + v[7] = [lbb, ltb2, ltf2] return data