diff --git a/flutter/lib/src/curve_painter.dart b/flutter/lib/src/curve_painter.dart new file mode 100644 index 0000000..cebba79 --- /dev/null +++ b/flutter/lib/src/curve_painter.dart @@ -0,0 +1,333 @@ +import 'package:arrow_path/arrow_path.dart'; +import 'package:graphs/src/graph.dart'; +import 'package:flutter/material.dart'; +import 'dart:math'; + +class Operations { + static const String bfs = "bfs"; + static const String dfs = "dfs"; + static const String dijkstra = "djkstr"; + static const String none = "none"; +} + +class CurvePainter extends CustomPainter { + CurvePainter({ + Key? key, + required this.graphData, + required this.intListPath, + required this.dfsAccessTable, + required this.start, + required this.end, + required this.op, + }); + + List? intListPath; + List? dfsAccessTable; + String op; + int? start; + int? end; + Graphs graphData; + final double _dotRad = 7; + final double _lineWidth = 1.5; + final Color _lineColor = Colors.black; + final double _aboveHeight = 5; + double _circleRad = 100; + final TextStyle _textStyle = TextStyle( + color: Colors.red.shade900, + decorationColor: Colors.green.shade900, + decorationThickness: 10, + decorationStyle: TextDecorationStyle.dashed, + fontSize: 20, + ); + Map _off = {}; + void _drawLine(Canvas canvas, Offset p1, Offset p2) { + Paint p = Paint(); + p.color = _lineColor; + p.strokeWidth = _lineWidth; + canvas.drawLine(p1, p2, p); + } + + void _drawDot(Canvas canvas, Offset p1, [double plusRad = 0, Color? col]) { + col ??= Colors.yellow.shade900; + var p = Paint(); + p.color = col; + p.strokeWidth = _lineWidth + 2; + canvas.drawCircle(p1, _dotRad + plusRad, p); + } + + void _drawSelfConnect(Canvas canvas, Offset p1) { + var p = Paint(); + p.color = _lineColor; + p.strokeWidth = _lineWidth; + p.style = PaintingStyle.stroke; + canvas.drawCircle(Offset(p1.dx + _dotRad + 20, p1.dy), _dotRad + 20, p); + } + + TextSpan _getTextSpan(String s) => TextSpan(text: s, style: _textStyle); + TextPainter _getTextPainter(String s) => TextPainter( + text: _getTextSpan(s), + textDirection: TextDirection.ltr, + textAlign: TextAlign.center); + + void _drawDotNames(Canvas canvas, Offset place, String s) { + var textPainter = _getTextPainter(s); + textPainter.layout(); + textPainter.paint( + canvas, + Offset((place.dx - textPainter.width), + (place.dy - textPainter.height) - _aboveHeight)); + } + + void _drawDotNum(Canvas canvas, Offset size, String s) { + var textPainter = TextPainter( + text: TextSpan( + text: s, + style: const TextStyle( + color: Colors.black, + fontSize: 17, + )), + textDirection: TextDirection.ltr, + textAlign: TextAlign.center); + textPainter.layout(); + textPainter.paint( + canvas, + Offset((size.dx - textPainter.width) + 25, + (size.dy - textPainter.height) + _aboveHeight + 30)); + } + + int _getHighInputConnections() { + if (graphData.getDots().length != 1 && graphData.getDots().length <= 3) { + return -1; + } + int higest = -1; + for (var i in graphData.getDots()) { + if (i.getL().length > higest) higest = i.num; + } + return higest; + } + + Map _getDotPos(int dotsAm, Size size) { + Map off = {}; + var width = size.width / 2; + var height = size.height / 2; + int add = 0; + int h = _getHighInputConnections(); + for (int i = 0; i < dotsAm; i++) { + if ((i + 1) != h) { + double x = + cos(2 * pi * (i - add) / (dotsAm - add)) * _circleRad + width; + double y = + sin(2 * pi * (i - add) / (dotsAm - add)) * _circleRad + height; + + off[i + 1] = Offset(x, y); + } else if ((i + 1) == h) { + off[i + 1] = Offset(width + 2, height - 2); + add = 1; + h = 0; + } else { + print("GetDotPos error"); + } + //print(off.length); + } + + //print(off); + return off; + } + + void _drawHArrow(Canvas canvas, Size size, Offset from, Offset to, + [bool doubleSided = false]) { + Path path; + + // The arrows usually looks better with rounded caps. + Paint paint = Paint() + ..color = Colors.black + ..style = PaintingStyle.stroke + ..strokeCap = StrokeCap.round + ..strokeJoin = StrokeJoin.round + ..strokeWidth = _lineWidth; + + var length = sqrt((to.dx - from.dx) * (to.dx - from.dx) + + (to.dy - from.dy) * (to.dy - from.dy)); + + /// Draw a single arrow. + path = Path(); + path.moveTo(from.dx, from.dy); + path.relativeCubicTo( + 0, + 0, + -(from.dx + to.dx + length) / (length) - 40, + -(from.dy + to.dy + length) / (length) - 40, + to.dx - from.dx, + to.dy - from.dy); + path = + ArrowPath.make(path: path, isDoubleSided: doubleSided, tipLength: 16); + canvas.drawPath(path, paint); + } + + void _drawHighArrow(Canvas canvas, Size size, Offset from, Offset to, + [bool doubleSided = false]) { + Path path; + + // The arrows usually looks better with rounded caps. + Paint paint = Paint() + ..color = Colors.black + ..style = PaintingStyle.stroke + ..strokeCap = StrokeCap.round + ..strokeJoin = StrokeJoin.round + ..strokeWidth = _lineWidth; + + var length = sqrt((to.dx - from.dx) * (to.dx - from.dx) + + (to.dy - from.dy) * (to.dy - from.dy)); + + /// Draw a single arrow. + path = Path(); + path.moveTo(from.dx, from.dy); + path.relativeCubicTo( + 0, + 0, + (from.dx + to.dx) / (length * 2) + 40, + (from.dy + to.dy) / (length * 2) + 40, + to.dx - from.dx, + to.dy - from.dy); + path = ArrowPath.make( + path: path, + isDoubleSided: doubleSided, + tipLength: 13, + isAdjusted: false); + canvas.drawPath(path, paint); + } + + void _drawConnections( + Canvas canvas, Size size, List dots, Map off) { + for (var i in dots) { + var list = i.getL(); + var beg = off[i.num]; + for (var d in list.keys) { + if (d == i.num) { + _drawSelfConnect(canvas, off[d]!); + } else { + if (graphData.getDoubleSidedBool()) { + if (d > i.num) { + _drawHArrow(canvas, size, beg!, off[d]!, false); + if (graphData.getUseLengthBool()) { + _drawDotNames( + canvas, + Offset((off[d]!.dx + beg.dx) / 2 - 18, + (off[d]!.dy + beg.dy) / 2 - 18), + i.getL()[d].toString()); + } + } else { + _drawHighArrow(canvas, size, beg!, off[d]!, false); + if (graphData.getUseLengthBool()) { + _drawDotNames( + canvas, + Offset((off[d]!.dx + beg.dx) / 2 + 30, + (off[d]!.dy + beg.dy) / 2 + 30), + i.getL()[d].toString()); + } + } + } else { + _drawLine(canvas, beg!, off[d]!); + if (graphData.getUseLengthBool()) { + _drawDotNames( + canvas, + Offset((off[d]!.dx + beg.dx) / 2, (off[d]!.dy + beg.dy) / 2), + i.getL()[d].toString()); + } + } + } + } + } + } + + void _drawBFS(Canvas canvas) { + if (intListPath != null) { + for (int i = 0; i < intListPath!.length; i++) { + _drawDot(canvas, _off[intListPath![i]]!, 8, Colors.yellow); + } + _drawDot(canvas, _off[start]!, 9, Colors.green); + _drawDot(canvas, _off[end]!, 7, Colors.red.shade200); + for (int i = 0; i < intListPath!.length; i++) { + _drawDotNum(canvas, _off[intListPath![i]]!, "bfs: №${i + 1}"); + } + } + } + + void _drawDFS(Canvas canvas) { + if (dfsAccessTable != null) { + for (int i = 0; i < dfsAccessTable!.length; i++) { + if (dfsAccessTable![i]) { + _drawDot(canvas, _off[i + 1]!, 8, Colors.green.shade500); + _drawDotNum(canvas, _off[i + 1]!, "dfs: visible"); + } else { + _drawDot(canvas, _off[i + 1]!, 7, Colors.red.shade500); + _drawDotNum(canvas, _off[i + 1]!, "dfs: not visible"); + } + } + _drawDot(canvas, _off[start]!, 9, Colors.green.shade900); + } + } + + void _drawDijkstra(Canvas canvas) { + if (intListPath != null) { + _drawDot(canvas, _off[start]!, 9, Colors.green); + for (int i = 0; i < intListPath!.length; i++) { + if (intListPath![i] == null) { + _drawDotNum(canvas, _off[i + 1]!, "len: INF"); + } else { + _drawDotNum(canvas, _off[i + 1]!, "len: ${intListPath![i]}"); + } + } + } + } + + @override + void paint(Canvas canvas, Size size) { + if (size.width > size.height) { + _circleRad = size.height / 3; + } else { + _circleRad = size.width / 3; + } + + _off = _getDotPos(graphData.getDotAmount(), size); //, higest); + for (int i in _off.keys) { + _drawDot(canvas, _off[i]!); + //drawDotNames(canvas, off[i]!, "${graphData.getDots()[i - 1].getName()}:[$i]"); + } + + var g = graphData.getDots(); + switch (op) { + case Operations.bfs: + { + _drawBFS(canvas); + break; + } + case Operations.dfs: + { + _drawDFS(canvas); + break; + } + case Operations.dijkstra: + { + _drawDijkstra(canvas); + break; + } + default: + { + break; + } + } + + _drawConnections(canvas, size, g, _off); + for (int i in _off.keys) { + //drawDot(canvas, off[i]!); + _drawDotNames( + canvas, _off[i]!, "${graphData.getDots()[i - 1].getName()}:[$i]"); + } + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) { + return true; + } +} diff --git a/flutter/lib/src/graph.dart b/flutter/lib/src/graph.dart index cd8707b..9dfe2d4 100644 --- a/flutter/lib/src/graph.dart +++ b/flutter/lib/src/graph.dart @@ -89,6 +89,7 @@ class Dot { } class Graphs { + static const int intMax = 0x7fffffffffffffff; //Data String _name = "Undefined"; //Имя int _amount = 0; //Количество вершин @@ -151,6 +152,9 @@ class Graphs { if (from <= 0 || from > _amount || to <= 0 && to > _amount) { return "Can't find specified path"; } + if (!_dots[from - 1].hasConnection(to)) { + return "Already no connection between $from and $to"; + } _dots[from - 1].delPath(to); if (!_oriented) { _dots[to - 1].delPath(from); @@ -652,28 +656,41 @@ class Graphs { return label; } - void dijkstra(int source) { + List dijkstra(int from) { /* - create vertex set Q; + int n; + ... чтение n ... + //vector>> g(_amount); + */ + List d = List.filled(_amount, intMax); + List p = List.filled(_amount, -1); - for each vertex v in Graph{ - dist[v] ← INFINITY ; - prev[v] ← UNDEFINED ; - add v to Q;} - dist[source] ← 0; - - while Q is not empty{ - u ← vertex in Q with min dist[u] - - remove u from Q - - for each neighbor v of u still in Q{ - alt ← dist[u] + length(u, v); - if alt < dist[v]: { - dist[v] ← alt; - prev[v] ← u;} - }} - return dist[], prev[]*/ + d[from - 1] = 0; + List u = List.filled(_amount, false); + for (int i = 0; i < _amount; ++i) { + int v = -1; + for (int j = 0; j < _amount; ++j) { + // int t; + if (!u[j] && (v == -1 || d[j]! < d[v]!)) { + v = j; + } + } + if (d[v] == intMax) break; + u[v] = true; + for (int to in _dots[v].getL().keys) { + int len = _dots[v].getL()[to]!; + if (!_useLength && len == 0) len = 1; + if (d[v]! + len < d[to - 1]!) { + d[to - 1] = d[v]! + len; + p[to - 1] = v; + } + } + } + for (int i = 0; i < d.length; i++) { + // подумать как убрать эту часть + if (d[i] == intMax) d[i] = null; + } + return d; } //************Алгоритмы************ }