Skip to content

Commit

Permalink
Allow result to include collapsed lines
Browse files Browse the repository at this point in the history
Signed-off-by: Martin Davis <mtnclimb@gmail.com>
  • Loading branch information
dr-jts committed Sep 10, 2020
1 parent f65936b commit 764bb49
Show file tree
Hide file tree
Showing 10 changed files with 279 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public int getAreaIndex() {
}

public boolean isLine(int geomIndex) {
return geom[geomIndex].getDimension() == 1;
return getDimension(geomIndex) == 1;
}

public boolean isAllPoints() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,39 +113,67 @@ private void markResultLines() {
*/
private boolean isResultLine(OverlayLabel lbl) {
/**
* An edge which is a boundary of a single geometry cannot form a line result.
* This is a short-circuit for area edges if no collapses or lines are present.
* Omit edge which is a boundary of a single geometry
* (i.e. not a collapse or line edge as well).
* These are only included if part of a result area.
* This is a short-circuit for the most common area edge case
*/
if (lbl.isBoundarySingleton()) return false;

/**
* Edges which are collapses along boundaries are not output.
* Omit edge which is a collapse along a boundary.
* I.e a result line edge must be from a input line
* or two coincident area boundaries.
* OR two coincident area boundaries.
*
* This logic is only used if not including collapse lines in result.
*/
if (lbl.isBoundaryCollapse()) return false;
if (! OverlayNG.ALLOW_COLLAPSE_LINES
&& lbl.isBoundaryCollapse()) return false;

/**
* Omit edge which is a collapse interior to its parent area.
* (E.g. a narrow gore, or spike off a hole)
*/
if (lbl.isInteriorCollapse()) return false;

/**
* For ops other than Intersection, omit a line edge
* if it is interior to the other area.
*
* For Intersection, a line edge interior to an area is included.
*/
if (opCode != OverlayNG.INTERSECTION) {
/**
* Omit collapsed edge in other area interior.
*/
if (lbl.isCollapseAndNotPartInterior()) return false;

/**
* If there is a result area, omit line edge inside it.
* It is sufficient to check against the input area rather
* than the result area,
* because if line edges are present then there is only one input area,
* and the result area must be the same as the input area.
*/
if (hasResultArea && lbl.isLineInArea(inputAreaIndex))
return false;
}

/**
* Include line edge formed by touching area boundaries,
* if enabled.
*/
if (OverlayNG.ALLOW_INT_MIXED_RESULT
&& opCode == OverlayNG.INTERSECTION && lbl.isBoundaryTouch()) {
return true;
}

/**
* Skip edges that are inside result area, if there is one.
* It is sufficient to check against an input area rather
* than the result area, since
* if lines are being included then the result area
* must be the same as the input area.
* This logic relies on the semantic that if both inputs
* are areas, lines are only output if there is no
* result area.
* Finally, determine included line edge
* according to overlay op boolean logic.
*/
if (hasResultArea && lbl.isLineInArea(inputAreaIndex))
return false;

int aLoc = effectiveLocation(lbl, 0);
int bLoc = effectiveLocation(lbl, 1);

boolean isInResult = OverlayNG.isResultOfOp(opCode, aLoc, bLoc);
return isInResult;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
* Accessors for orientation-sensitive information
* are parameterized by the orientation of the containing edge.
* <p>
* For each input geometry, the label records
* For each input geometry (0 and 1), the label records
* that an edge is in one of the following states
* (identified by the <code>dim</code> field).
* Each state has additional information about the edge topology.
Expand All @@ -45,17 +45,17 @@
* <li><code>locLeft, locRight</code> : the locations of the edge sides for the Area</li>
* <li><code>locLine</code> : INTERIOR</li>
* <li><code>isHole</code> : whether the
* edge was in a shell or a hole (the ring role)</li>
* edge is in a shell or a hole (the ring role)</li>
* </ul>
* </li>
* <li>A <b>Collapsed</b> edge of an input Area
* (formed by merging two or more parent edges)
* <ul>
* <li><code>dim</code> = DIM_COLLAPSE</li>
* <li><code>locLine</code> : the location of the edge relative to the Area</li>
* <li><code>isHole</code> : whether some
* contributing edge was in a shell (<code>false</code>).
* Otherwise all parent edges were in holes</li> (<code>true</code>)
* <li><code>locLine</code> : the location of the edge relative to the effective input Area
* (a collapsed spike is EXTERIOR, a collapsed gore or hole is INTERIOR)</li>
* <li><code>isHole</code> : <code>true</code> if all parent edges are in holes;
* <code>false</code> if some parent edge is in a shell
* </ul>
* </li>
* <li>A <b>Line</b> edge from an input line
Expand Down Expand Up @@ -96,22 +96,25 @@ class OverlayLabel {
public static final int DIM_UNKNOWN = -1;

/**
* The dimension of an edge which is not part of a specified input geometry
* The dimension of an edge which is not part of a specified input geometry.
*/
public static final int DIM_NOT_PART = DIM_UNKNOWN;

/**
* The dimension of an edge which is a line
* The dimension of an edge which is a line.
*/
public static final int DIM_LINE = 1;

/**
* The dimension for an edge which is part of an input Area geometry boundary
* The dimension for an edge which is part of an input Area geometry boundary.
*/
public static final int DIM_BOUNDARY = 2;

/**
* The dimension for an edge which is a collapsed part of an input Area geometry boundary
* The dimension for an edge which is a collapsed part of an input Area geometry boundary.
* A collapsed edge represents two or more line segments which have the same endpoints.
* They usually are caused by edges in valid polygonal geometries
* having their endpoints become identical due to precision reduction.
*/
public static final int DIM_COLLAPSE = 3;

Expand Down Expand Up @@ -230,6 +233,10 @@ public void initBoundary(int index, int locLeft, int locRight, boolean isHole) {
/**
* Initializes the label for an edge which is the collapse of
* part of the boundary of an Area input geometry.
* The location of the collapsed edge relative to the
* parent area geometry is initially unknown.
* It must be determined from the topology of the overlay graph
*
* @param index the index of the parent input geometry
* @param isHole whether the dominant edge role is a hole or a shell
*/
Expand Down Expand Up @@ -367,7 +374,7 @@ public boolean isLinear(int index) {
}

/**
* Tests whether a source is known.
* Tests whether a the source of a label is known.
*
* @param index the index of the source geometry
* @return true if the source is known
Expand Down Expand Up @@ -412,7 +419,7 @@ public boolean isBoundaryBoth() {
}

/**
* Tests if the label is for a collapsed area edge
* Tests if the label is a collapsed edge of one area
* and is a (non-collapsed) boundary edge of the other area.
*
* @return true if the label is for a collapse coincident with a boundary
Expand All @@ -435,6 +442,7 @@ public boolean isBoundaryTouch() {

/**
* Tests if a label is for an edge which is in the boundary of a source geometry.
* Collapses are not reported as being in the boundary.
*
* @param index the index of the input geometry
* @return true if the label is a boundary for the source
Expand Down Expand Up @@ -512,6 +520,30 @@ public boolean isCollapse(int index) {
return dimension(index) == DIM_COLLAPSE;
}

/**
* Tests if a label is a Collapse has location {@link Location#INTERIOR},
* to at least one source geometry.
*
* @return true if the label is an Interior Collapse to a source geometry
*/
public boolean isInteriorCollapse() {
if (aDim == DIM_COLLAPSE && aLocLine == Location.INTERIOR) return true;
if (bDim == DIM_COLLAPSE && bLocLine == Location.INTERIOR) return true;
return false;
}

/**
* Tests if a label is a Collapse
* and NotPart with location {@link Location#INTERIOR} for the other geometry.
*
* @return true if the label is a Collapse and a NotPart with Location Interior
*/
public boolean isCollapseAndNotPartInterior() {
if (aDim == DIM_COLLAPSE && bDim == DIM_NOT_PART && bLocLine == Location.INTERIOR) return true;
if (bDim == DIM_COLLAPSE && aDim == DIM_NOT_PART && aLocLine == Location.INTERIOR) return true;
return false;
}

/**
* Gets the line location for a source geometry.
*
Expand Down Expand Up @@ -644,6 +676,7 @@ private String locationString(int index, boolean isForward) {
buf.append( Location.toLocationSymbol( getLocation(index, Position.RIGHT, isForward) ) );
}
else {
// is a linear edge
buf.append( Location.toLocationSymbol( index == 0 ? aLocLine : bLocLine ));
}
if (isKnown(index))
Expand All @@ -654,10 +687,22 @@ private String locationString(int index, boolean isForward) {
return buf.toString();
}

public static Object ringRoleSymbol(boolean isHole) {
/**
* Gets a symbol for the a ring role (Shell or Hole).
*
* @param isHole true for a hole, false for a shell
* @return the ring role symbol character
*/
public static char ringRoleSymbol(boolean isHole) {
return isHole ? 'h' : 's';
}

/**
* Gets the symbol for the dimension code of an edge.
*
* @param dim the dimension code
* @return the dimension symbol character
*/
public static char dimensionSymbol(int dim) {
switch (dim) {
case DIM_LINE: return SYM_LINE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public void computeLabelling() {
/**
* Labels edges around nodes based on the arrangement
* of incident area boundary edges.
* Also propagates the labelling to connected linear edges.
* Also propagates the labeling to connected linear edges.
*
* @param nodes the nodes to label
*/
Expand Down Expand Up @@ -124,6 +124,7 @@ public void propagateAreaLocations(OverlayEdge nodeEdge, int geomIndex) {
label.setLocationLine(geomIndex, currLoc);
}
else {
// must be a boundary edge
Assert.isTrue(label.hasSides(geomIndex));
/**
* This is a boundary edge for the input area geom.
Expand Down Expand Up @@ -151,7 +152,10 @@ public void propagateAreaLocations(OverlayEdge nodeEdge, int geomIndex) {
}

/**
* Finds a boundary edge for this geom, if one exists.
* Finds a boundary edge for this geom originating at the given
* node, if one exists.
* A boundary edge should exist if this is a node on the boundary
* of the parent area geometry.
*
* @param nodeEdge an edge for this node
* @param geomIndex the parent geometry index
Expand Down

0 comments on commit 764bb49

Please sign in to comment.