DIRECTORY ClientStateInfo, RVHandleUtils, RCombiner, IO USING [int, rope, PutF, STREAM], Rope; RCombinerImpl: CEDAR PROGRAM IMPORTS IO, ClientStateInfo, RVHandleUtils EXPORTS RCombiner = BEGIN OPEN RVHU: RVHandleUtils, CSI: ClientStateInfo, RCombiner; in, out: PUBLIC IO.STREAM; Sig1: PUBLIC SIGNAL = CODE; -- signals for debugging Sig2: PUBLIC SIGNAL = CODE; Combiner: PUBLIC PROC[ currentw, inputw: RVHU.VertexHandle, inputState: CSI.State, stateCombiner: CSI.StateCombiner ] RETURNS [combinedw: RVHU.VertexHandle]={ intersectionPolygonv, intersectionPolygonw: RVHU.VertexHandle; [combinedw, intersectionPolygonv, intersectionPolygonw] _ CombineGeometry[currentw, inputw, TRUE]; IF intersectionPolygonv#NIL THEN { CombineState[intersectionPolygonv, intersectionPolygonw, inputState, stateCombiner]; ClearIntersectPolygonFields[intersectionPolygonv, intersectionPolygonw]; }; }; CombineGeometry: PUBLIC PROC[ currentw, inputw: RVHU.VertexHandle, setIntersectionPolygonEdges: BOOL _ TRUE] RETURNS [combinedw, intersectionPolygonv, intersectionPolygonw: RVHU.VertexHandle]={ currentv, inputv, combinedv, dummyv, dummyw: RVHU.VertexHandle; currentoutlinecom, inputfigurecom: RVHU.Point; status: RVHU.PointSectorRelation; outsideCurrentStatus: OutsideCurrentTrilean; intsideCurrentStatus: InsideCurrentTrilean; IF inputw = NIL THEN RETURN[currentw, NIL, NIL]; IF currentw = NIL THEN RETURN[inputw, NIL, NIL]; IF NOT RVHU.ThreeOrMoreVertices[currentw] OR NOT RVHU.ThreeOrMoreVertices[inputw] THEN ERROR; IF currentw.visited # inputw.visited THEN SetFigureVisitedValues[inputw, currentw.visited]; currentv _ RVHU.PreviousOutlineVertex[currentw]; inputv _ RVHU.PreviousOutlineVertex[inputw]; currentoutlinecom _ RVHU.CenterOfMass[ currentv, currentw, TRUE]; inputfigurecom _ RVHU.CenterOfMass[ inputv, inputw, FALSE]; [status, currentv, currentw] _ RVHU.FindSubtendForPoint[ currentv, currentw, currentoutlinecom, inputv.coordinates, TRUE]; combinedv _ currentv; combinedw _ currentw; -- "Cache" where on the current outline the input activity is taking place, in case the next input activity happens to occur at the same locale. I.e., if [ combinedv, combinedw] is not subsequently reassigned, we will return currentw as it has just been assigned. SELECT status FROM SUBTENDINTERIOR, RIGHTRAYINTERIOR, LEFTRAYINTERIOR => { [outsideCurrentStatus, currentv, currentw, inputv, inputw] _ InputvOutsideCurrentOutline[ currentoutlinecom, currentv, currentw, inputv, inputw]; SELECT outsideCurrentStatus FROM INTERSECTSOUTLINE => [ combinedv, combinedw, intersectionPolygonv, intersectionPolygonw ] _ InputIntersectsCurrent[ EXTERIORTOINTERIOR, currentv, currentw, inputv, inputw, TRUE]; EXTERNAL => { [ combinedv, combinedw ] _ InputExternalToCurrent[ currentv, currentw, inputv, inputw, currentoutlinecom, inputfigurecom]; intersectionPolygonv _ intersectionPolygonw _ NIL; }; ENCLOSES => { v, w, firstv: RVHU.VertexHandle; vTow, wTov: RVHU.AdjacencyHandle; outline: BOOL _ TRUE; inputPolygonState: CSI.State; [ vTow, wTov ] _ RVHU.FindAdjacency[ inputv, inputw ]; inputPolygonState _ vTow.leftState; v _ firstv _ currentv; w _ currentw; [ vTow, wTov ] _ RVHU.FindAdjacency[ v, w ]; vTow.intersectionPolygonEdge _ TRUE; wTov.leftRegionExterior _ FALSE; wTov.leftState _ inputPolygonState; WHILE w # firstv DO [v, w] _ RVHU.StepVertex[v,w, outline]; [ vTow, wTov ] _ RVHU.FindAdjacency[ v, w ]; vTow.intersectionPolygonEdge _ TRUE; wTov.leftRegionExterior _ FALSE; wTov.leftState _ inputPolygonState; ENDLOOP; PolygonEnclosesPolygon[currentoutlinecom, currentv, currentw, inputv, inputw ]; combinedv _ inputv; combinedw _ inputw; intersectionPolygonv _ currentv; intersectionPolygonw _ currentw; }; ENDCASE => ERROR }; SECTORINTERIOR, RIGHTSPOKEINTERIOR, LEFTSPOKEINTERIOR => { [intsideCurrentStatus, currentv, currentw, inputv, inputw] _ InputvInsideCurrentOutline[ currentoutlinecom, currentv, currentw, inputv, inputw]; SELECT intsideCurrentStatus FROM INTERSECTSOUTLINE => [ combinedv, combinedw, intersectionPolygonv, intersectionPolygonw ] _ InputIntersectsCurrent[INTERIORTOEXTERIOR, currentv, currentw, inputv, inputw, TRUE]; INSINGLEPOLYGON => { v, w, firstv: RVHU.VertexHandle; vTow, wTov: RVHU.AdjacencyHandle; outline: BOOL _ TRUE; currentPolygonState: CSI.State; [ vTow, wTov ] _ RVHU.FindAdjacency[ currentv, currentw ]; currentPolygonState _ vTow.leftState; RVHU.SetPolygonState[inputv, inputw, currentPolygonState]; v _ firstv _ inputv; w _ inputw; [ vTow, wTov ] _ RVHU.FindAdjacency[ v, w ]; vTow.intersectionPolygonEdge _ TRUE; wTov.leftRegionExterior _ FALSE; wTov.leftState _ currentPolygonState; WHILE w # firstv DO [v, w] _ RVHU.StepVertex[v,w, outline]; [ vTow, wTov ] _ RVHU.FindAdjacency[ v, w ]; vTow.intersectionPolygonEdge _ TRUE; wTov.leftRegionExterior _ FALSE; wTov.leftState _ currentPolygonState; ENDLOOP; PolygonEnclosesPolygon[inputfigurecom, inputv, inputw, currentv, currentw]; intersectionPolygonv _ inputv; intersectionPolygonw _ inputw; -- leave combinedv _ currentv, combinedw _ currentw }; INMULTIPLEPOLYGONS => [dummyv, dummyw, intersectionPolygonv, intersectionPolygonw] _ InputIntersectsCurrent[ INTERIORTOINTERIOR, currentv, currentw, inputv, inputw, TRUE]; -- Don't change settting of [combinedv, combinedw] _ [currentv, currentw] in this case, since current outline didn't change ENDCASE => ERROR }; ENDCASE => ERROR; -- EQUALCOM, EQUALRIGHTVERTEX, EQUALLEFTVERTEX, POLYGONEDGEINTERIOR not currently handled 9/19 RETURN [combinedw, intersectionPolygonv, intersectionPolygonw] }; CombineState: PUBLIC PROC[intersectionPolygonv, intersectionPolygonw: RVHU.VertexHandle, inputState: CSI.State, stateCombiner: CSI.StateCombiner] = { polygonState: CSI.State; v: RVHU.VertexHandle _ intersectionPolygonv; w: RVHU.VertexHandle _ intersectionPolygonw; firstv: RVHU.VertexHandle _ v; vTow, wTov: RVHU.AdjacencyHandle; outline: BOOL _ FALSE; stateRope: Rope.ROPE; [ vTow, wTov ] _ RVHU.FindAdjacency[ v, w ]; polygonState _ stateCombiner[ vTow.leftState, inputState ]; stateRope _ CSI.RopeFromState[ polygonState ]; out.PutF["\nCombineState entered with argument [#%g , #%g ], state %g \n", IO.int[v.index], IO.int[w.index], IO.rope[stateRope]]; vTow.leftState _ polygonState; vTow.leftRegionVisited _ TRUE; WHILE w # firstv DO [v, w] _ RVHU.StepVertex[v,w, outline]; [ vTow, wTov ] _ RVHU.FindAdjacency[ v, w ]; vTow.leftState _ polygonState; vTow.leftRegionVisited _ TRUE; ENDLOOP; [v, w] _ RVHU.StepVertex[v,w, outline]; WHILE w # firstv DO [v, w] _ RVHU.StepVertex[v,w, outline]; [ vTow, wTov ] _ RVHU.FindAdjacency[ v, w ]; IF NOT vTow.intersectionPolygonEdge THEN IF NOT wTov.leftRegionVisited THEN CombineState[w, v, inputState, stateCombiner ]; ENDLOOP; }; InputvOutsideCurrentOutline: PUBLIC PROC[ currentfigurecom: RVHU.Point, v1, w1, v2, w2: RVHU.VertexHandle ] RETURNS [outsideCurrentStatus: OutsideCurrentTrilean, v3, w3, v4, w4: RVHU.VertexHandle] = { external: BOOL _ FALSE; v1first: RVHU.VertexHandle _ v1; v2first: RVHU.VertexHandle _ v2; done: BOOL _ FALSE; pointSectorRelation: RVHU.PointSectorRelation; outcome: RVHU.DirectedEdgeIntersectionStatus; value: RVHU.DirectedEdgeIntersectionStatusValue; intPoint: RVHU.Point; pointSectorRelation _ RVHU.PointSectorTest[ v1.coordinates, w1.coordinates, currentfigurecom, w2.coordinates ]; WHILE NOT done DO SELECT pointSectorRelation FROM RIGHTRAYINTERIOR, LEFTRAYINTERIOR, SUBTENDINTERIOR => NULL; ENDCASE => { [ outcome, intPoint] _ RVHU.IntersectDirectedEdges[ v1.coordinates, w1.coordinates, v2.coordinates, w2.coordinates ]; value _ outcome.first; SELECT value FROM PROPERINTERSECT => RETURN[ INTERSECTSOUTLINE, v1, w1, v2, w2]; DISJOINT => NULL; ENDCASE => ERROR; -- only cases handled 9/19 WHILE pointSectorRelation = RIGHTOFSUBTEND OR pointSectorRelation = LEFTOFSUBTEND DO [v1, w1] _ RVHU.StepVertex[ v1, w1, TRUE ]; -- step current outline to catch up with input polygon edge [ outcome, intPoint] _ RVHU.IntersectDirectedEdges[ v1.coordinates, w1.coordinates, v2.coordinates, w2.coordinates ]; -- check for intersection with input polygon edge as we pass over each current structure edge value _ outcome.first; SELECT value FROM PROPERINTERSECT => RETURN[ INTERSECTSOUTLINE, v1, w1, v2, w2]; DISJOINT => NULL; ENDCASE => ERROR; -- only cases handled 9/19 pointSectorRelation _ RVHU.PointSectorTest[ v1.coordinates, w1.coordinates, currentfigurecom, w2.coordinates ]; ENDLOOP; }; IF RVHU.PointLineTest[ v2.coordinates, w2.coordinates, v1first.coordinates] = RIGHTOFLINE THEN external _ TRUE; -- if current figure is external to input figure, then any vertex of the current figure (we arbitrarily picked v1first) must lie 'external' to at least one edge of the input figure. This test will eventually uncover such an input edge if one exists. [v2,w2] _ RVHU.StepVertex[ v2, w2, TRUE ]; -- move to next edge of input polygon IF v2 = v2first THEN done _ TRUE ELSE pointSectorRelation _ RVHU.PointSectorTest[ v1.coordinates, w1.coordinates, currentfigurecom, w2.coordinates ]; -- check if have walked completely around input polygon ENDLOOP; IF external THEN RETURN[ EXTERNAL, v1, w1, v2, w2 ] ELSE RETURN [ ENCLOSES, v1, w1, v2, w2 ]; }; InputvInsideCurrentOutline: PUBLIC PROC[ currentfigurecom: RVHU.Point, v1, w1, v2, w2: RVHU.VertexHandle ] RETURNS [intsideCurrentStatus: InsideCurrentTrilean, v3, w3, v4, w4: RVHU.VertexHandle ] = { v1first: RVHU.VertexHandle _ v1; w1first: RVHU.VertexHandle _ w1; v2first: RVHU.VertexHandle _ v2; polygoncom: RVHU.Point; status: RVHU.PointSectorRelation; pointSectorRelation: RVHU.PointSectorRelation; v5, w5, temp: RVHU.VertexHandle; done: BOOL _ FALSE; outcome: RVHU.DirectedEdgeIntersectionStatus; value: RVHU.DirectedEdgeIntersectionStatusValue; intPoint: RVHU.Point; pointSectorRelation _ RVHU.PointSectorTest[ v1.coordinates, w1.coordinates, currentfigurecom, w2.coordinates ]; WHILE NOT done DO SELECT pointSectorRelation FROM RIGHTSPOKEINTERIOR, LEFTSPOKEINTERIOR, SECTORINTERIOR => NULL; RIGHTRAYINTERIOR, LEFTRAYINTERIOR, SUBTENDINTERIOR, RIGHTOFSUBTEND, LEFTOFSUBTEND => { [ outcome, intPoint] _ RVHU.IntersectDirectedEdges[ v1.coordinates, w1.coordinates, v2.coordinates, w2.coordinates ]; value _ outcome.first; SELECT value FROM PROPERINTERSECT => RETURN[ INTERSECTSOUTLINE, v1, w1, v2, w2]; DISJOINT => NULL; ENDCASE => ERROR; -- only cases handled 9/19 WHILE pointSectorRelation = RIGHTOFSUBTEND OR pointSectorRelation = LEFTOFSUBTEND DO [v1, w1] _ RVHU.StepVertex[ v1, w1, TRUE ]; -- step current outline to catch up with input polygon edge [ outcome, intPoint] _ RVHU.IntersectDirectedEdges[ v1.coordinates, w1.coordinates, v2.coordinates, w2.coordinates ]; -- check for intersection with input polygon edge as we pass over each current structure edge value _ outcome.first; SELECT value FROM PROPERINTERSECT => RETURN[ INTERSECTSOUTLINE, v1, w1, v2, w2]; DISJOINT => NULL; ENDCASE => ERROR; -- only cases handled 9/19 pointSectorRelation _ RVHU.PointSectorTest[ v1.coordinates, w1.coordinates, currentfigurecom, w2.coordinates ]; ENDLOOP; }; ENDCASE => ERROR; -- other cases not handled 9/19 [v2,w2] _ RVHU.StepVertex[ v2, w2, TRUE ]; IF v2 = v2first THEN done _ TRUE ELSE pointSectorRelation _ RVHU.PointSectorTest[ v1.coordinates, w1.coordinates, currentfigurecom, w2.coordinates ]; ENDLOOP; v1 _ v1first; -- reinitialize our position on the current figure w1 _ w1first; polygoncom _ RVHU.CenterOfMass[ v1, w1, FALSE]; -- find com of internal polygon. [status, v5, w5] _ RVHU.FindSubtendForPoint[ v1, w1, polygoncom, v2.coordinates, FALSE ]; -- [v5,w5] defines a particular sector of this polygon WHILE status = RIGHTRAYINTERIOR OR status = LEFTRAYINTERIOR OR status = SUBTENDINTERIOR DO -- flip flop polygons until you find the one that contains v2 temp _ v5; v5 _ w5; w5 _ temp; -- flip v5 and w5 to move to next polygon polygoncom _ RVHU.CenterOfMass[ v5, w5, FALSE]; [status, v5, w5] _ RVHU.FindSubtendForPoint[ v5, w5, polygoncom, v2.coordinates, FALSE ]; ENDLOOP; SELECT status FROM RIGHTSPOKEINTERIOR, LEFTSPOKEINTERIOR, SECTORINTERIOR => NULL; ENDCASE => ERROR; -- other cases not handled 9/19 done _ FALSE; pointSectorRelation _ RVHU.PointSectorTest[ v5.coordinates, w5.coordinates, currentfigurecom, w2.coordinates ]; WHILE NOT done DO SELECT pointSectorRelation FROM EQUALCOM, RIGHTSPOKEINTERIOR, LEFTSPOKEINTERIOR, SECTORINTERIOR => NULL; RIGHTRAYINTERIOR, LEFTRAYINTERIOR, SUBTENDINTERIOR, RIGHTOFSUBTEND, LEFTOFSUBTEND => { [ outcome, intPoint] _ RVHU.IntersectDirectedEdges[ v5.coordinates, w5.coordinates, v2.coordinates, w2.coordinates ]; value _ outcome.first; SELECT value FROM PROPERINTERSECT => RETURN[ INMULTIPLEPOLYGONS, v5, w5, v2, w2]; DISJOINT => NULL; ENDCASE => ERROR; WHILE pointSectorRelation = RIGHTOFSUBTEND OR pointSectorRelation = LEFTOFSUBTEND DO [v5, w5] _ RVHU.StepVertex[ v5, w5, FALSE ]; [ outcome, intPoint] _ RVHU.IntersectDirectedEdges[ v5.coordinates, w5.coordinates, v2.coordinates, w2.coordinates ]; value _ outcome.first; SELECT value FROM PROPERINTERSECT => RETURN[ INMULTIPLEPOLYGONS, v5, w5, v2, w2]; DISJOINT => NULL; ENDCASE => ERROR; pointSectorRelation _ RVHU.PointSectorTest[ v5.coordinates, w5.coordinates, currentfigurecom, w2.coordinates ]; ENDLOOP; }; ENDCASE => ERROR; -- other cases not handled 9/19 [v2, w2] _ RVHU.StepVertex[ v2, w2, TRUE ]; IF v2 = v2first THEN done _ TRUE ELSE pointSectorRelation _ RVHU.PointSectorTest[ v5.coordinates, w5.coordinates, currentfigurecom, w2.coordinates ]; ENDLOOP; RETURN [INSINGLEPOLYGON, v5, w5, v2, w2]; -- As a result of the last RVHU.StepVertex of the just-finished loop, v2 is now in the sector determined by [v5,w5]. }; InputIntersectsCurrent: PUBLIC PROC[ lensTransitionType: LensTransitionType, currentv, currentw, inputv, inputw: RVHU.VertexHandle, setIntersectionPolygonEdges: BOOL _ TRUE] RETURNS [voutline, woutline: RVHU.VertexHandle _ NIL, intersectionPolygonv, intersectionPolygonw: RVHU.VertexHandle] = { pj1, pj, qi1, qi: RVHU.VertexHandle; v, firstv, temp : RVHU.VertexHandle; returnedToFirstv: BOOL _ FALSE; lensInExterior, nextLensInExterior: BOOL; qi1Tov, vToqi1: RVHU.AdjacencyHandle; IF lensTransitionType = EXTERIORTOINTERIOR THEN { pj1 _ currentv; pj _ currentw; qi1 _ inputv; qi _ inputw; lensInExterior _ TRUE } ELSE IF lensTransitionType = INTERIORTOEXTERIOR THEN { pj1 _ inputv; pj _ inputw; qi1 _ currentv; qi _ currentw; lensInExterior _ FALSE } ELSE { -- lensTransitionType = INTERIORTOINTERIOR pj1 _ inputv; pj _ inputw; qi1 _ currentv; qi _ currentw; lensInExterior _ FALSE }; v _ firstv _ InsertIntersectionPoint[pj1, pj, qi1, qi]; WHILE NOT returnedToFirstv DO IF lensInExterior THEN { -- lensTransitionType = EXTERIORTOINTERIOR lensTransitionType _ EXTERIORTOINTERIOR; -- set it [voutline, woutline] _ GrowToConvexOutline[ qi1, v, pj ]; temp _ pj; pj _ qi; qi _ temp; nextLensInExterior _ FALSE; } ELSE { [ qi1Tov, vToqi1 ] _ RVHU.FindAdjacency[ qi1, v ]; -- compute nextLensInExterior nextLensInExterior _ vToqi1.leftRegionExterior; IF nextLensInExterior THEN { -- lensTransitionType = INTERIORTOEXTERIOR lensTransitionType _ INTERIORTOEXTERIOR; [voutline, woutline] _ GrowToConvexOutline[ qi1, v, pj]; temp _ pj; pj _ qi; qi _ temp; } ELSE { -- lensTransitionType = INTERIORTOINTERIOR lensTransitionType _ INTERIORTOINTERIOR; qi _ qi1; }; }; lensInExterior _ nextLensInExterior; [returnedToFirstv, v, pj1, pj, qi1, qi ] _ LensDecomposition[ firstv, v, pj, qi, lensInExterior, setIntersectionPolygonEdges]; ENDLOOP; RETURN[ voutline, woutline, pj1, pj]; }; InputExternalToCurrent: PUBLIC PROC[ currentv, currentw, inputv, inputw: RVHU.VertexHandle, currentoutlinecom, inputfigurecom: RVHU.Point] RETURNS [combinedv, combinedw: RVHU.VertexHandle] = { previousInv, previousCurv: RVHU.VertexHandle; forwardCurw: RVHU.VertexHandle; pointLineRelation: RVHU.PointLineRelation; status: RVHU.PointSectorRelation; [status, inputv, inputw] _ RVHU.FindSubtendForPoint[ inputv, inputw, inputfigurecom, currentoutlinecom, TRUE]; [status, currentv, currentw] _ RVHU.FindSubtendForPoint[ currentv, currentw, currentoutlinecom, inputv.coordinates, TRUE]; previousCurv _ RVHU.NewPrevOutlineVertex[currentv, currentw]; JoinVerticesOnOutline[previousCurv, currentv, inputv, inputw, FALSE]; -- triangle not yet formed, so call with setLRE = FALSE out.PutF["\nInputExternalToCurrent has joined vertices #%g and #%g\n", IO.int[currentv.index], IO.int[inputv.index]]; previousInv _ RVHU.PreviousVertex[inputv, inputw, FALSE]; -- can't do PreviousOutlineVertex because of new edge from currentv to inputv forwardCurw _ RVHU.NextOutlineVertex[currentv, currentw]; -- NextOutlineVertex will still work because edge from inputv to currentw not yet added JoinVerticesOnOutline[previousInv, inputv, currentw, forwardCurw]; -- now triangle formed, so set LRE fields of its inward-facing edges. out.PutF["\nInputExternalToCurrent has joined vertices #%g and #%g\n", IO.int[inputv.index], IO.int[currentw.index]]; pointLineRelation _ RVHU.PointLineTest[previousInv.coordinates, inputv.coordinates, currentw.coordinates]; IF pointLineRelation = RIGHTOFLINE THEN [combinedv, combinedw] _ GrowToConvexOutline[ previousInv, inputv, currentw] ELSE { pointLineRelation _ RVHU.PointLineTest[inputv.coordinates, currentw.coordinates, forwardCurw.coordinates]; IF pointLineRelation = RIGHTOFLINE THEN [combinedv, combinedw] _ GrowToConvexOutline[ inputv, currentw, forwardCurw] }; pointLineRelation _ RVHU.PointLineTest[previousCurv.coordinates, currentv.coordinates, inputv.coordinates]; IF pointLineRelation = RIGHTOFLINE THEN [combinedv, combinedw] _ GrowToConvexOutline[ previousCurv, currentv, inputv] ELSE { pointLineRelation _ RVHU.PointLineTest[currentv.coordinates, inputv.coordinates, inputw.coordinates]; IF pointLineRelation = RIGHTOFLINE THEN [combinedv, combinedw] _ GrowToConvexOutline[ currentv, inputv, inputw] }; RETURN [combinedv, combinedw] }; PolygonEnclosesPolygon: PUBLIC PROC[ innercom: RVHU.Point, innerv, innerw, outerv, outerw: RVHU.VertexHandle] = { status: RVHU.PointSectorRelation; returnedToFirstv: BOOL _ FALSE; nextInner, prevInner, thisInner: RVHU.VertexHandle; vending, pj1ending, pjending, qi1ending, qiending: RVHU.VertexHandle; pointLineRelation: RVHU.PointLineRelation; [status, innerv, innerw] _ RVHU.FindSubtendForPoint[ innerv, innerw, innercom, outerv.coordinates, FALSE]; out.PutF["\nEnter PolygonEnclosesPolygon\n"]; nextInner _ innerw; thisInner _ innerv; prevInner _ RVHU.NewPrevOutlineVertex[thisInner, nextInner]; pointLineRelation _ RVHU.PointLineTest[outerv.coordinates, thisInner.coordinates, prevInner.coordinates]; WHILE pointLineRelation = LEFTOFLINE DO nextInner _ thisInner; thisInner _ prevInner; prevInner _ RVHU.NewPrevOutlineVertex[thisInner, nextInner]; pointLineRelation _ RVHU.PointLineTest[outerv.coordinates, thisInner.coordinates, prevInner.coordinates]; ENDLOOP; JoinVerticesInLens[thisInner, nextInner, outerv, outerw]; pointLineRelation _ RVHU.PointLineTest[outerv.coordinates, thisInner.coordinates, nextInner.coordinates]; WHILE pointLineRelation = RIGHTOFLINE DO prevInner _ thisInner; [thisInner, nextInner] _ RVHU.StepVertex[thisInner, nextInner, TRUE]; -- outline step, since inner figure could be current figure JoinVerticesInLens[thisInner, nextInner, outerv, outerw]; pointLineRelation _ RVHU.PointLineTest[outerv.coordinates, thisInner.coordinates, nextInner.coordinates]; ENDLOOP; [returnedToFirstv, vending, pj1ending, pjending, qi1ending, qiending ] _ LensDecomposition[ outerv, outerv, thisInner, outerw, TRUE, FALSE]; -- lensInExterior = TRUE is the right thing, to get outline stepping of the inner figure. setIntersectionPolygonEdges = FALSE, since we assume already done by caller, and presence of cone makes it tricky in any case. IF NOT returnedToFirstv THEN ERROR; RETURN }; LensDecomposition: PUBLIC PROC[ firstv, v, pj, qi: RVHU.VertexHandle, lensInExterior: BOOL, setIntersectionPolygonEdges: BOOL _ TRUE] RETURNS [ returnedToFirstv: BOOL, vending, pj1ending, pjending, qi1ending, qiending: RVHU.VertexHandle] = { pj1, qi1, qip1, pjp1, intersection: RVHU.VertexHandle; pj1Topj, pjTopj1, qi1Toqi, qiToqi1: RVHU.AdjacencyHandle; pj1Toint, intTopj1: RVHU.AdjacencyHandle; ReachedNextIntersection: BOOL _ FALSE; polygonStepping: BOOL _ FALSE; outcome: RVHU.DirectedEdgeIntersectionStatus; value: RVHU.DirectedEdgeIntersectionStatusValue; intPoint: RVHU.Point; pointLineRelation: RVHU.PointLineRelation; lensState: CSI.State; pj1 _ qi1 _ v; returnedToFirstv _ FALSE; [qi1Toqi, qiToqi1] _ RVHU.FindAdjacency[ qi1, qi ]; lensState _ qi1Toqi.leftState; -- state of lens is (internal) state of outer polygon qip1 _ RVHU.NextVertex[qi1, qi, polygonStepping]; -- qip1 will be used for avoiding calls to RVHU.NextVertex and RVHU.StepVertex following insertion of new edges (would go off and follow wrong polygon) pjp1 _ RVHU.NextVertex[pj1, pj, lensInExterior]; -- same as for qip1 WHILE (NOT ReachedNextIntersection) AND (NOT returnedToFirstv) DO pointLineRelation _ RVHU.PointLineTest[pj1.coordinates, pj.coordinates, qip1.coordinates]; WHILE pointLineRelation # LEFTOFLINE AND qip1 # pj DO -- ( qip1 # pj ) needed for last lens when we are going to be returnedToFirstv IF pointLineRelation = ONLINE THEN ERROR; -- not yet handled qi1 _ qi; [qi, qip1] _ RVHU.StepVertex[ qi, qip1, polygonStepping]; pointLineRelation _ RVHU.PointLineTest[pj1.coordinates, pj.coordinates, qip1.coordinates]; ENDLOOP; IF pj = firstv AND qip1 = firstv THEN returnedToFirstv _ TRUE ELSE { [ outcome, intPoint] _ RVHU.IntersectDirectedEdges[ pj1.coordinates, pj.coordinates, qi.coordinates, qip1.coordinates ]; value _ outcome.first; SELECT value FROM PROPERINTERSECT => ReachedNextIntersection _ TRUE; DISJOINT => NULL; ENDCASE => ERROR; }; IF (NOT ReachedNextIntersection) AND (NOT returnedToFirstv) THEN { JoinVerticesInLens[ pj, pjp1, qi, qip1 ]; [pj1Topj, pjTopj1] _ RVHU.FindAdjacency[ pj1, pj ]; pjTopj1.leftRegionExterior _ FALSE; pjTopj1.leftState _ lensState; IF setIntersectionPolygonEdges THEN { pj1Topj.intersectionPolygonEdge _ TRUE; -- note that here we set a field of pj1Topj, whereas in previous two assignments we set fields of pjTopj1. out.PutF["\nLensDecomposition marks edge [ #%g , #%g ] on intersection polygon\n", IO.int[pj1.index], IO.int[pj.index]]; -- verbosity }; pj1 _ pj; [pj, pjp1] _ RVHU.StepVertex[ pj, pjp1, lensInExterior ]; pointLineRelation _ RVHU.PointLineTest[pj1.coordinates, pj.coordinates, qi.coordinates]; IF pointLineRelation = LEFTOFLINE THEN { qi1 _ qi; [qi, qip1] _ RVHU.StepVertex[ qi, qip1, polygonStepping]; JoinVerticesInLens[ pj1, pj, qi, qip1 ]; -- Note use of pj1 and pj instead of pj and pjp1, since we want an edge to the same vertex of inner polygon, but we've done a StepVertex }; }; ENDLOOP; IF ReachedNextIntersection THEN { intersection _ InsertIntersectionPoint[pj1, pj, qi, qip1]; [pj1Toint, intTopj1] _ RVHU.FindAdjacency[ pj1, intersection ]; intTopj1.leftRegionExterior _ FALSE; intTopj1.leftState _ lensState; IF setIntersectionPolygonEdges THEN { pj1Toint.intersectionPolygonEdge _ TRUE; out.PutF["\nLensDecomposition marks edge [ #%g , #%g ] on intersection polygon\n", IO.int[pj1.index], IO.int[intersection.index]]; -- verbosity }; RETURN [returnedToFirstv, intersection, pj1, pj, qi, qip1]; } ELSE { -- returnedToFirstv [pj1Topj, pjTopj1] _ RVHU.FindAdjacency[ pj1, pj ]; -- pj = firstv pjTopj1.leftRegionExterior _ FALSE; pjTopj1.leftState _ lensState; IF setIntersectionPolygonEdges THEN { pj1Topj.intersectionPolygonEdge _ TRUE; out.PutF["\nLensDecomposition marks edge [ #%g , #%g ] on intersection polygon\n", IO.int[pj1.index], IO.int[pj.index]]; -- verbosity }; RETURN [returnedToFirstv, firstv, pj1, pj, qi, qip1]; -- note that pj = qip1 = firstv }; }; GrowToConvexOutline: PUBLIC PROC[w2, v, w1: RVHU.VertexHandle, outline: BOOL _ TRUE] RETURNS [v3,w3: RVHU.VertexHandle] = { ok: BOOL _ FALSE; prew1, forw1, prew2, forw2: RVHU.VertexHandle; -- in the counterclockwise order on the outline (with respect to interior of figure), we have (prew2, w2, forw2, prew1, w1, forw1) pointLineRelation: RVHU.PointLineRelation; prew2 _ RVHU.PreviousVertex[w2, v, outline]; forw2 _ v; prew1 _ v; forw1 _ RVHU.NextVertex[v, w1, outline]; WHILE NOT ok DO ok _ TRUE; pointLineRelation _ RVHU.PointLineTest[w1.coordinates, w2.coordinates, prew2.coordinates]; IF pointLineRelation = ONLINE THEN ERROR; -- not yet handled WHILE pointLineRelation = LEFTOFLINE DO ok _ FALSE; IF NOT RVHU.ConvexPolygonOnOutline[ prew2, w2, prew1, w1, outline] THEN { JoinVerticesOnOutline[prew2, w2, w1, forw1]; prew1 _ w2; -- take account of presence of new edge joining w1 and w2 out.PutF["\nGrowToConvexOutline has joined vertices #%g and #%g\n", IO.int[w2.index], IO.int[w1.index]]; }; forw2 _ w2; w2 _ prew2; prew2 _ RVHU.PreviousVertex[w2, forw2, outline]; pointLineRelation _ RVHU.PointLineTest[w1.coordinates, w2.coordinates, prew2.coordinates]; IF pointLineRelation = ONLINE THEN ERROR; -- not yet handled ENDLOOP; pointLineRelation _ RVHU.PointLineTest[forw1.coordinates, w1.coordinates, w2.coordinates]; IF pointLineRelation = ONLINE THEN ERROR; -- not yet handled WHILE pointLineRelation = LEFTOFLINE DO ok _ FALSE; IF NOT RVHU.ConvexPolygonOnOutline[ w2, forw2, w1, forw1, outline] THEN { JoinVerticesOnOutline[ prew2, w2, w1, forw1]; forw2 _ w1; -- take account of presence of new edge joining w1 and w2 out.PutF["\nGrowToConvexOutline has joined vertices #%g and #%g\n", IO.int[w2.index], IO.int[w1.index]]; }; prew1 _ w1; [w1, forw1] _ RVHU.StepVertex[w1, forw1, outline]; pointLineRelation _ RVHU.PointLineTest[forw1.coordinates, w1.coordinates, w2.coordinates]; IF pointLineRelation = ONLINE THEN ERROR; -- not yet handled ENDLOOP; ENDLOOP; JoinVerticesOnOutline[prew2, w2, w1, forw1]; -- no need to update forw2 or prew1 since this is the last edge that will be added out.PutF["\nGrowToConvexOutline has joined vertices #%g and #%g\n", IO.int[w2.index], IO.int[w1.index]]; RETURN [w2, w1] }; InsertIntersectionPoint: PUBLIC PROC[ pj1, pj, qi, qip1: RVHU.VertexHandle] RETURNS [ vh: RVHU.VertexHandle] = { ah: RVHU.AdjacencyHandle; ahlsave: RVHU.AdjacencyHandleList; outcome: RVHU.DirectedEdgeIntersectionStatus; intPoint: RVHU.Point; pj1Topj, pjTopj1, qiToqip1, qip1Toqi: RVHU.AdjacencyHandle; [outcome, intPoint] _ RVHU.IntersectDirectedEdges[ pj1.coordinates, pj.coordinates, qi.coordinates, qip1.coordinates ]; IF outcome.first # PROPERINTERSECT THEN ERROR; vh _ NEW[ RVHU.Vertex _ [coordinates: intPoint, index: RVHU.vertexIndex, adjacentVertices: NIL, visited: pj.visited] ]; RVHU.vertexIndex _ RVHU.vertexIndex + 1; [pj1Topj, pjTopj1] _ RVHU.FindAdjacency[ pj1, pj]; [qiToqip1, qip1Toqi] _ RVHU.FindAdjacency[ qi, qip1]; pj1Topj.vertex _ vh; pjTopj1.vertex _ vh; qiToqip1.vertex _ vh; qip1Toqi.vertex _ vh; ah _ NEW[ RVHU.Adjacency _ [vertex: pj, leftState: pj1Topj.leftState, leftRegionVisited : pj1Topj.leftRegionVisited, leftRegionExterior: pj1Topj.leftRegionExterior, intersectionPolygonEdge: pj1Topj.intersectionPolygonEdge] ]; vh.adjacentVertices _ CONS[ ah, vh.adjacentVertices ]; ahlsave _ vh.adjacentVertices; -- save pointer to last list cell for making circular list ah _ NEW[ RVHU.Adjacency _ [vertex: qip1, leftState: qiToqip1.leftState, leftRegionVisited : qiToqip1.leftRegionVisited, leftRegionExterior : qiToqip1.leftRegionExterior, intersectionPolygonEdge : qiToqip1.intersectionPolygonEdge] ]; vh.adjacentVertices _ CONS[ ah, vh.adjacentVertices ]; ah _ NEW[ RVHU.Adjacency _ [vertex: pj1, leftState: pjTopj1.leftState, leftRegionVisited : pjTopj1.leftRegionVisited, leftRegionExterior : pjTopj1.leftRegionExterior, intersectionPolygonEdge : pjTopj1.intersectionPolygonEdge] ]; vh.adjacentVertices _ CONS[ ah, vh.adjacentVertices ]; ah _ NEW[ RVHU.Adjacency _ [vertex: qi, leftState: qip1Toqi.leftState, leftRegionVisited : qip1Toqi.leftRegionVisited, leftRegionExterior : qip1Toqi.leftRegionExterior, intersectionPolygonEdge : qip1Toqi.intersectionPolygonEdge] ]; vh.adjacentVertices _ CONS[ ah, vh.adjacentVertices ]; ahlsave.rest _ vh.adjacentVertices; -- make list of adjacencies circular out.PutF["\nInsertIntersectionPoint has inserted vertex #%g.\n", IO.int[vh.index]]; RETURN [vh]; }; JoinVerticesOnOutline: PUBLIC PROC[ prew2, w2, w1, forw1: RVHU.VertexHandle, setLRE: BOOL _ TRUE] = { ahl, ahltrail, ahltemp: RVHU.AdjacencyHandleList; w2Toprew2, prew2Tow2, w1Toforw1, forw1Tow1, ah: RVHU.AdjacencyHandle; [prew2Tow2, w2Toprew2] _ RVHU.FindAdjacency[ prew2, w2]; [w1Toforw1, forw1Tow1] _ RVHU.FindAdjacency[ w1, forw1]; ah _ NEW[ RVHU.Adjacency _ [vertex: w2, leftState: NIL, -- we are in the exterior of the outline, so there is no state leftRegionVisited : prew2Tow2.leftRegionVisited, leftRegionExterior : TRUE, intersectionPolygonEdge : FALSE ] ]; ahl _ w1.adjacentVertices; ahltrail _ ahl; ahl _ ahl.rest; WHILE ahltrail.first.vertex # forw1 DO ahltrail _ ahl; ahl _ ahl.rest; ENDLOOP; ahltemp _ CONS[ah, ahl]; ahltrail.rest _ ahltemp; ah _ NEW[ RVHU.Adjacency _ [vertex: w1, leftState: NIL, -- we are in the exterior of the outline, so there is no state leftRegionVisited : prew2Tow2.leftRegionVisited, leftRegionExterior : FALSE, intersectionPolygonEdge : FALSE ] ]; ahl _ w2.adjacentVertices; ahltrail _ ahl; ahl _ ahl.rest; WHILE ahl.first.vertex # prew2 DO ahltrail _ ahl; ahl _ ahl.rest; ENDLOOP; ahltemp _ CONS[ah, ahl]; ahltrail.rest _ ahltemp; IF setLRE THEN RVHU.SetPolygonState[w2, w1, NIL, TRUE]; }; JoinVerticesInLens: PUBLIC PROC[ pj1, pj, qi1, qi: RVHU.VertexHandle] = { ahl, ahltrail, ahltemp: RVHU.AdjacencyHandleList; qi1Toqi, qiToqi1, ah: RVHU.AdjacencyHandle; [qi1Toqi, qiToqi1] _ RVHU.FindAdjacency[ qi1, qi]; ah _ NEW[ RVHU.Adjacency _ [vertex: qi1, leftState: qi1Toqi.leftState, leftRegionVisited : qi1Toqi.leftRegionVisited, leftRegionExterior : FALSE, intersectionPolygonEdge : FALSE ] ]; ahl _ pj1.adjacentVertices; ahltrail _ ahl; ahl _ ahl.rest; WHILE ahltrail.first.vertex # pj DO ahltrail _ ahl; ahl _ ahl.rest; ENDLOOP; ahltemp _ CONS[ah, ahl]; ahltrail.rest _ ahltemp; ah _ NEW[ RVHU.Adjacency _ [vertex: pj1, leftState: qi1Toqi.leftState, leftRegionVisited : qi1Toqi.leftRegionVisited, leftRegionExterior : FALSE, intersectionPolygonEdge : FALSE ] ]; ahl _ qi1.adjacentVertices; ahltrail _ ahl; ahl _ ahl.rest; WHILE ahl.first.vertex # qi DO ahltrail _ ahl; ahl _ ahl.rest; ENDLOOP; ahltemp _ CONS[ah, ahl]; ahltrail.rest _ ahltemp; out.PutF["\nJoinVerticesInLens has joined vertices #%g and #%g\n", IO.int[pj1.index], IO.int[qi1.index]]; }; SetFigureVisitedValues: PUBLIC PROC[v: RVHU.VertexHandle, visitedValue: BOOLEAN] = { IF v = NIL THEN RETURN; -- no vertices IF v.adjacentVertices = NIL THEN { v.visited _ visitedValue; RETURN }; -- one vertex SetFigureVisitedValuesSubr[v, visitedValue ]; -- two or more vertices RETURN; }; SetFigureVisitedValuesSubr: PROC[v: RVHU.VertexHandle, visitedValue: BOOLEAN] = { vAdjList: RVHU.AdjacencyHandleList _ v.adjacentVertices; nextAdjacency: RVHU.AdjacencyHandle; lastVertex, nextVertex: RVHU.VertexHandle; done: BOOL _ FALSE; v.visited _ visitedValue; -- record that have visited this vertex lastVertex _ vAdjList.first.vertex; vAdjList _ vAdjList.rest; -- shift past marker vertex (i.e. first iteration of loop begins after it) done _ FALSE; WHILE NOT done DO nextAdjacency _ vAdjList.first; nextVertex _ nextAdjacency.vertex; IF nextVertex = lastVertex THEN done _ TRUE; -- test for last vertex to be processed IF (nextVertex.visited # visitedValue) THEN SetFigureVisitedValuesSubr[ nextVertex, visitedValue]; -- Recursive call for w vAdjList _ vAdjList.rest; ENDLOOP; RETURN; }; ClearIntersectPolygonFields: PUBLIC PROC[v, w: RVHU.VertexHandle] = { vTow, wTov: RVHU.AdjacencyHandle; firstv: RVHU.VertexHandle _ v; outline: BOOL _ FALSE; out.PutF["\nClearIntersectPolygonFields entered with argument [#%g , #%g ] \n", IO.int[v.index], IO.int[w.index] ]; [ vTow, wTov ] _ RVHU.FindAdjacency[ v, w ]; vTow.leftRegionVisited _ FALSE; vTow.intersectionPolygonEdge _ FALSE; WHILE w # firstv DO [v, w] _ RVHU.StepVertex[v,w, outline]; [ vTow, wTov ] _ RVHU.FindAdjacency[ v, w ]; vTow.leftRegionVisited _ FALSE; vTow.intersectionPolygonEdge _ FALSE; ENDLOOP; [v, w] _ RVHU.StepVertex[v,w, outline]; [ vTow, wTov ] _ RVHU.FindAdjacency[ v, w ]; IF wTov.leftRegionVisited THEN ClearIntersectPolygonFields[w, v]; WHILE w # firstv DO [v, w] _ RVHU.StepVertex[v,w, outline]; [ vTow, wTov ] _ RVHU.FindAdjacency[ v, w ]; IF wTov.leftRegionVisited THEN ClearIntersectPolygonFields[w, v]; ENDLOOP; }; END. RŠRCombinerImpl.mesa Last Edited by: Arnon, June 20, 1985 4:44:33 pm PDT Main procedures currentw is a vertex on the (convex) outline of the current figure. inputw is a vertex on the input polygon. combinedw is a vertex on the (convex) outline of the combined figure. -- ClearIntersectPolygonFields not executed when intersectionPolygonv = NIL, i.e. we assume no intersectionPolygonEdge fields set in this case currentw is a vertex on the (convex) outline of the current figure. inputw is a vertex on the input polygon. combinedw is a vertex on the (convex) outline of the combined figure. [intersectionPolygonv, intersectionPolygonw] is a (directed) edge in the new (i.e. combined) geometry which is contained in the intersection polygon (of current figure and input polygon), and such that the interior of the intersection polygon lies to its left. If the intersection polygon is empty, then intersectionPolygonv _ intersectionPolygonw _ NIL. Either currentw or inputw may be NIL, in which case the other is returned. If non-NIL, it is assumed that each structure has at least three vertices. If setIntersectionPolygonEdges, then CombineGeometry sets the intersectionPolygonEdge fields of all (directed) edges in the new (i.e. combined) geometry which are contained in the intersection polygon (of current figure and input polygon), and such that the interior of the intersection polygon lies to their left. Although this functionality is provided as an essential preprocessing step for CombineState, note that it is a purely geometrical operation. It can be undone with ClearIntersectPolygonFields. Check for empty arguments, valid inputs. Insure that current figure and input polygon have same setting of vertex visited flag. Adjacency flag fields (leftRegionVisited, intersectionPolygonEdge) assumed to be cleared (set to FALSE) in both current figure and input polygon. Find preceding vertices on outline so that [[currentv, currentw] is a counterclockwise oriented edge on the (convex) outline of the current figure, and [inputv, inputw] is a counterclockwise oriented edge on the input polygon. Centers of mass of both figures, and locate wedge of current figure outline that contains vertex inputv of the input polygon. Get state of input polygon Set intersectionPolygonEdge fields of "inward-facing" edges, and clear leftRegionExterior and set leftState fields, of "outward-facing" edges, on outline of current figure Decompose the annulus formed by input polygon - current outline Set state of input polygon to state of internal polygon of current figure that encloses it. This is the right thing to do since CombineState assumes that interior of the intersection polygon has current state, and will combine that with input polygon state. Hence CombineGeometry has side effects with respect to state (this is not the only example of that by any means). Set intersectionPolygonEdge fields of "inward-facing" edges, and clear leftRegionExterior and set leftState fields, of "outward-facing" edges, on input polygon Decompose annulus formed by current internal polygon - input polygon. [intersectionPolygonv, intersectionPolygonw] is a counterclockwise oriented (directed) edge in the current (i.e. combined) geometry which is contained in the intersection polygon (of current figure and input polygon), and such that the interior of the intersection polygon lies to its left. inputState is the state of the input polygon. stateCombiner is the rule for state combination. The leftState fields of the edges of all polygons interior to the intersection polygon are set to the result of state combining inputState with the states those polygons had in the current figure. The algorithm uses Depth First Search. CombineState assumes that the intersectionPolygonEdge fields of all (directed) edges in the new (i.e. combined) geometry which are contained in the intersection polygon (of current figure and input polygon), and such that the interior of the intersection polygon lies to their left, are set. It doesn't clear them. Verbosity Set leftState and leftRegionVisited fields of edges of this polygon Go around edges again; check each to see if an unprocessed polygon lies across it, if so, recursive call on the flipped edge. Don't need to consider first edge since on outermost call, [v,w] is on intersection polygon, and on recursive calls, wTov.leftRegionVisited will have been set true by the caller. Combiner case analysis [v1,w1] is a counterclockwise oriented edge on the outline of the current figure, [v2,w2] is a counterclockwise oriented edge of the input figure, such that v2 lies in the wedge of the sector of the current outline defined by [v1,w1], but not in the sector itself (it follows that we don't have to do the loop below for the initial v2). We trace the current outline to follow the input polygon, until an intersection of the input polygon with the current outline is found, or we determine that there is no intersection (i.e. we've gone all the way around the input polygon without finding one). As we trace, we simultaneously collect information to be able to decide whether the input polygon is external to or encloses the outline of the current figure, in case there is no intersection. If an intersection is found, then [v3,w3] is the edge of the current figure, and [v4,w4] the edge of the input figure, which have been found to intersect. If there is no intersection, then [v3,w3] is an edge of the current figure, and [v4,w4] an edge of the input figure, such that v4 is in the wedge defined by [v3,w3]. currentfigurecom is the center of mass of the outline of the current figure. [v1,w1] is a counterclockwise oriented edge on the outline of the current figure, [v2,w2] is a counterclockwise oriented edge of the input figure. We assume that v2 is in the sector defined by [v1,w1]. We first look for an intersection of the input figure with the outline of the current figure. In the event of an intersection, [v3,w3] is the edge of the current figures, and [v4,w4] the edge of the input figure, which have been found to intersect. If we do not find such an intersection, then we next look to see if the input polygon is contained within the current outline, in such a way as to intersect multiple polygons of the current figure. If so, then [v3,w3] is an internal edge of the current figure which intersects an edge [v4,w4] of the input figure, such that [v3,w3] is a counterclockwise oriented edge of the unique internal polygon of the current figure which contains v4. The remaining possibility is that the input polygon is completely contained within a single polygon of the current figure. If so, then [v3,w3] is a counterclockwise oriented edge of the internal polygon of the current figure that contains the input polygon, and [v4,w4] is an edge of the input polygon such that v4 is in the sector of the current figure polygon determined by v3 and w3. Look for an intersection of the input polygon with the outline of the current figure. If we arrive at this point, then there was no intersection of input polygon with current outline, and so the input polygon is contained within the outline of the current figure At termination of this loop, [v5,w5] is a (counterclockwise oriented) edge of the unique internal polygon which contains v2. polygoncom is the center of mass of this internal polygon. Check to see if the input figure leaves this polygon of the current figure Note that w2 has its correct value as the input vertex following v2, because an earlier loop terminated with v2=v2first and w2 following v2 [currentv, currentw] is an edge of the current figure, and [inputv, inputw] is an edge of the input figure, which intersect and thus constitute a lens transition pont. lensTransitionType specifies the type of transition which is occurring at their intersection. [voutline,woutline] is a counterclockwise oriented edge on the outline of the combined figure, unless all lenses are internal to the current outline (i.e. all lens transitions are INTERIORTOINTERIOR), in which case voutline _ woutline _ NIL; Initialize for first (i.e. next) lens to be decomposed: set up information pertaining to the lens that terminates at the intersection of [currentv, currentw] and [inputv, inputw] (i.e. the previous lens). Construct the intersection point for the beginning of the first lens. Lens decompositions until we get back to firstv Set up for next lens, i.e. the one that begins at the intersection we have just computed. Get some return values, grow outline of combined figure out be convex Set up arguments for next call to LensDecomposition Get some return values, grow outline of combined figure out be convex Set up arguments for next call to LensDecomposition Set up arguments for next call to LensDecomposition Decompose next lens After the last execution of this loop, [pj1, pj] is an edge on the intersection polygon, since it is an edge of the inner polygon of the last lens, and in all lenses, inner polygon edges are intersection polygon edges, indeed, the intersection polygon is the union of all inner polygon edges. [currentv, currentw] is a counterclockwise oriented edge on the (convex) outline of the current figure. [inputv, inputw] is a counterclockwise oriented edge on the input polygon. [currentv, currentw] defines a sector of the current figure in (the closure of) which inputv lies. [combinedv, combinedw] is a counterclockwise oriented edge on the (convex) outline of the combined figure. Advance [inputv, inputw] until it defines sector (in closure of which) currentoutlinecom lies (this is to insure that inputv is not on the "backside" of the input polygon with respect to the current outline). Then advance [currentv, currentw] until it defines a sector of the current figure in (the closure of) which inputv lies. Join currentv and currentw to inputv with edges (to form a triangle), then for each of these new edges, call GrowToConvexOutline if necessary. [innerv, innerw] is a counterclockwise oriented edge on the inner polygon. [outerv, outerw] is a counterclockwise oriented edge on the outer polygon. The algorithm is: pick a random vertex on outer polygon (we pick outerv), pinwheel inner polygon until wedge for that vertex is found. Back up along inner polygon until last vertex "visible" from outerv is found; then add a "cone" of edges from outerv to vertices of inner polygon until a convex outline is obtained. This leaves a "lens-like region" (i.e. a region which is a lens by some suitably general definition of lens) to be decomposed into convex polygons; we do so by applying the existing lens decomposition procedure. Back up along inner polygon Go forward along inner polygon, adding cone of edges. It is assumed that leftState and leftRegionExterior fields of previously external (outward-facing) edges of internal polygon have already been updated by caller. Lens decomposition to finish Convex decomposers Each of the lenses that two polygons give rise to is external to one of them and internal to the other (in fact, a lens of two polynomials p1 and p2 can be defined as a connected component of p1 - p2 or p2 - p1). The polygon for which the lens is external is said to be the inner polygon of the lens; the other is the outer polygon of the lens. firstv is the intersection point which started the first lens. [v, pj] is an edge of the polygon which is coming from outer and going inner. [v, qi] is an edge of the polygon which is coming from inner and going outer. lensInExterior specifies whether the forthcoming lens is in the exterior of the current figure. returnedToFirstv tells us whether we have come back to the first lens. vending is the intersection point which terminates the lens. [pj1ending, pjending] is the edge of the polygon which was inner in this lens, that intersected with edge [qi1ending, qiending] of the polygon which was outer in this lens, at point vending to terminate the lens (for the very last lens, at whose end we will have returned to the first intersection point firstv, we will have pjending = qiending = vending = firstv). It is expected that these five outputs will be used for setting up the processing for the next lens, as will nextLensInExterior. General note on the 'outline' (third, boolean) argument in the calls below to RVHU.NextVertex and RVHU.StepVertex: If lensInExterior, then the input polygon is the outer (q) polygon, and the current figure provides the inner (p) polygon. Clearly we must have outline = TRUE (i.e. outline stepping) in this case for stepping the current figure; as always, we can either outline step or polygon step the input polygon. If NOT lensInExterior, then the current figure provides the outer (q) polygon, and the input polygon is the inner (p) polygon. Clearly we must have outline = FALSE (i.e. polygon stepping) in this case for stepping the current figure; again, we can either outline or polygon step the input polygon. We see that we can achieve the desired stepping in both cases by passing outline = lensInExterior in the calls to RVHU.NextVertex and RVHU.StepVertex. Change 1/22/85 - Bug found in the reasoning of the previous paragraph for the case lensInExterior. GrowToConvexOutline will have been called by InputIntersectsCurrent prior to the call to LensDecomposition, and will have put in (at least two directed = one undirected) edges between (one or more) vertices of the current figure and (one or more) vertices of the input polygon. Hence in this case, the input (outer) polygon cannot be outline stepped starting from firstv. Hence a correct thing to do for all cases is to pass outline = lensInExterior when stepping the inner polygon, and pass outline = FALSE, i.e. polygon step, the outer polygon. 11/27/85 - Shouldn't the rule be just to outline step the inner polygon, and polygon step the outer polygon? Initialize Follow the lens, biting off convex polygons Find last q vertex above current p edge, updating states for q edges as we go. Check for intersection, i.e. whether we have reached end of lens -- the test for returnedToFirstv must be pj = firstv AND qip1 = firstv, because pj = firstv, NOT returnedToFirstv, ReachedNextIntersection, could occur by pj reaching ahead through the next lens to happen to coincide with firstv (- qip1 could reach ahead in the same way?; don't know, not necessary to know 1/30/85). If no intersection, then insert edges between pj and qi, and possibly edges between pj and qip1. Should attempt to do local collection of regions, to a depth of two, at this point (perhaps also elsewhere) Update leftRegionExterior and leftState for [pj, pj1], and record that [pj1, pj] is an edge of the intersection polygon of the input polygon and the outline of the current figure. Note that these updates are done for a (pair of) p edges only after we have stepped to the next (pair of) p edges. This is important for correctness, because if we updated too soon, we might e.g. assign a new state to a region that at one point in time extended for the whole length of a p edge, but the lens might in fact terminate by cutting across that p edge, meaning that this region was being subdivided into two regions, the one of which turns out to be outside the lens should not have gotten the new state, but would get it because of the simple inheritance rule followed by InsertIntersectionPoint. Corner check, i.e. check that qi is not on the left side of the ray from pj1 through pj, because if it is, then the next polygon that we bite out of the lens won't be convex. Should attempt to do local collection of regions, to a depth of two, at this point (perhaps also elsewhere) Fill in fields of edges between intersection point and pj1 Fill in fields of edges between firstv and pj1 (no need to insert intersection point since already there) w2, v, and w1 are assumed to be in counterclockwise order (with respect to the interior of the outline) on an outline. [v3,w3] is a counterclockwise-oriented (with respect to the interior of the figure) edge on the new outline. The algorithm assumes that [w2, v, w1] is a nontrivial triangle (in particular, that the three points are not collinear). This triangle is the initial convex polygon. WHILE the figure does not (in the neighborhood of w2, v, w1) have a convex outline, the algorithm enlarges this polygon, breaking it off (i.e. inserting an edge) and starting a new polygon as necessary. Invariant assumed to hold at the beginning of this loop: w1 and w2 have the property that an edge joining them will "close off", i.e. form, a convex polygon "on the side of [w1,w2] towards the interior of the figure". Check, in direction from w1 to w2, whether current edge [w1, w2] added to the outline will give a "convex extension" of the outline, i.e. the outline will satisfy convexity check from before w2 up to and including w1. Check also whether an edge from w1 to prew2 will form a convex polygon; if not, put in edges between w1 and w2 (by loop invariant, this will yield a convex polygon). Check, in direction from w2 to w1, whether current edge [w1, w2] added to the outline will give a "convex extension" of the outline, i.e. the outline will satisfy convexity check from w2 up through and beyond w1. Check also whether an edge from w2 to forw1 will form a convex polygon; if not, put in edges between w1 and w2 (by loop invariant, this will yield a convex polygon). Vertex and adjacency insertion Split the intersecting edges [pj1, pj] and [qi, qip1] which terminate a lens. Assumes that Counterclockwise[pj1, pj, qip1], i.e. [pj1, pj] and [qi, qip1] are at the end of a lens in which [pj1, pj] was on the inner polygon and [qi, qip1] was on the outer. A simple inheritance rule is followed for setting the leftState, leftRegionVisited, leftRegionExterior, and intersectionPolygonEdge fields of the new edges. Compute intersection point. Create a VertexHandle vh for the intersection point. Get adjacency information needed to create the new edges. Set up adjacencies from each existing vertex to the intersection point. Fill in adjacencies of intersection point. Edge from vh to pj. Edge from vh to qip1. Edge from vh to pj1. Edge from vh to qi. [prew2, w2] and [w1, forw1] are counterclockwise-oriented edges (with respect to the interior of the figure) on an outline. It is assumed that edges between w2 and w1 will lie external to the current outline, and that an edge from w1 to w2 will extend this outline in such a way that the exterior of the new outline will lie to the left of this edge. Edges between w2 and w1 are created, in such a fashion that prew2 follows w1 in the clockwise ordering of adjacent vertices of w2, and forw1 precedes w2 in the clockwise ordering of adjacent vertices of w1. We first set up the edge from w1 to w2 which has leftRegionExterior = TRUE. Then we set up the edge from w2 to w1, which typically creates a polygon (hence the default setLRE _ TRUE), and (unless setLRE _ FALSE) we go around this polygon setting leftRegionExterior = FALSE and leftState= NIL. Get adjacency information needed to create the new edges. Create an edge from w1 to w2 Insert in list of w1 adjacentVertices so that forw1 precedes w2 Create an edge from w2 to w1 Insert in list of w2 adjacentVertices so that w1 precedes prew2 IF setLRE, then set leftRegionExterior = FALSE for all polygon edges. [pj1,pj] is a counterclockwise-oriented edge on the inner polygon. [qi1,qi] is a counterclockwise-oriented edge on the outer polygon. Edges between pj1 and qi1 are created, in such a fashion that qi1 follows pj in the clockwise ordering of adjacent vertices of pj1, and qi follows pj1 in the clockwise ordering of adjacent vertices of qi1. leftField values (leftState, leftRegionVisited) are inherited from edge [qi1,qi], i.e. the inward facing edge of the outer polygon. Get adjacency information needed to create the new edges. Create an edge from pj1 to qi1 Insert in list of pj1 adjacentVertices so that qi1 follows pj Create an edge from qi1 to pj1 Insert in list of qi1 adjacentVertices so that pj1 precedes qi Boolean field modification Set visited field of all vertices in figure to visitedValue. Does Depth First Search. Flag fields (leftRegionVisited, intersectionPolygonEdge) left unchanged. Assumes two or more vertices Process adjacent vertices. Set all flag fields of edges to these vertices to visitedValue. Recursive call on the vertex if and only if we haven't been there yet. [intersectionPolygonv, intersectionPolygonw] is a (directed) edge in the current (i.e. combined) geometry which is contained in the intersection polygon (of current figure and input polygon), and such that the interior of the intersection polygon lies to its left. The leftRegionVisited and intersectionPolygonEdge fields of all edges of all polygons interior to the intersection polygon are cleared. The algorithm uses Depth First Search. Verbosity Clear leftRegionVisited and intersectionPolygonEdge fields of edges of this polygon Go around edges again; check each to see if an unprocessed polygon lies across it, if so, recursive call on the flipped edge. Ê!k˜™J™3J™—šÏk ˜ Jšœ˜Jšœ˜Jšœ ˜ Icodešœœœ˜#Kšœ˜J˜—InamešÏn œœ˜šœœœ ˜.Jšœ ˜—Jšœœœœ˜@J˜Jšœ œœœ˜Jšœ œœÏc˜4Jšœ œœ˜˜J˜—šœ™J˜—šžœœœœœœœ œ˜žJ•commentTRUEšœ´™´Jšœ,œ˜>Jšœ\œ˜bšœœœ˜"JšœT˜TJšœH˜HJšœŽ™ŽJ˜—J˜J™—šžœœœœ,œœœ9œ˜ÁJ–TRUEšœ—™—Jšœ•™•Jšœü™üJ˜Jšœ-œ˜?Jšœ#œ˜.Jšœœ˜!Jšœ,˜,Jšœ+˜+J˜Jšœ(™(Jš œ œœœ œœ˜0Jš œ œœœ œœ˜0Jšœœœœœœœœ˜]J˜Jšœé™éJšœ#œ2˜[J˜Jšœâ™âJšœ œ!˜0Jšœ œ˜,J˜Jšœ~™~Jšœœ#œ˜AJšœœœ˜;JšœœQœ˜zJšœ˜JšœŸ‡˜J˜Kšœ˜˜Kšœœœ˜7˜Jšœ‘˜‘J˜Jšœ˜ ˜Jšœcœ&œ˜²J˜šœ˜ Jšœz˜zJšœ.œ˜2J˜—J˜šœ˜ Jšœœ˜ Jšœ œ˜!Jšœ œœ˜Jšœœ˜J˜Jšœ™Jšœœ!˜6Jšœ$˜$J™Jšœ«™«Jšœ%˜%Jšœœ˜,Jšœœ˜$Jšœœ˜ Jšœ#˜#šœ ˜Jšœ œ˜'Jšœœ˜,Jšœœ˜$Jšœœ˜ Jšœ#˜#Jšœ˜—J˜Jšœ?™?JšœO˜OJšœ˜Jšœ˜Jšœ ˜ Jšœ ˜ J˜—J˜Jšœ˜—Jšœ˜J˜—Jšœœœ˜;˜Jšœ˜J˜Jšœ˜ ˜Jšœbœ&œ˜±J˜šœ˜Jšœœ˜ Jšœ œ˜!Jšœ œœ˜Jšœœ˜ J˜Jšœõ™õJšœœ%˜:Jšœ&˜&Jšœ6˜:J˜JšœŸ™ŸJšœ!˜!Jšœœ˜,Jšœœ˜$Jšœœ˜ Jšœ%˜%šœ ˜Jšœ œ˜'Jšœœ˜,Jšœœ˜$Jšœœ˜ Jšœ%˜%Jšœ˜—J™JšœE™EJšœK˜KJšœ˜Jšœ Ÿ3˜SJ˜—J˜Jšœ[œ&œŸ{˜§J˜Jšœ˜—Jšœ˜J˜—KšœœŸ^˜pK˜—Jšœ8˜>J˜J˜—š ž œœœ-œœœ˜•J–TRUEšœ™Jšœë™ëJšœ»™»Jšœœ˜Jšœœ%˜,Jšœœ%˜,Jšœœ˜Jšœ œ˜!Jšœ œœ˜Jšœœ˜J˜Jšœœ˜,Jšœ;˜;J˜Jšœ ™ Jšœ œ˜.JšœKœœœ˜J™JšœC™CJšœ˜Jšœœ˜šœ ˜Jšœ œ˜'Jšœœ˜,Jšœ˜Jšœœ˜Jšœ˜—J˜Jšœ°™°Jšœ œ˜'šœ ˜Jšœ œ˜'Jšœœ˜,šœœ˜(šœœ˜"Jšœ/˜/——Jšœ˜—J˜J˜—J™šœ™J™—šžœœœœœœ?œ˜ÈJšœÕ™ÕJšœ œœ˜Jšœ œ˜ Jšœ œ˜ Jšœœœ˜Jšœœ˜.Jšœ œ ˜-Jšœœ%˜0Jšœ œ˜JšœœU˜ošœœ˜šœ˜Kšœœœœ˜;šœ˜ JšœœZ˜uJ˜šœ˜Jšœœœ˜>Jš˜JšœœŸ˜,—šœœ œ˜TJšœ œœŸ;˜gJšœœ[Ÿ]˜ÓJ˜šœ˜Jšœœœ˜>Jš˜JšœœŸ˜,—JšœœU˜oJšœ˜—J˜——Jš œœEœ œŸù˜éJšœ œœŸ%˜PJš œœ œœWŸ7˜ÎJšœ˜—Jšœ œœœœœœ˜]J˜J˜—šžœœœœœœ>œ˜ÇJšœ–™–Jšœ´™´J™UJšœ œ˜ Jšœ œ˜ Jšœ œ˜ Jšœ œ˜Jšœœ˜!Jšœœ˜.Jšœœ˜ Jšœœœ˜Jšœ œ ˜-Jšœœ%˜0Jšœ œ˜JšœœU˜ošœœ˜šœ˜Kš5œœ˜>šQœ˜VJšœœZ˜uJ˜šœ˜Jšœœœ˜>Jš˜JšœœŸ˜,—šœœ œ˜TJšœ œœŸ;˜gJšœœ[Ÿ]˜ÓJ˜šœ˜Jšœœœ˜>Jš˜JšœœŸ˜,—JšœœU˜oJšœ˜—J˜—JšœœŸ˜2—Jšœ œœ˜*Jš œœœœœU˜•Jšœ˜—J˜Jšœ°™°JšœŸ2˜AJšœ ˜ Jšœ œœŸ ˜PJšœœ:œŸ6˜J˜š œ œ œ œœŸ=˜™Jšœ!Ÿ)˜JJšœ œœ˜/Jšœœ:œ˜YJšœ˜—šœ˜Kšœœœœ˜>KšœœŸ˜2—Jšœ¸™¸J˜J™JJšœœ˜ JšœœU˜ošœœ˜šœ˜Kšœœ!œœ˜HšUœ˜VJšŸ‹™‹JšœœZ˜uJ˜šœ˜Jšœœœ˜?Jš˜Jšœœ˜—šœœ œ˜TJšœ œœ˜,JšœœZ˜uJ˜šœ˜Jšœœœ˜?Jš˜Jšœœ˜—JšœœU˜oJšœ˜—J˜—JšœœŸ˜1—Jšœ œœ˜+Jšœœ œœU˜•Jšœ˜—JšœœŸt˜ŸJ˜J˜—šžœœœNœ,œœœœœ.œ˜¦J–TRUEšœºœ'œ™÷Jšœœ˜$Jšœœ˜$Jšœœœ˜Jšœ$œ˜)Jšœœ˜%J˜JšœÌ™Ìšœœœ˜1JšœOœ˜U—šœœœœ˜6JšœOœ˜V—šœŸ˜3JšœOœ˜W—J˜JšœE™EJšœ7˜7J˜Jšœ0™0šœœ˜J˜JšœY™YšœœŸ*˜CJ˜JšœœŸ ˜2J˜JšœE™EJšœ:˜:J™JšŸ3™3Jšœ6œ˜œŸ7˜}JšœGœœ˜uJšœœ œŸM˜‡Jšœœ(ŸW˜‘JšœCŸE˜ˆJšœGœœ˜uJšœœR˜jšœ œ˜'JšœL˜L—šœ˜JšœœR˜jJšœ œœM˜tJ˜—JšœœS˜kšœ œ˜'JšœN˜N—šœ˜JšœœM˜eJšœ œœH˜oJ˜—Jšœ˜J˜J˜—š žœœœ œ(œ˜qJ–TRUEšœ•™•J™‘Jšœœ˜!Jšœœœ˜Jšœ!œ˜3Jšœ3œ˜EJšœœ˜*J˜JšœœDœ˜jJšœ-˜-J˜Jšœ™Jšœ˜Jšœ˜Jšœ œ,˜JšœœB˜ZJš œœœœŸ˜šœœœ˜'Jšœ˜Jšœ.˜.Jšœ/˜/Jšœ9˜9Jšœ˜—Jšœœ˜6Jšœ Ÿ:˜ZJ˜J™šœœœ˜)Jšœ˜Jšœ/˜/Jšœ1˜1Jšœ;˜;Jšœ˜—Jšœœ˜6J˜J™šœœœ˜)Jšœ˜Jšœ.˜.Jšœ0˜0Jšœ:˜:Jšœ˜—Jšœœ˜6J˜J™šœœœ˜(Jšœ˜Jšœ/˜/Jšœ1˜1Jšœ;˜;Jšœ˜—Jšœœ˜6J˜Jšœ%Ÿ$˜IJ˜JšœAœ˜SJ˜Jšœ˜ J˜J˜—š žœœœœœœ˜eJ–TRUEšœãœœQ™ÖJšœœ˜1Jšœ0œ˜EJ™J™9Jšœœ˜9Jšœœ˜9J˜J™šœœœ˜(Jšœ œŸ>˜NJšœ0˜0Jšœœ˜Jšœœ˜!Jšœ˜J˜—Jšœ?™?Jšœ<˜<šœ˜&Jšœ˜Jšœ˜—Jšœ œ ˜J˜J˜Jšœ™šœœœ˜(Jšœ œŸ>˜NJšœ0˜0Jšœœ˜Jšœœ˜!Jšœ˜J˜—Jšœ?™?Jšœ<˜<šœ˜!Jšœ˜Jšœ˜—Jšœ œ ˜J˜J˜JšœE™EJš œœœœœ˜7Jšœ˜—šžœœœœ˜IJ–TRUEšœÚ™ÚJšœœ˜1Jšœœ˜+J™J™9Jšœœ˜3J˜J™šœœœ˜)Jšœ˜Jšœ.˜.Jšœœ˜Jšœœ˜!Jšœ˜J˜—Jšœ=™=Jšœ=˜=šœ˜#Jšœ˜Jšœ˜—Jšœ œ ˜J˜J˜J™šœœœ˜)Jšœ˜Jšœ.˜.Jšœœ˜Jšœœ˜!Jšœ˜J˜—Jšœ>™>Jšœ=˜=šœ˜Jšœ˜Jšœ˜—Jšœ œ ˜J˜J˜JšœCœœ˜iJšœ˜J˜—J˜šœ™J˜—š žœœœœœ˜TJ–TRUEšœž™žJš œœœœŸ˜'Jš œœœ œŸ ˜TJšœ.Ÿ˜EJšœ˜J˜J˜—šžœœœœ˜QJšŸ™Jšœ œ*˜8Jšœœ˜$Jšœœ˜*Jšœœœ˜J˜JšœŸ'˜AJ˜Jšœ¢™¢Jšœ$˜$JšœŸJ˜eJšœœ˜ šœœ˜Jšœ˜Jšœ"˜"JšœœœŸ(˜VJšœ%Ÿœ7Ÿ˜zJšœ˜Jšœ˜—J˜Jšœ˜Jšœ˜J˜—šžœœœœ˜EJ–TRUEšœ·™·Jšœ œ˜!Jšœœ˜Jšœ œœ˜J˜Jšœ ™ Jšœs˜sJ™JšœS™SJšœœ˜,Jšœœ˜Jšœœ˜%šœ ˜Jšœ œ˜'Jšœœ˜,Jšœœ˜Jšœœ˜%Jšœ˜—J˜Jšœ~™~Jšœ œ˜'Jšœœ˜,Jšœœ#˜Ašœ ˜Jšœ œ˜'Jšœœ˜,Jšœœ#˜AJšœ˜—J˜—J˜Jšœ˜˜J™——…—NóC