/// call edgeDrop on each cutter and pick the correct (highest valid CL-point) result //******** edge **************************************************** */ //C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#: //ORIGINAL LINE: bool edgeDrop(CLPoint &cl, const Triangle &t) const public new bool edgeDrop(CLPoint cl, Triangle t) { bool result = false; for (int n = 0; n < cutter.Count; ++n) { // loop through cutters CLPoint cl_tmp = cl + new Point(0, 0, zoffset[n]); CCPoint cc_tmp; if (cutter[n].edgeDrop(cl_tmp, t)) { // drop sub-cutter against edge if (ccValidRadius(n, cl_tmp)) { // check if cc-point is valid cc_tmp = new CCPoint(cl_tmp.cc); if (cl.liftZ(cl_tmp.z - zoffset[n])) { // we need to lift the cutter cc_tmp.type = CCType.EDGE; cl.cc = cc_tmp; result = true; } else { if (cc_tmp != null) { cc_tmp.Dispose(); } } } } } return(result); }
/// first simple implementation of this operation // use OpenMP to share work between threads protected void pointDropCutter1(CLPoint clp) { nCalls = 0; int calls = 0; LinkedList <Triangle> tris; //tris=new std::list<Triangle>(); tris = root.search_cutter_overlap(cutter, clp); LinkedList <Triangle> .Enumerator it; //C++ TO C# CONVERTER TODO TASK: Iterators are only converted within the context of 'while' and 'for' loops: for (it = tris.GetEnumerator(); it != tris.end(); ++it) { // loop over found triangles //C++ TO C# CONVERTER TODO TASK: Iterators are only converted within the context of 'while' and 'for' loops: if (cutter.overlaps(clp, it)) { // cutter overlap triangle? check //C++ TO C# CONVERTER TODO TASK: Iterators are only converted within the context of 'while' and 'for' loops: if (clp.below(it)) { //C++ TO C# CONVERTER TODO TASK: Iterators are only converted within the context of 'while' and 'for' loops: cutter.dropCutter(clp, it); ++calls; } } } /* * delete(tris); */ nCalls = calls; return; }
/// \brief drop the MillingCutter at Point cl down along the z-axis until it makes contact with Triangle t. /// This function calls vertexDrop, facetDrop, and edgeDrop to do its job. /// Follows the template-method, or "self-delegation" design pattern. /// if cl.z is too low, updates cl.z so that the cutter does not cut Triangle t. // call vertex, facet, and edge drop methods on input Triangle t //C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#: //ORIGINAL LINE: bool dropCutter(CLPoint &cl, const Triangle &t) const public bool dropCutter(CLPoint cl, Triangle t) { bool facet = false; bool vertex = false; bool edge = false; /* // alternative ordering of the tests: * if (cl.below(t)) * vertexDrop(cl,t); * * // optimisation: if we are now above the triangle we don't need facet and edge * if ( cl.below(t) ) { * facetDrop(cl,t); * edgeDrop(cl,t); * }*/ if (cl.below(t)) { facet = facetDrop(cl, t); // if we make contact with the facet... if (!facet) { // ...then we will not hit an edge/vertex, so don't check for that vertex = vertexDrop(cl, t); if (cl.below(t)) { edge = edgeDrop(cl, t); } } } return(facet || vertex || edge); }
/// return true if cl.cc is within the radial range of cutter n /// for cutter n the valid radial distance from cl is /// between radiusvec[n-1] and radiusvec[n] //C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#: //ORIGINAL LINE: bool ccValidRadius(uint n, CLPoint& cl) const protected bool ccValidRadius(int n, CLPoint cl) { if (cl.cc.type == CCType.NONE) { return(false); } double d = cl.xyDistance(cl.cc); double lolimit; double hilimit; if (n == 0) { lolimit = -1E-6; } else { lolimit = radiusvec[n - 1] - 1E-6; } hilimit = radiusvec[n] + 1e-6; // FIXME: really ugly solution this one... if (d < lolimit) { return(false); } else if (d > hilimit) { return(false); } else { return(true); } }
/// \brief drop cutter at (cl.x, cl.y) against facet of Triangle t /// calls xy_normal_length(), normal_length(), and center_height() on the subclass. /// if cl.z is too low, updates cl.z so that cutter does not cut the facet. /// CompositeCutter may be the only sub-class that needs to reimplement this function. // general purpose facet-drop which calls xy_normal_length(), normal_length(), // and center_height() on the subclass //C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#: //ORIGINAL LINE: virtual bool facetDrop(CLPoint &cl, const Triangle &t) const public virtual bool facetDrop(CLPoint cl, Triangle t) { // Drop cutter at (cl.x, cl.y) against facet of Triangle t Point normal = t.upNormal(); // facet surface normal if (GlobalMembers.isZero_tol(normal.z)) // vertical surface { return(false); //can't drop against vertical surface } Debug.Assert(GlobalMembers.isPositive(normal.z)); if ((GlobalMembers.isZero_tol(normal.x)) && (GlobalMembers.isZero_tol(normal.y))) { // horizontal plane special case CCPoint cc_tmp = new CCPoint(cl.x, cl.y, t.p[0].z, CCType.FACET); return(cl.liftZ_if_inFacet(cc_tmp.z, cc_tmp, t)); } else { // general case // plane containing facet: a*x + b*y + c*z + d = 0, so // d = -a*x - b*y - c*z, where (a,b,c) = surface normal double d = -normal.dot(t.p[0]); normal.normalize(); // make length of normal == 1.0 Point xyNormal = new Point(normal.x, normal.y, 0.0); xyNormal.xyNormalize(); // define the radiusvector which points from the cc-point to the cutter-center Point radiusvector = this.xy_normal_length * xyNormal + this.normal_length * normal; CCPoint cc_tmp = new CCPoint(cl - radiusvector); // NOTE xy-coords right, z-coord is not. cc_tmp.z = (1.0 / normal.z) * (-d - normal.x * cc_tmp.x - normal.y * cc_tmp.y); // cc-point lies in the plane. cc_tmp.type = CCType.FACET; double tip_z = cc_tmp.z + radiusvector.z - this.center_height; return(cl.liftZ_if_inFacet(tip_z, cc_tmp, t)); } }
/// CompositeCutter can not use the base-class facetDrop, instead we here /// call facetDrop() on each cutter in turn, and pick the valid CC/CL point /// as the result for the CompositeCutter //******** facetDrop ********************** */ // call facetDrop on each cutter and pick a valid cc-point //C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#: //ORIGINAL LINE: bool facetDrop(CLPoint &cl, const Triangle &t) const public new bool facetDrop(CLPoint cl, Triangle t) { bool result = false; for (int n = 0; n < cutter.Count; ++n) { // loop through cutters CLPoint cl_tmp = cl + new CLPoint(0, 0, zoffset[n]); CCPoint cc_tmp; if (cutter[n].facetDrop(cl_tmp, t)) { Debug.Assert(cl_tmp.cc != null); if (ccValidRadius(n, cl_tmp)) { // cc-point is valid cc_tmp = new CCPoint(cl_tmp.cc); if (cl.liftZ(cl_tmp.z - zoffset[n])) { // we need to lift the cutter cc_tmp.type = CCType.FACET; cl.cc = cc_tmp; result = true; } else { if (cc_tmp != null) { cc_tmp.Dispose(); } } } } } return(result); }
/// search for overlap with a MillingCutter c positioned at cl, return found objects public LinkedList <Triangle> search_cutter_overlap(MillingCutter c, CLPoint cl) { double r = c.getRadius(); // build a bounding-box at the current CL Bbox bb = new Bbox(cl.x - r, cl.x + r, cl.y - r, cl.y + r, cl.z, cl.z + c.getLength()); return(this.search(bb)); }
/// flatness predicate for adaptive sampling protected bool flat(CLPoint start_cl, CLPoint mid_cl, CLPoint stop_cl) { CLPoint v1 = new CLPoint(mid_cl - start_cl); CLPoint v2 = new CLPoint(stop_cl - mid_cl); v1.normalize(); v2.normalize(); return(v1.dot(v2) > cosLimit); }
/// \brief call dropCutter on all Triangles in an STLSurf /// drops the MillingCutter at Point cl down along the z-axis /// until it makes contact with a triangle in the STLSurf s /// NOTE: no kd-tree optimization, this function will make /// dropCutter() calls for each and every Triangle in s. /// NOTE: should not really be used for real work, demo/debug only // TESTING ONLY, don't use for real //C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#: //ORIGINAL LINE: bool dropCutterSTL(CLPoint &cl, const STLSurf &s) const public bool dropCutterSTL(CLPoint cl, STLSurf s) { bool result = false; foreach (Triangle t in s.tris) { if (this.dropCutter(cl, t)) { result = true; } } return(result); }
/// 2nd version of algorithm /// push-cutter which uses KDNode2 kd-tree search to find triangles /// overlapping with the cutter. protected void pushCutter2() { // std::cout << "BatchPushCutter2 with " << fibers->size() << // " fibers and " << surf->tris.size() << " triangles..." << std::endl; nCalls = 0; LinkedList <Triangle> overlap_triangles; /* * boost::progress_display show_progress = new boost::progress_display(fibers.Count); */ foreach (Fiber f in fibers) { CLPoint cl = new CLPoint(); if (x_direction) { cl.x = 0; cl.y = f.p1.y; cl.z = f.p1.z; } else if (y_direction) { cl.x = f.p1.x; cl.y = 0; cl.z = f.p1.z; } else { Debug.Assert(false); } overlap_triangles = root.search_cutter_overlap(cutter, cl); Debug.Assert(overlap_triangles.Count <= surf.size()); // can't possibly find more triangles than in the STLSurf foreach (Triangle t in overlap_triangles) { //if ( bb->overlaps( t.bb ) ) { Interval i = new Interval(); cutter.pushCutter(f, i, t); f.addInterval(i); ++nCalls; //} } /* * delete(overlap_triangles); ++show_progress; */ } // std::cout << "BatchPushCutter2 done." << std::endl; return; }
/// run adaptive sampling protected void adaptive_sampling_run() { //std::cout << " apdc::adaptive_sampling_run()... "; clpoints.Clear(); foreach (Span span in path.span_list) { // this loop could run in parallel, since spans don't depend on eachother CLPoint start = new CLPoint(span.getPoint(0.0)); CLPoint stop = new CLPoint(span.getPoint(1.0)); subOp[0].run(start); subOp[0].run(stop); clpoints.Add(start); adaptive_sample(span, 0.0, 1.0, new ocl.CLPoint(start), new ocl.CLPoint(stop)); } //std::cout << " DONE clpoints.size()=" << clpoints.size() << "\n"; }
/// sample the span unfirormly with tolerance sampling // this samples the Span and pushes the corresponding sampled points to bdc private void sample_span(Span span) { Debug.Assert(sampling > 0.0); uint num_steps = (uint)(span.length2d() / sampling + 1); for (uint i = 0; i <= num_steps; i++) { double fraction = (double)i / num_steps; Point ptmp = span.getPoint(fraction); CLPoint p = new CLPoint(ptmp.x, ptmp.y, ptmp.z); p.z = minimumZ; subOp[0].appendPoint(p); if (p != null) { p.Dispose(); } } }
/// \brief drop cutter at (cl.x, cl.y) against the three vertices of Triangle t. /// calls this->height(r) on the subclass of MillingCutter we are using. /// if cl.z is too low, updates cl.z so that cutter does not cut any vertex. // general purpose vertex-drop which delegates to this->height(r) of subclass //C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#: //ORIGINAL LINE: bool vertexDrop(CLPoint &cl, const Triangle &t) const public bool vertexDrop(CLPoint cl, Triangle t) { bool result = false; foreach (Point p in t.p) { // test each vertex of triangle double q = cl.xyDistance(p); // distance in XY-plane from cl to p if (q <= radius) { // p is inside the cutter CCPoint cc_tmp = new CCPoint(p, CCType.VERTEX); if (cl.liftZ(p.z - this.height(q), cc_tmp)) { result = true; } } } return(result); }
/// Cone facet-drop is special, since we can make contact with either the tip or the circular rim // because this checks for contact with both the tip and the circular edge it is hard to move to the base-class // we either hit the tip, when the slope of the plane is smaller than angle // or when the slope is steep, the circular edge between the cone and the cylindrical shaft //C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#: //ORIGINAL LINE: bool facetDrop(CLPoint &cl, const Triangle &t) const public new bool facetDrop(CLPoint cl, Triangle t) { bool result = false; Point normal = t.upNormal(); // facet surface normal if (GlobalMembers.isZero_tol(normal.z)) // vertical surface { return(false); //can't drop against vertical surface } if ((GlobalMembers.isZero_tol(normal.x)) && (GlobalMembers.isZero_tol(normal.y))) { // horizontal plane special case CCPoint cc_tmp = new CCPoint(cl.x, cl.y, t.p[0].z, CCType.FACET_TIP); // so any vertex is at the correct height return(cl.liftZ_if_inFacet(cc_tmp.z, cc_tmp, t)); } else { // define plane containing facet // a*x + b*y + c*z + d = 0, so // d = -a*x - b*y - c*z, where (a,b,c) = surface normal double a = normal.x; double b = normal.y; double c = normal.z; double d = -normal.dot(t.p[0]); normal.xyNormalize(); // make xy length of normal == 1.0 // cylindrical contact point case // find the xy-coordinates of the cc-point CCPoint cyl_cc_tmp = new CCPoint(cl - radius * normal); cyl_cc_tmp.z = (1.0 / c) * (-d - a * cyl_cc_tmp.x - b * cyl_cc_tmp.y); double cyl_cl_z = cyl_cc_tmp.z - length; // tip positioned here cyl_cc_tmp.type = CCType.FACET_CYL; // tip contact with facet CCPoint tip_cc_tmp = new CCPoint(cl.x, cl.y, 0.0); tip_cc_tmp.z = (1.0 / c) * (-d - a * tip_cc_tmp.x - b * tip_cc_tmp.y); double tip_cl_z = tip_cc_tmp.z; tip_cc_tmp.type = CCType.FACET_TIP; result = result || cl.liftZ_if_inFacet(tip_cl_z, tip_cc_tmp, t); result = result || cl.liftZ_if_inFacet(cyl_cl_z, cyl_cc_tmp, t); return(result); } }
// DROP-CUTTER /// drop cutter against edge p1-p2 at xy-distance d from cl /// translates to cl=(0,0) and rotates edge to be along x-axis /// for call to singleEdgeDropCanonical() // 1) translate the geometry so that in the XY plane cl = (0,0) // 2) rotate the p1-p2 edge so that a new edge u1-u2 lies along the x-axis // 3) call singleEdgeDropCanonical(), implemented in the sub-class. // this returns the x-coordinate of the CC point and cl.z // 4) rotate/translate back to the original geometry // 5) update cl.z if required and if CC lies within the edge // // The edge test can be done in a "dual" geometry. // instead of testing the original cutter against an infinitely thin edge // we can test a virtual CylCutter with radius VR against an infinite ER-radius cylinder around the edge. // This reduces to a 2D problem in the XY plane, where section of the ER-radius cylinder is an ellipse. // in the general case CL lies on an offset ellipse with offset OR. // The general cases only applies for BullCutter(R1,R2) where R1 is the shaft radius // and R2 is the corner radius. // For CylCutter and BallCutter there are simple analytic solutions and an offset ellipse approach is not required. // // The dual problem for each cutter type is: // // CylCutter(R): VR = R, ER = 0, OR=R (the z-position of VR is the same as for CylCutter) // BallCutter(R): VR = 0, ER = R, OR=0 (the z-position of VR is a distance R up from the tip of BallCutter) // BullCutter(R1,R2): VR=R1-R2, ER=R2, OR=R1-R2 (z-position of VR is a distance R2 up from the tip of BullCutter) // // cone: ??? (how is this an ellipse??) // // d is the distance from the p1-p2 line to cl, in the 2D XY plane //C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#: //ORIGINAL LINE: bool singleEdgeDrop(CLPoint& cl, const Point& p1, const Point& p2, double d) const protected bool singleEdgeDrop(CLPoint cl, Point p1, Point p2, double d) { Point v = p2 - p1; // vector along edge, from p1 -> p2 Point vxy = new Point(v.x, v.y, 0.0); // XY projection vxy.xyNormalize(); // normalized XY edge vector // figure out u-coordinates of p1 and p2 (i.e. x-coord in the rotated system) Point sc = cl.xyClosestPoint(p1, p2); Debug.Assert(((cl - sc).xyNorm() - d) < 1E-6); // edge endpoints in the new coordinate system, in these coordinates, CL is at origo Point u1 = new Point((p1 - sc).dot(vxy), d, p1.z); // d, the distance to line, is the y-coord in the rotated system Point u2 = new Point((p2 - sc).dot(vxy), d, p2.z); Tuple <double, double> contact = this.singleEdgeDropCanonical(u1, u2); // the subclass handles this CCPoint cc_tmp = new CCPoint(sc + contact.Item1 * vxy, CCType.EDGE); // translate back into original coord-system cc_tmp.z_projectOntoEdge(p1, p2); // update cl.z if required, and the cc-point lies inside the p1-p2 edge. return(cl.liftZ_if_InsidePoints(contact.Item2, cc_tmp, p1, p2)); }
/// run adaptive sample on the given Span between t-values of start_t and stop_t protected void adaptive_sample(Span span, double start_t, double stop_t, CLPoint start_cl, CLPoint stop_cl) { double mid_t = start_t + (stop_t - start_t) / 2.0; // mid point sample Debug.Assert(mid_t > start_t); Debug.Assert(mid_t < stop_t); CLPoint mid_cl = new CLPoint(span.getPoint(mid_t)); //std::cout << " apdc sampling at " << mid_t << "\n"; subOp[0].run(mid_cl); double fw_step = (stop_cl - start_cl).xyNorm(); if ((fw_step > sampling) || ((!flat(start_cl, mid_cl, stop_cl)) && (fw_step > min_sampling))) { // OR not flat, and not max sampling adaptive_sample(span, start_t, mid_t, new ocl.CLPoint(start_cl), new ocl.CLPoint(mid_cl)); adaptive_sample(span, mid_t, stop_t, new ocl.CLPoint(mid_cl), new ocl.CLPoint(stop_cl)); } else { clpoints.Add(stop_cl); } }
/// use kd-tree search to find overlapping triangles protected void pushCutter2(Fiber f) { LinkedList <Triangle> .Enumerator it; // for looping over found triangles LinkedList <Triangle> .Enumerator it_end; Interval i; LinkedList <Triangle> tris; CLPoint cl = new CLPoint(); if (x_direction) { cl.x = 0; cl.y = f.p1.y; cl.z = f.p1.z; } else if (y_direction) { cl.x = f.p1.x; cl.y = 0; cl.z = f.p1.z; } tris = root.search_cutter_overlap(cutter, cl); it_end = tris.end(); for (it = tris.GetEnumerator(); it != it_end; ++it) { i = new Interval(); //C++ TO C# CONVERTER TODO TASK: Iterators are only converted within the context of 'while' and 'for' loops: cutter.pushCutter(f, i, it.Current); f.addInterval(i); ++nCalls; if (i != null) { i.Dispose(); } } /* * delete(tris); */ }
/// \brief drop cutter at (cl.x, cl.y) against the three edges of input Triangle t. /// calls the sub-class MillingCutter::singleEdgeDrop on each edge /// if cl.z is too low, updates cl.z so that cutter does not cut any edge. // edge-drop function which calls the sub-class MillingCutter::singleEdgeDrop on each // edge of the input Triangle t. //C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#: //ORIGINAL LINE: virtual bool edgeDrop(CLPoint &cl, const Triangle &t) const public virtual bool edgeDrop(CLPoint cl, Triangle t) { bool result = false; for (int n = 0; n < 3; n++) { // loop through all three edges int start = n; // index of the start-point of the edge int end = (n + 1) % 3; // index of the end-point of the edge Point p1 = t.p[start]; Point p2 = t.p[end]; if (!GlobalMembers.isZero_tol(p1.x - p2.x) || !GlobalMembers.isZero_tol(p1.y - p2.y)) { double d = cl.xyDistanceToLine(p1, p2); if (d <= radius) // potential contact with edge { if (this.singleEdgeDrop(cl, p1, p2, d)) { result = true; } } } } return(result); }
/// run algorithm on a single input CLPoint public virtual void run(CLPoint cl) { Debug.Assert(false); }
/// 3rd version of algorithm /// use kd-tree search to find overlapping triangles /// use OpenMP for multi-threading protected void pushCutter3() { // std::cout << "BatchPushCutter3 with " << fibers->size() << // " fibers and " << surf->tris.size() << " triangles." << std::endl; // std::cout << " cutter = " << cutter->str() << "\n"; nCalls = 0; /* * boost::progress_display show_progress = new boost::progress_display(fibers.Count); */ #if _OPENMP Console.Write("OpenMP is enabled"); omp_set_num_threads(nthreads); //omp_set_nested(1); #endif LinkedList <Triangle> .Enumerator it; // for looping over found triabgles LinkedList <Triangle> .Enumerator it_end; Interval i; LinkedList <Triangle> tris; List <Fiber> fiberr = fibers; #if _WIN32 // OpenMP version 2 of VS2013 OpenMP need signed loop variable int n; // loop variable int Nmax = fibers.Count; // the number of fibers to process #else int n; // loop variable uint Nmax = (uint)fibers.Count; // the number of fibers to process #endif uint calls = 0; //C++ TO C# CONVERTER TODO TASK: There is no equivalent to most C++ 'pragma' directives in C#: // #pragma omp parallel for schedule(dynamic) shared(calls, fiberr) private(n,i,tris,it,it_end) //#pragma omp parallel for shared( calls, fiberr) private(n,i,tris,it,it_end) for (n = 0; n < Nmax; ++n) { // loop through all fibers #if _OPENMP if (n == 0) { // first iteration if (omp_get_thread_num() == 0) { Console.Write("Number of OpenMP threads = "); Console.Write(omp_get_num_threads()); Console.Write("\n"); } } #endif CLPoint cl = new CLPoint(); // cl-point on the fiber if (x_direction) { cl.x = 0; cl.y = fiberr[n].p1.y; cl.z = fiberr[n].p1.z; } else if (y_direction) { cl.x = fiberr[n].p1.x; cl.y = 0; cl.z = fiberr[n].p1.z; } tris = root.search_cutter_overlap(cutter, cl); it_end = tris.end(); for (it = tris.GetEnumerator(); it != it_end; ++it) { // loop through the found overlapping triangles //if ( bb->overlaps( it->bb ) ) { // todo: optimization where method-calls are skipped if triangle bbox already in the fiber i = new Interval(); //C++ TO C# CONVERTER TODO TASK: Iterators are only converted within the context of 'while' and 'for' loops: cutter.pushCutter(fiberr[n], i, it.Current); fiberr[n].addInterval(i); ++calls; if (i != null) { i.Dispose(); } //} } /* * delete(tris); ++show_progress; */ } // OpenMP parallel region ends here this.nCalls = (int)calls; // std::cout << "\nBatchPushCutter3 done." << std::endl; return; }
/// append to list of CL-points to evaluate public new void appendPoint(CLPoint p) { clpoints.Add(p); }
/// add an input CLPoint to this Operation public virtual void appendPoint(CLPoint p) { }
public new void run(CLPoint clp) { //std::cout << "PointDropCutter::run() clp= " << clp << " dropped to "; pointDropCutter1(clp); //std::cout << clp << " nCalls = " << nCalls <<"\n "; }
public override void addCLPoint(CLPoint p) { clpoints.AddLast(p); }