//-----------------------------------------------------------------------------
// Routines to merge multiple coincident surfaces (each with their own trim
// curves) into a single surface, with all of the trim curves.
//-----------------------------------------------------------------------------
#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;

        SEdgeList sel;
        ZERO(&sel);

        bool merged = 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 != si->color) continue;
            // But we do merge surfaces with different face entities, since
            // otherwise we'd hardly ever merge anything.

            // This surface is coincident, so it gets merged.
            sj->tag = 1;
            merged = true;
            sj->MakeEdgesInto(this, &sel, false);
            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(merged) {
            si->MakeEdgesInto(this, &sel, false);
            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);
            }

            // 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);
        }
    }

    surface.RemoveTagged();
}

