From 45ccc4bdfc05aa867cdddaa2061dd8da79c4de83 Mon Sep 17 00:00:00 2001 From: Peter Stace Date: Mon, 7 Nov 2022 20:19:20 +1100 Subject: [PATCH] Simplify DCEL label management In the DCEL data structure, there are labels on the vertex records, (half) edge records, and face records. These labels keep track information such as which input geometry the record originates from and whether it will appear in the output geometry. The original approach to labelling was to use the same data structure for all record types. The label had two booleans per operand. The first boolean determined whether the record would appear in the final output, and the second boolean would show whether the first boolean was populated or not. This was essentially tri-state logic. The trouble with this approach was that the DCEL algorithm doesn't know whether each record should appear in the output geometry right up until the last stage. The labels are used in the earlier stages of the algorithm to record other facts, such as whether or not the records appear in the input geometry. The semantics of the fields subtly change over the course of the algorithm, making the code hard to understand and maintain. The new approach (implemented by this PR) changes how label management is performed. Instead of having a single labels data structure, the fields are slightly different for vertex, edge, and face records. This is to account for the differences in information available when processing vertexes vs. edges vs. faces. The number of fields is also expanded, so that concepts like "is this record explicitly present in the input geometry's control points" are separate from concepts like "is this record present in the input geometry but not explicitly in its control points". The populated fields are also removed, and tracked separately in each algorithm that modifies the labels. --- geom/alg_set_op.go | 15 +- geom/dcel.go | 144 +-- geom/dcel_extract.go | 26 +- geom/dcel_extract_intersection_matrix.go | 22 +- geom/dcel_label.go | 66 +- geom/dcel_overlay.go | 119 +-- geom/dcel_test.go | 1205 ++++++++++++---------- 7 files changed, 815 insertions(+), 782 deletions(-) diff --git a/geom/alg_set_op.go b/geom/alg_set_op.go index 2cfa8f46..0a21958e 100644 --- a/geom/alg_set_op.go +++ b/geom/alg_set_op.go @@ -13,7 +13,7 @@ func Union(a, b Geometry) (Geometry, error) { if b.IsEmpty() { return a, nil } - g, err := setOp(a, b, selectUnion) + g, err := setOp(a, or, b) return g, wrap(err, "executing union") } @@ -24,7 +24,7 @@ func Intersection(a, b Geometry) (Geometry, error) { if a.IsEmpty() || b.IsEmpty() { return Geometry{}, nil } - g, err := setOp(a, b, selectIntersection) + g, err := setOp(a, and, b) return g, wrap(err, "executing intersection") } @@ -38,7 +38,7 @@ func Difference(a, b Geometry) (Geometry, error) { if b.IsEmpty() { return a, nil } - g, err := setOp(a, b, selectDifference) + g, err := setOp(a, andNot, b) return g, wrap(err, "executing difference") } @@ -55,11 +55,11 @@ func SymmetricDifference(a, b Geometry) (Geometry, error) { if b.IsEmpty() { return a, nil } - g, err := setOp(a, b, selectSymmetricDifference) + g, err := setOp(a, xor, b) return g, wrap(err, "executing symmetric difference") } -func setOp(a, b Geometry, include func([2]label) bool) (Geometry, error) { +func setOp(a Geometry, include func([2]bool) bool, b Geometry) (Geometry, error) { overlay, err := createOverlay(a, b) if err != nil { return Geometry{}, wrap(err, "internal error creating overlay") @@ -71,3 +71,8 @@ func setOp(a, b Geometry, include func([2]label) bool) (Geometry, error) { } return g, nil } + +func or(b [2]bool) bool { return b[0] || b[1] } +func and(b [2]bool) bool { return b[0] && b[1] } +func xor(b [2]bool) bool { return b[0] != b[1] } +func andNot(b [2]bool) bool { return b[0] && !b[1] } diff --git a/geom/dcel.go b/geom/dcel.go index 512550a4..2c3382f4 100644 --- a/geom/dcel.go +++ b/geom/dcel.go @@ -11,8 +11,12 @@ type doublyConnectedEdgeList struct { } type faceRecord struct { - cycle *halfEdgeRecord - labels [2]label + cycle *halfEdgeRecord + + // inSet encodes whether this face is part of the input geometry for each + // operand. + inSet [2]bool + extracted bool } @@ -29,9 +33,20 @@ type halfEdgeRecord struct { incident *faceRecord // only populated in the overlay next, prev *halfEdgeRecord seq Sequence - edgeLabels [2]label - faceLabels [2]label - extracted bool + + // srcEdge encodes whether or not this edge is explicitly appears as part + // of the input geometries. + srcEdge [2]bool + + // srcFace encodes whether or not this edge explicitly borders onto a face + // in the input geometries. + srcFace [2]bool + + // inSet encodes whether or not this edge is (explicitly or implicitly) + // part of the input geometry for each operand. + inSet [2]bool + + extracted bool } // String shows the origin and destination of the edge (for debugging @@ -46,7 +61,15 @@ func (e *halfEdgeRecord) String() string { type vertexRecord struct { coords XY incidents []*halfEdgeRecord - labels [2]label + + // src encodes whether on not this vertex explicitly appears in the input + // geometries. + src [2]bool + + // inSet encodes whether or not this vertex is part of each input geometry + // (although it might not be explicitly encoded there). + inSet [2]bool + locations [2]location extracted bool } @@ -123,9 +146,9 @@ func (d *doublyConnectedEdgeList) addMultiPolygon(mp MultiPolygon, operand opera vr := &vertexRecord{ coords: xy, incidents: nil, // populated later - labels: newHalfPopulatedLabels(operand, true), locations: newLocationsOnBoundary(operand), } + vr.src[operand] = true d.vertices[xy] = vr } } @@ -139,25 +162,24 @@ func (d *doublyConnectedEdgeList) addMultiPolygon(mp MultiPolygon, operand opera vertA := d.vertices[segment.GetXY(0)] vertB := d.vertices[reverseSegment.GetXY(0)] internalEdge := &halfEdgeRecord{ - origin: vertA, - twin: nil, // populated later - incident: nil, // only populated in the overlay - next: nil, // populated later - prev: nil, // populated later - seq: segment, - edgeLabels: newHalfPopulatedLabels(operand, true), - faceLabels: newHalfPopulatedLabels(operand, true), + origin: vertA, + twin: nil, // populated later + incident: nil, // only populated in the overlay + next: nil, // populated later + prev: nil, // populated later + seq: segment, } externalEdge := &halfEdgeRecord{ - origin: vertB, - twin: internalEdge, - incident: nil, // only populated in the overlay - next: nil, // populated later - prev: nil, // populated later - seq: reverseSegment, - edgeLabels: newHalfPopulatedLabels(operand, true), - faceLabels: newHalfPopulatedLabels(operand, false), + origin: vertB, + twin: internalEdge, + incident: nil, // only populated in the overlay + next: nil, // populated later + prev: nil, // populated later + seq: reverseSegment, } + internalEdge.srcEdge[operand] = true + internalEdge.srcFace[operand] = true + externalEdge.srcEdge[operand] = true internalEdge.twin = externalEdge vertA.incidents = append(vertA.incidents, internalEdge) vertB.incidents = append(vertB.incidents, externalEdge) @@ -197,13 +219,12 @@ func (d *doublyConnectedEdgeList) addMultiLineString(mls MultiLineString, operan } else { locs[operand].interior = true } - d.vertices[xy] = &vertexRecord{ - xy, - nil, // populated later - newHalfPopulatedLabels(operand, true), - locs, - false, + vr := &vertexRecord{ + coords: xy, + locations: locs, } + vr.src[operand] = true + d.vertices[xy] = vr } else { if onBoundary { if v.locations[operand].boundary { @@ -243,25 +264,23 @@ func (d *doublyConnectedEdgeList) addMultiLineString(mls MultiLineString, operan vDestin := d.vertices[endXY] fwd := &halfEdgeRecord{ - origin: vOrigin, - twin: nil, // set later - incident: nil, // only populated in overlay - next: nil, // set later - prev: nil, // set later - seq: segment, - edgeLabels: newHalfPopulatedLabels(operand, true), - faceLabels: newUnpopulatedLabels(), + origin: vOrigin, + twin: nil, // set later + incident: nil, // only populated in overlay + next: nil, // set later + prev: nil, // set later + seq: segment, } rev := &halfEdgeRecord{ - origin: vDestin, - twin: fwd, - incident: nil, // only populated in overlay - next: fwd, - prev: fwd, - seq: reverseSegment, - edgeLabels: newHalfPopulatedLabels(operand, true), - faceLabels: newUnpopulatedLabels(), + origin: vDestin, + twin: fwd, + incident: nil, // only populated in overlay + next: fwd, + prev: fwd, + seq: reverseSegment, } + fwd.srcEdge[operand] = true + rev.srcEdge[operand] = true fwd.twin = rev fwd.next = rev fwd.prev = rev @@ -286,12 +305,11 @@ func (d *doublyConnectedEdgeList) addMultiPoint(mp MultiPoint, operand operand) record = &vertexRecord{ coords: xy, incidents: nil, - labels: [2]label{}, // set below locations: [2]location{}, // set below } d.vertices[xy] = record } - record.labels[operand] = label{populated: true, inSet: true} + record.src[operand] = true record.locations[operand].interior = true } } @@ -317,10 +335,10 @@ func (d *doublyConnectedEdgeList) addGhosts(mls MultiLineString, operand operand endXY := reverseSegment.GetXY(0) if _, ok := d.vertices[startXY]; !ok { - d.vertices[startXY] = &vertexRecord{coords: startXY, incidents: nil, labels: [2]label{}} + d.vertices[startXY] = &vertexRecord{coords: startXY} } if _, ok := d.vertices[endXY]; !ok { - d.vertices[endXY] = &vertexRecord{coords: endXY, incidents: nil, labels: [2]label{}} + d.vertices[endXY] = &vertexRecord{coords: endXY} } if edges.containsStartIntermediateEnd(segment) { @@ -340,24 +358,20 @@ func (d *doublyConnectedEdgeList) addGhostLine(segment, reverseSegment Sequence, vertB := d.vertices[reverseSegment.GetXY(0)] e1 := &halfEdgeRecord{ - origin: vertA, - twin: nil, // populated later - incident: nil, // only populated in the overlay - next: nil, // popluated later - prev: nil, // populated later - seq: segment, - edgeLabels: newHalfPopulatedLabels(operand, false), - faceLabels: [2]label{}, + origin: vertA, + twin: nil, // populated later + incident: nil, // only populated in the overlay + next: nil, // popluated later + prev: nil, // populated later + seq: segment, } e2 := &halfEdgeRecord{ - origin: vertB, - twin: e1, - incident: nil, // only populated in the overlay - next: e1, - prev: e1, - seq: reverseSegment, - edgeLabels: newHalfPopulatedLabels(operand, false), - faceLabels: [2]label{}, + origin: vertB, + twin: e1, + incident: nil, // only populated in the overlay + next: e1, + prev: e1, + seq: reverseSegment, } e1.twin = e2 e1.next = e2 diff --git a/geom/dcel_extract.go b/geom/dcel_extract.go index 69179bde..7cc27ad3 100644 --- a/geom/dcel_extract.go +++ b/geom/dcel_extract.go @@ -3,7 +3,7 @@ package geom import "fmt" // extractGeometry converts the DECL into a Geometry that represents it. -func (d *doublyConnectedEdgeList) extractGeometry(include func([2]label) bool) (Geometry, error) { +func (d *doublyConnectedEdgeList) extractGeometry(include func([2]bool) bool) (Geometry, error) { areals, err := d.extractPolygons(include) if err != nil { return Geometry{}, err @@ -52,12 +52,12 @@ func (d *doublyConnectedEdgeList) extractGeometry(include func([2]label) bool) ( } } -func (d *doublyConnectedEdgeList) extractPolygons(include func([2]label) bool) ([]Polygon, error) { +func (d *doublyConnectedEdgeList) extractPolygons(include func([2]bool) bool) ([]Polygon, error) { var polys []Polygon for _, face := range d.faces { // Skip any faces not selected to be include in the output geometry, or // any faces already extracted. - if !include(face.labels) || face.extracted { + if !include(face.inSet) || face.extracted { continue } @@ -81,7 +81,7 @@ func (d *doublyConnectedEdgeList) extractPolygons(include func([2]label) bool) ( if seen[edge] { return } - if include(edge.twin.incident.labels) { + if include(edge.twin.incident.inSet) { // Adjacent face is in the polygon, so this edge cannot be part // of the boundary. seen[edge] = true @@ -135,7 +135,7 @@ func extractPolygonBoundary(faceSet map[*faceRecord]bool, start *halfEdgeRecord, // findFacesMakingPolygon finds all faces that belong to the polygon that // contains the start face (according to the given inclusion criteria). -func findFacesMakingPolygon(include func([2]label) bool, start *faceRecord) map[*faceRecord]bool { +func findFacesMakingPolygon(include func([2]bool) bool, start *faceRecord) map[*faceRecord]bool { expanded := make(map[*faceRecord]bool) toExpand := make(map[*faceRecord]bool) toExpand[start] = true @@ -151,7 +151,7 @@ func findFacesMakingPolygon(include func([2]label) bool, start *faceRecord) map[ adj := adjacentFaces(popped) expanded[popped] = true for _, f := range adj { - if !include(f.labels) { + if !include(f.inSet) { continue } if expanded[f] { @@ -177,7 +177,7 @@ func orderCCWRingFirst(rings []LineString) { } } -func (d *doublyConnectedEdgeList) extractLineStrings(include func([2]label) bool) ([]LineString, error) { +func (d *doublyConnectedEdgeList) extractLineStrings(include func([2]bool) bool) ([]LineString, error) { var lss []LineString for _, e := range d.halfEdges { if shouldExtractLine(e, include) { @@ -196,20 +196,20 @@ func (d *doublyConnectedEdgeList) extractLineStrings(include func([2]label) bool return lss, nil } -func shouldExtractLine(e *halfEdgeRecord, include func([2]label) bool) bool { +func shouldExtractLine(e *halfEdgeRecord, include func([2]bool) bool) bool { return !e.extracted && - include(e.edgeLabels) && - !include(e.incident.labels) && - !include(e.twin.incident.labels) + include(e.inSet) && + !include(e.incident.inSet) && + !include(e.twin.incident.inSet) } // extractPoints extracts any vertices in the DCEL that should be part of the // output geometry, but aren't yet represented as part of any previously // extracted geometries. -func (d *doublyConnectedEdgeList) extractPoints(include func([2]label) bool) ([]Point, error) { +func (d *doublyConnectedEdgeList) extractPoints(include func([2]bool) bool) ([]Point, error) { var pts []Point for _, vert := range d.vertices { - if include(vert.labels) && !vert.extracted { + if include(vert.inSet) && !vert.extracted { vert.extracted = true pt, err := vert.coords.AsPoint() if err != nil { diff --git a/geom/dcel_extract_intersection_matrix.go b/geom/dcel_extract_intersection_matrix.go index a714c1d0..6bae7a88 100644 --- a/geom/dcel_extract_intersection_matrix.go +++ b/geom/dcel_extract_intersection_matrix.go @@ -21,29 +21,25 @@ func (d *doublyConnectedEdgeList) extractIntersectionMatrix() matrix { } func (f *faceRecord) location(operand operand) imLocation { - assertPresence(f.labels) - if !f.labels[operand].inSet { + if !f.inSet[operand] { return imExterior } return imInterior } func (e *halfEdgeRecord) location(operand operand) imLocation { - assertPresence(e.edgeLabels) - - if !e.edgeLabels[operand].inSet { - return imExterior + face1Present := e.incident.inSet[operand] + face2Present := e.twin.incident.inSet[operand] + if face1Present && face2Present { + return imInterior } - - assertPresence(e.incident.labels) - assertPresence(e.twin.incident.labels) - - face1Present := e.incident.labels[operand].inSet - face2Present := e.twin.incident.labels[operand].inSet if face1Present != face2Present { return imBoundary } - return imInterior + if e.inSet[operand] { + return imInterior + } + return imExterior } func (v *vertexRecord) location(operand operand) imLocation { diff --git a/geom/dcel_label.go b/geom/dcel_label.go index 97480ca5..bef07098 100644 --- a/geom/dcel_label.go +++ b/geom/dcel_label.go @@ -1,7 +1,5 @@ package geom -import "fmt" - // operand represents either the first (A) or second (B) geometry in a binary // operation (such as Union or Covers). type operand int @@ -11,40 +9,14 @@ const ( operandB operand = 1 ) -type label struct { - // Set to true once inSet has a valid value. - populated bool - - // Indicates whether the thing being labelled is in a set or not (context - // specific to how the label is used). - inSet bool +func forEachOperand(fn func(operand operand)) { + fn(operandA) + fn(operandB) } -func newUnpopulatedLabels() [2]label { - return [2]label{} -} - -func newHalfPopulatedLabels(operand operand, inSet bool) [2]label { - var labels [2]label - labels[operand].populated = true - labels[operand].inSet = inSet - return labels -} - -func newPopulatedLabels(inSet bool) [2]label { - var labels [2]label - labels[0].populated = true - labels[1].populated = true - labels[0].inSet = inSet - labels[1].inSet = inSet - return labels -} - -func mergeLabels(dst *[2]label, src [2]label) { - dst[0].populated = dst[0].populated || src[0].populated - dst[1].populated = dst[1].populated || src[1].populated - dst[0].inSet = dst[0].inSet || src[0].inSet - dst[1].inSet = dst[1].inSet || src[1].inSet +func mergeBools(dst *[2]bool, src [2]bool) { + dst[0] = dst[0] || src[0] + dst[1] = dst[1] || src[1] } type location struct { @@ -64,29 +36,3 @@ func newLocationsOnBoundary(operand operand) [2]location { locs[operand].boundary = true return locs } - -func assertPresence(labels [2]label) { - if !labels[0].populated || !labels[1].populated { - panic(fmt.Sprintf("all presence flags in labels not set: %v", labels)) - } -} - -func selectUnion(labels [2]label) bool { - assertPresence(labels) - return labels[0].inSet || labels[1].inSet -} - -func selectIntersection(labels [2]label) bool { - assertPresence(labels) - return labels[0].inSet && labels[1].inSet -} - -func selectDifference(labels [2]label) bool { - assertPresence(labels) - return labels[0].inSet && !labels[1].inSet -} - -func selectSymmetricDifference(labels [2]label) bool { - assertPresence(labels) - return labels[0].inSet != labels[1].inSet -} diff --git a/geom/dcel_overlay.go b/geom/dcel_overlay.go index 0aecc5e0..ec62860a 100644 --- a/geom/dcel_overlay.go +++ b/geom/dcel_overlay.go @@ -30,14 +30,15 @@ func (d *doublyConnectedEdgeList) overlay(other *doublyConnectedEdgeList) { d.overlayEdges(other) d.fixVertices() d.reAssignFaces() - d.fixLabels() + d.populateInSetLabels() } func (d *doublyConnectedEdgeList) overlayVertices(other *doublyConnectedEdgeList) { for xy, otherVert := range other.vertices { vert, ok := d.vertices[xy] if ok { - mergeLabels(&vert.labels, otherVert.labels) + mergeBools(&vert.src, otherVert.src) + mergeBools(&vert.inSet, otherVert.inSet) mergeLocations(&vert.locations, otherVert.locations) } else { d.vertices[xy] = otherVert @@ -69,8 +70,9 @@ func (d *doublyConnectedEdgeList) overlayEdges(other *doublyConnectedEdgeList) { for _, e := range other.halfEdges { if existing, ok := edges.lookupEdge(e); ok { - mergeLabels(&existing.edgeLabels, e.edgeLabels) - mergeLabels(&existing.faceLabels, e.faceLabels) + mergeBools(&existing.srcEdge, e.srcEdge) + mergeBools(&existing.srcFace, e.srcFace) + mergeBools(&existing.inSet, e.inSet) } else { edges.insertEdge(e) e.origin = d.vertices[e.origin.coords] @@ -129,67 +131,52 @@ func (d *doublyConnectedEdgeList) reAssignFaces() { d.faces = nil for _, cycle := range cycles { f := &faceRecord{ - cycle: cycle, - labels: [2]label{}, // populated below + cycle: cycle, } d.faces = append(d.faces, f) forEachEdge(cycle, func(e *halfEdgeRecord) { - mergeLabels(&f.labels, e.faceLabels) + forEachOperand(func(operand operand) { + if e.srcFace[operand] { + f.inSet[operand] = true + } + }) e.incident = f }) } + // Populate inSet for faces that did not have edges from their respective + // input geometries. + forEachOperand(func(operand operand) { + visited := make(map[*faceRecord]bool) + var dfs func(*faceRecord) + dfs = func(f *faceRecord) { + if visited[f] { + return + } + visited[f] = true + forEachEdge(f.cycle, func(e *halfEdgeRecord) { + if !e.srcFace[operand] { + e.twin.incident.inSet[operand] = true + dfs(e.twin.incident) + } + }) + } + for _, f := range d.faces { + if f.inSet[operand] { + dfs(f) + } + } + }) + // If we couldn't find any cycles, then we wouldn't have constructed any // faces. This happens in the case where there are only point geometries. // We need to artificially create an infinite face. if len(d.faces) == 0 { d.faces = append(d.faces, &faceRecord{ - cycle: nil, - labels: newPopulatedLabels(false), + cycle: nil, + inSet: [2]bool{}, }) } - - for _, face := range d.faces { - d.completePartialFaceLabel(face) - } -} - -// completePartialFaceLabel checks to see if the face label for the given face -// is complete (i.e. contains a part for both A and B). If it's not complete, -// then in searches adjacent faces until it finds a face that it can copy the -// missing part of the label from. This situation occurs whenever a face in the -// overlay DCEL doesn't have any edges from one of the original geometries. -func (d *doublyConnectedEdgeList) completePartialFaceLabel(face *faceRecord) { - labelIsComplete := func() bool { - return face.labels[0].populated && face.labels[1].populated - } - if labelIsComplete() { - return - } - expanded := make(map[*faceRecord]bool) - stack := []*faceRecord{face} - for len(stack) > 0 { - popped := stack[len(stack)-1] - stack = stack[:len(stack)-1] - adjacent := adjacentFaces(popped) - expanded[popped] = true - for _, adj := range adjacent { - face.labels = completeLabels(face.labels, adj.labels) - if labelIsComplete() { - return - } - if !expanded[adj] { - stack = append(stack, adj) - } - } - } - - // It's possible that we're still missing part of the face label. This - // could happen if one of the inputs is a Point/MultiPoint input because - // its associated ghost lines would not add to the label pool. We can - // safely fill in the presence bits for this case. - face.labels[0].populated = true - face.labels[1].populated = true } // adjacentFaces finds all of the faces that adjacent to f. @@ -206,36 +193,18 @@ func adjacentFaces(f *faceRecord) []*faceRecord { return adjacent } -// completeLabels copies any missing portion (part A or part B) of the label -// from donor to recipient, and then returns recipient. -func completeLabels(recipient, donor [2]label) [2]label { - for i := 0; i < 2; i++ { - if !recipient[i].populated && donor[i].populated { - recipient[i].populated = true - recipient[i].inSet = donor[i].inSet - } - } - return recipient -} - -// fixLabels updates edge and vertex labels after performing an overlay. -func (d *doublyConnectedEdgeList) fixLabels() { +// populateInSetLabels populates the inSet labels for edges and vertices. +func (d *doublyConnectedEdgeList) populateInSetLabels() { for _, e := range d.halfEdges { // Copy labels from incident faces into edge since the edge represents // the (closed) border of the face. - mergeLabels(&e.edgeLabels, e.incident.labels) - mergeLabels(&e.edgeLabels, e.twin.incident.labels) - - // If we haven't seen an edge label yet for one of the two input - // geometries, we can assume that we'll never see it. So we mark off - // that side as having the bit populated. - e.edgeLabels[0].populated = true - e.edgeLabels[1].populated = true + e.inSet[0] = e.srcEdge[0] || e.incident.inSet[0] || e.twin.incident.inSet[0] + e.inSet[1] = e.srcEdge[1] || e.incident.inSet[1] || e.twin.incident.inSet[1] // Copy edge labels onto the labels of adjacent vertices. This is // because the vertices represent the endpoints of the edges, and // should have at least those bits set. - mergeLabels(&e.origin.labels, e.edgeLabels) - mergeLabels(&e.origin.labels, e.prev.edgeLabels) + e.origin.inSet[0] = e.origin.src[0] || e.inSet[0] || e.prev.inSet[0] + e.origin.inSet[1] = e.origin.src[1] || e.inSet[1] || e.prev.inSet[1] } } diff --git a/geom/dcel_test.go b/geom/dcel_test.go index c92b9391..b8540bf7 100644 --- a/geom/dcel_test.go +++ b/geom/dcel_test.go @@ -15,47 +15,24 @@ type DCELSpec struct { Faces []FaceSpec } -type LabelSpec struct { - APopulated bool - BPopulated bool - AInSet bool - BInSet bool -} - -type LabelOption func(LabelSpec) LabelSpec - -func OperandA(inSet bool) LabelOption { - return func(s LabelSpec) LabelSpec { - s.APopulated = true - s.AInSet = inSet - return s - } -} - -func OperandB(inSet bool) LabelOption { - return func(s LabelSpec) LabelSpec { - s.BPopulated = true - s.BInSet = inSet - return s - } -} - type FaceSpec struct { // Origin and destination of an edge that is incident to the face. First XY Second XY Cycle []XY - Labels []LabelOption + InSet [2]bool } type EdgeSpec struct { - EdgeLabels []LabelOption - FaceLabels []LabelOption - Sequence []XY + SrcEdge [2]bool + SrcFace [2]bool + InSet [2]bool + Sequence []XY } type VertexSpec struct { - Labels []LabelOption + Src [2]bool + InSet [2]bool Vertices []XY } @@ -99,7 +76,14 @@ func CheckDCEL(t *testing.T, dcel *doublyConnectedEdgeList, spec DCELSpec) { t.Errorf("no vertex %v", wantXY) continue } - CheckLabels(t, vert.labels, want.Labels) + if want.Src != vert.src { + t.Errorf("%v: src mismatch, want:%v got:%v", + wantXY, want.Src, vert.src) + } + if want.InSet != vert.inSet { + t.Errorf("%v: inSet mismatch, want:%v got:%v", + wantXY, want.InSet, vert.inSet) + } delete(unchecked, vert) } } @@ -153,8 +137,18 @@ func CheckDCEL(t *testing.T, dcel *doublyConnectedEdgeList, spec DCELSpec) { t.Fatalf("could not find edge spec matching edge: %v", e) } - CheckLabels(t, e.edgeLabels, want.EdgeLabels) - CheckLabels(t, e.faceLabels, want.FaceLabels) + if want.SrcEdge != e.srcEdge { + t.Errorf("%v: srcEdge mismatch, want:%v got:%v", + want.Sequence, want.SrcEdge, e.srcEdge) + } + if want.SrcFace != e.srcFace { + t.Errorf("%v: srcFace mismatch, want:%v got:%v", + want.Sequence, want.SrcFace, e.srcFace) + } + if want.InSet != e.inSet { + t.Errorf("%v: inSet mismatch, want:%v got:%v", + want.Sequence, want.InSet, e.inSet) + } } }) @@ -162,7 +156,10 @@ func CheckDCEL(t *testing.T, dcel *doublyConnectedEdgeList, spec DCELSpec) { t.Run(fmt.Sprintf("face_%d", i), func(t *testing.T) { got := findEdge(t, dcel, want.First, want.Second).incident CheckCycle(t, got, got.cycle, want.Cycle) - CheckLabels(t, got.labels, want.Labels) + if want.InSet != got.inSet { + t.Errorf("%v: inSet mismatch, want:%v got:%v", + want.Cycle, want.InSet, got.inSet) + } }) } } @@ -292,28 +289,6 @@ outer: t.Errorf("XY sequences don't match:\ngot: %v\nwant: %v", got, want) } -func CheckLabels(t *testing.T, got [2]label, wantOpts []LabelOption) { - t.Helper() - - var want LabelSpec - for _, opt := range wantOpts { - want = opt(want) - } - - if want.APopulated != got[0].populated { - t.Errorf("A populated: want=%v got=%v", want.APopulated, got[0].populated) - } - if want.BPopulated != got[1].populated { - t.Errorf("B populated: want=%v got=%v", want.BPopulated, got[1].populated) - } - if want.AInSet != got[0].inSet { - t.Errorf("A inSet: want=%v got=%v", want.AInSet, got[0].inSet) - } - if want.BInSet != got[1].inSet { - t.Errorf("B inSet: want=%v got=%v", want.BInSet, got[1].inSet) - } -} - func createOverlayFromWKTs(t *testing.T, wktA, wktB string) *doublyConnectedEdgeList { gA, err := UnmarshalWKT(wktA) if err != nil { @@ -362,19 +337,19 @@ func TestGraphTriangle(t *testing.T) { NumEdges: 2, NumFaces: 0, Vertices: []VertexSpec{{ - Labels: []LabelOption{OperandA(true)}, + Src: [2]bool{true}, Vertices: []XY{v0}, }}, Edges: []EdgeSpec{ { - EdgeLabels: []LabelOption{OperandA(true)}, - FaceLabels: []LabelOption{OperandA(true)}, - Sequence: []XY{v0, v1, v2, v0}, + SrcEdge: [2]bool{true}, + SrcFace: [2]bool{true}, + Sequence: []XY{v0, v1, v2, v0}, }, { - EdgeLabels: []LabelOption{OperandA(true)}, - FaceLabels: []LabelOption{OperandA(false)}, - Sequence: []XY{v0, v2, v1, v0}, + SrcEdge: [2]bool{true}, + SrcFace: [2]bool{false}, + Sequence: []XY{v0, v2, v1, v0}, }, }, Faces: nil, @@ -425,39 +400,39 @@ func TestGraphWithHoles(t *testing.T) { NumEdges: 6, NumFaces: 0, Vertices: []VertexSpec{{ - Labels: []LabelOption{OperandB(true)}, + Src: [2]bool{false, true}, Vertices: []XY{v0, v4, v8}, }}, Edges: []EdgeSpec{ { - EdgeLabels: []LabelOption{OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, - Sequence: []XY{v0, v1, v2, v3, v0}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + Sequence: []XY{v0, v1, v2, v3, v0}, }, { - EdgeLabels: []LabelOption{OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, - Sequence: []XY{v0, v3, v2, v1, v0}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + Sequence: []XY{v0, v3, v2, v1, v0}, }, { - EdgeLabels: []LabelOption{OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, - Sequence: []XY{v4, v5, v6, v7, v4}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + Sequence: []XY{v4, v5, v6, v7, v4}, }, { - EdgeLabels: []LabelOption{OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, - Sequence: []XY{v4, v7, v6, v5, v4}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + Sequence: []XY{v4, v7, v6, v5, v4}, }, { - EdgeLabels: []LabelOption{OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, - Sequence: []XY{v8, v9, v10, v11, v8}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + Sequence: []XY{v8, v9, v10, v11, v8}, }, { - EdgeLabels: []LabelOption{OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, - Sequence: []XY{v8, v11, v10, v9, v8}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + Sequence: []XY{v8, v11, v10, v9, v8}, }, }, Faces: nil, @@ -495,29 +470,29 @@ func TestGraphWithMultiPolygon(t *testing.T) { NumEdges: 4, NumFaces: 0, Vertices: []VertexSpec{{ - Labels: []LabelOption{OperandB(true)}, + Src: [2]bool{false, true}, Vertices: []XY{v0, v4}, }}, Edges: []EdgeSpec{ { - EdgeLabels: []LabelOption{OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, - Sequence: []XY{v0, v1, v2, v3, v0}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + Sequence: []XY{v0, v1, v2, v3, v0}, }, { - EdgeLabels: []LabelOption{OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, - Sequence: []XY{v0, v3, v2, v1, v0}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + Sequence: []XY{v0, v3, v2, v1, v0}, }, { - EdgeLabels: []LabelOption{OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, - Sequence: []XY{v4, v5, v6, v7, v4}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + Sequence: []XY{v4, v5, v6, v7, v4}, }, { - EdgeLabels: []LabelOption{OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, - Sequence: []XY{v4, v7, v6, v5, v4}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + Sequence: []XY{v4, v7, v6, v5, v4}, }, }, Faces: nil, @@ -555,29 +530,29 @@ func TestGraphMultiLineString(t *testing.T) { NumEdges: 4, NumFaces: 0, Vertices: []VertexSpec{{ - Labels: []LabelOption{OperandA(true)}, + Src: [2]bool{true}, Vertices: []XY{v0, v2, v3, v5}, }}, Edges: []EdgeSpec{ { - EdgeLabels: []LabelOption{OperandA(true)}, - FaceLabels: nil, - Sequence: []XY{v0, v1, v2}, + SrcEdge: [2]bool{true}, + SrcFace: [2]bool{false}, + Sequence: []XY{v0, v1, v2}, }, { - EdgeLabels: []LabelOption{OperandA(true)}, - FaceLabels: nil, - Sequence: []XY{v2, v1, v0}, + SrcEdge: [2]bool{true}, + SrcFace: [2]bool{false}, + Sequence: []XY{v2, v1, v0}, }, { - EdgeLabels: []LabelOption{OperandA(true)}, - FaceLabels: nil, - Sequence: []XY{v3, v4, v5}, + SrcEdge: [2]bool{true}, + SrcFace: [2]bool{false}, + Sequence: []XY{v3, v4, v5}, }, { - EdgeLabels: []LabelOption{OperandA(true)}, - FaceLabels: nil, - Sequence: []XY{v5, v4, v3}, + SrcEdge: [2]bool{true}, + SrcFace: [2]bool{false}, + Sequence: []XY{v5, v4, v3}, }, }, Faces: nil, @@ -611,49 +586,41 @@ func TestGraphSelfOverlappingLineString(t *testing.T) { NumEdges: 8, NumFaces: 0, Vertices: []VertexSpec{{ - Labels: []LabelOption{OperandA(true)}, + Src: [2]bool{true}, Vertices: []XY{v0, v1, v2, v4}, }}, Edges: []EdgeSpec{ { - EdgeLabels: []LabelOption{OperandA(true)}, - FaceLabels: nil, - Sequence: []XY{v0, v1}, + SrcEdge: [2]bool{true}, + Sequence: []XY{v0, v1}, }, { - EdgeLabels: []LabelOption{OperandA(true)}, - FaceLabels: nil, - Sequence: []XY{v1, v0}, + SrcEdge: [2]bool{true}, + Sequence: []XY{v1, v0}, }, { - EdgeLabels: []LabelOption{OperandA(true)}, - FaceLabels: nil, - Sequence: []XY{v1, v2}, + SrcEdge: [2]bool{true}, + Sequence: []XY{v1, v2}, }, { - EdgeLabels: []LabelOption{OperandA(true)}, - FaceLabels: nil, - Sequence: []XY{v2, v1}, + SrcEdge: [2]bool{true}, + Sequence: []XY{v2, v1}, }, { - EdgeLabels: []LabelOption{OperandA(true)}, - FaceLabels: nil, - Sequence: []XY{v1, v3, v2}, + SrcEdge: [2]bool{true}, + Sequence: []XY{v1, v3, v2}, }, { - EdgeLabels: []LabelOption{OperandA(true)}, - FaceLabels: nil, - Sequence: []XY{v2, v3, v1}, + SrcEdge: [2]bool{true}, + Sequence: []XY{v2, v3, v1}, }, { - EdgeLabels: []LabelOption{OperandA(true)}, - FaceLabels: nil, - Sequence: []XY{v2, v4}, + SrcEdge: [2]bool{true}, + Sequence: []XY{v2, v4}, }, { - EdgeLabels: []LabelOption{OperandA(true)}, - FaceLabels: nil, - Sequence: []XY{v4, v2}, + SrcEdge: [2]bool{true}, + Sequence: []XY{v4, v2}, }, }, Faces: nil, @@ -686,34 +653,30 @@ func TestGraphGhostDeduplication(t *testing.T) { NumFaces: 0, Vertices: []VertexSpec{ { - Labels: []LabelOption{OperandA(true)}, + Src: [2]bool{true}, Vertices: []XY{v0, v1}, }, { - Labels: nil, + Src: [2]bool{false}, Vertices: []XY{v2}, }, }, Edges: []EdgeSpec{ { - EdgeLabels: []LabelOption{OperandA(true)}, - FaceLabels: nil, - Sequence: []XY{v0, v1}, + SrcEdge: [2]bool{true}, + Sequence: []XY{v0, v1}, }, { - EdgeLabels: []LabelOption{OperandA(true)}, - FaceLabels: nil, - Sequence: []XY{v1, v0}, + SrcEdge: [2]bool{true}, + Sequence: []XY{v1, v0}, }, { - EdgeLabels: []LabelOption{OperandA(false)}, - FaceLabels: nil, - Sequence: []XY{v0, v2}, + SrcEdge: [2]bool{false}, + Sequence: []XY{v0, v2}, }, { - EdgeLabels: []LabelOption{OperandA(false)}, - FaceLabels: nil, - Sequence: []XY{v2, v0}, + SrcEdge: [2]bool{false}, + Sequence: []XY{v2, v0}, }, }, }) @@ -757,64 +720,76 @@ func TestGraphOverlayDisjoint(t *testing.T) { NumFaces: 4, Vertices: []VertexSpec{ { - Labels: []LabelOption{OperandA(true), OperandB(false)}, + Src: [2]bool{true, false}, + InSet: [2]bool{true, false}, Vertices: []XY{v0, v2}, }, { - Labels: []LabelOption{OperandA(false), OperandB(true)}, + Src: [2]bool{false, true}, + InSet: [2]bool{false, true}, Vertices: []XY{v4}, }, }, Edges: []EdgeSpec{ { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(true)}, - Sequence: []XY{v0, v1, v2}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{true, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v0, v1, v2}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(false)}, - Sequence: []XY{v2, v1, v0}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v2, v1, v0}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(true)}, - Sequence: []XY{v2, v3, v0}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{true, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v2, v3, v0}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(false)}, - Sequence: []XY{v0, v3, v2}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v0, v3, v2}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, - Sequence: []XY{v4, v5, v6, v7, v4}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + InSet: [2]bool{false, true}, + Sequence: []XY{v4, v5, v6, v7, v4}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, - Sequence: []XY{v4, v7, v6, v5, v4}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, true}, + Sequence: []XY{v4, v7, v6, v5, v4}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: nil, - Sequence: []XY{v0, v2}, + SrcEdge: [2]bool{false, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v0, v2}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: nil, - Sequence: []XY{v2, v0}, + SrcEdge: [2]bool{false, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v2, v0}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(false)}, - FaceLabels: nil, - Sequence: []XY{v2, v4}, + SrcEdge: [2]bool{false, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, false}, + Sequence: []XY{v2, v4}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(false)}, - FaceLabels: nil, - Sequence: []XY{v4, v2}, + SrcEdge: [2]bool{false, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, false}, + Sequence: []XY{v4, v2}, }, }, Faces: []FaceSpec{ @@ -823,28 +798,28 @@ func TestGraphOverlayDisjoint(t *testing.T) { First: v2, Second: v1, Cycle: []XY{v2, v1, v0, v3, v2, v4, v7, v6, v5, v4, v2}, - Labels: []LabelOption{OperandA(false), OperandB(false)}, + InSet: [2]bool{false, false}, }, { // f1 First: v0, Second: v1, Cycle: []XY{v0, v1, v2, v0}, - Labels: []LabelOption{OperandA(true), OperandB(false)}, + InSet: [2]bool{true, false}, }, { // f2 First: v2, Second: v3, Cycle: []XY{v2, v3, v0, v2}, - Labels: []LabelOption{OperandA(true), OperandB(false)}, + InSet: [2]bool{true, false}, }, { // f3 First: v4, Second: v5, Cycle: []XY{v4, v5, v6, v7, v4}, - Labels: []LabelOption{OperandA(false), OperandB(true)}, + InSet: [2]bool{false, true}, }, }, }) @@ -889,88 +864,105 @@ func TestGraphOverlayIntersecting(t *testing.T) { NumFaces: 5, Vertices: []VertexSpec{ { - Labels: []LabelOption{OperandA(true), OperandB(false)}, + Src: [2]bool{true, false}, + InSet: [2]bool{true, false}, Vertices: []XY{v0}, }, { - Labels: []LabelOption{OperandA(false), OperandB(true)}, + Src: [2]bool{false, true}, + InSet: [2]bool{false, true}, Vertices: []XY{v5}, }, { - Labels: []LabelOption{OperandA(true), OperandB(true)}, + Src: [2]bool{true, true}, + InSet: [2]bool{true, true}, Vertices: []XY{v2, v4}, }, }, Edges: []EdgeSpec{ { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(true)}, - Sequence: []XY{v4, v0}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{true, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v4, v0}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(true)}, - Sequence: []XY{v0, v1, v2}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{true, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v0, v1, v2}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(false)}, - Sequence: []XY{v2, v1, v0}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v2, v1, v0}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(false)}, - Sequence: []XY{v0, v4}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v0, v4}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, - Sequence: []XY{v2, v6, v7, v5}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + InSet: [2]bool{false, true}, + Sequence: []XY{v2, v6, v7, v5}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, - Sequence: []XY{v5, v4}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + InSet: [2]bool{false, true}, + Sequence: []XY{v5, v4}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, - Sequence: []XY{v4, v5}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, true}, + Sequence: []XY{v4, v5}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, - Sequence: []XY{v5, v7, v6, v2}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, true}, + Sequence: []XY{v5, v7, v6, v2}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, - Sequence: []XY{v2, v4}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, true}, + Sequence: []XY{v2, v4}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, - Sequence: []XY{v4, v2}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + InSet: [2]bool{true, true}, + Sequence: []XY{v4, v2}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(true)}, - FaceLabels: []LabelOption{OperandA(false)}, - Sequence: []XY{v4, v3, v2}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, true}, + Sequence: []XY{v4, v3, v2}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(true)}, - FaceLabels: []LabelOption{OperandA(true)}, - Sequence: []XY{v2, v3, v4}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{true, false}, + InSet: [2]bool{true, true}, + Sequence: []XY{v2, v3, v4}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(false)}, - FaceLabels: nil, - Sequence: []XY{v5, v0}, + SrcEdge: [2]bool{false, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, false}, + Sequence: []XY{v5, v0}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(false)}, - FaceLabels: nil, - Sequence: []XY{v0, v5}, + SrcEdge: [2]bool{false, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, false}, + Sequence: []XY{v0, v5}, }, }, Faces: []FaceSpec{ @@ -979,35 +971,35 @@ func TestGraphOverlayIntersecting(t *testing.T) { First: v2, Second: v1, Cycle: []XY{v2, v1, v0, v5, v7, v6, v2}, - Labels: []LabelOption{OperandA(false), OperandB(false)}, + InSet: [2]bool{false, false}, }, { // f1 First: v0, Second: v1, Cycle: []XY{v0, v1, v2, v4, v0}, - Labels: []LabelOption{OperandA(true), OperandB(false)}, + InSet: [2]bool{true, false}, }, { // f2 First: v2, Second: v6, Cycle: []XY{v2, v6, v7, v5, v4, v3, v2}, - Labels: []LabelOption{OperandA(false), OperandB(true)}, + InSet: [2]bool{false, true}, }, { // f3 First: v4, Second: v2, Cycle: []XY{v4, v2, v3, v4}, - Labels: []LabelOption{OperandA(true), OperandB(true)}, + InSet: [2]bool{true, true}, }, { // f4 First: v0, Second: v4, Cycle: []XY{v0, v4, v5, v0}, - Labels: []LabelOption{OperandA(false), OperandB(false)}, + InSet: [2]bool{false, false}, }, }, }) @@ -1048,44 +1040,52 @@ func TestGraphOverlayInside(t *testing.T) { NumFaces: 3, Vertices: []VertexSpec{ { - Labels: []LabelOption{OperandA(true), OperandB(false)}, + Src: [2]bool{true, false}, + InSet: [2]bool{true, false}, Vertices: []XY{v0}, }, { - Labels: []LabelOption{OperandA(true), OperandB(true)}, + Src: [2]bool{false, true}, + InSet: [2]bool{true, true}, Vertices: []XY{v4}, }, }, Edges: []EdgeSpec{ { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(false)}, - Sequence: []XY{v0, v3, v2, v1, v0}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v0, v3, v2, v1, v0}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(true)}, - Sequence: []XY{v0, v1, v2, v3, v0}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{true, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v0, v1, v2, v3, v0}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, - Sequence: []XY{v4, v7, v6, v5, v4}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, true}, + Sequence: []XY{v4, v7, v6, v5, v4}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, - Sequence: []XY{v4, v5, v6, v7, v4}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + InSet: [2]bool{true, true}, + Sequence: []XY{v4, v5, v6, v7, v4}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: nil, - Sequence: []XY{v0, v4}, + SrcEdge: [2]bool{false, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v0, v4}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: nil, - Sequence: []XY{v4, v0}, + SrcEdge: [2]bool{false, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v4, v0}, }, }, Faces: []FaceSpec{ @@ -1094,21 +1094,21 @@ func TestGraphOverlayInside(t *testing.T) { First: v0, Second: v3, Cycle: []XY{v0, v3, v2, v1, v0}, - Labels: []LabelOption{OperandA(false), OperandB(false)}, + InSet: [2]bool{false, false}, }, { // f1 First: v0, Second: v1, Cycle: []XY{v0, v1, v2, v3, v0, v4, v7, v6, v5, v4, v0}, - Labels: []LabelOption{OperandA(true), OperandB(false)}, + InSet: [2]bool{true, false}, }, { // f2 First: v4, Second: v5, Cycle: []XY{v4, v5, v6, v7, v4}, - Labels: []LabelOption{OperandA(true), OperandB(true)}, + InSet: [2]bool{true, true}, }, }, }) @@ -1164,149 +1164,178 @@ func TestGraphOverlayReproduceHorizontalHoleLinkageBug(t *testing.T) { NumFaces: 7, Vertices: []VertexSpec{ { - Labels: []LabelOption{OperandA(true), OperandB(false)}, + Src: [2]bool{true, false}, + InSet: [2]bool{true, false}, Vertices: []XY{v1, v2, v5}, }, { - Labels: []LabelOption{OperandA(true), OperandB(true)}, + Src: [2]bool{true, true}, + InSet: [2]bool{true, true}, Vertices: []XY{v17, v18}, }, { - Labels: []LabelOption{OperandA(false), OperandB(true)}, + Src: [2]bool{false, true}, + InSet: [2]bool{false, true}, Vertices: []XY{v9, v12, v13}, }, }, Edges: []EdgeSpec{ { - EdgeLabels: []LabelOption{OperandA(false), OperandB(false)}, - FaceLabels: nil, - Sequence: []XY{v5, v2}, + SrcEdge: [2]bool{false, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, false}, + Sequence: []XY{v5, v2}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(false)}, - FaceLabels: nil, - Sequence: []XY{v2, v5}, + SrcEdge: [2]bool{false, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, false}, + Sequence: []XY{v2, v5}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(false)}, - FaceLabels: nil, - Sequence: []XY{v12, v13}, + SrcEdge: [2]bool{false, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, false}, + Sequence: []XY{v12, v13}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(false)}, - FaceLabels: nil, - Sequence: []XY{v13, v12}, + SrcEdge: [2]bool{false, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, false}, + Sequence: []XY{v13, v12}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(true)}, - Sequence: []XY{v5, v6, v7, v8, v5}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{true, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v5, v6, v7, v8, v5}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(false)}, - Sequence: []XY{v5, v8, v7, v6, v5}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v5, v8, v7, v6, v5}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, - Sequence: []XY{v13, v14, v15, v16, v13}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + InSet: [2]bool{false, true}, + Sequence: []XY{v13, v14, v15, v16, v13}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, - Sequence: []XY{v13, v16, v15, v14, v13}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, true}, + Sequence: []XY{v13, v16, v15, v14, v13}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(false)}, - Sequence: []XY{v2, v1}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v2, v1}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(false)}, - Sequence: []XY{v1, v17}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v1, v17}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, - Sequence: []XY{v17, v9}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, true}, + Sequence: []XY{v17, v9}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, - Sequence: []XY{v9, v12}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, true}, + Sequence: []XY{v9, v12}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(true)}, - Sequence: []XY{v17, v1}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{true, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v17, v1}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(true)}, - Sequence: []XY{v1, v2}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{true, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v1, v2}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, - Sequence: []XY{v12, v9}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + InSet: [2]bool{false, true}, + Sequence: []XY{v12, v9}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, - Sequence: []XY{v9, v17}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + InSet: [2]bool{false, true}, + Sequence: []XY{v9, v17}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, - Sequence: []XY{v18, v10, v17}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, true}, + Sequence: []XY{v18, v10, v17}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(true)}, - FaceLabels: []LabelOption{OperandA(false)}, - Sequence: []XY{v17, v4, v18}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, true}, + Sequence: []XY{v17, v4, v18}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, - Sequence: []XY{v17, v10, v18}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + InSet: [2]bool{true, true}, + Sequence: []XY{v17, v10, v18}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(true)}, - FaceLabels: []LabelOption{OperandA(true)}, - Sequence: []XY{v18, v4, v17}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{true, false}, + InSet: [2]bool{true, true}, + Sequence: []XY{v18, v4, v17}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(false)}, - FaceLabels: nil, - Sequence: []XY{v1, v9}, + SrcEdge: [2]bool{false, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, false}, + Sequence: []XY{v1, v9}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(false)}, - FaceLabels: nil, - Sequence: []XY{v9, v1}, + SrcEdge: [2]bool{false, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, false}, + Sequence: []XY{v9, v1}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(false)}, - Sequence: []XY{v18, v3, v2}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v18, v3, v2}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(true)}, - Sequence: []XY{v2, v3, v18}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{true, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v2, v3, v18}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, - Sequence: []XY{v18, v11, v12}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + InSet: [2]bool{false, true}, + Sequence: []XY{v18, v11, v12}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, - Sequence: []XY{v12, v11, v18}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, true}, + Sequence: []XY{v12, v11, v18}, }, }, Faces: []FaceSpec{ @@ -1319,49 +1348,49 @@ func TestGraphOverlayReproduceHorizontalHoleLinkageBug(t *testing.T) { v6, v5, v2, v1, v9, v12, v13, v16, v15, v14, v13, v12, }, - Labels: []LabelOption{OperandA(false), OperandB(false)}, + InSet: [2]bool{false, false}, }, { // f1 First: v5, Second: v6, Cycle: []XY{v5, v6, v7, v8, v5}, - Labels: []LabelOption{OperandA(true), OperandB(false)}, + InSet: [2]bool{true, false}, }, { // f2 First: v13, Second: v14, Cycle: []XY{v13, v14, v15, v16, v13}, - Labels: []LabelOption{OperandA(false), OperandB(true)}, + InSet: [2]bool{false, true}, }, { // f3 First: v1, Second: v2, Cycle: []XY{v1, v2, v3, v18, v10, v17, v1}, - Labels: []LabelOption{OperandA(true), OperandB(false)}, + InSet: [2]bool{true, false}, }, { // f4 First: v17, Second: v4, Cycle: []XY{v17, v4, v18, v11, v12, v9, v17}, - Labels: []LabelOption{OperandA(false), OperandB(true)}, + InSet: [2]bool{false, true}, }, { // f5 First: v17, Second: v10, Cycle: []XY{v17, v10, v18, v4, v17}, - Labels: []LabelOption{OperandA(true), OperandB(true)}, + InSet: [2]bool{true, true}, }, { // f6 First: v1, Second: v17, Cycle: []XY{v1, v17, v9, v1}, - Labels: []LabelOption{OperandA(false), OperandB(false)}, + InSet: [2]bool{false, false}, }, }, }) @@ -1394,53 +1423,63 @@ func TestGraphOverlayFullyOverlappingEdge(t *testing.T) { Vertices: []VertexSpec{ { Vertices: []XY{v0}, - Labels: []LabelOption{OperandA(true), OperandB(false)}, + Src: [2]bool{true, false}, + InSet: [2]bool{true, false}, }, { Vertices: []XY{v1, v4}, - Labels: []LabelOption{OperandA(true), OperandB(true)}, + Src: [2]bool{true, true}, + InSet: [2]bool{true, true}, }, }, Edges: []EdgeSpec{ { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(false)}, - Sequence: []XY{v1, v0}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v1, v0}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(false)}, - Sequence: []XY{v0, v5, v4}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v0, v5, v4}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(true)}, - Sequence: []XY{v4, v5, v0}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{true, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v4, v5, v0}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(true)}, - Sequence: []XY{v0, v1}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{true, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v0, v1}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, - Sequence: []XY{v4, v3, v2, v1}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, true}, + Sequence: []XY{v4, v3, v2, v1}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, - Sequence: []XY{v1, v2, v3, v4}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + InSet: [2]bool{false, true}, + Sequence: []XY{v1, v2, v3, v4}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(true)}, - FaceLabels: []LabelOption{OperandA(true), OperandB(false)}, - Sequence: []XY{v1, v4}, + SrcEdge: [2]bool{true, true}, + SrcFace: [2]bool{true, false}, + InSet: [2]bool{true, true}, + Sequence: []XY{v1, v4}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(true)}, - FaceLabels: []LabelOption{OperandA(false), OperandB(true)}, - Sequence: []XY{v4, v1}, + SrcEdge: [2]bool{true, true}, + SrcFace: [2]bool{false, true}, + InSet: [2]bool{true, true}, + Sequence: []XY{v4, v1}, }, }, Faces: []FaceSpec{ @@ -1448,19 +1487,19 @@ func TestGraphOverlayFullyOverlappingEdge(t *testing.T) { First: v1, Second: v0, Cycle: []XY{v0, v5, v4, v3, v2, v1, v0}, - Labels: []LabelOption{OperandA(false), OperandB(false)}, + InSet: [2]bool{false, false}, }, { First: v1, Second: v4, Cycle: []XY{v1, v4, v5, v0, v1}, - Labels: []LabelOption{OperandA(true), OperandB(false)}, + InSet: [2]bool{true, false}, }, { First: v1, Second: v2, Cycle: []XY{v1, v2, v3, v4, v1}, - Labels: []LabelOption{OperandA(false), OperandB(true)}, + InSet: [2]bool{false, true}, }, }, }) @@ -1498,79 +1537,92 @@ func TestGraphOverlayPartiallyOverlappingEdge(t *testing.T) { Vertices: []VertexSpec{ { Vertices: []XY{v0}, - Labels: []LabelOption{OperandA(true), OperandB(false)}, + Src: [2]bool{true, false}, + InSet: [2]bool{true, false}, }, { Vertices: []XY{v2}, - Labels: []LabelOption{OperandA(false), OperandB(true)}, + Src: [2]bool{false, true}, + InSet: [2]bool{false, true}, }, { Vertices: []XY{v1, v5}, - Labels: []LabelOption{OperandA(true), OperandB(true)}, + Src: [2]bool{true, true}, + InSet: [2]bool{true, true}, }, }, Edges: []EdgeSpec{ { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(false)}, - Sequence: []XY{v1, v0}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v1, v0}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(false)}, - Sequence: []XY{v0, v7, v6, v5}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v0, v7, v6, v5}, }, - { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(true)}, - Sequence: []XY{v5, v6, v7, v0}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{true, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v5, v6, v7, v0}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: []LabelOption{OperandA(true)}, - Sequence: []XY{v0, v1}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{true, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v0, v1}, }, - { - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, - Sequence: []XY{v5, v4, v3, v2}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, true}, + Sequence: []XY{v5, v4, v3, v2}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, - Sequence: []XY{v2, v1}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, true}, + Sequence: []XY{v2, v1}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, - Sequence: []XY{v1, v2}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + InSet: [2]bool{false, true}, + Sequence: []XY{v1, v2}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, - Sequence: []XY{v2, v3, v4, v5}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + InSet: [2]bool{false, true}, + Sequence: []XY{v2, v3, v4, v5}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(true)}, - FaceLabels: []LabelOption{OperandA(true), OperandB(false)}, - Sequence: []XY{v1, v5}, + SrcEdge: [2]bool{true, true}, + SrcFace: [2]bool{true, false}, + InSet: [2]bool{true, true}, + Sequence: []XY{v1, v5}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(true)}, - FaceLabels: []LabelOption{OperandA(false), OperandB(true)}, - Sequence: []XY{v5, v1}, + SrcEdge: [2]bool{true, true}, + SrcFace: [2]bool{false, true}, + InSet: [2]bool{true, true}, + Sequence: []XY{v5, v1}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(false)}, - FaceLabels: nil, - Sequence: []XY{v2, v0}, + SrcEdge: [2]bool{false, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, false}, + Sequence: []XY{v2, v0}, }, { - EdgeLabels: []LabelOption{OperandA(false), OperandB(false)}, - FaceLabels: nil, - Sequence: []XY{v0, v2}, + SrcEdge: [2]bool{false, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, false}, + Sequence: []XY{v0, v2}, }, }, Faces: []FaceSpec{ @@ -1579,28 +1631,28 @@ func TestGraphOverlayPartiallyOverlappingEdge(t *testing.T) { First: v0, Second: v7, Cycle: []XY{v0, v7, v6, v5, v4, v3, v2, v0}, - Labels: []LabelOption{OperandA(false), OperandB(false)}, + InSet: [2]bool{false, false}, }, { // f1 First: v0, Second: v1, Cycle: []XY{v0, v1, v5, v6, v7, v0}, - Labels: []LabelOption{OperandA(true), OperandB(false)}, + InSet: [2]bool{true, false}, }, { // f2 First: v1, Second: v2, Cycle: []XY{v1, v2, v3, v4, v5, v1}, - Labels: []LabelOption{OperandA(false), OperandB(true)}, + InSet: [2]bool{false, true}, }, { // f3 First: v2, Second: v1, Cycle: []XY{v2, v1, v0, v2}, - Labels: []LabelOption{OperandA(false), OperandB(false)}, + InSet: [2]bool{false, false}, }, }, }) @@ -1630,19 +1682,22 @@ func TestGraphOverlayFullyOverlappingCycle(t *testing.T) { NumEdges: 2, NumFaces: 2, Vertices: []VertexSpec{{ - Labels: []LabelOption{OperandA(true), OperandB(true)}, + Src: [2]bool{true, true}, + InSet: [2]bool{true, true}, Vertices: []XY{v0}, }}, Edges: []EdgeSpec{ { - EdgeLabels: []LabelOption{OperandA(true), OperandB(true)}, - FaceLabels: []LabelOption{OperandA(true), OperandB(true)}, - Sequence: []XY{v0, v1, v2, v3, v0}, + SrcEdge: [2]bool{true, true}, + SrcFace: [2]bool{true, true}, + InSet: [2]bool{true, true}, + Sequence: []XY{v0, v1, v2, v3, v0}, }, { - EdgeLabels: []LabelOption{OperandA(true), OperandB(true)}, - FaceLabels: []LabelOption{OperandA(false), OperandB(false)}, - Sequence: []XY{v0, v3, v2, v1, v0}, + SrcEdge: [2]bool{true, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, true}, + Sequence: []XY{v0, v3, v2, v1, v0}, }, }, Faces: []FaceSpec{ @@ -1651,14 +1706,14 @@ func TestGraphOverlayFullyOverlappingCycle(t *testing.T) { First: v0, Second: v3, Cycle: []XY{v0, v3, v2, v1, v0}, - Labels: []LabelOption{OperandA(false), OperandB(false)}, + InSet: [2]bool{false, false}, }, { // f1 First: v0, Second: v1, Cycle: []XY{v0, v1, v2, v3, v0}, - Labels: []LabelOption{OperandA(true), OperandB(true)}, + InSet: [2]bool{true, true}, }, }, }) @@ -1686,37 +1741,53 @@ func TestGraphOverlayTwoLineStringsIntersectingAtEndpoints(t *testing.T) { NumEdges: 4, NumFaces: 1, Vertices: []VertexSpec{ - {Vertices: []XY{v2}, Labels: []LabelOption{OperandA(true), OperandB(false)}}, - {Vertices: []XY{v0}, Labels: []LabelOption{OperandA(false), OperandB(true)}}, - {Vertices: []XY{v1}, Labels: []LabelOption{OperandA(true), OperandB(true)}}, + { + Vertices: []XY{v2}, + Src: [2]bool{true, false}, + InSet: [2]bool{true, false}, + }, + { + Vertices: []XY{v0}, + Src: [2]bool{false, true}, + InSet: [2]bool{false, true}, + }, + { + Vertices: []XY{v1}, + Src: [2]bool{true, true}, + InSet: [2]bool{true, true}, + }, }, Edges: []EdgeSpec{ { - Sequence: []XY{v1, v2}, - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: nil, + Sequence: []XY{v1, v2}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, false}, }, { - Sequence: []XY{v2, v1}, - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: nil, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, false}, + Sequence: []XY{v2, v1}, }, { - Sequence: []XY{v0, v1}, - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: nil, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, true}, + Sequence: []XY{v0, v1}, }, { - Sequence: []XY{v1, v0}, - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: nil, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, true}, + Sequence: []XY{v1, v0}, }, }, Faces: []FaceSpec{{ First: v0, Second: v1, Cycle: []XY{v0, v1, v2, v1, v0}, - Labels: []LabelOption{OperandA(false), OperandB(false)}, + InSet: [2]bool{false, false}, }}, }) } @@ -1756,97 +1827,114 @@ func TestGraphOverlayReproduceFaceAllocationBug(t *testing.T) { Vertices: []VertexSpec{ { Vertices: []XY{v1, v3}, - Labels: []LabelOption{OperandA(true), OperandB(true)}, + Src: [2]bool{true, true}, + InSet: [2]bool{true, true}, }, { Vertices: []XY{v0, v4, v8}, - Labels: []LabelOption{OperandA(false), OperandB(true)}, + Src: [2]bool{false, true}, + InSet: [2]bool{false, true}, }, }, Edges: []EdgeSpec{ { - Sequence: []XY{v1, v3}, - EdgeLabels: []LabelOption{OperandA(true), OperandB(true)}, - FaceLabels: nil, + Sequence: []XY{v1, v3}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, true}, }, { - Sequence: []XY{v3, v1}, - EdgeLabels: []LabelOption{OperandA(true), OperandB(true)}, - FaceLabels: nil, + Sequence: []XY{v3, v1}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, true}, }, { - Sequence: []XY{v0, v1}, - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, + Sequence: []XY{v0, v1}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + InSet: [2]bool{false, true}, }, { - Sequence: []XY{v1, v0}, - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, + Sequence: []XY{v1, v0}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, true}, }, { - Sequence: []XY{v3, v0}, - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, + Sequence: []XY{v3, v0}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + InSet: [2]bool{false, true}, }, { - Sequence: []XY{v0, v3}, - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, + Sequence: []XY{v0, v3}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, true}, }, { - Sequence: []XY{v4, v5, v6, v7, v4}, - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, + Sequence: []XY{v4, v5, v6, v7, v4}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + InSet: [2]bool{false, true}, }, { - Sequence: []XY{v4, v7, v6, v5, v4}, - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, + Sequence: []XY{v4, v7, v6, v5, v4}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, true}, }, { - Sequence: []XY{v1, v8}, - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, + Sequence: []XY{v1, v8}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + InSet: [2]bool{false, true}, }, { - Sequence: []XY{v8, v1}, - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, + Sequence: []XY{v8, v1}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, true}, }, { - Sequence: []XY{v4, v8}, - EdgeLabels: []LabelOption{OperandA(false), OperandB(false)}, - FaceLabels: nil, + Sequence: []XY{v4, v8}, + SrcEdge: [2]bool{false, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, false}, }, { - Sequence: []XY{v8, v4}, - EdgeLabels: []LabelOption{OperandA(false), OperandB(false)}, - FaceLabels: nil, + Sequence: []XY{v8, v4}, + SrcEdge: [2]bool{false, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, false}, }, { - Sequence: []XY{v3, v8}, - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: nil, + Sequence: []XY{v3, v8}, + SrcEdge: [2]bool{false, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, true}, }, { - Sequence: []XY{v8, v3}, - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: nil, + Sequence: []XY{v8, v3}, + SrcEdge: [2]bool{false, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, true}, }, - { - Sequence: []XY{v8, v2, v3}, - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, + Sequence: []XY{v8, v2, v3}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + InSet: [2]bool{false, true}, }, { - Sequence: []XY{v3, v2, v8}, - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, + Sequence: []XY{v3, v2, v8}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, true}, }, }, Faces: []FaceSpec{ @@ -1855,35 +1943,35 @@ func TestGraphOverlayReproduceFaceAllocationBug(t *testing.T) { First: v1, Second: v0, Cycle: []XY{v1, v0, v3, v2, v8, v4, v7, v6, v5, v4, v8, v1}, - Labels: []LabelOption{OperandA(false), OperandB(false)}, + InSet: [2]bool{false, false}, }, { // f1 First: v1, Second: v8, Cycle: []XY{v1, v8, v3, v1}, - Labels: []LabelOption{OperandA(false), OperandB(true)}, + InSet: [2]bool{false, true}, }, { // f2 First: v8, Second: v2, Cycle: []XY{v8, v2, v3, v8}, - Labels: []LabelOption{OperandA(false), OperandB(true)}, + InSet: [2]bool{false, true}, }, { // f3 First: v0, Second: v1, Cycle: []XY{v0, v1, v3, v0}, - Labels: []LabelOption{OperandA(false), OperandB(true)}, + InSet: [2]bool{false, true}, }, { // f4 First: v4, Second: v5, Cycle: []XY{v4, v5, v6, v7, v4}, - Labels: []LabelOption{OperandA(false), OperandB(true)}, + InSet: [2]bool{false, true}, }, }, }) @@ -1914,33 +2002,39 @@ func TestGraphOverlayReproducePointOnLineStringPrecisionBug(t *testing.T) { Vertices: []VertexSpec{ { Vertices: []XY{v0, v2}, - Labels: []LabelOption{OperandA(true), OperandB(false)}, + Src: [2]bool{true, false}, + InSet: [2]bool{true, false}, }, { Vertices: []XY{v1}, - Labels: []LabelOption{OperandA(true), OperandB(true)}, + Src: [2]bool{true, true}, + InSet: [2]bool{true, true}, }, }, Edges: []EdgeSpec{ { - Sequence: []XY{v0, v1}, - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: nil, + Sequence: []XY{v0, v1}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, false}, }, { - Sequence: []XY{v1, v2}, - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: nil, + Sequence: []XY{v1, v2}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, false}, }, { - Sequence: []XY{v2, v1}, - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: nil, + Sequence: []XY{v2, v1}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, false}, }, { - Sequence: []XY{v1, v0}, - EdgeLabels: []LabelOption{OperandA(true), OperandB(false)}, - FaceLabels: nil, + Sequence: []XY{v1, v0}, + SrcEdge: [2]bool{true, false}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, false}, }, }, Faces: []FaceSpec{ @@ -1948,7 +2042,7 @@ func TestGraphOverlayReproducePointOnLineStringPrecisionBug(t *testing.T) { First: v0, Second: v1, Cycle: []XY{v0, v1, v2, v1, v0}, - Labels: []LabelOption{OperandA(false), OperandB(false)}, + InSet: [2]bool{false, false}, }, }, }) @@ -1985,39 +2079,46 @@ func TestGraphOverlayReproduceGhostOnGeometryBug(t *testing.T) { Vertices: []VertexSpec{ { Vertices: []XY{v0, v1, v3}, - Labels: []LabelOption{OperandA(true), OperandB(true)}, + Src: [2]bool{true, true}, + InSet: [2]bool{true, true}, }, }, Edges: []EdgeSpec{ { - Sequence: []XY{v0, v1}, - EdgeLabels: []LabelOption{OperandA(true), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, + Sequence: []XY{v0, v1}, + SrcEdge: [2]bool{true, true}, + SrcFace: [2]bool{false, true}, + InSet: [2]bool{true, true}, }, { - Sequence: []XY{v1, v0}, - EdgeLabels: []LabelOption{OperandA(true), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, + Sequence: []XY{v1, v0}, + SrcEdge: [2]bool{true, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, true}, }, { - Sequence: []XY{v1, v2, v3}, - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, + Sequence: []XY{v1, v2, v3}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, true}, + InSet: [2]bool{false, true}, }, { - Sequence: []XY{v3, v2, v1}, - EdgeLabels: []LabelOption{OperandA(false), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, + Sequence: []XY{v3, v2, v1}, + SrcEdge: [2]bool{false, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{false, true}, }, { - Sequence: []XY{v3, v4, v0}, - EdgeLabels: []LabelOption{OperandA(true), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(true)}, + Sequence: []XY{v3, v4, v0}, + SrcEdge: [2]bool{true, true}, + SrcFace: [2]bool{false, true}, + InSet: [2]bool{true, true}, }, { - Sequence: []XY{v0, v4, v3}, - EdgeLabels: []LabelOption{OperandA(true), OperandB(true)}, - FaceLabels: []LabelOption{OperandB(false)}, + Sequence: []XY{v0, v4, v3}, + SrcEdge: [2]bool{true, true}, + SrcFace: [2]bool{false, false}, + InSet: [2]bool{true, true}, }, }, Faces: []FaceSpec{ @@ -2025,13 +2126,13 @@ func TestGraphOverlayReproduceGhostOnGeometryBug(t *testing.T) { First: v1, Second: v0, Cycle: []XY{v1, v0, v4, v3, v2, v1}, - Labels: []LabelOption{OperandA(false), OperandB(false)}, + InSet: [2]bool{false, false}, }, { First: v0, Second: v1, Cycle: []XY{v0, v1, v2, v3, v4, v0}, - Labels: []LabelOption{OperandA(false), OperandB(true)}, + InSet: [2]bool{false, true}, }, }, }) @@ -2048,15 +2149,15 @@ func TestGraphWithEmptyGeometryCollection(t *testing.T) { func TestGraphWithGeometryCollection(t *testing.T) { gc, err := UnmarshalWKT(`GEOMETRYCOLLECTION( - POINT(0 0), - LINESTRING(0 1,1 1), - POLYGON((2 0,3 0,3 1,2 1,2 0)) - )`) + POINT(0 0), + LINESTRING(0 1,1 1), + POLYGON((2 0,3 0,3 1,2 1,2 0)) + )`) if err != nil { t.Fatal(err) } dcel := &doublyConnectedEdgeList{vertices: make(map[XY]*vertexRecord)} - dcel.addGeometryCollection(gc.MustAsGeometryCollection(), operandA, findInteractionPoints([]Geometry{gc})) + dcel.addGeometry(gc, operandA, findInteractionPoints([]Geometry{gc})) /* v0 v3-----v4 @@ -2078,28 +2179,30 @@ func TestGraphWithGeometryCollection(t *testing.T) { NumEdges: 4, Vertices: []VertexSpec{ { - Labels: []LabelOption{OperandA(true)}, + Src: [2]bool{true}, Vertices: []XY{v0, v1, v2, v3}, }, }, Edges: []EdgeSpec{ { - EdgeLabels: []LabelOption{OperandA(true)}, - Sequence: []XY{v1, v2}, + SrcEdge: [2]bool{true}, + SrcFace: [2]bool{false}, + Sequence: []XY{v1, v2}, }, { - EdgeLabels: []LabelOption{OperandA(true)}, - Sequence: []XY{v2, v1}, + SrcEdge: [2]bool{true}, + SrcFace: [2]bool{false}, + Sequence: []XY{v2, v1}, }, { - EdgeLabels: []LabelOption{OperandA(true)}, - FaceLabels: []LabelOption{OperandA(true)}, - Sequence: []XY{v3, v4, v5, v6, v3}, + SrcEdge: [2]bool{true}, + SrcFace: [2]bool{true}, + Sequence: []XY{v3, v4, v5, v6, v3}, }, { - EdgeLabels: []LabelOption{OperandA(true)}, - FaceLabels: []LabelOption{OperandA(false)}, - Sequence: []XY{v3, v6, v5, v4, v3}, + SrcEdge: [2]bool{true}, + SrcFace: [2]bool{false}, + Sequence: []XY{v3, v6, v5, v4, v3}, }, }, })