My Surrealist Object
For 3D Sensing and Visualization class, we had to construct our own 3D objects. I was inspired by the drawing assignment, and wanted to create a physical object out of my own drawing with the Kinect. Building the drawing component turned out to be a little more intensive than I thought, so I decided to save the Kinect for my final. I wrote a program that takes the path of the user’s mouse and extrudes it into a closed tube that can be printed in a 3D printer. I made a video of the process.
I started with a polyline, which adds vertices to itself at every point the mouse touches in space. The polyline reference is fed into the buildTube function, which constructs a 3D frame around it. It assumes that there is a planar (flat) polygon at each point along the polyline. It then takes an up vector (i.e. a vector that always points up) and the vector that describes the distance between the drawn points and spirals around the polyline building a mesh. Each point in the mesh is then connected by triangles.
All of this is necessary to build a printable 3D model. In order to build an object with a 3D printer, all of the surfaces of the 3D model must be topologically closed. It’s a little like the paint bucket tool in Photoshop; if any part of the selected area has a hole, the color will leak out. In this case, the printer will get confused about the structure of the layer and refuse to print. All of the points in the model must be connected.
The main code is below. Read the rest on Github.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | #include "ofApp.h" void ofApp::setup() { ofSetVerticalSync(true); } void ofApp::update() { } void ofApp::draw() { easycam.begin(); ofBackground(0); ofSetColor(255); if(ofGetKeyPressed('c')) { line.clear(); } else if(ofGetKeyPressed(' ')) { if(mouseX != ofGetPreviousMouseX() && mouseY != ofGetPreviousMouseY()) { line.addVertex(ofVec2f(mouseX-ofGetWidth()/2, mouseY-ofGetHeight()/2)); } } line.draw(); mesh = buildTube(line, 6, 40); ofSetColor(ofColor::red); mesh.draw(); ofSetColor(0); mesh.drawWireframe(); easycam.end(); } void ofApp::keyPressed(int key) { if(key == 'f') { ofToggleFullscreen(); } if(key == 's') { mesh.save("out.ply"); } } //mesh indices int getIndex(int i, int j, int sides) { return i * sides + (j % sides); } ofMesh ofApp::buildTube(const ofPolyline& path, int sides, float radius) { ofMesh mesh; mesh.setMode(OF_PRIMITIVE_TRIANGLES); //up vector orthogonal to polygonal slice surface ofVec3f up(0, 1, 0); for (int i = 0; i < path.size(); i++) { ofVec3f diff, cur, next; if(i + 1 == path.size()) { cur = path[i]; next = path[i-1]; diff = cur - next; } else { cur = path[i]; next = path[i+1]; diff = next - cur; } //get perpendicular vectors with cross products ofVec3f u = diff.getCrossed(up).normalize() * radius; ofVec3f v = u.getCrossed(diff).normalize() * radius; //rotate around polyline building mesh for (int j = 0; j < sides; j++) { float theta = ofMap(j, 0, sides, 0, TWO_PI); float x = cos(theta); float y = sin(theta); ofVec3f pos = x*u + y*v + cur; mesh.addVertex(pos); } } //closing tube sides - connect side indices to mesh by forming triangles for (int i = 0; i+1 < path.size(); i++) { for (int j = 0; j < sides; j++) { mesh.addIndex(getIndex(i, j, sides)); mesh.addIndex(getIndex(i+1, j, sides)); mesh.addIndex(getIndex(i, j+1, sides)); mesh.addIndex(getIndex(i+1, j, sides)); mesh.addIndex(getIndex(i+1, j+1, sides)); mesh.addIndex(getIndex(i, j+1, sides)); } } //closing tube ends if (path.size() > 0){ int centerStart = mesh.getNumVertices(); //center of tube start mesh.addVertex(path[0]); int centerEnd = mesh.getNumVertices(); //center of tube end mesh.addVertex(path[path.size()-1]); //connect side indices to centers of tube ends for (int j = 0; j < sides; j++) { mesh.addIndex(centerStart); mesh.addIndex(getIndex(0, j, sides)); mesh.addIndex(getIndex(0, j+1, sides)); mesh.addIndex(centerEnd); mesh.addIndex(getIndex(path.size()-1, j+1, sides)); mesh.addIndex(getIndex(path.size()-1, j, sides)); } } return mesh; } |
[…] a previous post titled “My Surrealist Object,” I discussed writing a program that would transform a user’s line drawing into a printable 3D […]