Class MedialAxis
- java.lang.Object
-
- micycle.medialAxis.MedialAxis
-
public class MedialAxis extends Object
Based on ideas from https://www.cs.ubc.ca/labs/imager/th/2003/Tam2004/Tam2004.pdf and Voronoi Ball/Disk Models for Computational Shape ApplicationsProduces a directed acyclic graph of the MD / more specifically rooted tree
Medial Axis library models medial axes of shapes as a (rooted) tree of medial disks. Medial disks reference a parent and have upto 3 children (the medial axis bifurcates/forks at disks with 2 children). The root is the disk with the largest circumcirle, also the single disk that trifurcates. All disks have an indegree of 1.
Medial axes are...
For geometries of genus zero, the graph is naturally a tree. For objects with holes, we break each cycle by imposing an appropriate breakpoint in the loop. This is only for the purposes of traversing the graph without running into infinite loops. In order to preserve the topology of the original object, cycles are never pruned
Decompose the disk coordinates into curves.
If angle of successive line branches is the same, merge them
* One way to get a smoother initial axis (i.e. without pruning) is to apply on a smoothed version of the shape.
- Author:
- Michael Carleton
-
-
Nested Class Summary
Nested Classes Modifier and Type Class Description class
MedialAxis.Branch
Branches are series of successive disks found in between.class
MedialAxis.Edge
Edges are straight-line segments that connect two adjacent disks.class
MedialAxis.MedialDisk
Voronoi Disk.
-
Field Summary
Fields Modifier and Type Field Description static boolean
debug
MedialAxis.MedialDisk
furthestNode
-
Constructor Summary
Constructors Constructor Description MedialAxis(org.locationtech.jts.geom.Coordinate[] coordinates)
MedialAxis(org.locationtech.jts.geom.Geometry g)
The medial axis is computed for the geometry during construction.
-
Method Summary
All Methods Instance Methods Concrete Methods Modifier and Type Method Description void
calcFeatureArea()
void
drawVDM(processing.core.PApplet p)
void
drawVDM(processing.core.PApplet p, double fraction)
void
drawVDM(processing.core.PApplet p, int maxDepth)
void
drawVDMPrune(processing.core.PApplet p, double threshold)
Prune disks with feature area that is smaller in area than the given significance threshold.List<MedialAxis.MedialDisk>
getAncestors(MedialAxis.MedialDisk child)
The ancestors of a node d are the nodes on the path from d to the root.List<MedialAxis.MedialDisk>
getBifurcations()
Nodes/medial disks with two descendent lineages (two children disks).List<MedialAxis.Branch>
getBranches()
Aka features, aka branches Segment is a linear portion of medial disks.MedialAxis.MedialDisk
getDeepestDisk()
List<MedialAxis.MedialDisk>
getDescendants(MedialAxis.MedialDisk parent)
Returns the children of a disk into a linear arrayList<MedialAxis.MedialDisk>
getDescendants(MedialAxis.MedialDisk parent, int maxDepth)
List<MedialAxis.MedialDisk>
getDisks()
org.locationtech.jts.geom.Geometry
getDissolvedGeometry()
Returns a JTS geometry where medial axis edges are dissolved into a set of maximal-length Linestrings.Map<MedialAxis.MedialDisk,MedialAxis.Edge>
getEdgeMap()
Collection<MedialAxis.Edge>
getEdges()
List<MedialAxis.Edge>
getEdgesToRoot(MedialAxis.MedialDisk d)
Returns the edges comprising the path from the given disk to the root of the medial axis.MedialAxis.MedialDisk
getFurthestDisk()
List<MedialAxis.MedialDisk>
getLeaves()
End nodes / A VDs of degree 1/no childrenorg.locationtech.jts.operation.linemerge.LineMergeGraph
getLineMergeGraph()
Get the medial axis in the form of an undirected planar graph.List<MedialAxis.Edge>
getPrunedEdges(double threshold)
Returns a subset of the axis' edges; in this method edges are pruned by their axial gradient value.List<MedialAxis.Edge>
getPrunedEdges(double axialThreshold, double distanceThreshold)
Returns a subset of the axis' edges; in this method edges are pruned by their axial gradient value and axis distance from the root node.List<MedialAxis.Edge>
getPrunedEdges(double axialThreshold, double distanceThreshold, double areaThreshold)
MedialAxis.MedialDisk
getRoot()
Returns the root disk -- the disk with largest radius and 3 children.void
iteratorBF(Consumer<MedialAxis.MedialDisk> consumer)
Will call consumer with the next disk in a BF manner, starting at the root node.void
iteratorBfUp(Consumer<MedialAxis.MedialDisk> consumer)
void
iteratorDF(Consumer<MedialAxis.MedialDisk> consumer)
void
iteratorDfUp(Consumer<MedialAxis.MedialDisk> consumer)
MedialAxis.MedialDisk
nearestDisk(double x, double y)
MedialAxis.MedialDisk
nearestDisk(org.locationtech.jts.geom.Coordinate coordinate)
-
-
-
Field Detail
-
furthestNode
public MedialAxis.MedialDisk furthestNode
-
debug
public static boolean debug
-
-
Constructor Detail
-
MedialAxis
public MedialAxis(org.locationtech.jts.geom.Coordinate[] coordinates)
-
MedialAxis
public MedialAxis(org.locationtech.jts.geom.Geometry g)
The medial axis is computed for the geometry during construction.- Parameters:
g
- Geometry whose medial axis to compute. Can contain holes. Must not be a multigeometry. Must be fairly dense.
-
-
Method Detail
-
getDisks
public List<MedialAxis.MedialDisk> getDisks()
-
getRoot
public MedialAxis.MedialDisk getRoot()
Returns the root disk -- the disk with largest radius and 3 children.- Returns:
-
nearestDisk
public MedialAxis.MedialDisk nearestDisk(double x, double y)
-
nearestDisk
public MedialAxis.MedialDisk nearestDisk(org.locationtech.jts.geom.Coordinate coordinate)
-
getDescendants
public List<MedialAxis.MedialDisk> getDescendants(MedialAxis.MedialDisk parent)
Returns the children of a disk into a linear array- Parameters:
parent
- contains chil out: contains children and the parent (at position 0)- Returns:
-
getDescendants
public List<MedialAxis.MedialDisk> getDescendants(MedialAxis.MedialDisk parent, int maxDepth)
-
getAncestors
public List<MedialAxis.MedialDisk> getAncestors(MedialAxis.MedialDisk child)
The ancestors of a node d are the nodes on the path from d to the root. This includes node d.TODO another method that returns paths from fork VDs in the ancestors (Except rootnode)
- Parameters:
child
-- Returns:
-
getDissolvedGeometry
public org.locationtech.jts.geom.Geometry getDissolvedGeometry()
Returns a JTS geometry where medial axis edges are dissolved into a set of maximal-length Linestrings. The output can be further simplfied with DouglasPeuckerSimplifier for example. * Returns a simplified medial axis graph leaving only maximal-length lines in which every unique segment appears once only. The output lines run between node vertices of the input, which are vertices which have either degree 1, or degree 3 or greater.- Returns:
-
getLineMergeGraph
public org.locationtech.jts.operation.linemerge.LineMergeGraph getLineMergeGraph()
Get the medial axis in the form of an undirected planar graph. The graph is based on the dissolved geometry.- Returns:
-
getLeaves
public List<MedialAxis.MedialDisk> getLeaves()
End nodes / A VDs of degree 1/no children- Returns:
-
getDeepestDisk
public MedialAxis.MedialDisk getDeepestDisk()
-
getFurthestDisk
public MedialAxis.MedialDisk getFurthestDisk()
-
getBifurcations
public List<MedialAxis.MedialDisk> getBifurcations()
Nodes/medial disks with two descendent lineages (two children disks). Note the root node (a trifurcating node) is not included in the output.- Returns:
-
getEdges
public Collection<MedialAxis.Edge> getEdges()
- Returns:
- list of edges (in breadth-first order from the root node) connecting medial disks.
-
getEdgeMap
public Map<MedialAxis.MedialDisk,MedialAxis.Edge> getEdgeMap()
- Returns:
- a map of edge-tail -> edge for easier access in pruning methods
-
getEdgesToRoot
public List<MedialAxis.Edge> getEdgesToRoot(MedialAxis.MedialDisk d)
Returns the edges comprising the path from the given disk to the root of the medial axis.- Parameters:
d
-- Returns:
-
getBranches
public List<MedialAxis.Branch> getBranches()
Aka features, aka branches Segment is a linear portion of medial disks.- Returns:
- List of
branches
-
getPrunedEdges
public List<MedialAxis.Edge> getPrunedEdges(double threshold)
Returns a subset of the axis' edges; in this method edges are pruned by their axial gradient value. Edges with negative axial gradient values indicate a narrowing of the shape along that edge, and are more likely to be noise. The threshold takes into account this shape's minimum and maximum axial values when filtering.Presently this method prunes based on global min and max axial values (rather than per branch).
- Parameters:
threshold
- between 0...1, where 0 is no pruning and 1 is maximal pruning. the shape may be fully pruned before threshold = 1- Returns:
-
getPrunedEdges
public List<MedialAxis.Edge> getPrunedEdges(double axialThreshold, double distanceThreshold)
Returns a subset of the axis' edges; in this method edges are pruned by their axial gradient value and axis distance from the root node.- Parameters:
axialThreshold
- between 0...1, where 0 is no pruning and 1 is maximal pruningdistanceThreshold
- between 0...1, where 0 is no pruning and 1 is maximal pruning- Returns:
-
getPrunedEdges
public List<MedialAxis.Edge> getPrunedEdges(double axialThreshold, double distanceThreshold, double areaThreshold)
- Parameters:
axialThreshold
-distanceThreshold
-areaThreshold
- between 0...1, where 0 is no pruning and 1 is maximal pruning for this feature- Returns:
-
iteratorBF
public void iteratorBF(Consumer<MedialAxis.MedialDisk> consumer)
Will call consumer with the next disk in a BF manner, starting at the root node.- Parameters:
consumer
-
-
iteratorDF
public void iteratorDF(Consumer<MedialAxis.MedialDisk> consumer)
-
iteratorBfUp
public void iteratorBfUp(Consumer<MedialAxis.MedialDisk> consumer)
-
iteratorDfUp
public void iteratorDfUp(Consumer<MedialAxis.MedialDisk> consumer)
-
calcFeatureArea
public void calcFeatureArea()
-
drawVDM
public void drawVDM(processing.core.PApplet p)
-
drawVDM
public void drawVDM(processing.core.PApplet p, int maxDepth)
- Parameters:
p
-maxDepth
- inclusive
-
drawVDM
public void drawVDM(processing.core.PApplet p, double fraction)
- Parameters:
p
-fraction
- 0...1
-
drawVDMPrune
public void drawVDMPrune(processing.core.PApplet p, double threshold)
Prune disks with feature area that is smaller in area than the given significance threshold. The significance value of a feature can be determined by summing the areas of all triangles in the subtree associated with that feature. Any subtree that has a value below the threshold is pruned. Each feature can be seen as being supported by the branches of the subtree, so when the subtree is pruned, the feature is eliminated.- Parameters:
p
-threshold
- percentage of the total area of the object 0...1 (where 0 includes everything and 1 root node only)
-
-