diff --git a/flutter/lib/curve_painter.dart b/flutter/lib/curve_painter.dart new file mode 100644 index 0000000..4363d0b --- /dev/null +++ b/flutter/lib/curve_painter.dart @@ -0,0 +1,329 @@ +import 'package:arrow_path/arrow_path.dart'; +import 'package:graphs/src/graph.dart'; +import 'package:flutter/material.dart'; +import 'dart:math'; + +class CurvePainter extends CustomPainter { + CurvePainter({ + Key? key, + required this.graphData, + required this.bfsPath, + required this.start, + required this.end, + required this.dfsAccessTable, + }); + + List? bfsPath; + List? dfsAccessTable; + int? start; + int? end; + Graphs graphData; + final double _dotRad = 6; + 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 (bfsPath != null) { + for (int i = 0; i < bfsPath!.length; i++) { + _drawDot(canvas, _off[bfsPath![i]]!, 8, Colors.yellow); + } + _drawDot(canvas, _off[start]!, 9, Colors.green); + _drawDot(canvas, _off[end]!, 7, Colors.red.shade200); + for (int i = 0; i < bfsPath!.length; i++) { + _drawDotNum(canvas, _off[bfsPath![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); + } + } + + @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(); + _drawBFS(canvas); + _drawDFS(canvas); + _drawConnections(canvas, size, g, _off); + //pringArr(canvas, size); + //drawArrow(canvas, Offset(size.width / 2, size.height / 2), + // Offset(size.width / 2 + 50, size.height / 2 + 200)); + //} + + 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; + } +} + +/*void _pringArr(Canvas canvas, Size size) { + TextSpan textSpan; + TextPainter textPainter; + 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 = 3.0; + + /// Draw a single arrow. + path = Path(); + path.moveTo(size.width * 0.25, size.height * 0.10); + path.relativeCubicTo(0, 0, size.width * 0.25, 50, size.width * 0.5, 0); + path = ArrowPath.make(path: path); + canvas.drawPath(path, paint..color = Colors.blue); + + textSpan = const TextSpan( + text: 'Single arrow', + style: TextStyle(color: Colors.blue), + ); + textPainter = TextPainter( + text: textSpan, + textAlign: TextAlign.center, + textDirection: TextDirection.ltr, + ); + textPainter.layout(minWidth: size.width); + textPainter.paint(canvas, Offset(0, size.height * 0.06)); +}*/