diff --git a/Scratch.ipynb b/Scratch.ipynb index 5427aa7..ab708c2 100644 --- a/Scratch.ipynb +++ b/Scratch.ipynb @@ -14,27 +14,37 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 65, "metadata": {}, "outputs": [], "source": [ "mesh_fname = \"cube_test.stl\"\n", + "\n", "# center at (0,0,0) for sanity reasons\n", - "center = meshutil.TransformStack().translate(-0.5,-0.5,-0.5)\n", - "base = meshutil.cube(open_xz=False).transform(center.mtx)\n", - "xform = meshutil.TransformStack()#.translate(0,1,0).scale(0.5).translate(-0.5, -0.5, -0.5)\n", + "center = meshutil.Transform().translate(-0.5, -0.5, -0.5)\n", + "base = meshutil.cube(open_xz=False).transform(center)\n", + "\n", + "xform = center.translate(-0.5,1.5,-0.5).scale(0.5)\n", + "ang = 0.15\n", + "l2 = meshutil.cube_distort(ang, open_xz=False)\n", + "\n", "mesh = base\n", - "l2 = meshutil.cube_distort(0.1, open_xz=False)\n", - "for i in range(2):\n", - " m = l2.transform(xform.scale(2).rotate([0,1,0], i*numpy.pi/2).mtx)\n", - " mesh = mesh.concat(m)\n", - "mesh.to_stl_mesh().save(mesh_fname)\n", - "# WTF, higher ranges make visualizer fail" + "for layer in range(2):\n", + " for i in range(4):\n", + " m = l2.transform(xform.rotate([0,1,0], i*numpy.pi/2))\n", + " mesh = mesh.concat(m)\n", + " xform = xform.rotate([-1,0,1], ang).scale(1.0).translate(0, 0.7, 0)\n", + "\n", + "# Work around some annoying-ass trimesh/threejs bug:\n", + "wtf = meshutil.Transform().scale(0.1).translate(5,0,0)\n", + "base = base.transform(wtf)\n", + "mesh = mesh.concat(base)\n", + "mesh.to_stl_mesh().save(mesh_fname)" ] }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 66, "metadata": {}, "outputs": [ { @@ -239,7 +249,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="Z2xURgIAAABkBgAAkAMAAEpTT057InNjZW5lIjogMCwgInNjZW5lcyI6IFt7Im5vZGVzIjogWzBdfV0sICJhc3NldCI6IHsidmVyc2lvbiI6ICIyLjAiLCAiZ2VuZXJhdG9yIjogImh0dHBzOi8vZ2l0aHViLmNvbS9taWtlZGgvdHJpbWVzaCJ9LCAiYWNjZXNzb3JzIjogW3siYnVmZmVyVmlldyI6IDAsICJjb21wb25lbnRUeXBlIjogNTEyNSwgImNvdW50IjogMTA4LCAibWF4IjogWzIxXSwgIm1pbiI6IFswXSwgInR5cGUiOiAiU0NBTEFSIn0sIHsiYnVmZmVyVmlldyI6IDEsICJjb21wb25lbnRUeXBlIjogNTEyNiwgImNvdW50IjogMjIsICJ0eXBlIjogIlZFQzMiLCAiYnl0ZU9mZnNldCI6IDAsICJtYXgiOiBbMi4wLCAyLjM4NDM0NjI0NjcxOTM2MDQsIDIuMF0sICJtaW4iOiBbLTAuNSwgLTAuNSwgLTIuMF19XSwgIm1lc2hlcyI6IFt7Im5hbWUiOiAiY3ViZV90ZXN0LnN0bCIsICJwcmltaXRpdmVzIjogW3siYXR0cmlidXRlcyI6IHsiUE9TSVRJT04iOiAxfSwgImluZGljZXMiOiAwLCAibW9kZSI6IDR9XX1dLCAiY2FtZXJhcyI6IFt7Im5hbWUiOiAiY2FtZXJhX09BS0dSNiIsICJ0eXBlIjogInBlcnNwZWN0aXZlIiwgInBlcnNwZWN0aXZlIjogeyJhc3BlY3RSYXRpbyI6IDEuMzMzMzMzMzMzMzMzMzMzMywgInlmb3YiOiAwLjc4NTM5ODE2MzM5NzQ0ODMsICJ6bmVhciI6IDAuMDF9fV0sICJub2RlcyI6IFt7Im5hbWUiOiAid29ybGQiLCAiY2hpbGRyZW4iOiBbMV19LCB7Im5hbWUiOiAiY3ViZV90ZXN0LnN0bF9PUUs0TzJLQlgxS1AiLCAibWVzaCI6IDB9XSwgImJ1ZmZlcnMiOiBbeyJieXRlTGVuZ3RoIjogNjk2fV0sICJidWZmZXJWaWV3cyI6IFt7ImJ1ZmZlciI6IDAsICJieXRlT2Zmc2V0IjogMCwgImJ5dGVMZW5ndGgiOiA0MzJ9LCB7ImJ1ZmZlciI6IDAsICJieXRlT2Zmc2V0IjogNDMyLCAiYnl0ZUxlbmd0aCI6IDI2NH1dfSC4AgAAQklOAAoAAAAQAAAADgAAAAoAAAAMAAAAEAAAAA4AAAARAAAADwAAAA4AAAAQAAAAEQAAAA8AAAANAAAACwAAAA8AAAARAAAADQAAAAsAAAAMAAAACgAAAAsAAAANAAAADAAAAAwAAAARAAAAEAAAAAwAAAANAAAAEQAAAAoAAAAOAAAADwAAAAoAAAAPAAAACwAAAAAAAAASAAAAAwAAAAAAAAAGAAAAEgAAAAMAAAAVAAAABQAAAAMAAAASAAAAFQAAAAUAAAAJAAAAAgAAAAUAAAAVAAAACQAAAAIAAAAGAAAAAAAAAAIAAAAJAAAABgAAAAYAAAAVAAAAEgAAAAYAAAAJAAAAFQAAAAAAAAADAAAABQAAAAAAAAAFAAAAAgAAAAAAAAAIAAAAAQAAAAAAAAAHAAAACAAAAAEAAAAUAAAABAAAAAEAAAAIAAAAFAAAAAQAAAATAAAAAwAAAAQAAAAUAAAAEwAAAAMAAAAHAAAAAAAAAAMAAAATAAAABwAAAAcAAAAUAAAACAAAAAcAAAATAAAAFAAAAAAAAAABAAAABAAAAAAAAAAEAAAAAwAAAAAAAAAAAAAAAAAAAAAAACYAAAAAAAAAwAAAAAAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAEAAAAAAAAAAwAAAAEAAAAAAAAAAQHZ1TL7kFP4/dnVMvnZ1TL7kFP4/dnVMPriwVr7J0QtAnc3lv7iwVr7J0QtAnc3lPwAAAL8AAAC/AAAAvwAAAL8AAAC/AAAAPwAAAL8AAAA/AAAAvwAAAL8AAAA/AAAAPwAAAD8AAAC/AAAAvwAAAD8AAAC/AAAAPwAAAD8AAAA/AAAAvwAAAD8AAAA/AAAAP53N5T/J0QtAuLBWvp3N5T/J0QtAuLBWPjWG5D8hmRhANYbkvzWG5D8hmRhANYbkPw==";;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="Z2xURgIAAADIDAAA0AMAAEpTT057InNjZW5lIjogMCwgInNjZW5lcyI6IFt7Im5vZGVzIjogWzBdfV0sICJhc3NldCI6IHsidmVyc2lvbiI6ICIyLjAiLCAiZ2VuZXJhdG9yIjogImh0dHBzOi8vZ2l0aHViLmNvbS9taWtlZGgvdHJpbWVzaCJ9LCAiYWNjZXNzb3JzIjogW3siYnVmZmVyVmlldyI6IDAsICJjb21wb25lbnRUeXBlIjogNTEyNSwgImNvdW50IjogMzYwLCAibWF4IjogWzY4XSwgIm1pbiI6IFswXSwgInR5cGUiOiAiU0NBTEFSIn0sIHsiYnVmZmVyVmlldyI6IDEsICJjb21wb25lbnRUeXBlIjogNTEyNiwgImNvdW50IjogNjksICJ0eXBlIjogIlZFQzMiLCAiYnl0ZU9mZnNldCI6IDAsICJtYXgiOiBbNS4wNTAwMDAxOTA3MzQ4NjMsIDEuNzkwNTQyODQwOTU3NjQxNiwgMC43MzIyOTE5MzY4NzQzODk2XSwgIm1pbiI6IFstMC43MzIyOTE5MzY4NzQzODk2LCAtMC41LCAtMC43MzIyOTE5MzY4NzQzODk2XX1dLCAibWVzaGVzIjogW3sibmFtZSI6ICJjdWJlX3Rlc3Quc3RsIiwgInByaW1pdGl2ZXMiOiBbeyJhdHRyaWJ1dGVzIjogeyJQT1NJVElPTiI6IDF9LCAiaW5kaWNlcyI6IDAsICJtb2RlIjogNH1dfV0sICJjYW1lcmFzIjogW3sibmFtZSI6ICJjYW1lcmFfRVhYN0dLIiwgInR5cGUiOiAicGVyc3BlY3RpdmUiLCAicGVyc3BlY3RpdmUiOiB7ImFzcGVjdFJhdGlvIjogMS4zMzMzMzMzMzMzMzMzMzMzLCAieWZvdiI6IDAuNzg1Mzk4MTYzMzk3NDQ4MywgInpuZWFyIjogMC4wMX19XSwgIm5vZGVzIjogW3sibmFtZSI6ICJ3b3JsZCIsICJjaGlsZHJlbiI6IFsxXX0sIHsibmFtZSI6ICJjdWJlX3Rlc3Quc3RsX0VOV1NNOE5NTkpKSSIsICJtZXNoIjogMH1dLCAiYnVmZmVycyI6IFt7ImJ5dGVMZW5ndGgiOiAyMjY4fV0sICJidWZmZXJWaWV3cyI6IFt7ImJ1ZmZlciI6IDAs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";;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", @@ -250,14 +260,42 @@ "" ] }, - "execution_count": 57, + "execution_count": 66, "metadata": {}, "output_type": "execute_result" } ], "source": [ + "# TODO: Just use the mesh data directly (no sense in saving & re-loading)\n", "m = trimesh.load_mesh(mesh_fname)\n", - "m.show()" + "#m.show()\n", + "scene = trimesh.Scene([m])\n", + "# Seems to do nothing:\n", + "# scene.camera_transform = scene.camera.look_at(m.vertices, distance=10)\n", + "scene.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 1. , 0. , 0. , -0.07059288],\n", + " [ 0. , 1. , 0. , 0.09608653],\n", + " [-0. , 0. , 1. , 2.14795328],\n", + " [ 0. , 0. , 0. , 1. ]])" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "scene.camera_transform" ] }, { @@ -284,7 +322,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.2" + "version": "3.6.8" } }, "nbformat": 4, diff --git a/meshutil.py b/meshutil.py index 0742479..a9188fc 100644 --- a/meshutil.py +++ b/meshutil.py @@ -27,9 +27,10 @@ class FaceVertexMesh(object): f2 = numpy.concatenate([self.f, other_mesh.f + self.v.shape[0]]) m2 = FaceVertexMesh(v2, f2) return m2 - def transform(self, mtx): + def transform(self, xform): + # Pass a Transform vh = numpy.hstack([self.v, numpy.ones((self.v.shape[0], 1), dtype=self.v.dtype)]) - v2 = vh.dot(mtx.T)[:,0:3] + v2 = vh.dot(xform.mtx.T)[:,0:3] # TODO: why transpose? # TODO: fix homogenous (even if I don't use it) return FaceVertexMesh(v2, self.f) @@ -40,14 +41,14 @@ class FaceVertexMesh(object): v[i] = [self.v[iv0], self.v[iv1], self.v[iv2]] return stl.mesh.Mesh(data) -class TransformStack(object): +class Transform(object): def __init__(self, mtx=None): if mtx is None: self.mtx = numpy.identity(4) else: self.mtx = mtx def _compose(self, mtx2): - return TransformStack(mtx2 @ self.mtx) + return Transform(mtx2 @ self.mtx) def scale(self, *a, **kw): return self._compose(mtx_scale(*a, **kw)) def translate(self, *a, **kw): diff --git a/trimesh_fail.ipynb b/trimesh_fail.ipynb new file mode 100644 index 0000000..91ade85 --- /dev/null +++ b/trimesh_fail.ipynb @@ -0,0 +1,263 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "face_normals didn't match triangles, ignoring!\n", + "/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" + ] + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import trimesh\n", + "m = trimesh.load_mesh(\"trimesh_fail.stl\")\n", + "m.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.8" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/trimesh_fail.stl b/trimesh_fail.stl new file mode 100644 index 0000000..580ab4a Binary files /dev/null and b/trimesh_fail.stl differ