//----------------------------------------------------------------------------- // Routines to merge multiple coincident surfaces (each with their own trim // curves) into a single surface, with all of the trim curves. // // Copyright 2008-2013 Jonathan Westhues. //----------------------------------------------------------------------------- #include "../solvespace.h" void SShell::MergeCoincidentSurfaces(void) { surface.ClearTags(); int i, j; SSurface *si, *sj; for(i = 0; i < surface.n; i++) { si = &(surface.elem[i]); if(si->tag) continue; // Let someone else clean up the empty surfaces; we can certainly merge // them, but we don't know how to calculate a reasonable bounding box. if(si->trim.n == 0) continue; // And for now we handle only coincident planes, so no sense wasting // time on other surfaces. if(si->degm != 1 || si->degn != 1) continue; SEdgeList sel; ZERO(&sel); si->MakeEdgesInto(this, &sel, SSurface::AS_XYZ); bool mergedThisTime, merged = false; do { mergedThisTime = false; for(j = i + 1; j < surface.n; j++) { sj = &(surface.elem[j]); if(sj->tag) continue; if(!sj->CoincidentWith(si, true)) continue; if(!sj->color.Equals(si->color)) continue; // But we do merge surfaces with different face entities, since // otherwise we'd hardly ever merge anything. // This surface is coincident. But let's not merge coincident // surfaces if they contain disjoint contours; that just makes // the bounding box tests less effective, and possibly things // less robust. SEdgeList tel; ZERO(&tel); sj->MakeEdgesInto(this, &tel, SSurface::AS_XYZ); if(!sel.ContainsEdgeFrom(&tel)) { tel.Clear(); continue; } tel.Clear(); sj->tag = 1; merged = true; mergedThisTime = true; sj->MakeEdgesInto(this, &sel, SSurface::AS_XYZ); sj->trim.Clear(); // All the references to this surface get replaced with the // new srf SCurve *sc; for(sc = curve.First(); sc; sc = curve.NextAfter(sc)) { if(sc->surfA.v == sj->h.v) sc->surfA = si->h; if(sc->surfB.v == sj->h.v) sc->surfB = si->h; } } // If this iteration merged a contour onto ours, then we have to // go through the surfaces again; that might have made a new // surface touch us. } while(mergedThisTime); if(merged) { sel.CullExtraneousEdges(); si->trim.Clear(); si->TrimFromEdgeList(&sel, false); // And we must choose control points such that all the trims lie // with u and v in [0, 1], so that the bbox tests work. Vector u, v, n; si->TangentsAt(0.5, 0.5, &u, &v); u = u.WithMagnitude(1); v = v.WithMagnitude(1); n = si->NormalAt(0.5, 0.5).WithMagnitude(1); v = (n.Cross(u)).WithMagnitude(1); double umax = VERY_NEGATIVE, umin = VERY_POSITIVE, vmax = VERY_NEGATIVE, vmin = VERY_POSITIVE; SEdge *se; for(se = sel.l.First(); se; se = sel.l.NextAfter(se)) { double ut = (se->a).Dot(u), vt = (se->a).Dot(v); umax = max(umax, ut); vmax = max(vmax, vt); umin = min(umin, ut); vmin = min(vmin, vt); } // An interesting problem here; the real curve could extend // slightly beyond the bounding box of the piecewise linear // bits. Not a problem for us, but some apps won't import STEP // in that case. So give a bit of extra room; in theory just // a chord tolerance, but more can't hurt. double muv = max((umax - umin), (vmax - vmin)); double tol = muv/50 + 3*SS.ChordTolMm(); umax += tol; vmax += tol; umin -= tol; vmin -= tol; // We move in the +v direction as v goes from 0 to 1, and in the // +u direction as u goes from 0 to 1. So our normal ends up // pointed the same direction. double nt = (si->ctrl[0][0]).Dot(n); si->ctrl[0][0] = Vector::From(umin, vmin, nt).ScaleOutOfCsys(u, v, n); si->ctrl[0][1] = Vector::From(umin, vmax, nt).ScaleOutOfCsys(u, v, n); si->ctrl[1][1] = Vector::From(umax, vmax, nt).ScaleOutOfCsys(u, v, n); si->ctrl[1][0] = Vector::From(umax, vmin, nt).ScaleOutOfCsys(u, v, n); } sel.Clear(); } surface.RemoveTagged(); }