Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option for different types of lines for Sugiyama #122

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion lib/Graph.dart
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,13 @@ class Graph {

}

enum LineType {
Default,
DottedLine,
DashedLine,
SineLine,
}

class Node {
ValueKey? key;

Expand All @@ -163,6 +170,8 @@ class Node {

Offset position = Offset(0, 0);

LineType lineType = LineType.Default;

double get height => size.height;

double get width => size.width;
Expand All @@ -189,7 +198,7 @@ class Node {

@override
String toString() {
return 'Node{position: $position, key: $key, _size: $size}';
return 'Node{position: $position, key: $key, _size: $size, lineType: $lineType}';
}
}

Expand Down
1 change: 1 addition & 0 deletions lib/GraphView.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'dart:ui';
import 'package:collection/collection.dart' show IterableExtension;

part 'Graph.dart';
Expand Down
4 changes: 2 additions & 2 deletions lib/layered/SugiyamaAlgorithm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class SugiyamaAlgorithm extends Algorithm {
void initSugiyamaData() {
graph.nodes.forEach((node) {
node.position = Offset(0, 0);
nodeData[node] = SugiyamaNodeData();
nodeData[node] = SugiyamaNodeData(node.lineType);
});

graph.edges.forEach((edge) {
Expand Down Expand Up @@ -149,7 +149,7 @@ class SugiyamaAlgorithm extends Algorithm {
while (iterator.moveNext()) {
final edge = iterator.current;
final dummy = Node.Id(dummyId.hashCode);
final dummyNodeData = SugiyamaNodeData();
final dummyNodeData = SugiyamaNodeData(node.lineType);
dummyNodeData.isDummy = true;
dummyNodeData.layer = indexNextLayer;
nextLayer.add(dummy);
Expand Down
127 changes: 120 additions & 7 deletions lib/layered/SugiyamaEdgeRenderer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class SugiyamaEdgeRenderer extends ArrowEdgeRenderer {
var trianglePaint = Paint()
..color = paint.color
..style = PaintingStyle.fill;


graph.edges.forEach((edge) {
final source = edge.source;
Expand Down Expand Up @@ -100,19 +101,48 @@ class SugiyamaEdgeRenderer extends ArrowEdgeRenderer {

clippedLine = clipLine(startX, startY, stopX, stopY, destination);

var destinationPoint = Offset(stopX, stopY);

if (addTriangleToEdge) {
final triangleCentroid = drawTriangle(
canvas, edgeTrianglePaint ?? trianglePaint, clippedLine[0],
clippedLine[1], clippedLine[2], clippedLine[3]);

canvas.drawLine(
Offset(clippedLine[0], clippedLine[1]), Offset(triangleCentroid[0], triangleCentroid[1]), currentPaint);
} else {
canvas.drawLine(
Offset(clippedLine[0], clippedLine[1]), Offset(stopX, stopY), currentPaint);
destinationPoint = Offset(triangleCentroid[0], triangleCentroid[1]);
}
}
});

// Draw the line
switch (nodeData[destination]?.lineType) {
case LineType.DashedLine:
_drawDashedLine(
canvas,
Offset(clippedLine[0], clippedLine[1]), destinationPoint,
currentPaint, 0.6
);
break;
case LineType.DottedLine:
// dotted line uses the same method as dashed line, but with a lineLength of 0.0
_drawDashedLine(
canvas,
Offset(clippedLine[0], clippedLine[1]), destinationPoint,
currentPaint, 0.0
);
break;
case LineType.SineLine:
_drawSineLine(
canvas,
Offset(clippedLine[0], clippedLine[1]), destinationPoint,
currentPaint
);
break;
default:
canvas.drawLine(
Offset(clippedLine[0], clippedLine[1]), destinationPoint,
currentPaint
);
break;
}
}});
}

void _drawSharpBendPointsEdge(List<Offset> bendPoints) {
Expand Down Expand Up @@ -152,4 +182,87 @@ class SugiyamaEdgeRenderer extends ArrowEdgeRenderer {
}
}
}


void _drawDashedLine(Canvas canvas, Offset source, Offset destination, Paint paint, double lineLength) {
// Calculate the distance between the source and destination points
var dx = destination.dx - source.dx;
var dy = destination.dy - source.dy;

// Calculate the Euclidean distance
var distance = sqrt(dx * dx + dy * dy);

var numLines = lineLength == 0.0 ? (distance / 5).ceil() : 14;

// Calculate the step size for each line
var stepX = dx / numLines;
var stepY = dy / numLines;

// Set a fixed radius for the circles
var circleRadius = 1.0;

// Set a fixed stroke width for the circles
var circleStrokeWidth = 1.0;
var circlePaint = Paint()
..color = paint.color
..strokeWidth = circleStrokeWidth
..style = PaintingStyle.fill; // Change to fill style

// Draw the lines or dots between the two points
Iterable<int>.generate(numLines).forEach((i) {
var startX = source.dx + (i * stepX);
var startY = source.dy + (i * stepY);
if (lineLength == 0.0) {
// Draw a dot with a fixed radius and stroke width
canvas.drawCircle(Offset(startX, startY), circleRadius, circlePaint);
} else {
// Draw a dash
var endX = startX + (stepX * lineLength);
var endY = startY + (stepY * lineLength);
canvas.drawLine(Offset(startX, startY), Offset(endX, endY), paint);
}
});
}

void _drawSineLine(Canvas canvas, Offset source, Offset destination, Paint paint) {
paint..strokeWidth = 1.5;

final dx = destination.dx - source.dx;
final dy = destination.dy - source.dy;
final distance = sqrt(dx * dx + dy * dy);
final lineLength = 6;
var phaseOffset = 2;

// Verify dx and dy to avoid NaN to Offset()
if (dx != 0 || dy != 0) {
var distanceTraveled = 0.0;
var phase = 0.0;
final path = Path()..moveTo(source.dx, source.dy);

while (distanceTraveled < distance) {
final segmentLength = min(lineLength, distance - distanceTraveled);
final segmentFraction = segmentLength / distance;
final segmentDestination = Offset(
source.dx + dx * segmentFraction,
source.dy + dy * segmentFraction,
);

final y = sin(phase + phaseOffset) * segmentLength;

num x;
if ((dx > 0 && dy < 0) || (dx < 0 && dy > 0)) {
x = sin(phase + phaseOffset) * segmentLength;
} else { // dx < 0 && dy < 0
x = -sin(phase + phaseOffset) * segmentLength;
}

path.lineTo(segmentDestination.dx + x, segmentDestination.dy + y);

distanceTraveled += segmentLength;
source = segmentDestination;
phase += pi * segmentLength / lineLength;
}
canvas.drawPath(path, paint);
}
}
}
5 changes: 4 additions & 1 deletion lib/layered/SugiyamaNodeData.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ class SugiyamaNodeData {
int position = -1;
List<Node> predecessorNodes = [];
List<Node> successorNodes = [];
LineType lineType;

SugiyamaNodeData(this.lineType);

bool get isReversed => reversed.isNotEmpty;

@override
String toString() {
return 'SugiyamaNodeData{reversed: $reversed, isDummy: $isDummy, median: $median, layer: $layer, position: $position';
return 'SugiyamaNodeData{reversed: $reversed, isDummy: $isDummy, median: $median, layer: $layer, position: $position, lineType: $lineType}';
}
}