plane triangle intersection raveh gonen's blog

17
Raveh Gonen's Blog Just another WordPress.com weblog Stereolithography (3D Printing) Algorithms and Thoughts February 19, 2013 Playing with 3D Printing Algorithmic Concepts. Let’s start with what we (the world) already have: lot’s of software, open source and commercial, lot’s of file formats, but STL is still dominant and … lots of 3D Printers. This week, I was looking into the algorithms (Math, Computer Science) behind the world of 3D Printing. As an excersize, I started to explore the mechanics of taking a digital model of an object from an STL file (ThingVerse, F16 Model) and generating slices from this model so a printer could print. Here is a slide of the basic process: Home About Me Follow Follow “Raveh

Upload: eddy-castro

Post on 04-Apr-2016

228 views

Category:

Documents


5 download

DESCRIPTION

 

TRANSCRIPT

Page 1: Plane triangle intersection raveh gonen's blog

Raveh Gonen's BlogJust another WordPress.com weblog

Stereolithography (3D Printing) Algorithmsand ThoughtsFebruary 19, 2013

Play ing with 3D Printing Algorithmic Concepts. Let’s start with what

we (the world) already hav e: lot’s of software, open source and

commercial, lot’s of file formats, but STL is still dominant and … lots

of 3D Printers. This week, I was looking into the algorithms (Math,

Computer Science) behind the world of 3D Printing. As an excersize,

I started to explore the mechanics of taking a digital model of an

object from an STL file (ThingVerse, F16 Model) and generating slices

from this model so a printer could print. Here is a slide of the basic

process:

Home About Me

Follow

Follow “Raveh

Page 2: Plane triangle intersection raveh gonen's blog

The input: F16.stl (ascii or binary format)

. The output: an ascii or binary file with a set of slices where each

slice has a set of line segments. a line segment has 2 coordinates {p0,

p1} with {x ,y ,z} v alues. Before we div e into the math (simple) and

algorithmic issues, let’s see the images: the 3D model, the slices on a

2D image, side-by -side from left-to-right top-to-bottom, the 3D

Model constructed by multiple adjacent slices:

Follow “RavehGonen's Blog”

Get every new post delivered

to your Inbox.

Enter your email address

Sign me up

Pow ered by WordPress.com

Page 3: Plane triangle intersection raveh gonen's blog

The Main Algorithm (Pseudo Code)

v 3 -> a v ector with 3 floats {x ,y ,z}

LineSegment -> {v 3 point0, point1}

Plane -> {v 3 normal, float distance}

Triangle -> {v 3 v ertices[3], normal}

TriangleMesh -> {v ector of Triangle}

read-stl-file -> generate a TriangleMesh ‘TM’

nSlices -> compute-number-of-slices using slice-size and 3D Model

aabb

for each slice

- fix -a-cutting-plane using the slice-index

- for each Triangle in TriangleMesh

- – intersect(Triangle, Plane) -> find zero or two intersection points

- – take only the second case and generate a Line Segment from two

points

- – Gather all Line Segment for this slice in a v ector

output all line segments to an Ascii or Binary file for v iewing or

manipulating

That’s all. Here are a set of slides that explain the Triangle-Plane-

Intersection mecahnics:

Page 6: Plane triangle intersection raveh gonen's blog

Here is the full source code that compiles under Microsoft VisualC++ 2010. Just create a solution and add this code as main.cpp,

download the open source Opengl Mathematics c++ library (Header

files only ) from http://glm.g-truc.net/. After y ou build it, download

any STL file y ou want.

Adjusting Parameters: use the command line parameters:

-slicesize <size>

-model <stl file name>

-ascii (the default is a binary STL file)

-eulerangles rx ry rz (to rotate the 3D Model before slicing starts)

Please note that this is a demonstration code (and documented) and

not a bullet-proof one. It is targeted as an educational and

exploratory tool. I hope y ou hav e fun with it.

Some more output from the program {Stanford Bunny , Spaceship}

Page 9: Plane triangle intersection raveh gonen's blog

The Full Source Code (C++):

#include <fstream>

#include <string>

#include <v ector>#include <math.h>

Page 10: Plane triangle intersection raveh gonen's blog

#include “glm/glm/glm.hpp”

#include “glm/glm/gtc/matrix_transform.hpp”

#include “glm/glm/gtc/ty pe_ptr.hpp”

#define DEG_TO_RAD(x) (x*0.017 4532925199f)

using namespace std;

struct v 3 {

float x , y , z; v 3(float _x=0, float _y =0, float _z=0) : x (_x), y (_y ), z(_z) {}

float dotproduct(const v 3 &v ) const { return x*v .x + y *v .y + z*v .z;

}

v oid transform(const glm::mat4 &mat) { glm::v ec4 v =

glm::v ec4(x , y , z, 1 .0f), v t; v t = mat*v ; x=v t.x ; y =v t.y ; z=v t.z;}

v 3& operator-=(const v 3 &pt) { x -=pt.x ; y -=pt.y ; z-=pt.z; return

*this; }

v 3 operator-(const v 3 &pt) { return v 3(x-pt.x , y -pt.y , z-pt.z); }

v 3 operator+(const v 3 &pt) { return v 3(x+pt.x , y +pt.y , z+pt.z); } v 3 operator/(float a) { return v 3(x/a, y /a, z/a); }

v 3 operator*(float a) { return v 3(x*a, y *a, z*a); }

float normalize() const { return sqrt(x*x+y *y +z*z); }

};

v 3 operator-(const v 3 &a, const v 3 &b) {return v 3(a.x-b.x , a.y -b.y ,

a.z-b.z); }

v 3 operator+(const v 3 &a, const v 3 &b) {return v 3(a.x+b.x , a.y +b.y ,

a.z+b.z); }

struct LineSegment {

LineSegment(v 3 p0=v 3(), v 3 p1=v 3()) { v [0]=p0; v [1]=p1; }

v 3 v [2];

};

class Plane {

public:

Plane() : mDistance(0) {}

float distance() const { return mDistance; }

float distanceToPoint(const v 3 &v ertex) const { returnv ertex .dotproduct(mNormal) – mDistance; }

v oid setNormal(v 3 normal) { mNormal = normal; }

v oid setDistance(float distance) { mDistance = distance; }

protected:

v 3 mNormal; // normalized Normal-Vector of the plane

float mDistance; // shortest distance from plane to Origin

};

struct Triangle {

Triangle(v 3 n, v 3 v 0, v 3 v 1 , v 3 v 2) : normal(n) { v [0]=v 0; v [1]=v 1 ;

v [2]=v 2; } Triangle& operator-=(const v 3 &pt) { v [0]-=pt; v [1]-=pt; v [2]-=pt;

return *this;}

v oid transform(const glm::mat4 &mat) { v [0].transform(mat);

v [1].transform(mat); v [2].transform(mat);}

// @return -1 = all triangle is on plane back side

// 0 = plane intersects the triangle

// 1 = all triangle is on plane front side

// -2 = error in function

int intersectPlane(const Plane &plane, LineSegment &ls) const { // a triangle has 3 v ertices that construct 3 line segments

size_t cntFront=0, cntBack=0;

for (size_t j=0; j<3; ++j) {

float distance = plane.distanceToPoint(v [j]);

if (distance<0) ++cntBack;

else ++cntFront;

Page 11: Plane triangle intersection raveh gonen's blog

}

if (3 == cntBack) {

return -1 ;

}

else if (3 == cntFront) {

return 1 ;

}

size_t lines[] = {0,1 ,1 ,2,2,0}; // CCW Triangle

std::v ector<v 3> intersectPoints; for (size_t i=0; i<3; ++i) {

const v 3 &a = v [lines[i*2+0]];

const v 3 &b = v [lines[i*2+1]];

const float da = plane.distanceToPoint(a);

const float db = plane.distanceToPoint(b);

if (da*db<0) {

const float s = da/(da-db); // intersection factor (between 0and 1)

v 3 bMinusa = b-a; intersectPoints.push_back(a+bMinusa*s);

}

else if (0==da) { // plane falls exactly on one of the threeTriangle v ertices

if (intersectPoints.size()<2

intersectPoints.push_back(a);

}

else if (0==db) { // plane falls exactly on one of the threeTriangle v ertices

if (intersectPoints.size()<2

intersectPoints.push_back(b);

}

}

if (2==intersectPoints.size()) {

// Output the intersecting line segment object

ls.v [0]=intersectPoints[0];

ls.v [1]=intersectPoints[1];

return 0;

}

return -2;

}

v 3 v [3], normal;

};class TriangleMesh {

public:

TriangleMesh() : bottomLeftVertex(999999,999999,999999),upperRightVertex(-999999,-999999,-999999) {}

size_t size() const { return mesh.size(); }

// mov e 3D Model coordinates to be center around COG(0,0,0)

v oid normalize() {

v 3 halfBbox = (upperRightVertex – bottomLeftVertex)/2.0f;

v 3 start = bottomLeftVertex + halfBbox;

for (size_t i=0; i<mesh.size(); ++i) {

Triangle &triangle = mesh[i];

triangle -= start;

}

bottomLeftVertex = halfBbox*-1 .0f;

upperRightVertex = halfBbox;

}

v oid push_back(const Triangle &t) {

mesh.push_back(t); for (size_t i=0; i<3; ++i) {

if (t.v [i].x < bottomLeftVertex .x) bottomLeftVertex .x =

Page 12: Plane triangle intersection raveh gonen's blog

t.v [i].x ;

if (t.v [i].y < bottomLeftVertex .y ) bottomLeftVertex .y =t.v [i].y ;

if (t.v [i].z < bottomLeftVertex .z) bottomLeftVertex .z = t.v [i].z;

if (t.v [i].x > upperRightVertex .x) upperRightVertex .x =t.v [i].x ;

if (t.v [i].y > upperRightVertex .y ) upperRightVertex .y =t.v [i].y ;

if (t.v [i].z > upperRightVertex .z) upperRightVertex .z = t.v [i].z;

}

}

v 3 meshAABBSize() const {

return v 3(upperRightVertex .x-bottomLeftVertex .x ,

upperRightVertex .y -bottomLeftVertex .y , upperRightVertex .z-bottomLeftVertex .z);

}

std::v ector<Triangle>& getMesh() { return mesh; }

const std::v ector<Triangle>& getMesh() const { return mesh; }

v 3 getBottomLeftVertex() const { return bottomLeftVertex ; }

v 3 getUpperRightVertex() const { return upperRightVertex ; }

// Mesh COG point should be at (0,0,0)

int transform(const glm::mat4 &mat) {

for (size_t i=0; i<mesh.size(); ++i) {

Triangle &triangle = mesh[i];

triangle.transform(mat);

} return 0;

}

protected:

std::v ector<Triangle> mesh;

v 3 bottomLeftVertex , upperRightVertex ;

};

// read the giv en STL file name (ascii or binary is set using

‘isBinary Format’)

// and generate a Triangle Mesh object in output parameter ‘mesh’

int stlToMeshInMemory (const char *stlFile, TriangleMesh *mesh,

bool isBinary Format) {

if (!isBinary Format) {

ifstream in(stlFile);

if (!in.good()) return 1 ;

char title[80];

std::string s0, s1 ;

float n0, n1 , n2, f0, f1 , f2, f3, f4, f5, f6, f7 , f8; in.read(title, 80);

while (!in.eof()) {

in >> s0; // facet || endsolid

if (s0==”facet”) {

in >> s1 >> n0 >> n1 >> n2; // normal x y z

in >> s0 >> s1 ; // outer loop

in >> s0 >> f0 >> f1 >> f2; // v ertex x y z

in >> s0 >> f3 >> f4 >> f5; // v ertex x y z

in >> s0 >> f6 >> f7 >> f8; // v ertex x y z

in >> s0; // endloop

in >> s0; // endfacet

// Generate a new Triangle with Normal as 3 Vertices

Triangle t(v 3(n0, n1 , n2), v 3(f0, f1 , f2), v 3(f3, f4, f5), v 3(f6,

f7 , f8));

mesh->push_back(t);

}

else if (s0==”endsolid”) {

Page 13: Plane triangle intersection raveh gonen's blog

break;

}

}

in.close();

}

else {

FILE *f = fopen(stlFile, “rb”);

if (!f) return 1 ;

char title[80]; int nFaces;

fread(title, 80, 1 , f);

fread((v oid*)&nFaces, 4, 1 , f);

float v [12]; // normal=3, v ertices=3*3 = 12

unsigned short uint16; // Ev ery Face is 50 By tes: Normal(3*float), Vertices(9*float), 2

By tes Spacer

for (size_t i=0; i<nFaces; ++i) {

for (size_t j=0; j<12; ++j) {

fread((v oid*)&v [j], sizeof(float), 1 , f);

} fread((v oid*)&uint16, sizeof(unsigned short), 1 , f); // spacer

between successiv e faces Triangle t(v 3(v [0], v [1], v [2]), v 3(v [3], v [4], v [5]), v 3(v [6],

v [7 ], v [8]), v 3(v [9], v [10], v [11]));

mesh->push_back(t);

}

fclose(f); }

mesh->normalize();

return 0;}

// Take an input Triangle Mesh ‘mesh’ and fill the output

// parameter ‘slicesWithLineSegments’ with line segments for

// each slice

int triMeshSlicer(

const TriangleMesh *mesh, // the const input mesh

std::v ector<std::v ector<LineSegment>>

&slicesWithLineSegments,

// the result slices

const float sliceSize) {// slice size in 3D Model

digital units

Plane plane; // The intersection plane

plane.setNormal(v 3(0, 0, 1 )); // normal does not

change during slicing

const v 3 aabb = mesh->meshAABBSize(); // as the model

for it’s 3D ax is-aligned bounding-box const size_t nSlices = 1+(int)(aabb.z/sliceSize); // compute

number of output slices

const std::v ector<Triangle> &m = mesh->getMesh(); // get a

const handle to the input mesh

const float z0 = mesh->getBottomLeftVertex().z; // find the

minimal z coordinate of the model (z0)

for (size_t i=0; i<nSlices; ++i) { // start generating slices

std::v ector<LineSegment> linesegs; // the linesegs

v ector for each slice

plane.setDistance(z0+(float)i*sliceSize); // position the plane

according to slice index

for (size_t t=0; t<m.size(); ++t) { // iterate all mesh

triangles

const Triangle &triangle = m[t]; // get a const handle to a

Page 14: Plane triangle intersection raveh gonen's blog

triangle

LineSegment ls; if (0==triangle.intersectPlane(plane, ls)) {// the plane does

intersect the triangle linesegs.push_back(ls); // push a new Line Segment

object to this slice

}

} slicesWithLineSegments.push_back(linesegs); // push this

v ector to the slices v ector

}

return 0;}

// GIV is a free v iewer: http://giv .sourceforge.net/giv /

// output a single GIV ascii file with the slices, lay ed out in a matrix

form

int exportSingleGIVFormat(

const std::v ector<std::v ector<LineSegment>>

&slicesWithLineSegments,

const v 3 &aabbSize) {

char filename[256];

FILE *f=NULL;

float dx=0, dy =0;

f=fopen(“out.marks”, “w”);

if (!f) return 1 ;

const size_t nSlices = slicesWithLineSegments.size();

const size_t slicePerRow = (size_t)sqrt((float)nSlices);

for (size_t i=0; i<nSlices; ++i) {

const std::v ector<LineSegment> &lss =

slicesWithLineSegments[i]; dx = (float)(i%slicePerRow)*(aabbSize.x*1 .05f);

dy = (float)(i/slicePerRow)*(aabbSize.y *1 .05f);

for (size_t j=0; j<lss.size(); ++j) {

fprintf(f, “\n\n$line”);

fprintf(f, “\n$color blue”);

fprintf(f, “\n%f %f”, dx+lss[j].v [0].x , dy +lss[j].v [0].y );

fprintf(f, “\n%f %f”, dx+lss[j].v [1].x , dy +lss[j].v [1].y );

}

}

fclose(f);

return 0;

}

// GIV is a free v iewer: http://giv .sourceforge.net/giv /

// output a single GIV ascii file with the slices, lay ed out in 3D form

int exportSingleGIVFormat3D(

const std::v ector<std::v ector<LineSegment>>

&slicesWithLineSegments,

const v 3 &aabbSize) {

// Generate a 3D View using OpenGL Math Library

glm::detail::tv ec3<float> eulerAngles(0, 0.0f, 45.0f);

glm::detail::tquat<float> quaternion = glm::detail::tquat<float>

(DEG_TO_RAD(eulerAngles)); // This glm func wants v alues as

radians

float angle = glm::angle(quaternion);

glm::detail::tv ec3<float> ax is = glm::ax is(quaternion);

glm::mat4 View = glm::rotate(glm::mat4(1 .0), angle, ax is);

glm::mat4 Projection = glm::perspectiv e(45.0f, 4.0f / 3.0f, 0.1 f,

100.f);

Page 15: Plane triangle intersection raveh gonen's blog

glm::mat4 Model = glm::lookAt(

glm::v ec3(1 , 1 , 1 ), // Ey e point (where am I?)

glm::v ec3(0, 0, 0), // Look at Center point (What am I lookingat?)

glm::v ec3(0, 1 , 0));// UP v ector of the camera (Where is my UPv ector?)

glm::mat4 MVP = Projection * View * Model;

// Start Output

char filename[256];

FILE *f=NULL;

v 3 p0, p1 ;

f=fopen(“out3D.marks”, “w”);

if (!f) return 1 ;

const size_t nSlices = slicesWithLineSegments.size();

const size_t slicePerRow = (size_t)sqrt((float)nSlices); for (size_t i=0; i<nSlices; ++i) {

const std::v ector<LineSegment> &lss =

slicesWithLineSegments[i];

for (size_t j=0; j<lss.size(); ++j) {

p0=lss[j].v [0];

p1=lss[j].v [1];

p0.transform(MVP);

p1 .transform(MVP);

fprintf(f, “\n\n$line”);

fprintf(f, “\n$color blue”);

fprintf(f, “\n%f %f”, p0.x , p0.y );

fprintf(f, “\n%f %f”, p1 .x , p1 .y );

}

}

fclose(f);

return 0;

}

int main(int argc, char **argv ) {

float sliceSize = 1 .0f;

char modelFileName[1024];

bool isBinary Format = true;

glm::detail::tv ec3<float> eulerAngles(0,0,0);

for (int i=1 ; i<argc;) {

if (0==strcmp(argv [i], “-slicesize”)) {

sliceSize = atof(argv [i+1]);

i+=2;

}

else if (0==strcmp(argv [i], “-model”)) {

strcpy (modelFileName, argv [i+1]);

i+=2;

}

else if (0==strcmp(argv [i], “-ascii”)) {

isBinary Format = false;

i+=1;

}

else if (0==strcmp(argv [i], “-eulerangles”)) {

eulerAngles.x = atof(argv [i+1]); eulerAngles.y = atof(argv [i+2]);

eulerAngles.z = atof(argv [i+3]);

i+=4;

}

else {

++i;

Page 16: Plane triangle intersection raveh gonen's blog

Uncategorized(20)

CategoriesJanuary 2014December 2013February 2013May 2012January 2012December 2011November 2011October 2011August 2011November 2010October 2010April 2010February 2010January 2010

ArchivesRegisterLog inValid XHTMLXFNWordPress

Meta

}

}

// Parse the file and generate a TriangleMesh

TriangleMesh mesh;

if (stlToMeshInMemory (modelFileName, &mesh,isBinary Format)!=0)

return 1 ;

fprintf(stderr, “Mesh has %d triangles\n”, mesh.size());

// Optional Model Rotation Around COG Point using GLM Library

glm::mat4 mat = glm::mat4(1 .0f);

glm::detail::tquat<float> quaternion = glm::detail::tquat<float>

(DEG_TO_RAD(eulerAngles)); // This glm func wants v alues as

radians

float angle = glm::angle(quaternion);

glm::detail::tv ec3<float> ax is = glm::ax is(quaternion);

mat = glm::rotate(mat, angle, ax is);

mesh.transform(mat);

// Generate Slices

std::v ector<std::v ector<LineSegment>> slicesWithLineSegments;

triMeshSlicer(&mesh, slicesWithLineSegments, sliceSize);

// Output in 2D Matrix form

exportSingleGIVFormat(slicesWithLineSegments,

mesh.meshAABBSize());

// Output in 3D Lay ered Form

exportSingleGIVFormat3D(slicesWithLineSegments,

mesh.meshAABBSize());

return 0;

}

Posted in Uncategorized | Tagged: 3D Printing, Algorithms, GLMLibrary, Mesh Slicing, Plane-Triangle-Intersection,Stereolithography, STL File Format | 9 Comments »

Page 17: Plane triangle intersection raveh gonen's blog

Type and Press Enter to Search...

Th e Da y Dr ea m Th em e. Blog a t Wor dPr ess.com .