Skip to content

Commit

Permalink
Merge pull request nabil6391#71 from tappeddev/master
Browse files Browse the repository at this point in the history
Add option to round edges in SugiyamaEdgeRenderer
  • Loading branch information
nabil6391 committed Feb 21, 2022
2 parents f54518b + 865b7e8 commit f1ac307
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 5 deletions.
4 changes: 3 additions & 1 deletion example/lib/layer_graphview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,12 @@ class _LayeredGraphViewPageState extends State<LayeredGraphViewPage> {

final Graph graph = Graph();

SugiyamaConfiguration builder = SugiyamaConfiguration();
SugiyamaConfiguration builder = SugiyamaConfiguration()
..bendPointShape = CurvedBendPointShape(curveLength: 20);

@override
void initState() {
super.initState();
final node1 = Node.Id(1);
final node2 = Node.Id(2);
final node3 = Node.Id(3);
Expand Down
2 changes: 1 addition & 1 deletion lib/layered/SugiyamaAlgorithm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class SugiyamaAlgorithm extends Algorithm {
var nodeCount = 1;

SugiyamaAlgorithm(this.configuration) {
renderer = SugiyamaEdgeRenderer(nodeData, edgeData);
renderer = SugiyamaEdgeRenderer(nodeData, edgeData, configuration.bendPointShape);
}

int get dummyId => 'Dummy ${nodeCount++}'.hashCode;
Expand Down
15 changes: 15 additions & 0 deletions lib/layered/SugiyamaConfiguration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class SugiyamaConfiguration {
int nodeSeparation = X_SEPARATION;
int orientation = DEFAULT_ORIENTATION;
int iterations = DEFAULT_ITERATIONS;
BendPointShape bendPointShape = SharpBendPointShape();

int getLevelSeparation() {
return levelSeparation;
Expand All @@ -28,3 +29,17 @@ class SugiyamaConfiguration {
return orientation;
}
}

abstract class BendPointShape {}

class SharpBendPointShape extends BendPointShape {}

class MaxCurvedBendPointShape extends BendPointShape {}

class CurvedBendPointShape extends BendPointShape {
final double curveLength;

CurvedBendPointShape({
required this.curveLength,
});
}
67 changes: 64 additions & 3 deletions lib/layered/SugiyamaEdgeRenderer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ part of graphview;
class SugiyamaEdgeRenderer extends ArrowEdgeRenderer {
Map<Node, SugiyamaNodeData> nodeData;
Map<Edge, SugiyamaEdgeData> edgeData;
BendPointShape bendPointShape;

SugiyamaEdgeRenderer(this.nodeData, this.edgeData);
SugiyamaEdgeRenderer(this.nodeData, this.edgeData, this.bendPointShape);

var path = Path();

Expand Down Expand Up @@ -56,8 +57,30 @@ class SugiyamaEdgeRenderer extends ArrowEdgeRenderer {
path.reset();
path.moveTo(bendPoints[0], bendPoints[1]);

for (var i = 3; i < size - 2; i = i + 2) {
path.lineTo(bendPoints[i - 1], bendPoints[i]);
final bendPointsWithoutDuplication = <Offset>[];

for (var i = 0; i < bendPoints.length; i += 2) {
final isLastPoint = i == bendPoints.length - 2;

final x = bendPoints[i];
final y = bendPoints[i + 1];
final x2 = isLastPoint ? -1 : bendPoints[i + 2];
final y2 = isLastPoint ? -1 : bendPoints[i + 3];
if (x == x2 && y == y2) {
// Skip when two consecutive points are identical
// because drawing a line between would be redundant in this case.
continue;
}
bendPointsWithoutDuplication.add(Offset(x, y));
}

if (bendPointShape is MaxCurvedBendPointShape) {
_drawMaxCurvedBendPointsEdge(bendPointsWithoutDuplication);
} else if (bendPointShape is CurvedBendPointShape) {
final shape = bendPointShape as CurvedBendPointShape;
_drawCurvedBendPointsEdge(bendPointsWithoutDuplication, shape.curveLength);
} else {
_drawSharpBendPointsEdge(bendPointsWithoutDuplication);
}

path.lineTo(triangleCentroid[0], triangleCentroid[1]);
Expand All @@ -78,4 +101,42 @@ class SugiyamaEdgeRenderer extends ArrowEdgeRenderer {
}
});
}

void _drawSharpBendPointsEdge(List<Offset> bendPoints) {
for (var i = 1; i < bendPoints.length - 1; i++) {
path.lineTo(bendPoints[i].dx, bendPoints[i].dy);
}
}

void _drawMaxCurvedBendPointsEdge(List<Offset> bendPoints) {
for (var i = 1; i < bendPoints.length - 1; i++) {
final nextNode = bendPoints[i];
final afterNextNode = bendPoints[i + 1];
final curveEndPoint = Offset((nextNode.dx + afterNextNode.dx) / 2, (nextNode.dy + afterNextNode.dy) / 2);
path.quadraticBezierTo(nextNode.dx, nextNode.dy, curveEndPoint.dx, curveEndPoint.dy);
}
}

void _drawCurvedBendPointsEdge(List<Offset> bendPoints, double curveLength) {
for (var i = 1; i < bendPoints.length - 1; i++) {
final previousNode = i == 1 ? null : bendPoints[i - 2];
final currentNode = bendPoints[i - 1];
final nextNode = bendPoints[i];
final afterNextNode = bendPoints[i + 1];

final arcStartPointRadians = atan2(nextNode.dy - currentNode.dy, nextNode.dx - currentNode.dx);
final arcStartPoint = nextNode - Offset.fromDirection(arcStartPointRadians, curveLength);
final arcEndPointRadians = atan2(nextNode.dy - afterNextNode.dy, nextNode.dx - afterNextNode.dx);
final arcEndPoint = nextNode - Offset.fromDirection(arcEndPointRadians, curveLength);

if (previousNode != null &&
((currentNode.dx == nextNode.dx && nextNode.dx == afterNextNode.dx) ||
(currentNode.dy == nextNode.dy && nextNode.dy == afterNextNode.dy))) {
path.lineTo(nextNode.dx, nextNode.dy);
} else {
path.lineTo(arcStartPoint.dx, arcStartPoint.dy);
path.quadraticBezierTo(nextNode.dx, nextNode.dy, arcEndPoint.dx, arcEndPoint.dy);
}
}
}
}

0 comments on commit f1ac307

Please sign in to comment.