Add FaceVertexMesh

This commit is contained in:
Chris Hodapp 2019-09-17 04:57:15 +02:00
parent d846569130
commit 9fd413df2b
2 changed files with 80 additions and 47 deletions

View File

@ -13,11 +13,17 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 13, "execution_count": 2,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"c = meshutil.cube(open_xz=False)\n", "c = meshutil.cube(open_xz=False)\n",
"mesh_fname = \"cube_test.stl\"\n",
"c2 = meshutil.cube_distort(0.3)\n",
"c2.v = c2.v/2 + [0,1,0]\n",
"c = c.concat(c2)\n",
"m = c.to_stl_mesh()\n",
"\"\"\"\n",
"c1 = meshutil.cube_distort(0.3)\n", "c1 = meshutil.cube_distort(0.3)\n",
"c1[\"vectors\"] = c1[\"vectors\"]/2 + [0,1,0]\n", "c1[\"vectors\"] = c1[\"vectors\"]/2 + [0,1,0]\n",
"c2 = meshutil.cube_distort(0.3)\n", "c2 = meshutil.cube_distort(0.3)\n",
@ -25,32 +31,23 @@
"c3 = meshutil.cube_distort(0.3)\n", "c3 = meshutil.cube_distort(0.3)\n",
"c3[\"vectors\"] = c3[\"vectors\"]/2 + [0,1,0.5]\n", "c3[\"vectors\"] = c3[\"vectors\"]/2 + [0,1,0.5]\n",
"c4 = meshutil.cube_distort(0.3)\n", "c4 = meshutil.cube_distort(0.3)\n",
"c4[\"vectors\"] = c4[\"vectors\"]/2 + [0.5,1,0.5]" "c4[\"vectors\"] = c4[\"vectors\"]/2 + [0.5,1,0.5]\n",
"\"\"\"\n",
"m.save(mesh_fname)"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 14, "execution_count": 3,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"d = numpy.concatenate([c,c1,c2,c3,c4])" "#d = numpy.concatenate([c,c1,c2,c3,c4])"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 15, "execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"mesh = stl.mesh.Mesh(d)\n",
"mesh_fname = \"cube_test.stl\"\n",
"mesh.save(mesh_fname)"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@ -59,14 +56,16 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 17, "execution_count": 5,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
"name": "stderr", "name": "stderr",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"face_normals didn't match triangles, ignoring!\n" "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"
] ]
}, },
{ {
@ -264,7 +263,7 @@
"Math.pow(distanceToCamera,2)+\n", "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", "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 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="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 init(){scene=new THREE.Scene();scene.background=new THREE.Color(0xffffff);tracklight=new THREE.DirectionalLight(0xffffff,1.75);scene.add(tracklight);base64_data="Z2xURgIAAABwBQAAsAMAAEpTT057InNjZW5lIjogMCwgInNjZW5lcyI6IFt7Im5vZGVzIjogWzBdfV0sICJhc3NldCI6IHsidmVyc2lvbiI6ICIyLjAiLCAiZ2VuZXJhdG9yIjogImh0dHBzOi8vZ2l0aHViLmNvbS9taWtlZGgvdHJpbWVzaCJ9LCAiYWNjZXNzb3JzIjogW3siYnVmZmVyVmlldyI6IDAsICJjb21wb25lbnRUeXBlIjogNTEyNSwgImNvdW50IjogNjAsICJtYXgiOiBbMTRdLCAibWluIjogWzBdLCAidHlwZSI6ICJTQ0FMQVIifSwgeyJidWZmZXJWaWV3IjogMSwgImNvbXBvbmVudFR5cGUiOiA1MTI2LCAiY291bnQiOiAxNSwgInR5cGUiOiAiVkVDMyIsICJieXRlT2Zmc2V0IjogMCwgIm1heCI6IFsxLjAsIDEuNzYyMDIyNDk1MjY5Nzc1NCwgMS4wXSwgIm1pbiI6IFstMC4xNzAwOTE4NTI1NDU3MzgyMiwgMC4wLCAtMC4xNzAwOTE4NTI1NDU3MzgyMl19XSwgIm1lc2hlcyI6IFt7Im5hbWUiOiAiY3ViZV90ZXN0LnN0bCIsICJwcmltaXRpdmVzIjogW3siYXR0cmlidXRlcyI6IHsiUE9TSVRJT04iOiAxfSwgImluZGljZXMiOiAwLCAibW9kZSI6IDR9XX1dLCAiY2FtZXJhcyI6IFt7Im5hbWUiOiAiY2FtZXJhXzBHUVdIQyIsICJ0eXBlIjogInBlcnNwZWN0aXZlIiwgInBlcnNwZWN0aXZlIjogeyJhc3BlY3RSYXRpbyI6IDEuMzMzMzMzMzMzMzMzMzMzMywgInlmb3YiOiAwLjc4NTM5ODE2MzM5NzQ0ODMsICJ6bmVhciI6IDAuMDF9fV0sICJub2RlcyI6IFt7Im5hbWUiOiAid29ybGQiLCAiY2hpbGRyZW4iOiBbMV19LCB7Im5hbWUiOiAiY3ViZV90ZXN0LnN0bF8wVkZQWUxVMENMWFMiLCAibWVzaCI6IDB9XSwgImJ1ZmZlcnMiOiBbeyJieXRlTGVuZ3RoIjogNDIwfV0sICJidWZmZXJWaWV3cyI6IFt7ImJ1ZmZlciI6IDAsICJieXRlT2Zmc2V0IjogMCwgImJ5dGVMZW5ndGgiOiAyNDB9LCB7ImJ1ZmZlciI6IDAsICJieXRlT2Zmc2V0IjogMjQwLCAiYnl0ZUxlbmd0aCI6IDE4MH1dfSAgIKQBAABCSU4AAAAAAAcAAAAFAAAAAAAAAAIAAAAHAAAABQAAAAgAAAAGAAAABQAAAAcAAAAIAAAABgAAAAMAAAABAAAABgAAAAgAAAADAAAAAQAAAAIAAAAAAAAAAQAAAAMAAAACAAAAAgAAAAgAAAAHAAAAAgAAAAMAAAAIAAAAAAAAAAUAAAAGAAAAAAAAAAYAAAABAAAAAgAAAAkAAAALAAAAAgAAAAoAAAAJAAAACwAAAA0AAAAMAAAACwAAAAkAAAANAAAADAAAAA4AAAAEAAAADAAAAA0AAAAOAAAABAAAAAoAAAACAAAABAAAAA4AAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAAAAAAAAAAAAgD8AAIA/AAAAAAAAgD8AAAA/AACAPwAAAAAAAAAAAACAPwAAAAAAAIA/AACAPwAAgD8AAAAAAACAPwAAgD8AAIA/QaGuPiegzj+PLC6+bU4Xvlq2uz9tThe+AAAAPwAAgD8AAAAAAAAAPwAAgD8AAAA/MDKjPvSJ4T8wMqM+jywuviegzj9Boa4+";;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 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 animate(){requestAnimationFrame(animate);controls.update();}\n",
"function render(){tracklight.position.copy(camera.position);renderer.render(scene,camera);}\n", "function render(){tracklight.position.copy(camera.position);renderer.render(scene,camera);}\n",
@ -275,7 +274,7 @@
"<IPython.core.display.HTML object>" "<IPython.core.display.HTML object>"
] ]
}, },
"execution_count": 17, "execution_count": 5,
"metadata": {}, "metadata": {},
"output_type": "execute_result" "output_type": "execute_result"
} }

View File

@ -16,42 +16,76 @@ rbb = numpy.array([1,0,1])
ltb = numpy.array([0,1,1]) ltb = numpy.array([0,1,1])
rtb = numpy.array([1,1,1]) rtb = numpy.array([1,1,1])
def cube(open_xz=False): class FaceVertexMesh(object):
if open_xz: def __init__(self, v, f):
data = numpy.zeros(8, dtype=stl.mesh.Mesh.dtype) # v & f should both be of shape (N,3)ll
else: self.v = v
data = numpy.zeros(12, dtype=stl.mesh.Mesh.dtype) self.f = f
def concat(self, other_mesh):
v2 = numpy.concatenate([self.v, other_mesh.v])
# Note index shift!
f2 = numpy.concatenate([self.f, other_mesh.f + self.v.shape[0]])
m2 = FaceVertexMesh(v2, f2)
return m2
def to_stl_mesh(self):
data = numpy.zeros(self.f.shape[0], dtype=stl.mesh.Mesh.dtype)
v = data["vectors"] v = data["vectors"]
v[0] = [lbf, rtf, rbf] for i, (iv0, iv1, iv2) in enumerate(self.f):
v[1] = [lbf, ltf, rtf] v[i] = [self.v[iv0], self.v[iv1], self.v[iv2]]
v[2] = [rbf, rtb, rbb] return stl.mesh.Mesh(data)
v[3] = [rbf, rtf, rtb]
v[4] = [rbb, ltb, lbb] def cube(open_xz=False):
v[5] = [rbb, rtb, ltb] verts = numpy.array([
v[6] = [lbb, ltf, lbf] lbf, rbf, ltf, rtf,
v[7] = [lbb, ltb, ltf] lbb, rbb, ltb, rtb,
], dtype=numpy.float32)
if open_xz:
faces = numpy.zeros((8,3), dtype=int)
else:
faces = numpy.zeros((12,3), dtype=int)
faces[0,:] = [0, 3, 1]
faces[1,:] = [0, 2, 3]
faces[2,:] = [1, 7, 5]
faces[3,:] = [1, 3, 7]
faces[4,:] = [5, 6, 4]
faces[5,:] = [5, 7, 6]
faces[6,:] = [4, 2, 0]
faces[7,:] = [4, 6, 2]
if not open_xz: if not open_xz:
v[8] = [ltf, rtb, rtf] faces[8,:] = [2, 7, 3]
v[9] = [ltf, ltb, rtb] faces[9,:] = [2, 6, 7]
v[10] = [lbf, rbf, rbb] faces[10,:] = [0, 1, 5]
v[11] = [lbf, rbb, lbb] faces[11,:] = [0, 5, 4]
return data # winding order?
return FaceVertexMesh(verts, faces)
def cube_distort(angle): 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) q = quat.rotation_quaternion(numpy.array([-1,0,1]), angle)
ltf2 = quat.conjugate_by(ltf, q)[0,:] ltf2 = quat.conjugate_by(ltf, q)[0,:]
rtf2 = quat.conjugate_by(rtf, q)[0,:] rtf2 = quat.conjugate_by(rtf, q)[0,:]
ltb2 = quat.conjugate_by(ltb, q)[0,:] ltb2 = quat.conjugate_by(ltb, q)[0,:]
rtb2 = quat.conjugate_by(rtb, q)[0,:] rtb2 = quat.conjugate_by(rtb, q)[0,:]
# TODO: Just make these functions work right with single vectors # TODO: Just make these functions work right with single vectors
v[0] = [lbf, rtf2, rbf] verts = numpy.array([
v[1] = [lbf, ltf2, rtf2] lbf, rbf, ltf2, rtf2,
v[2] = [rbf, rtb2, rbb] lbb, rbb, ltb2, rtb2,
v[3] = [rbf, rtf2, rtb2] ], dtype=numpy.float32)
v[4] = [rbb, ltb2, lbb] if True: #open_xz:
v[5] = [rbb, rtb2, ltb2] faces = numpy.zeros((8,3), dtype=int)
v[6] = [lbb, ltf2, lbf] else:
v[7] = [lbb, ltb2, ltf2] faces = numpy.zeros((12,3), dtype=int)
return data faces[0,:] = [0, 3, 1]
faces[1,:] = [0, 2, 3]
faces[2,:] = [1, 7, 5]
faces[3,:] = [1, 3, 7]
faces[4,:] = [5, 6, 4]
faces[5,:] = [5, 7, 6]
faces[6,:] = [4, 2, 0]
faces[7,:] = [4, 6, 2]
if False: # not open_xz:
faces[8,:] = [2, 7, 3]
faces[9,:] = [2, 6, 7]
faces[10,:] = [0, 1, 5]
faces[11,:] = [0, 5, 4]
# winding order?
return FaceVertexMesh(verts, faces)