use of org.eclipse.elk.core.math.KVector in project elk by eclipse.
the class GraphRenderer method renderEdge.
/**
* Paints an edge for the given dirty area.
*
* @param edge the edge to paint
* @param graphics the graphics context used to paint
* @param area dirty area that needs painting
* @param offset offset to be added to relative coordinates
* @param labelAlpha alpha value for labels
*/
private void renderEdge(final ElkEdge edge, final GC graphics, final Rectangle area, final KVector offset, final int labelAlpha) {
if (configurator.getEdgeColor() == null) {
return;
}
// Find our if the edge is actually eligible to be painted
if (isEdgeFullyContainedInGraphToDraw(edge)) {
// Get a PaintRectangle ready for the edge
PaintRectangle rect = boundsMap.get(edge);
if (rect == null) {
rect = new PaintRectangle(edge, offset, scale);
boundsMap.put(edge, rect);
}
if (!rect.painted && rect.intersects(area)) {
// Gather some information
final boolean splineEdge = edge.getProperty(CoreOptions.EDGE_ROUTING) == EdgeRouting.SPLINES;
final boolean directedEdge = edge.getProperty(CoreOptions.EDGE_TYPE) != EdgeType.UNDIRECTED;
graphics.setAlpha(255);
// The background color is required to fill the arrow of directed edges
graphics.setForeground(configurator.getEdgeColor());
graphics.setBackground(configurator.getEdgeColor());
for (ElkEdgeSection edgeSection : edge.getSections()) {
KVectorChain bendPoints = ElkUtil.createVectorChain(edgeSection);
bendPoints.scale(scale).offset(offset);
// Draw the damn edge already...!
Path path = new Path(graphics.getDevice());
Iterator<KVector> pointIter = bendPoints.iterator();
KVector startPoint = pointIter.next();
path.moveTo((float) startPoint.x, (float) startPoint.y);
KVector point1 = null;
KVector point2 = null;
while (pointIter.hasNext()) {
if (splineEdge) {
if (point1 == null) {
point1 = pointIter.next();
} else if (point2 == null) {
point2 = pointIter.next();
} else {
KVector endPoint = pointIter.next();
path.cubicTo((float) point1.x, (float) point1.y, (float) point2.x, (float) point2.y, (float) endPoint.x, (float) endPoint.y);
point1 = null;
point2 = null;
}
} else {
KVector nextPoint = pointIter.next();
path.lineTo((float) nextPoint.x, (float) nextPoint.y);
}
}
if (splineEdge && point2 != null) {
path.quadTo((float) point1.x, (float) point1.y, (float) point2.x, (float) point2.y);
} else if (splineEdge && point1 != null) {
path.lineTo((float) point1.x, (float) point1.y);
}
graphics.drawPath(path);
if (directedEdge) {
// Draw an arrow at the last segment of the connection
KVector referencePoint;
if (splineEdge && (bendPoints.size() - 1) % 3 != 1) {
int beginIndex;
if ((bendPoints.size() - 1) % 3 == 2) {
beginIndex = bendPoints.size() - 2;
} else {
beginIndex = bendPoints.size() - 3;
}
referencePoint = ElkMath.getPointOnBezierSegment(0.5, bendPoints.toArray(beginIndex));
} else {
referencePoint = bendPoints.get(bendPoints.size() - 2);
}
int[] arrowPoly = makeArrow(referencePoint, bendPoints.getLast());
if (arrowPoly != null) {
graphics.fillPolygon(arrowPoly);
}
}
}
rect.painted = true;
}
}
// paint junction points
KVectorChain vc = edge.getProperty(CoreOptions.JUNCTION_POINTS);
if (vc != null) {
for (KVector v : vc) {
KVector center = v.clone().scale(scale).add(offset).sub(2, 2);
graphics.fillOval((int) center.x, (int) center.y, 6, 6);
}
}
// paint the edge labels
if (configurator.getEdgeLabelFont() != null) {
graphics.setFont(configurator.getEdgeLabelFont());
for (ElkLabel label : edge.getLabels()) {
renderLabel(label, graphics, area, offset, labelAlpha);
}
}
}
use of org.eclipse.elk.core.math.KVector in project elk by eclipse.
the class GraphRenderingCanvas method setLayoutGraph.
/**
* Sets the given layout graph as the painted graph.
*
* @param graph layout graph to be painted
*/
public void setLayoutGraph(final ElkNode graph) {
KVector baseOffset = calculateRequiredCanvasSizeAndBaseOffset(graph);
this.layoutGraph = graph;
graphRenderer.setBaseOffset(baseOffset);
redraw();
}
use of org.eclipse.elk.core.math.KVector in project elk by eclipse.
the class GraphRenderingCanvas method pan.
/**
* Pan the canvas by adjusting the base offset by the given delta.
*/
protected void pan(final KVector delta) {
KVector offset = graphRenderer.getBaseOffset().add(delta);
// Though the offset is modified in place, it needs to be reset in order to flush the cache
graphRenderer.setBaseOffset(offset);
}
use of org.eclipse.elk.core.math.KVector in project elk by eclipse.
the class GraphRenderingCanvas method calculateRequiredCanvasSizeAndBaseOffset.
/**
* Sets size of the canvas by looking up the biggest distances between graph elements in x- and y-direction
* and return a KVector with the required offset of the origin coordinates to fit all elements on the canvas.
*
* @param graph to be painted
*/
private KVector calculateRequiredCanvasSizeAndBaseOffset(final ElkNode graph) {
if (graph != null) {
double minX = Double.MAX_VALUE;
double maxX = Double.MIN_VALUE;
double minY = Double.MAX_VALUE;
double maxY = Double.MIN_VALUE;
// check all nodes for their coordinates
for (ElkNode node : graph.getChildren()) {
minX = Math.min(minX, node.getX());
maxX = Math.max(maxX, node.getX() + node.getWidth());
minY = Math.min(minY, node.getY());
maxY = Math.max(maxY, node.getY() + node.getHeight());
// check node labels
for (ElkLabel nodeLabel : node.getLabels()) {
minX = Math.min(minX, node.getX() + nodeLabel.getX());
maxX = Math.max(maxX, node.getX() + nodeLabel.getX() + nodeLabel.getWidth());
minY = Math.min(minY, node.getY() + nodeLabel.getY());
maxY = Math.max(maxY, node.getY() + nodeLabel.getY() + nodeLabel.getHeight());
}
}
for (ElkEdge edge : graph.getContainedEdges()) {
// check all sections of the edges for their coordinates
for (ElkEdgeSection edgeSection : edge.getSections()) {
minX = Math.min(minX, edgeSection.getStartX());
minY = Math.min(minY, edgeSection.getStartY());
maxX = Math.max(maxX, edgeSection.getStartX());
maxY = Math.max(maxY, edgeSection.getStartY());
minX = Math.min(minX, edgeSection.getEndX());
minY = Math.min(minY, edgeSection.getEndY());
maxX = Math.max(maxX, edgeSection.getEndX());
maxY = Math.max(maxY, edgeSection.getEndY());
for (ElkBendPoint bendPoint : edgeSection.getBendPoints()) {
minX = Math.min(minX, bendPoint.getX());
minY = Math.min(minY, bendPoint.getY());
maxX = Math.max(maxX, bendPoint.getX());
maxY = Math.max(maxY, bendPoint.getY());
}
}
// check edge labels
for (ElkLabel edgeLabel : edge.getLabels()) {
minX = Math.min(minX, edgeLabel.getX());
maxX = Math.max(maxX, edgeLabel.getX() + edgeLabel.getWidth());
minY = Math.min(minY, edgeLabel.getY());
maxY = Math.max(maxY, edgeLabel.getY() + edgeLabel.getHeight());
}
}
int x = ((int) (Math.max(graph.getWidth(), maxX) - Math.min(0, minX))) + 1;
int y = ((int) (Math.max(graph.getHeight(), maxY) - Math.min(0, minY))) + 1;
setSize(new Point(x, y));
return new KVector((-Math.min(0, minX)), (-Math.min(0, minY)));
}
return new KVector();
}
use of org.eclipse.elk.core.math.KVector in project elk by eclipse.
the class BoxLayoutProvider method placeBoxes.
/**
* Place the boxes of the given sorted list according to their order in the list.
*
* @param sortedBoxes sorted list of boxes
* @param minSpacing minimal spacing between elements
* @param borderSpacing spacing to the border
* @param minTotalWidth minimal width of the parent node
* @param minTotalHeight minimal height of the parent node
* @param expandNodes if true, the nodes are expanded to fill their parent
* @param aspectRatio the desired aspect ratio
* @return the bounding box of the resulting layout
*/
private KVector placeBoxes(final List<ElkNode> sortedBoxes, final double minSpacing, final ElkPadding padding, final double minTotalWidth, final double minTotalHeight, final boolean expandNodes, final double aspectRatio) {
// determine the maximal row width by the maximal box width and the total area
double maxRowWidth = 0.0f;
double totalArea = 0.0f;
for (ElkNode box : sortedBoxes) {
ElkUtil.resizeNode(box);
maxRowWidth = Math.max(maxRowWidth, box.getWidth());
totalArea += box.getWidth() * box.getHeight();
}
// calculate std deviation
// rationale: the greater the diversity of box sizes, the more space will be 'wasted',
// contributing to the total area. We address this by adding x*n*stddev to the area.
// TODO x should be assessed empirically, for the moment set it to 1
double mean = totalArea / sortedBoxes.size();
double stddev = areaStdDev(sortedBoxes, mean);
totalArea += (sortedBoxes.size() * 1 * stddev);
// calculate the required row width w to achieve the desired aspect ratio,
// i.e.: w*h=area s.t. w/h=dar -> w=sqrt(area * dar)
maxRowWidth = Math.max(maxRowWidth, Math.sqrt(totalArea * aspectRatio)) + padding.getLeft();
// place nodes iteratively into rows
double xpos = padding.getLeft();
double ypos = padding.getTop();
double highestBox = 0.0f;
double broadestRow = padding.getHorizontal();
LinkedList<Integer> rowIndices = new LinkedList<>();
rowIndices.add(Integer.valueOf(0));
LinkedList<Double> rowHeights = new LinkedList<>();
ListIterator<ElkNode> boxIter = sortedBoxes.listIterator();
while (boxIter.hasNext()) {
ElkNode box = boxIter.next();
double width = box.getWidth();
double height = box.getHeight();
if (xpos + width > maxRowWidth) {
// place box into the next row
if (expandNodes) {
rowHeights.addLast(Double.valueOf(highestBox));
rowIndices.addLast(Integer.valueOf(boxIter.previousIndex()));
}
xpos = padding.getLeft();
ypos += highestBox + minSpacing;
highestBox = 0.0f;
broadestRow = Math.max(broadestRow, padding.getHorizontal() + width);
}
box.setLocation(xpos, ypos);
broadestRow = Math.max(broadestRow, xpos + width + padding.getRight());
highestBox = Math.max(highestBox, height);
xpos += width + minSpacing;
}
broadestRow = Math.max(broadestRow, minTotalWidth);
double totalHeight = ypos + highestBox + padding.getBottom();
if (totalHeight < minTotalHeight) {
highestBox += minTotalHeight - totalHeight;
totalHeight = minTotalHeight;
}
// expand nodes if required
if (expandNodes) {
xpos = padding.getLeft();
boxIter = sortedBoxes.listIterator();
rowIndices.addLast(Integer.valueOf(sortedBoxes.size()));
ListIterator<Integer> rowIndexIter = rowIndices.listIterator();
int nextRowIndex = rowIndexIter.next();
rowHeights.addLast(Double.valueOf(highestBox));
ListIterator<Double> rowHeightIter = rowHeights.listIterator();
double rowHeight = 0.0f;
while (boxIter.hasNext()) {
if (boxIter.nextIndex() == nextRowIndex) {
xpos = padding.getLeft();
rowHeight = rowHeightIter.next();
nextRowIndex = rowIndexIter.next();
}
ElkNode box = boxIter.next();
double oldHeight = box.getHeight();
box.setHeight(rowHeight);
double newHeight = rowHeight;
if (boxIter.nextIndex() == nextRowIndex) {
double newWidth = broadestRow - xpos - padding.getRight();
double oldWidth = box.getWidth();
box.setWidth(newWidth);
// Content alignment
ElkUtil.translate(box, new KVector(newWidth, newHeight), new KVector(oldWidth, oldHeight));
}
xpos += box.getWidth() + minSpacing;
}
}
// return parent size
return new KVector(broadestRow, totalHeight);
}
Aggregations