/// \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)); } }
// drop-cutter: vertex and facet are handled in base-class // drop-cutter: Toroidal cutter edge-test handled here // called from MillingCutter::singleEdgeDrop() // // the canonical geometry is one where (cl.x, cl.y) = (0,0) // and the edge is rotated to lie along the x-axis. // since the u1-u2 edge is along the x-axis, the points u1 u2 have the same y-coordinate. // // when a radius2 cylinder around the edge is sliced by an XY plane this // results in an ellipse with a shorter axis of radius2, and a longer axis of radius2/sin(theta) // where theta is the slope of the edge in the XZ plane //C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#: //ORIGINAL LINE: System.Tuple< double, double > singleEdgeDropCanonical(const Point& u1, const Point& u2) const protected new Tuple <double, double> singleEdgeDropCanonical(Point u1, Point u2) { if (GlobalMembers.isZero_tol(u1.z - u2.z)) { // horizontal edge special case return(new Tuple <double, double>(0, u1.z - height(u1.y))); } else { // the general offset-ellipse case double b_axis = radius2; // short axis of ellipse = radius2 double theta = Math.Atan((u2.z - u1.z) / (u2.x - u1.x)); // theta is the slope of the line double a_axis = Math.Abs(radius2 / Math.Sin(theta)); // long axis of ellipse = radius2/sin(theta) Point ellcenter = new Point(0, u1.y, 0); Ellipse e = new Ellipse(ellcenter, a_axis, b_axis, radius1); // radius1 is the ellipse-offset int iters = e.solver_brent(); // numerical solver that searches for a point on the ellipse // such that a radius1-length normal-vector from this ellipse-point // is at the CL point (0,0) if (iters > 200) { Console.Write("WARNING: BullCutter::singleEdgeDropCanonical() iters>200 !!\n"); } Debug.Assert(iters < 200); e.setEllipsePositionHi(u1, u2); // this selects either EllipsePosition1 or // EllipsePosition2 and sets it to EllipsePosition_hi // pseudo cc-point on the ellipse/cylinder, in the CL=(0,0) system Point ell_ccp = e.ePointHi(); Debug.Assert(Math.Abs(ell_ccp.xyNorm() - radius1) < 1E-5); // ell_ccp should be on the cylinder-circle Point cc_tmp_u = ell_ccp.closestPoint(u1, u2); // find cc-point on u1-u2 edge return(new Tuple <double, double>(cc_tmp_u.x, e.getCenterZ() - radius2)); } }
/* * /// string repr * public static std::ostream operator << (std::ostream stream, BullCutter c) * { * stream << "BullCutter(d=" << c.diameter << ", r1=" << c.radius1 << " r2=" << c.radius2 << ", L=" << c.length << ")"; * return stream; * } * * //C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#: * //ORIGINAL LINE: string str() const * public new string str() * { * std::ostringstream o = new std::ostringstream(); * o << this; * return o.str(); * } */ // push-cutter: vertex and facet handled by base-class // edge handled here //C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#: //ORIGINAL LINE: bool generalEdgePush(const Fiber& f, Interval& i, const Point& p1, const Point& p2) const protected new bool generalEdgePush(Fiber f, Interval i, Point p1, Point p2) { //std::cout << " BullCutter::generalEdgePush() \n"; bool result = false; if (GlobalMembers.isZero_tol((p2 - p1).xyNorm())) { // this would be a vertical edge return(result); } if (GlobalMembers.isZero_tol(p2.z - p1.z)) // this would be a horizontal edge { return(result); } Debug.Assert(Math.Abs(p2.z - p1.z) > 0.0); // no horiz edges allowed hereafter // p1+t*(p2-p1) = f.p1.z+radius2 => double tplane = (f.p1.z + radius2 - p1.z) / (p2.z - p1.z); // intersect edge with plane at z = ufp1.z Point ell_center = p1 + tplane * (p2 - p1); Debug.Assert(GlobalMembers.isZero_tol(Math.Abs(ell_center.z - (f.p1.z + radius2)))); Point major_dir = (p2 - p1); Debug.Assert(major_dir.xyNorm() > 0.0); major_dir.z = 0; major_dir.xyNormalize(); Point minor_dir = major_dir.xyPerp(); double theta = Math.Atan((p2.z - p1.z) / (p2 - p1).xyNorm()); double major_length = Math.Abs(radius2 / Math.Sin(theta)); double minor_length = radius2; AlignedEllipse e = new AlignedEllipse(ell_center, major_length, minor_length, radius1, major_dir, minor_dir); if (e.aligned_solver(f)) { // now we want the offset-ellipse point to lie on the fiber Point pseudo_cc = e.ePoint1(); // pseudo cc-point on ellipse and cylinder Point pseudo_cc2 = e.ePoint2(); CCPoint cc = new CCPoint(pseudo_cc.closestPoint(p1, p2)); CCPoint cc2 = new CCPoint(pseudo_cc2.closestPoint(p1, p2)); cc.type = CCType.EDGE_POS; cc2.type = CCType.EDGE_POS; Point cl = e.oePoint1() - new Point(0, 0, center_height); Debug.Assert(GlobalMembers.isZero_tol(Math.Abs(cl.z - f.p1.z))); Point cl2 = e.oePoint2() - new Point(0, 0, center_height); Debug.Assert(GlobalMembers.isZero_tol(Math.Abs(cl2.z - f.p1.z))); double cl_t = f.tval(cl); double cl_t2 = f.tval(cl2); if (i.update_ifCCinEdgeAndTrue(cl_t, cc, p1, p2, true)) { result = true; } if (i.update_ifCCinEdgeAndTrue(cl_t2, cc2, p1, p2, true)) { result = true; } } //std::cout << " BullCutter::generalEdgePush() DONE result= " << result << "\n"; return(result); }
/// return true if vector parallel to y-axis //C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#: //ORIGINAL LINE: bool yParallel() const public bool yParallel() { if (GlobalMembers.isZero_tol(x) && GlobalMembers.isZero_tol(z)) { return(true); } return(false); }
// test for intersection with the fiber and a tip-tang line // if there is an intersection in tip-tang, calculate the cc-point and update the interval //C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#: //ORIGINAL LINE: bool cone_CC(const Point& tang, const Point& tip, const Point& base, const Point& p1, const Point& p2, const Fiber& f, Interval& i) const protected bool cone_CC(Point tang, Point tip, Point @base, Point p1, Point p2, Fiber f, Interval i) { double u = 0; double t = 0; if (GlobalMembers.xy_line_line_intersection(f.p1, f.p2, ref u, tang, tip, ref t)) { if ((t >= 0.0) && (t <= 1.0)) { CCPoint cc_tmp = new CCPoint(@base + t * (tip - @base)); cc_tmp.z_projectOntoEdge(p1, p2); cc_tmp.type = CCType.EDGE_CONE; return(i.update_ifCCinEdgeAndTrue(u, cc_tmp, p1, p2, (true))); } } return(false); }
/// return true if (s,t) is valid, i.e. lies on the unit circle /// checks s^2 + t^2 == 1 (to within tolerance) // check that s and t values are OK //C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#: //ORIGINAL LINE: bool isValid() const public bool isValid() { if (GlobalMembers.isZero_tol(ocl.GlobalMembers.square(s) + ocl.GlobalMembers.square(t) - 1.0)) { return(true); } else { Console.Write(" EllipsePosition="); Console.Write(this); Console.Write("\n"); Console.Write(" square(s) + square(t) - 1.0 = "); Console.Write(ocl.GlobalMembers.square(s) + ocl.GlobalMembers.square(t) - 1.0); Console.Write(" !!\n"); return(false); } }
/// push-cutter horizontal edge case /// horizontal are much simpler than the general case. /// we can consider the cutter circular with an effective radius of this->width(h) // this is the horizontal edge case //C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#: //ORIGINAL LINE: bool horizEdgePush(const Fiber& f, Interval& i, const Point& p1, const Point& p2) const protected bool horizEdgePush(Fiber f, Interval i, Point p1, Point p2) { bool result = false; double h = p1.z - f.p1.z; // height of edge above fiber if ((h > 0.0)) { if (GlobalMembers.isZero_tol(p2.z - p1.z)) { // this is the horizontal-edge special case double eff_radius = this.width(h); // the cutter acts as a cylinder with eff_radius // contact this cylinder/circle against edge in xy-plane double qt = 0; // fiber is f.p1 + qt*(f.p2-f.p1) double qv = 0; // line is p1 + qv*(p2-p1) if (GlobalMembers.xy_line_line_intersection(p1, p2, ref qv, f.p1, f.p2, ref qt)) { Point q = p1 + qv * (p2 - p1); // the intersection point // from q, go v-units along tangent, then eff_r*normal, and end up on fiber: // q + ccv*tangent + r*normal = p1 + clt*(p2-p1) double ccv = 0; double clt = 0; Point xy_tang = p2 - p1; xy_tang.z = 0; xy_tang.xyNormalize(); Point xy_normal = xy_tang.xyPerp(); Point q1 = q + eff_radius * xy_normal; Point q2 = q1 + (p2 - p1); if (GlobalMembers.xy_line_line_intersection(q1, q2, ref ccv, f.p1, f.p2, ref clt)) { double t_cl1 = clt; double t_cl2 = qt + (qt - clt); if (calcCCandUpdateInterval(t_cl1, ccv, q, p1, p2, f, i, f.p1.z, CCType.EDGE_HORIZ)) { result = true; } if (calcCCandUpdateInterval(t_cl2, -ccv, q, p1, p2, f, i, f.p1.z, CCType.EDGE_HORIZ)) { result = true; } } } } } //std::cout << " horizEdgePush = " << result << "\n"; 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); } }
/// push-cutter cylindrical shaft case. /// This is called when the contact is above the sphere/toroid/cone shaped lower part of the cutter // this is used for the cylindrical shaft of Cyl, Ball, Bull, Cone //C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#: //ORIGINAL LINE: bool shaftEdgePush(const Fiber& f, Interval& i, const Point& p1, const Point& p2) const protected bool shaftEdgePush(Fiber f, Interval i, Point p1, Point p2) { // push cutter along Fiber f in contact with edge p1-p2 // contact with cylindrical cutter shaft double u = 0; double v = 0; bool result = false; if (GlobalMembers.xy_line_line_intersection(p1, p2, ref u, f.p1, f.p2, ref v)) { // find XY-intersection btw fiber and edge Point q = p1 + u * (p2 - p1); // edge/fiber intersection point, on edge // Point q = f.p1 + v*(f.p2-f.p1); // q on fiber // from q, go v_cc*xy_tangent, then r*xy_normal, and end up on fiber: // q + v_cc*tangent + r*xy_normal = p1 + t_cl*(p2-p1) Point xy_tang = p2 - p1; xy_tang.z = 0; xy_tang.xyNormalize(); Point xy_normal = xy_tang.xyPerp(); Point q1 = q + radius * xy_normal; Point q2 = q1 + (p2 - p1); double u_cc = 0; double t_cl = 0; if (GlobalMembers.xy_line_line_intersection(q1, q2, ref u_cc, f.p1, f.p2, ref t_cl)) { double t_cl1 = t_cl; // cc_tmp1 = q +/- u_cc*(p2-p1); double t_cl2 = v + (v - t_cl); if (calcCCandUpdateInterval(t_cl1, u_cc, q, p1, p2, f, i, f.p1.z + center_height, CCType.EDGE_SHAFT)) { result = true; } if (calcCCandUpdateInterval(t_cl2, -u_cc, q, p1, p2, f, i, f.p1.z + center_height, CCType.EDGE_SHAFT)) { result = true; } } } //std::cout << " shaftEdgePush = " << result << "\n"; return(result); }
/// return closest Point to line through p1 and p2. Works in the XY plane. /// return Point on p1-p2 line which is closest in XY-plane to this //C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#: //ORIGINAL LINE: Point xyClosestPoint(const Point &p1, const Point &p2) const public Point xyClosestPoint(Point p1, Point p2) { // one explanation is here // http://local.wasp.uwa.edu.au/~pbourke/geometry/pointline/ Point pt1 = new Point(p1); // this required because of "const" arguments above. Point pt2 = new Point(p2); Point v = pt2 - pt1; if (GlobalMembers.isZero_tol(v.xyNorm())) { // if p1 and p2 do not make a line in the xy-plane Console.Write("point.cpp: xyClosestPoint ERROR!: can't calculate closest point from \n"); Console.Write("point.cpp: xyClosestPoint ERROR!: *this ="); Console.Write(this); Console.Write(" to line through\n"); Console.Write("point.cpp: xyClosestPoint ERROR!: p1="); Console.Write(p1); Console.Write(" and \n"); Console.Write("point.cpp: xyClosestPoint ERROR!: p2="); Console.Write(p2); Console.Write("\n"); Console.Write("point.cpp: xyClosestPoint ERROR!: in the xy-plane\n"); Debug.Assert(false); return(new Point(0, 0, 0)); } double u; // vector notation: // u = (p3-p1) dot v / (v dot v) u = (this.x - p1.x) * (v.x) + (this.y - p1.y) * (v.y); u = u / (v.x * v.x + v.y * v.y); // coordinates for closest point double x = p1.x + u * v.x; double y = p1.y + u * v.y; return(new Point(x, y, 0)); }
/// \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); }
// cone is pushed along Fiber f into contact with edge p1-p2 //C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#: //ORIGINAL LINE: bool generalEdgePush(const Fiber& f, Interval& i, const Point& p1, const Point& p2) const protected new bool generalEdgePush(Fiber f, Interval i, Point p1, Point p2) { bool result = false; if (GlobalMembers.isZero_tol(p2.z - p1.z)) // guard against horizontal edge { return(result); } Debug.Assert((p2.z - p1.z) != 0.0); // idea: as the ITO-cone slides along the edge it will pierce a z-plane at the height of the fiber // the shaped of the pierced area is either a circle if the edge is steep // or a 'half-circle' + cone shape if the edge is shallow (ice-cream cone...) // we can now intersect this 2D shape with the fiber and get the CL-points. // this is where the ITO cone pierces the z-plane of the fiber // edge-line: p1+t*(p2-p1) = zheight // => t = (zheight - p1)/ (p2-p1) double t_tip = (f.p1.z - p1.z) / (p2.z - p1.z); Point p_tip = p1 + t_tip * (p2 - p1); Debug.Assert(GlobalMembers.isZero_tol(Math.Abs(p_tip.z - f.p1.z))); // p_tip should be in plane of fiber // this is where the ITO cone base exits the plane double t_base = (f.p1.z + center_height - p1.z) / (p2.z - p1.z); Point p_base = p1 + t_base * (p2 - p1); p_base.z = f.p1.z; // project to plane of fiber double L = (p_base - p_tip).xyNorm(); //if ( L <= radius ){ // this is where the ITO-slice is a circle // find intersection points, if any, between the fiber and the circle // fiber is f.p1 - f.p2 // circle is centered at p_base double d = p_base.xyDistanceToLine(f.p1, f.p2); if (d <= radius) { // we know there is an intersection point. // http://mathworld.wolfram.com/Circle-LineIntersection.html // subtract circle center, math is for circle centered at (0,0) double dx = f.p2.x - f.p1.x; double dy = f.p2.y - f.p1.y; double dr = Math.Sqrt(ocl.GlobalMembers.square(dx) + ocl.GlobalMembers.square(dy)); double det = (f.p1.x - p_base.x) * (f.p2.y - p_base.y) - (f.p2.x - p_base.x) * (f.p1.y - p_base.y); // intersection given by: // x = det*dy +/- sign(dy) * dx * sqrt( r^2 dr^2 - det^2 ) / dr^2 // y = -det*dx +/- abs(dy) * sqrt( r^2 dr^2 - det^2 ) / dr^2 double discr = ocl.GlobalMembers.square(radius) * ocl.GlobalMembers.square(dr) - ocl.GlobalMembers.square(det); if (discr >= 0.0) { if (discr == 0.0) { // tangent case double x_tang = (det * dy) / ocl.GlobalMembers.square(dr); double y_tang = -(det * dx) / ocl.GlobalMembers.square(dr); Point p_tang = new Point(x_tang + p_base.x, y_tang + p_base.y); // translate back from (0,0) system! double t_tang = f.tval(p_tang); if (circle_CC(t_tang, p1, p2, f, i)) { result = true; } } else { // two intersection points with the base-circle double x_pos = (det * dy + Math.Sign(dy) * dx * Math.Sqrt(discr)) / ocl.GlobalMembers.square(dr); double y_pos = (-det * dx + Math.Abs(dy) * Math.Sqrt(discr)) / ocl.GlobalMembers.square(dr); Point cl_pos = new Point(x_pos + p_base.x, y_pos + p_base.y); double t_pos = f.tval(cl_pos); // the same with "-" sign: double x_neg = (det * dy - Math.Sign(dy) * dx * Math.Sqrt(discr)) / ocl.GlobalMembers.square(dr); double y_neg = (-det * dx - Math.Abs(dy) * Math.Sqrt(discr)) / ocl.GlobalMembers.square(dr); Point cl_neg = new Point(x_neg + p_base.x, y_neg + p_base.y); double t_neg = f.tval(cl_neg); if (circle_CC(t_pos, p1, p2, f, i)) { result = true; } if (circle_CC(t_neg, p1, p2, f, i)) { result = true; } } } } //} // circle-case if (L > radius) { // ITO-slice is cone + "half-circle" // lines from p_tip to tangent points of the base-circle // this page has an analytic solution: // http://mathworld.wolfram.com/CircleTangentLine.html // this page has a geometric construction: // http://www.mathopenref.com/consttangents.html // circle p_base, radius // top p_tip // tangent point at intersection of base-circle with this circle: Point p_mid = 0.5 * (p_base + p_tip); p_mid.z = f.p1.z; double r_tang = L / 2; // circle-circle intersection to find tangent-points // three cases: no intersection point // one intersection point // two intersection points //d is the distance between the circle centers Point pd = p_mid - p_base; pd.z = 0; double dist = pd.xyNorm(); //distance between the circles //Check for special cases which do not lead to solutions we want bool case1 = (GlobalMembers.isZero_tol(dist) && GlobalMembers.isZero_tol(Math.Abs(radius - r_tang))); bool case2 = (dist > (radius + r_tang)); //no solution. circles do not intersect bool case3 = (dist < Math.Abs(radius - r_tang)); //no solution. one circle is contained in the other bool case4 = (GlobalMembers.isZero_tol(dist - (radius + r_tang))); // tangent case if (case1 || case2 || case3 || case4) { } else { // here we know we have two solutions. //Determine the distance from point 0 to point 2 // law of cosines (http://en.wikipedia.org/wiki/Law_of_cosines) // rt^2 = d^2 + r^2 -2*d*r*cos(gamma) // so cos(gamma) = (rt^2-d^2-r^2)/ -(2*d*r) // and the sought distance is // a = r*cos(gamma) = (-rt^2+d^2+r^2)/ (2*d) double a = (-ocl.GlobalMembers.square(r_tang) + ocl.GlobalMembers.square(radius) + ocl.GlobalMembers.square(dist)) / (2.0 * dist); Debug.Assert(a >= 0.0); Point v2 = p_base + (a / dist) * pd; // v2 is the point where the line through the circle intersection points crosses the line between the circle centers. //Determine the distance from v2 to either of the intersection points double h = Math.Sqrt(ocl.GlobalMembers.square(radius) - ocl.GlobalMembers.square(a)); //Now determine the offsets of the intersection points from point 2 Point ofs = new Point(-pd.y * (h / dist), pd.x * (h / dist)); // now we know the tangent-points Point tang1 = v2 + ofs; Point tang2 = v2 - ofs; if (cone_CC(tang1, p_tip, p_base, p1, p2, f, i)) { result = true; } if (cone_CC(tang2, p_tip, p_base, p1, p2, f, i)) { result = true; } } } // end circle+cone case return(result); }
/// string repr //C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#: //ORIGINAL LINE: string str() const; //C++ TO C# CONVERTER TODO TASK: The implementation of the following method could not be found: // string str(); /// build and return a KDNode containing list *tris at depth dep. protected KDNode <Triangle> build_node(LinkedList <Triangle> tris, int dep, KDNode <Triangle> par) { // parent node //std::cout << "KDNode::build_node list.size()=" << tris->size() << "\n"; if (tris.Count == 0) { //this is a fatal error. Console.Write("ERROR: KDTree::build_node() called with tris->size()==0 !"); Debug.Assert(false); return(null); } Spread spr = calc_spread(tris); // calculate spread in order to know how to cut double cutvalue = spr.start + spr.val / 2; // cut in the middle //std::cout << " cutvalue= " << cutvalue << "\n"; if ((tris.Count <= bucketSize) || GlobalMembers.isZero_tol(spr.val)) { // then return a bucket/leaf node //std::cout << "KDNode::build_node BUCKET list.size()=" << tris->size() << "\n"; KDNode <Triangle> bucket; // dim cutv parent hi lo triangles depth bucket = new KDNode <Triangle>(spr.d, cutvalue, par, null, null, tris, dep); Debug.Assert(bucket.isLeaf); spr = null; return(bucket); // this is the leaf/end of the recursion-tree } // build lists of triangles for hi and lo child nodes LinkedList <Triangle> lolist = new LinkedList <Triangle>(); LinkedList <Triangle> hilist = new LinkedList <Triangle>(); foreach (Triangle t in tris) { // loop through each triangle and put it in either lolist or hilist if (t.bb[spr.d] > cutvalue) { hilist.AddLast(t); } else { lolist.AddLast(t); } } /* * if (hilist->empty() || lolist->empty()) {// an error ?? * std::cout << "kdtree: hilist.size()==0! or lolist.size()==0! \n"; * std::cout << "kdtree: tris->size()= " << tris->size()<< "\n"; * std::cout << "kdtree: hilist.size()= " << hilist->size()<< "\n"; * std::cout << "kdtree: lolist.size()= " << lolist->size()<< "\n"; * BOOST_FOREACH(Triangle t, *tris) { * std::cout << t << "\n"; * std::cout << t.bb << "\n"; * } * std::cout << "kdtree: spr->d= " << spr->d << "\n"; * std::cout << "kdtree: cutvalue= " << cutvalue << "\n"; * assert(0); * }*/ // create the current node dim value parent hi lo trilist depth KDNode <Triangle> node = new KDNode <Triangle>(spr.d, cutvalue, par, null, null, null, dep); // create the child-nodes through recursion // list depth parent if (hilist.Count > 0) { node.hi = build_node(hilist, dep + 1, node); } //else //std::cout << "hilist empty!\n"; if (lolist.Count > 0) { node.lo = build_node(lolist, dep + 1, node); } else { //std::cout << "lolist empty!\n"; } lolist.Clear(); hilist.Clear(); spr = null; lolist = null; hilist = null; return(node); // return a new node }
/// aligned offset-ellipse solver. callsn Numeric::brent_solver() // used by BullCutter pushcutter edge-test public bool aligned_solver(Fiber f) { error_dir.CopyFrom(f.dir.xyPerp()); // now calls to error(diangle) will give the right error Debug.Assert(error_dir.xyNorm() > 0.0); target.CopyFrom(f.p1); // target is either x or y-coord of f.p1 // find position(s) where ellipse tangent is parallel to fiber. Here error() will be minimized/maximized. // tangent at point is: -a t + b s = -a*major_dir*t + b*minor_dir*s // -at ma.y + bs mi.y = 0 for X-fiber // s = sqrt(1-t^2) // -a*ma.y * t + b*mi.y* sqrt(1-t^2) = 0 // => t^2 = b^2 / (a^2 + b^2) double t1 = 0.0; if (f.p1.y == f.p2.y) { t1 = Math.Sqrt(ocl.GlobalMembers.square(b * minor_dir.y) / (ocl.GlobalMembers.square(a * major_dir.y) + ocl.GlobalMembers.square(b * minor_dir.y))); } else if (f.p1.x == f.p2.x) { t1 = Math.Sqrt(ocl.GlobalMembers.square(b * minor_dir.x) / (ocl.GlobalMembers.square(a * major_dir.x) + ocl.GlobalMembers.square(b * minor_dir.x))); } else { Debug.Assert(false); } // bracket root EllipsePosition tmp = new EllipsePosition(); EllipsePosition apos = new EllipsePosition(); EllipsePosition bpos = new EllipsePosition(); double s1 = Math.Sqrt(1.0 - ocl.GlobalMembers.square(t1)); bool found_positive = false; bool found_negative = false; tmp.setDiangle(GlobalMembers.xyVectorToDiangle(s1, t1)); if (error(tmp.diangle) > 0) { found_positive = true; apos.CopyFrom(tmp); } else if (error(tmp.diangle) < 0) { found_negative = true; bpos.CopyFrom(tmp); } tmp.setDiangle(GlobalMembers.xyVectorToDiangle(s1, -t1)); if (error(tmp.diangle) > 0) { found_positive = true; apos.CopyFrom(tmp); } else if (error(tmp.diangle) < 0) { found_negative = true; bpos.CopyFrom(tmp); } tmp.setDiangle(GlobalMembers.xyVectorToDiangle(-s1, t1)); if (error(tmp.diangle) > 0) { found_positive = true; apos.CopyFrom(tmp); } else if (error(tmp.diangle) < 0) { found_negative = true; bpos.CopyFrom(tmp); } tmp.setDiangle(GlobalMembers.xyVectorToDiangle(-s1, -t1)); if (error(tmp.diangle) > 0) { found_positive = true; apos.CopyFrom(tmp); } else if (error(tmp.diangle) < 0) { found_negative = true; bpos.CopyFrom(tmp); } if (found_positive) { if (found_negative) { Debug.Assert(this.error(apos.diangle) * this.error(bpos.diangle) < 0.0); // root is now bracketed. double lolim = 0.0; double hilim = 0.0; if (apos.diangle > bpos.diangle) { lolim = bpos.diangle; hilim = apos.diangle; } else if (bpos.diangle > apos.diangle) { hilim = bpos.diangle; lolim = apos.diangle; } double dia_sln = ocl.GlobalMembers.brent_zero(lolim, hilim, 3E-16, DefineConstants.OE_ERROR_TOLERANCE, this); double dia_sln2 = ocl.GlobalMembers.brent_zero(hilim - 4.0, lolim, 3E-16, DefineConstants.OE_ERROR_TOLERANCE, this); EllipsePosition1.setDiangle(dia_sln); EllipsePosition2.setDiangle(dia_sln2); Debug.Assert(EllipsePosition1.isValid()); Debug.Assert(EllipsePosition2.isValid()); /* * // FIXME. This assert fails in some cases (30sphere.stl z=0, for example) * // FIXME. The allowed error should probably be in proportion to the difficulty of the case. * * if (!isZero_tol( error(EllipsePosition1.diangle) )) { * std::cout << "AlignedEllipse::aligned_solver() ERROR \n"; * std::cout << "error(EllipsePosition1.diangle)= "<< error(EllipsePosition1.diangle) << " (expected zero)\n"; * * } * assert( isZero_tol( error(EllipsePosition1.diangle) ) ); * assert( isZero_tol( error(EllipsePosition2.diangle) ) ); */ return(true); } } return(false); }
// push-cutter: vertex and facet handled in base-class //C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#: //ORIGINAL LINE: bool generalEdgePush(const Fiber& f, Interval& i, const Point& p1, const Point& p2) const protected new bool generalEdgePush(Fiber f, Interval i, Point p1, Point p2) { bool result = false; if (GlobalMembers.isZero_tol((p2 - p1).xyNorm())) { // this would be a vertical edge return(result); } Point ufp1 = f.p1 + new Point(0, 0, radius); // take a fiber which is raised up by radius Point ufp2 = f.p2 + new Point(0, 0, radius); // and intersect it with a cylinder around the edge p1-p2 // Ray : P(t) = O + t*V from point O, in direction V // Cylinder [A, B, r] from point A to point B, radius r // Point P on infinite cylinder if ((P - A) x (B - A))^2 = r^2 * (B - A)^2 // expand : ((O - A) x (B - A) + t * (V x (B - A)))^2 = r^2 * (B - A)^2 // equation in the form (X + t * Y)^2 = d , where: // X = (O - A) x (B - A) // Y = V x (B - A) // d = r^2 * (B - A)^2 // expand the equation : // t^2 * (Y . Y) + t * (2 * (X . Y)) + (X . X) - d = 0 // => second order equation in the form : a*t^2 + b*t + c = 0 where // a = (Y . Y) // b = 2 * (X . Y) // c = (X . X) - d Point ab = p2 - p1; // axis of the cylinder Point ao = (ufp1 - p1); // cyl start to ray start Point ao_x_ab = ao.cross(ab); // cross product Point v_x_ab = (ufp2 - ufp1).cross(ab); // cross product double ab2 = ab.dot(ab); // dot product double a = v_x_ab.dot(v_x_ab); // dot product double b = 2 * (v_x_ab.dot(ao_x_ab)); // dot product double c = ao_x_ab.dot(ao_x_ab) - (radius * radius * ab2); // solve second order equation : a*t^2 + b*t + c = 0 // t = (-b +/- sqrt( b^2 - 4ac ) ) / 2a double discr = b * b - 4 * a * c; double t1; double t2; if (GlobalMembers.isZero_tol(discr)) { // tangent case, only one root t1 = -b / (2 * a); if (calcCCandUpdateInterval(t1, p1, p2, f, i)) { result = true; } } else if (discr > 0.0) { // two roots t1 = (-b + Math.Sqrt(discr)) / (2 * a); t2 = (-b - Math.Sqrt(discr)) / (2 * a); if (calcCCandUpdateInterval(t1, p1, p2, f, i)) { result = true; } if (calcCCandUpdateInterval(t2, p1, p2, f, i)) { result = true; } } return(result); }
/// push cutter with given normal/center/xy_length into contact with Triangle facet // general purpose facetPush //C++ TO C# CONVERTER WARNING: 'const' methods are not available in C#: //ORIGINAL LINE: bool generalFacetPush(double normal_length, double center_height, double xy_normal_length, const Fiber& fib, Interval& i, const Triangle& t) const protected bool generalFacetPush(double normal_length, double center_height, double xy_normal_length, Fiber fib, Interval i, Triangle t) { bool result = false; Point normal = t.upNormal(); // facet surface normal, pointing up if (normal.zParallel()) // normal points in z-dir { return(result); //can't push against horizontal plane, stop here. } normal.normalize(); Point xy_normal = new Point(normal); xy_normal.z = 0; xy_normal.xyNormalize(); // find a point on the plane from which radius2*normal+radius1*xy_normal lands on the fiber+radius2*Point(0,0,1) // (u,v) locates a point on the triangle facet v0+ u*(v1-v0)+v*(v2-v0) u,v in [0,1] // t locates a point along the fiber: p1 + t*(p2-p1) t in [0,1] // // facet-point + r2 * n + r1* xy_n = fiber-point + r2*Point(0,0,1) // => // v0+ u*(v1-v0)+v*(v2-v0) + r2 * n + r1* xy_n = p1 + t*(p2-p1) + r2*Point(0,0,1) // // v0x + u*(v1x-v0x) + v*(v2x-v0x) + r2*nx + r1*xy_n.x = p1x + t*(p2x-p1x) p2x-p1x==0 for Y-fiber // v0y + u*(v1y-v0y) + v*(v2y-v0y) + r2*ny + r1*xy_n.y = p1y + t*(p2y-p1y) p2y-p1y==0 for X-fiber // v0z + u*(v1z-v0z) + v*(v2z-v0z) + r2*nz = p1z + t*(p2z-p1z) + r2 (p2z-p1z)==0 for XY-fibers!! // X-fiber: // v0x + u*(v1x-v0x) + v*(v2x-v0x) + r2*nx + r1*xy_n.x = p1x + t*(p2x-p1x) // v0y + u*(v1y-v0y) + v*(v2y-v0y) + r2*ny + r1*xy_n.y = p1y solve these two for (u,v) // v0z + u*(v1z-v0z) + v*(v2z-v0z) + r2*nz = p1z + r2 and substitute above for t // or // [ (v1y-v0y) (v2y-v0y) ] [ u ] = [ -v0y - r2*ny - r1*xy_n.y + p1y ] // [ (v1z-v0z) (v2z-v0z) ] [ v ] = [ -v0z - r2*nz + p1z + r2 ] // // Y-fiber: // [ (v1x-v0x) (v2x-v0x) ] [ u ] = [ -v0x - r2*nx - r1*xy_n.x + p1x ] double a; double b; double c = t.p[1].z - t.p[0].z; double d = t.p[2].z - t.p[0].z; double e; double f = -t.p[0].z - normal_length * normal.z + fib.p1.z + center_height; // note: the xy_normal does not have a z-component, so omitted here. double u = 0; // u and v are coordinates of the cc-point within the triangle facet double v = 0; // a,b,e depend on the fiber: if (fib.p1.y == fib.p2.y) { // XFIBER a = t.p[1].y - t.p[0].y; b = t.p[2].y - t.p[0].y; e = -t.p[0].y - normal_length * normal.y - xy_normal_length * xy_normal.y + fib.p1.y; if (!GlobalMembers.two_by_two_solver(a, b, c, d, e, f, ref u, ref v)) { return(result); } CCPoint cc = new CCPoint(t.p[0] + u * (t.p[1] - t.p[0]) + v * (t.p[2] - t.p[0])); cc.type = CCType.FACET; if (!cc.isInside(t)) { return(result); } // v0x + u*(v1x-v0x) + v*(v2x-v0x) + r2*nx + r1*xy_n.x = p1x + t*(p2x-p1x) // => // t = 1/(p2x-p1x) * ( v0x + r2*nx + r1*xy_n.x - p1x + u*(v1x-v0x) + v*(v2x-v0x) ) Debug.Assert(!GlobalMembers.isZero_tol(fib.p2.x - fib.p1.x)); // guard against division by zero double tval = (1.0 / (fib.p2.x - fib.p1.x)) * (t.p[0].x + normal_length * normal.x + xy_normal_length * xy_normal.x - fib.p1.x + u * (t.p[1].x - t.p[0].x) + v * (t.p[2].x - t.p[0].x)); if (tval < 0.0 || tval > 1.0) { Console.Write("MillingCutter::facetPush() tval= "); Console.Write(tval); Console.Write(" error!?\n"); //std::cout << " cutter: " << *this << "\n"; Console.Write(" triangle: "); Console.Write(t); Console.Write("\n"); Console.Write(" fiber: "); Console.Write(fib); Console.Write("\n"); } Debug.Assert(tval > 0.0 && tval < 1.0); i.update(tval, cc); result = true; } else if (fib.p1.x == fib.p2.x) { // YFIBER a = t.p[1].x - t.p[0].x; b = t.p[2].x - t.p[0].x; e = -t.p[0].x - normal_length * normal.x - xy_normal_length * xy_normal.x + fib.p1.x; if (!GlobalMembers.two_by_two_solver(a, b, c, d, e, f, ref u, ref v)) { return(result); } CCPoint cc = new CCPoint(t.p[0] + u * (t.p[1] - t.p[0]) + v * (t.p[2] - t.p[0])); cc.type = CCType.FACET; if (!cc.isInside(t)) { return(result); } Debug.Assert(!GlobalMembers.isZero_tol(fib.p2.y - fib.p1.y)); double tval = (1.0 / (fib.p2.y - fib.p1.y)) * (t.p[0].y + normal_length * normal.y + xy_normal_length * xy_normal.y - fib.p1.y + u * (t.p[1].y - t.p[0].y) + v * (t.p[2].y - t.p[0].y)); if (tval < 0.0 || tval > 1.0) { Console.Write("MillingCutter::facetPush() tval= "); Console.Write(tval); Console.Write(" error!?\n"); Console.Write(" (most probably a user error, the fiber is too short compared to the STL model?)\n"); } Debug.Assert(tval > 0.0 && tval < 1.0); i.update(tval, cc); result = true; } else { Debug.Assert(false); } return(result); }