def _candidate_intersect(ci, si): """ Check if the curve and surface intersect. """ bbi1 = ci.get_bbox() bbi2 = si.get_bbox() return bboxes_intersect(bbi1, bbi2, itol)
def _candidate_intersect(ci1, ci2): """ Check if the two curves intersect. """ bbi1 = ci1.get_bbox() bbi2 = ci2.get_bbox() return bboxes_intersect(bbi1, bbi2, itol)
def intersects(self, bbox): """ Test if the bounding box intersects with another. :param bbox: Another bounding box. :type bbox: :class:`.BoundingBox` :return: *True* if they intersect, *False* if not. :rtype: bool """ return bboxes_intersect(self, bbox)
def intersect_curve_surface(curve, surface, itol=None): """ Find the intersection points of a curve and a surface. :param curve: Curve to intersect. :type curve: :class:`.BezierCurve` or :class:`.NurbsCurve` :param surface: Surface to intersect. :type surface: :class:`.BezierSurface` or :class:`.NurbsSurface` :param float itol: Intersection tolerance. :return: Number of subdivisions, intersections, and list containing intersection results nsub, npts, [[(cui, su, sv), pi], ...]. :rtype: list """ # Global parameters ftol = Settings.ftol if itol is None: itol = Settings.gtol # Step 1: Check bounding boxes for possible intersection. bb1 = curve.get_bbox() bb2 = surface.get_bbox() if not bboxes_intersect(bb1, bb2, itol): return 0, 0, [] # Step 2: Decompose each into Bezier segments. _, bezier_curves = curve.decompose() bezier_patches = [] _, _, surfs = surface.decompose() for row in surfs: for s in row: bezier_patches.append(s) # Step 3: Build initial intersection list. csi_list = [] for c in bezier_curves: bb1 = c.get_bbox() for s in bezier_patches: bb2 = s.get_bbox() if bboxes_intersect(bb1, bb2, itol): csi_list.append((c, s)) # Step 4: Define methods for recursive subdivision. def _subdivide(ci, si): """ Recursive subdivision to find possible intersections. """ nsub[0] += 1 cpi = ci.cp spi = si.cp is_flat1 = _is_curve_flat(ci.n, cpi) is_flat2 = _is_surface_flat(si.n, si.m, spi) if is_flat1 and is_flat2: _intersect(ci, cpi, si, spi) else: ci12 = ci.split(0.5) si1234 = si.split(0.5, 0.5) for cii in ci12: for sii in si1234: if _candidate_intersect(cii, sii): _subdivide(cii, sii) def _is_curve_flat(ni, cpi): """ Check curve flatness. """ return is_curve_flat(ni, cpi, ftol) def _is_surface_flat(ni, mi, cpi): """ Check surface flatness. """ return is_surface_flat(ni, mi, cpi, ftol) def _intersect(ci, cpi, si, spi): """ Intersect line and plane. """ p0 = spi[0, 0] vu, vv = spi[-1, 0] - p0, spi[0, -1] - p0 p1, p2 = cpi[0], cpi[-1] pnorm = cross(vu, vv) pnorm /= norm(pnorm) denom = dot(pnorm, p2 - p1) if abs(denom) <= 1.0e-12: # If parallel use a starting point at midpoints. ti = ci.local_to_global_param(0.5) ui = si.local_to_global_param('u', 0.5) vi = si.local_to_global_param('v', 0.5) tuv0_list.append((ti, ui, vi)) else: ti = dot(pnorm, p0 - p1) / denom ti = ci.local_to_global_param(ti) pi = p1 + ti * (p2 - p1) tri3d = array([spi[0, 0], spi[-1, 0], spi[0, -1]], dtype=float64) tri2d = array([[si.au, si.av], [si.bu, si.av], [si.au, si.bv]], dtype=float64) ui, vi = invert_point_in_triangle(pi, tri3d, tri2d, False) tuv0_list.append((ti, ui, vi)) def _candidate_intersect(ci, si): """ Check if the curve and surface intersect. """ bbi1 = ci.get_bbox() bbi2 = si.get_bbox() return bboxes_intersect(bbi1, bbi2, itol) # Step 5: Perform recursive subdivision to find initial points. tuv0_list = [] nsub = [0] for c, s in csi_list: _subdivide(c, s) # Step 6: Refine points. def _obj(x): factor = 1. # Penalize objective function if variable is outside domain. if x[0] < a or x[0] > b: factor = 1000. if x[1] < au or x[1] > bu: factor = 1000. if x[2] < av or x[2] > bv: factor = 1000. pci = ceval(x[0], rtype='ndarray', domain='global') psi = seval(x[1], x[2], rtype='ndarray', domain='global') return norm(pci - psi) * factor ceval = curve.eval seval = surface.eval results = [] a, b = curve.a, curve.b au, bu, av, bv = surface.au, surface.bu, surface.av, surface.bv tol = itol / 100. for t0, u0, v0 in tuv0_list: tuv0 = array([t0, u0, v0], dtype=float64) sol = minimize(_obj, tuv0, method='Nelder-Mead', tol=tol, options={'ftol': tol}) t, u, v = sol.x t = curve.check_param(t) u, v = surface.check_params(u, v) pc = ceval(t, rtype='ndarray', domain='global') ps = seval(u, v, rtype='ndarray', domain='global') if norm(pc - ps) > itol: continue pii = array(0.5 * (pc + ps), dtype=float64) unique = True for _, p12 in results: if norm(p12 - pii) <= itol: unique = False break if not unique: continue results.append([(t, u, v), pii]) npts = len(results) return nsub[0], npts, results
def intersect_curve_curve(curve1, curve2, itol=None): """ Find the intersection points of two curves. :param curve1: Curve 1 to intersect. :type curve1: :class:`.BezierCurve` or :class:`.NurbsCurve` :param curve2: Curve 2 to intersect. :type curve2: :class:`.BezierCurve` or :class:`.NurbsCurve` :param float itol: Intersection tolerance. :return: Number of subdivisions, intersection points, and results as a list of lists nsub, npts, [[(u1, u2), pi], ...]. :rtype: tuple """ # Global parameters ftol = Settings.ftol if itol is None: itol = Settings.gtol # Step 1: Check bounding boxes for possible intersection. bb1 = curve1.get_bbox() bb2 = curve2.get_bbox() if not bboxes_intersect(bb1, bb2, itol): return 0, 0, [] # Step 2: Decompose each curve into Bezier segments. _, bezier_curves1 = curve1.decompose() _, bezier_curves2 = curve2.decompose() # Step 3: Build initial intersection list. cci_list = [] for c1 in bezier_curves1: bb1 = c1.get_bbox() for c2 in bezier_curves2: bb2 = c2.get_bbox() if bboxes_intersect(bb1, bb2, itol): cci_list.append((c1, c2)) # Step 4: Define methods for recursive subdivision. def _subdivide(ci1, ci2): """ Recursive subdivision to find possible intersections. """ subdivisions[0] += 1 cpi1 = ci1.cp cpi2 = ci2.cp is_flat1 = _is_flat(ci1.n, cpi1) is_flat2 = _is_flat(ci2.n, cpi2) if is_flat1 and is_flat2: _intersect(ci1, ci2, cpi1, cpi2) else: cs12 = ci1.split(0.5) cs34 = ci2.split(0.5) for csi1 in cs12: for csi2 in cs34: if _candidate_intersect(csi1, csi2): _subdivide(csi1, csi2) def _is_flat(ni, cpi): """ Check curve flatness. """ return is_curve_flat(ni, cpi, ftol) def _intersect(ci1, ci2, cpi1, cpi2): """ Intersect lines. """ pi1, pi2 = cpi1[0], cpi1[-1] pi3, pi4 = cpi2[0], cpi2[-1] d4321 = dot(pi4 - pi3, pi2 - pi1) d1321 = dot(pi1 - pi3, pi2 - pi1) d4343 = dot(pi4 - pi3, pi4 - pi3) d2121 = dot(pi2 - pi1, pi2 - pi1) denom = d2121 * d4343 - d4321 * d4321 if abs(denom) <= 1.0e-12: # If parallel add a point halfway between as starting point. ui1 = ci1.local_to_global_param(0.5) ui2 = ci2.local_to_global_param(0.5) u0_list.append((ui1, ui2)) else: d1343 = dot(pi1 - pi3, pi4 - pi3) numer = d1343 * d4321 - d1321 * d4343 u12 = numer / denom u34 = (d1343 + u12 * d4321) / d4343 ui1 = ci1.local_to_global_param(u12) ui2 = ci2.local_to_global_param(u34) u0_list.append((ui1, ui2)) def _candidate_intersect(ci1, ci2): """ Check if the two curves intersect. """ bbi1 = ci1.get_bbox() bbi2 = ci2.get_bbox() return bboxes_intersect(bbi1, bbi2, itol) # Step 5: Perform recursive subdivision to find initial points. u0_list = [] subdivisions = [0] for c1, c2 in cci_list: _subdivide(c1, c2) # Refine initial points. def _obj(x): factor = 1. # Penalize objective function if variable is outside domain. if x[0] < a1 or x[0] > b1: factor = 1000. if x[1] < a2 or x[1] > b2: factor = 1000. pi1 = c1eval(x[0], rtype='ndarray', domain='global') pi2 = c2eval(x[1], rtype='ndarray', domain='global') return norm(pi2 - pi1) * factor def _add_pnt(u1i, u2i, pi): """ Add point to intersection results. """ unique = True for _, pii in results: if norm(pii - pi) <= itol: unique = False break if not unique: return False results.append([(u1i, u2i), pi]) return True # Manually check curve endpoint intersection to improve robustness. results = [] p10 = curve1.eval(0., rtype='ndarray') p11 = curve1.eval(1., rtype='ndarray') p20 = curve2.eval(0., rtype='ndarray') p21 = curve2.eval(1., rtype='ndarray') if norm(p10 - p20) <= itol: _add_pnt(curve1.a, curve2.a, 0.5 * (p10 + p20)) elif norm(p10 - p21) <= itol: _add_pnt(curve1.a, curve2.b, 0.5 * (p10 + p21)) if norm(p11 - p20) <= itol: _add_pnt(curve1.b, curve2.a, 0.5 * (p11 + p20)) elif norm(p11 - p21) <= itol: _add_pnt(curve1.b, curve2.b, 0.5 * (p11 + p21)) c1eval = curve1.eval c2eval = curve2.eval check_param1 = curve1.check_param check_param2 = curve2.check_param a1, b1 = curve1.a, curve1.b a2, b2 = curve2.a, curve2.b tol = itol / 100. for u01, u02 in reversed(u0_list): u012 = array([u01, u02], dtype=float64) sol = minimize(_obj, u012, method='Nelder-Mead', tol=tol, options={'ftol': tol}) u1, u2 = sol.x u1 = check_param1(u1) u2 = check_param2(u2) p1 = c1eval(u1, rtype='ndarray', domain='global') p2 = c2eval(u2, rtype='ndarray', domain='global') if norm(p1 - p2) > itol: continue _add_pnt(u1, u2, 0.5 * (p1 + p2)) npts = len(results) nsub = subdivisions[0] return nsub, npts, results