SCurve SCurve::MakeCopySplitAgainst(SShell *agnstA, SShell *agnstB, SSurface *srfA, SSurface *srfB) { SCurve ret; ret = *this; ret.pts = {}; SCurvePt *p = pts.First(); if(!p) oops(); SCurvePt prev = *p; ret.pts.Add(p); p = pts.NextAfter(p); for(; p; p = pts.NextAfter(p)) { List<SInter> il = {}; // Find all the intersections with the two passed shells if(agnstA) agnstA->AllPointsIntersecting(prev.p, p->p, &il, true, false, true); if(agnstB) agnstB->AllPointsIntersecting(prev.p, p->p, &il, true, false, true); if(il.n > 0) { // The intersections were generated by intersecting the pwl // edge against a surface; so they must be refined to lie // exactly on the original curve. il.ClearTags(); SInter *pi; for(pi = il.First(); pi; pi = il.NextAfter(pi)) { if(pi->srf == srfA || pi->srf == srfB) { // The edge certainly intersects the surfaces that it // trims (at its endpoints), but those ones don't count. // They are culled later, but no sense calculating them // and they will cause numerical problems (since two // of the three surfaces they're refined to lie on will // be identical, so the matrix will be singular). pi->tag = 1; continue; } Point2d puv; (pi->srf)->ClosestPointTo(pi->p, &puv, false); // Split the edge if the intersection lies within the surface's // trim curves, or within the chord tol of the trim curve; want // some slop if points are close to edge and pwl is too coarse, // and it doesn't hurt to split unnecessarily. Point2d dummy = { 0, 0 }; int c = (pi->srf->bsp) ? pi->srf->bsp->ClassifyPoint(puv, dummy, pi->srf) : SBspUv::OUTSIDE; if(c == SBspUv::OUTSIDE) { double d = VERY_POSITIVE; if(pi->srf->bsp) d = pi->srf->bsp->MinimumDistanceToEdge(puv, pi->srf); if(d > SS.ChordTolMm()) { pi->tag = 1; continue; } } // We're keeping the intersection, so actually refine it. (pi->srf)->PointOnSurfaces(srfA, srfB, &(puv.x), &(puv.y)); pi->p = (pi->srf)->PointAt(puv); } il.RemoveTagged(); // And now sort them in order along the line. Note that we must // do that after refining, in case the refining would make two // points switch places. LineStart = prev.p; LineDirection = (p->p).Minus(prev.p); qsort(il.elem, il.n, sizeof(il.elem[0]), ByTAlongLine); // And now uses the intersections to generate our split pwl edge(s) Vector prev = Vector::From(VERY_POSITIVE, 0, 0); for(pi = il.First(); pi; pi = il.NextAfter(pi)) { // On-edge intersection will generate same split point for // both surfaces, so don't create zero-length edge. if(!prev.Equals(pi->p)) { SCurvePt scpt; scpt.tag = 0; scpt.p = pi->p; scpt.vertex = true; ret.pts.Add(&scpt); } prev = pi->p; } } il.Clear(); ret.pts.Add(p); prev = *p; } return ret; }
//----------------------------------------------------------------------------- // Find all points where a line through a and b intersects our surface, and // add them to the list. If seg is true then report only intersections that // lie within the finite line segment (not including the endpoints); otherwise // we work along the infinite line. And we report either just intersections // inside the trim curve, or any intersection with u, v in [0, 1]. And we // either disregard or report tangent points. //----------------------------------------------------------------------------- void SSurface::AllPointsIntersecting(Vector a, Vector b, List<SInter> *l, bool seg, bool trimmed, bool inclTangent) { if(LineEntirelyOutsideBbox(a, b, seg)) return; Vector ba = b.Minus(a); double bam = ba.Magnitude(); List<Inter> inters; ZERO(&inters); // All the intersections between the line and the surface; either special // cases that we can quickly solve in closed form, or general numerical. Vector center, axis, start, finish; double radius; if(degm == 1 && degn == 1) { // Against a plane, easy. Vector n = NormalAt(0, 0).WithMagnitude(1); double d = n.Dot(PointAt(0, 0)); // Trim to line segment now if requested, don't generate points that // would just get discarded later. if(!seg || (n.Dot(a) > d + LENGTH_EPS && n.Dot(b) < d - LENGTH_EPS) || (n.Dot(b) > d + LENGTH_EPS && n.Dot(a) < d - LENGTH_EPS)) { Vector p = Vector::AtIntersectionOfPlaneAndLine(n, d, a, b, NULL); Inter inter; ClosestPointTo(p, &(inter.p.x), &(inter.p.y)); inters.Add(&inter); } } else if(IsCylinder(&axis, ¢er, &radius, &start, &finish)) { // This one can be solved in closed form too. Vector ab = b.Minus(a); if(axis.Cross(ab).Magnitude() < LENGTH_EPS) { // edge is parallel to axis of cylinder, no intersection points return; } // A coordinate system centered at the center of the circle, with // the edge under test horizontal Vector u, v, n = axis.WithMagnitude(1); u = (ab.Minus(n.ScaledBy(ab.Dot(n)))).WithMagnitude(1); v = n.Cross(u); Point2d ap = (a.Minus(center)).DotInToCsys(u, v, n).ProjectXy(), bp = (b.Minus(center)).DotInToCsys(u, v, n).ProjectXy(), sp = (start. Minus(center)).DotInToCsys(u, v, n).ProjectXy(), fp = (finish.Minus(center)).DotInToCsys(u, v, n).ProjectXy(); double thetas = atan2(sp.y, sp.x), thetaf = atan2(fp.y, fp.x); Point2d ip[2]; int ip_n = 0; if(fabs(fabs(ap.y) - radius) < LENGTH_EPS) { // tangent if(inclTangent) { ip[0] = Point2d::From(0, ap.y); ip_n = 1; } } else if(fabs(ap.y) < radius) { // two intersections double xint = sqrt(radius*radius - ap.y*ap.y); ip[0] = Point2d::From(-xint, ap.y); ip[1] = Point2d::From( xint, ap.y); ip_n = 2; } int i; for(i = 0; i < ip_n; i++) { double t = (ip[i].Minus(ap)).DivPivoting(bp.Minus(ap)); // This is a point on the circle; but is it on the arc? Point2d pp = ap.Plus((bp.Minus(ap)).ScaledBy(t)); double theta = atan2(pp.y, pp.x); double dp = WRAP_SYMMETRIC(theta - thetas, 2*PI), df = WRAP_SYMMETRIC(thetaf - thetas, 2*PI); double tol = LENGTH_EPS/radius; if((df > 0 && ((dp < -tol) || (dp > df + tol))) || (df < 0 && ((dp > tol) || (dp < df - tol)))) { continue; } Vector p = a.Plus((b.Minus(a)).ScaledBy(t)); Inter inter; ClosestPointTo(p, &(inter.p.x), &(inter.p.y)); inters.Add(&inter); } } else { // General numerical solution by subdivision, fallback int cnt = 0, level = 0; AllPointsIntersectingUntrimmed(a, b, &cnt, &level, &inters, seg, this); } // Remove duplicate intersection points inters.ClearTags(); int i, j; for(i = 0; i < inters.n; i++) { for(j = i + 1; j < inters.n; j++) { if(inters.elem[i].p.Equals(inters.elem[j].p)) { inters.elem[j].tag = 1; } } } inters.RemoveTagged(); for(i = 0; i < inters.n; i++) { Point2d puv = inters.elem[i].p; // Make sure the point lies within the finite line segment Vector pxyz = PointAt(puv.x, puv.y); double t = (pxyz.Minus(a)).DivPivoting(ba); if(seg && (t > 1 - LENGTH_EPS/bam || t < LENGTH_EPS/bam)) { continue; } // And that it lies inside our trim region Point2d dummy = { 0, 0 }; int c = bsp->ClassifyPoint(puv, dummy, this); if(trimmed && c == SBspUv::OUTSIDE) { continue; } // It does, so generate the intersection SInter si; si.p = pxyz; si.surfNormal = NormalAt(puv.x, puv.y); si.pinter = puv; si.srf = this; si.onEdge = (c != SBspUv::INSIDE); l->Add(&si); } inters.Clear(); }