// a function for displaying the normals in an STL file public static void draw_stl_normals(GLWindow g, STLSurf s) { // draw triangle normas foreach (Geo.Tri t in s.tris) { // from STL file Geo.Point p1 = new Geo.Point(t.p[0].x, t.p[0].y, t.p[0].z); Geo.Point p2 = new Geo.Point(t.p[0].x + t.n.x, t.p[0].y + t.n.y, t.p[0].z + t.n.z); GeoLine l = new GeoLine(p1, p2); l.color = System.Drawing.Color.Green; g.addGeom(l); // calculate yourself: Vector v1 = new Vector(t.p[0].x - t.p[1].x, t.p[0].y - t.p[1].y, t.p[0].z - t.p[1].z); Vector v2 = new Vector(t.p[0].x - t.p[2].x, t.p[0].y - t.p[2].y, t.p[0].z - t.p[2].z); Vector n; n = v1.Cross(v2); // the normal is in the direction of the cross product between the edge vectors n = (1 / n.Length()) * n; // normalize to length==1 p1 = new Geo.Point(t.p[0].x, t.p[0].y, t.p[0].z); p2 = new Geo.Point(t.p[0].x + n.x, t.p[0].y + n.y, t.p[0].z + n.z); l = new GeoLine(p1, p2); l.color = System.Drawing.Color.Blue; g.addGeom(l); } }
public double Dot(Vector v) { // dot product return x * v.x + y * v.y + z * v.z; }
public Vector Cross(Vector v) { // cross product // NEEDS TESTING! double xc = y * v.z - z * v.y; double yc = z * v.x - x * v.z; double zc = x * v.y - y * v.x; return new Vector(xc, yc, zc); }
public void recalc_normals() { // normal data from STL files is usually junk, so recalculate: Vector v1 = new Vector(p[0].x - p[1].x, p[0].y - p[1].y, p[0].z - p[1].z); Vector v2 = new Vector(p[0].x - p[2].x, p[0].y - p[2].y, p[0].z- p[2].z); n = v1.Cross(v2); // the normal is in the direction of the cross product between the edge vectors n = (1 / n.Length()) * n; // normalize to length==1 }
public Tri(Vector N) { // a strange constructor indeed... p = new Point[3]; bb = new Bbox(); p[0] = new Point(); p[1] = new Point(); p[2] = new Point(); n = N; }
public Tri(Point P1, Point P2, Point P3, Vector N) { p = new Point[3]; bb = new Bbox(); p[0] = P1; p[1] = P2; p[2] = P3; n = N; }
public Tri(Point P1, Point P2, Point P3) { p = new Point[3]; bb = new Bbox(); p[0] = P1; p[1] = P2; p[2] = P3; // if normal is not given, calculate it here. Vector v1 = new Vector(p[0].x - p[1].x, p[0].y - p[1].y, p[0].z - p[1].z); Vector v2 = new Vector(p[0].x - p[2].x, p[0].y - p[2].y, p[0].z- p[2].z); n = v1.Cross(v2); // the normal is in the direction of the cross product between the edge vectors n = (1 / n.Length()) * n; // normalize to length==1 }
public static double? FacetTest(Cutter cu, Point e, Tri t) { // local copy of the surface normal t.recalc_normals(); // don't trust the pre-calculated normal! calculate it separately here. Vector n = new Vector(t.n.x, t.n.y, t.n.z); Point cc; if (n.z == 0) { // vertical plane, can't touch cutter against that! return null; } else if (n.z < 0) { // flip the normal so it points up (? is this always required?) n = -1*n; } // define plane containing facet double a = n.x; double b = n.y; double c = n.z; double d = - n.x * t.p[0].x - n.y * t.p[0].y - n.z * t.p[0].z; // the z-direction normal is a special case (?required?) // in debug phase, see if this is a useful case! if ((a == 0) && (b == 0)) { // System.Console.WriteLine("facet-test:z-dir normal case!"); e.z = t.p[0].z; cc = new Point(e.x,e.y,e.z); if (isinside(t, cc)) { // System.Console.WriteLine("facet-test:z-dir normal case!, returning {0}",e.z); // System.Console.ReadKey(); return e.z; } else return null; } // System.Console.WriteLine("facet-test:general case!"); // facet test general case // uses trigonometry, so might be too slow? // flat endmill and ballnose should be simple to do without trig // toroidal case might require offset-ellipse idea? /* theta = asin(c); zf= -d/c - (a*xe+b*ye)/c+ (R-r)/tan(theta) + r/sin(theta) -r; e=[xe ye zf]; u=[0 0 1]; rc=e + ((R-r)*tan(theta)+r)*u - ((R-r)/cos(theta) + r)*n; t=isinside(p1,p2,p3,rc); */ double theta = Math.Asin(c); double zf = -d/c - (a*e.x+b*e.y)/c + (cu.R-cu.r)/Math.Tan(theta) + cu.r/Math.Sin(theta) - cu.r; Vector ve = new Vector(e.x,e.y,zf); Vector u = new Vector(0,0,1); Vector rc = new Vector(); rc = ve +((cu.R-cu.r)*Math.Tan(theta)+cu.r)*u - ((cu.R-cu.r)/Math.Cos(theta)+cu.r)*n; /* if (rc.z > 1000) System.Console.WriteLine("z>1000 !"); */ cc = new Point(rc.x, rc.y, rc.z); // check that CC lies in plane: // a*rc(1)+b*rc(2)+c*rc(3)+d double test = a * cc.x + b * cc.y + c * cc.z + d; if (test > 0.000001) System.Console.WriteLine("FacetTest ERROR! CC point not in plane"); if (isinside(t, cc)) { if (Math.Abs(zf) > 100) { System.Console.WriteLine("serious problem... at" +e.x + "," + e.y); } return zf; } else return null; }
public Tri() { p = new Point[3]; bb = new Bbox(); p[0] = new Point(0,0,0); p[1] = new Point(0, 0, 0); p[2] = new Point(0, 0, 0); n = null; }
public static STLSurf Load(System.IO.StreamReader fs) { // Here's where autodetection will be provided for loading binary stl files and by wrapping STLA(scii) and STLB(inary) // but for now as only one format is provided, not bothering with it, we need to define a object format for handling file loading/saving // System.IO.StreamReader fs = new System.IO.StreamReader(FileName); STLSurf surf = new STLSurf(); int state = 0; int counter = 0; string[] data; Geo.Tri triangle = new Geo.Tri(); Vector normal = new Vector(); System.Globalization.CultureInfo locale = new System.Globalization.CultureInfo("en-GB"); int n_triangles=0; while ( !fs.EndOfStream ) { data = fs.ReadLine().TrimStart(' ').Split(' '); switch (state) { case 0: if (data[0].Equals("solid")) { surf.name = data[1]; state = 1; // continue readingx } break; case 1: if (data[0].Equals("facet")) { normal.x = double.Parse(data[2], locale); normal.y = double.Parse(data[3], locale); normal.z = double.Parse(data[4], locale); triangle = new Geo.Tri(normal); counter = 0; state = 2; } break; case 2: if (data[0].Equals("vertex")) { if ( counter <= 2 ) { triangle.p[counter].x = double.Parse(data[1], locale); triangle.p[counter].y = double.Parse(data[2], locale); triangle.p[counter].z = double.Parse(data[3], locale); // System.Console.WriteLine("STLReader: added point" + triangle.p[counter]); // System.Console.ReadKey(); counter++; } } else if (data[0].Equals("endfacet")) { if (counter == 3) { surf.AddTriangle(triangle); n_triangles += 1; } state = 1; } break; } } fs.Close(); System.Console.WriteLine("STLReader: read {0} triangles!",n_triangles); return (surf); }
public static STLSurf Load(System.IO.StreamReader fs) { STLSurf surf = new STLSurf(); int state = 0; int counter = 0; string[] data; Tri triangle = new Tri(); Vector normal = new Vector(); System.Globalization.CultureInfo locale = new System.Globalization.CultureInfo("en-GB"); int n_triangles=0; while ( !fs.EndOfStream ) { data = fs.ReadLine().TrimStart(' ').Split(' '); switch (state) { case 0: if (data[0].Equals("solid")) { state = 1; // continue readingx } break; case 1: if (data[0].Equals("facet")) { normal.x = double.Parse(data[2], locale); normal.y = double.Parse(data[3], locale); normal.z = double.Parse(data[4], locale); triangle = new Tri(normal); counter = 0; state = 2; } break; case 2: if (data[0].Equals("vertex")) { if ( counter <= 2 ) { triangle.p[counter].x = double.Parse(data[1], locale); triangle.p[counter].y = double.Parse(data[2], locale); triangle.p[counter].z = double.Parse(data[3], locale); // System.Console.WriteLine("STLReader: added point" + triangle.p[counter]); // System.Console.ReadKey(); counter++; } } else if (data[0].Equals("endfacet")) { if (counter == 3) { surf.AddTriangle(triangle); n_triangles += 1; } state = 1; } break; } } fs.Close(); System.Console.WriteLine("STLReader: read {0} triangles!",n_triangles); return (surf); }