use of org.eclipse.elk.alg.common.utils.SVGImage in project elk by eclipse.
the class BowyerWatsonTriangulation method triangulate.
/**
* Triangulates a list of points.
*
* @param vertices the input points
* @param debugOutputFile file name for debug SVG. Debug output will be deactivated if this is null.
* @return the edges of the triangulation
*/
public static Set<TEdge> triangulate(final List<KVector> vertices, final String debugOutputFile) {
/*d*/
SVGImage svg = new SVGImage(debugOutputFile);
/*d*/
svg.addGroups("invalid", "tri", "bndry", "done", "new");
/* preliminaries */
// determine the bounding box of the given points
KVector topleft = new KVector(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
KVector bottomright = new KVector(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
for (KVector v : vertices) {
topleft.x = Math.min(topleft.x, v.x);
topleft.y = Math.min(topleft.y, v.y);
bottomright.x = Math.max(bottomright.x, v.x);
bottomright.y = Math.max(bottomright.y, v.y);
// CHECKSTYLEOFF MagicNumber
/*d*/
svg.g("bb").addCircle(v.x, v.y, 18, "stroke=\"black\" stroke-width=\"1\" fill=\"lightgray\"");
}
KVector size = new KVector(bottomright.x - topleft.x, bottomright.y - topleft.y);
/*d*/
svg.g("bb").addRect(topleft.x, topleft.y, size.x, size.y, "stroke=\"blue\" stroke-width=\"4\" fill=\"none\"");
// find a super-triangle spanning over all vertices
final double wiggleroom = 50;
// This ensures that no vertices are too close to the edges of the super-triangle
// because that would result in circumcircles that are so large that a double would not
// be precise enough to compute the circumcircle criterion.
KVector sa = new KVector(topleft.x - wiggleroom, topleft.y - size.x - wiggleroom);
KVector sb = new KVector(topleft.x - wiggleroom, bottomright.y + size.x + wiggleroom);
KVector sc = new KVector(bottomright.x + size.y / 2 + wiggleroom, topleft.y + size.y / 2);
/*d*/
svg.g("bb").addPoly("stroke=\"gray\" stroke-width=\"4\" fill=\"none\" stroke-dasharray=\"20,20\"", sa, sb, sc, sa);
TTriangle superTriangle = new TTriangle(sa, sb, sc);
/*d*/
// circumcircles will make it gigantic
svg.setViewBox(sa.x, sa.y, sc.x - sa.x, sb.y - sa.y);
/*d*/
svg.isave();
/*d*/
svg.removeGroup("bb");
/* Bowyer Watson algorithm*/
Set<TTriangle> triangulation = Sets.newHashSet();
List<TTriangle> invalidTriangles = Lists.newArrayList();
List<TEdge> boundary = Lists.newArrayList();
triangulation.add(superTriangle);
// incrementally adding vertices
for (KVector vertex : vertices) {
/*d*/
svg.g("done").addCircle(vertex.x, vertex.y, 18, "stroke=\"black\" stroke-width=\"1\" fill=\"lightgray\"");
/*d*/
svg.g("new").addCircle(vertex.x, vertex.y, 18, "stroke=\"black\" stroke-width=\"1\" fill=\"black\"");
// gather invalid triangles where the new vertex lies inside the circumcircle
invalidTriangles.clear();
for (TTriangle triangle : triangulation) {
/*d*/
svg.g("tri").addPoly("stroke=\"black\" fill=\"none\" stroke-width=\"4\"", triangle.a, triangle.b, triangle.c, triangle.a);
/*d*/
KVector c = triangle.getCircumcenter();
/*d*/
svg.g("invalid").addCircle(c.x, c.y, c.distance(triangle.a), "stroke=\"orange\" stroke-width=\"4\" fill=\"none\"");
if (triangle.inCircumcircle(vertex)) {
invalidTriangles.add(triangle);
/*d*/
svg.g("invalid").addPoly("stroke=\"none\" fill=\"red\" opacity=\"0.18\"", triangle.a, triangle.b, triangle.c, triangle.a);
}
}
/*d*/
svg.isave();
/*d*/
svg.clearGroup("invalid");
// calculate boundary of invalid triangles
boundary.clear();
for (TTriangle triangle : invalidTriangles) {
for (TEdge tEdge : triangle.tEdges) {
boolean onBoundary = true;
// edges that are not shared with other invalid triangles are on the boundary
for (TTriangle other : invalidTriangles) {
if (other != triangle && other.contains(tEdge)) {
onBoundary = false;
}
}
if (onBoundary) {
boundary.add(tEdge);
/*d*/
svg.g("bndry").addLine(tEdge.u.x, tEdge.u.y, tEdge.v.x, tEdge.v.y, "stroke=\"purple\" stroke-width=\"18\" stroke-dasharray=\"20,20\"");
}
}
}
/*d*/
svg.isave();
// remove invalid triangles
triangulation.removeAll(invalidTriangles);
/*d*/
svg.clearGroup("tri");
/*d*/
triangulation.forEach(triangle -> svg.g("tri").addPoly("stroke=\"black\" fill=\"none\" stroke-width=\"4\"", triangle.a, triangle.b, triangle.c, triangle.a));
/*d*/
svg.isave();
// triangulate boundary
for (TEdge tEdge : boundary) {
triangulation.add(new TTriangle(vertex, tEdge.u, tEdge.v));
/*d*/
svg.g("tri").addPoly("stroke=\"black\" fill=\"none\" stroke-width=\"4\"", vertex, tEdge.u, tEdge.v, vertex);
}
/*d*/
svg.isave();
/*d*/
svg.clearGroup("new");
/*d*/
svg.clearGroup("bndry");
/*d*/
svg.clearGroup("tri");
}
// convert triangulation to set of edges
// Because it's a set, edges that were redundant in the triangulation exist only once in the set.
Set<TEdge> tEdges = Sets.newHashSet();
triangulation.forEach(triangle -> tEdges.addAll(triangle.tEdges));
// remove edges connected to super triangle
Iterator<TEdge> i = tEdges.iterator();
while (i.hasNext()) {
TEdge tEdge = i.next();
if (superTriangle.contains(tEdge.u) || superTriangle.contains(tEdge.v)) {
i.remove();
}
}
/*d*/
tEdges.forEach(tEdge -> svg.addLine(tEdge.u.x, tEdge.u.y, tEdge.v.x, tEdge.v.y, "stroke=\"black\" stroke-width=\"4\""));
/*d*/
svg.isave();
return tEdges;
}
use of org.eclipse.elk.alg.common.utils.SVGImage in project elk by eclipse.
the class NaiveMinST method createSpanningTree.
/**
* Creates a minimum spanning tree for a graph using the cost function specified in the constructor.
* @param tEdges the edges of the graph
* @param root the root node to start the spanning tree
* @param costFunction a function returning a cost value for a {@link TEdge}
* @param debugOutputFile file name for debug SVG. Debug output will be deactivated if this is null.
* @return the spanning tree
*/
public static Tree<KVector> createSpanningTree(final Set<TEdge> tEdges, final KVector root, final ICostFunction costFunction, final String debugOutputFile) {
// determine edge weights
Map<TEdge, Double> weight = Maps.newHashMap();
for (TEdge edge : tEdges) {
weight.put(edge, costFunction.cost(edge));
}
// sort edges by weight
List<TEdge> edgeList = Lists.newArrayList(tEdges);
edgeList.sort((TEdge e1, TEdge e2) -> weight.get(e1).compareTo(weight.get(e2)));
// preserves order
Set<TEdge> edges = Sets.newLinkedHashSet(edgeList);
// iteratively add cheapest edge where one node is contained in current tree and one is new
Tree<KVector> minST = new Tree<KVector>(root);
Map<KVector, Tree<KVector>> treeNodes = Maps.newHashMap();
treeNodes.put(root, minST);
// debug output ----------------------------------------------------------------------------------------------
SVGImage svg = new SVGImage(debugOutputFile);
// elkjs-exclude-start
svg.addGroups("e", "t");
for (TEdge e : edges) {
svg.g("e").addLine(e.u.x, e.u.y, e.v.x, e.v.y, "stroke=\"black\" stroke-width=\"1\"");
svg.g("t").addElementStr("<text x=\"" + (e.u.x + e.v.x) / 2 + "\" y=\"" + (e.u.y + e.v.y) / 2 + "\" fill=\"blue\"" + " font-size=\"20px\">" + String.format("%.2f", weight.get(e)) + "</text>");
}
svg.isave();
while (!edges.isEmpty()) {
TEdge nextEdge = null;
KVector nextNode = null;
KVector nodeInTree = null;
double minWeight = Double.POSITIVE_INFINITY;
for (TEdge edge : edges) {
if (weight.get(edge) <= minWeight) {
if (treeNodes.containsKey(edge.u) && !treeNodes.containsKey(edge.v)) {
nextNode = edge.v;
nodeInTree = edge.u;
nextEdge = edge;
// because edges is sorted we don't have to look any further
break;
}
if (treeNodes.containsKey(edge.v)) {
if (!treeNodes.containsKey(edge.u)) {
nextNode = edge.u;
nodeInTree = edge.v;
nextEdge = edge;
// because edges is sorted we don't have to look any further
break;
}
}
}
}
// connects a new node to the tree.
if (nextEdge == null) {
break;
}
// add the new node to the spanning tree
Tree<KVector> subTree = new Tree<KVector>(nextNode);
treeNodes.get(nodeInTree).children.add(subTree);
treeNodes.put(nextNode, subTree);
edges.remove(nextEdge);
// debug output -------------------------------------------------------------------------------------------
svg.g("e").addLine(nextEdge.u.x, nextEdge.u.y, nextEdge.v.x, nextEdge.v.y, "stroke=\"red\" stroke-width=\"3\"");
svg.isave();
// --------------------------------------------------------------------------------------------------------
}
return minST;
}
use of org.eclipse.elk.alg.common.utils.SVGImage in project elk by eclipse.
the class DepthFirstCompaction method compact.
/**
* Executes the compaction of the graph represented by the spanning tree.
* @param tree the spanning tree of a graph
* @param orthogonal whether to restrict the movement to orthogonal translations
* @param debugOutputFile file name for debug SVG. Debug output will be deactivated if this is null.
*/
public static void compact(final Tree<Node> tree, final boolean orthogonal, final String debugOutputFile) {
svg = new SVGImage(debugOutputFile);
orthogonalCompaction = orthogonal;
root = tree;
debugOut(tree, null);
compactTree(root);
}
use of org.eclipse.elk.alg.common.utils.SVGImage in project elk by eclipse.
the class GrowTreePhase method process.
@Override
public void process(final Graph graph, final IElkProgressMonitor progressMonitor) {
progressMonitor.begin("Grow Tree", 1);
root = graph.tree;
if (graph.getProperty(InternalProperties.DEBUG_SVG)) {
svg = new SVGImage(ElkUtil.debugFolderPath("spore") + "40or");
svg.addGroups("n", "e", "o");
debugOut();
} else {
svg = new SVGImage(null);
}
overlapsExisted = false;
growAt(graph.tree);
graph.setProperty(InternalProperties.OVERLAPS_EXISTED, overlapsExisted);
progressMonitor.done();
}
use of org.eclipse.elk.alg.common.utils.SVGImage in project elk by eclipse.
the class ShrinkTreeCompactionPhase method debugOut.
// --- debug utils --------------------------------------
private void debugOut(final Tree<Node> tree) {
SVGImage svg = new SVGImage(ElkUtil.debugFolderPath("spore") + "30Tree");
svg.clear();
// CHECKSTYLEOFF MagicNumber
svg.addCircle(tree.node.vertex.x, tree.node.vertex.y, 10, "fill=\"lime\"");
draw(tree, svg);
svg.save();
svg.debug = false;
}
Aggregations