diff --git a/flutter/lib/curve_painter.dart b/flutter/lib/curve_painter.dart index 7ff545c..379fbee 100644 --- a/flutter/lib/curve_painter.dart +++ b/flutter/lib/curve_painter.dart @@ -6,40 +6,42 @@ import 'dart:math'; class CurvePainter extends CustomPainter { CurvePainter({ Key? key, - required this.gr, + required this.graphData, + required this.bfsPath, }); - Graphs gr; - final double rad = 7; - final double width = 4; - final Color col = Colors.black; + List? bfsPath; + Graphs graphData; + final double dotRad = 6; + final double lineWidth = 2; + final Color lineColor = Colors.black; final double aboveHeight = 5; double circleRad = 100; - final TextStyle textStyle = const TextStyle( - color: Colors.black, - fontSize: 21, + final TextStyle textStyle = TextStyle( + color: Colors.green.shade900, + fontSize: 20, ); void drawLine(Canvas canvas, Offset p1, Offset p2) { Paint p = Paint(); - p.color = col; - p.strokeWidth = width - 2; + p.color = lineColor; + p.strokeWidth = lineWidth; canvas.drawLine(p1, p2, p); } void drawDot(Canvas canvas, Offset p1) { var p = Paint(); p.color = Colors.yellow.shade900; - p.strokeWidth = width; - canvas.drawCircle(p1, rad, p); + p.strokeWidth = lineWidth + 2; + canvas.drawCircle(p1, dotRad, p); } void drawSelfConnect(Canvas canvas, Offset p1) { var p = Paint(); - p.color = col; - p.strokeWidth = width - 2; + p.color = lineColor; + p.strokeWidth = lineWidth; p.style = PaintingStyle.stroke; - canvas.drawCircle(Offset(p1.dx + rad + 20, p1.dy), rad + 20, p); + canvas.drawCircle(Offset(p1.dx + dotRad + 20, p1.dy), dotRad + 20, p); } TextSpan _getTextSpan(String s) => TextSpan(text: s, style: textStyle); @@ -66,27 +68,14 @@ class CurvePainter extends CustomPainter { (size.dy - textPainter.height) - aboveHeight)); } - Graphs getGraph() { - List d = []; - d.add(Dot.fromTwoLists("1", [2, 3], [1, 1])); - d.add(Dot.fromTwoLists("2", [1, 3], [1, 1])); - d.add(Dot.fromTwoLists("3", [1, 2], [1, 1])); - d.add(Dot.fromTwoLists("Name1", [], [])); - d.add(Dot.fromTwoLists("Name2", [], [])); - return Graphs.fromList("1", d, false, false); - } - int getHighInputConnections() { - if (gr.getDots().length <= 3) return -1; + if (graphData.getDots().length != 1 && graphData.getDots().length <= 3) { + return -1; + } int higest = -1; - for (var i in gr.getDots()) { + for (var i in graphData.getDots()) { if (i.getL().length > higest) higest = i.num; } - /*gr.getDots().forEach((element) { - if (element.getL().length > higest) { - higest = element.num; - } - });*/ return higest; } @@ -110,14 +99,14 @@ class CurvePainter extends CustomPainter { } else { print("GetDotPos error"); } - print(off.length); + //print(off.length); } //print(off); return off; } - void drawLowArrow(Canvas canvas, Size size, Offset from, Offset to, + void drawHArrow(Canvas canvas, Size size, Offset from, Offset to, [bool doubleSided = false]) { Path path; @@ -127,7 +116,7 @@ class CurvePainter extends CustomPainter { ..style = PaintingStyle.stroke ..strokeCap = StrokeCap.round ..strokeJoin = StrokeJoin.round - ..strokeWidth = width - 1; + ..strokeWidth = lineWidth; var length = sqrt((to.dx - from.dx) * (to.dx - from.dx) + (to.dy - from.dy) * (to.dy - from.dy)); @@ -135,8 +124,13 @@ class CurvePainter extends CustomPainter { /// 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.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: 16); canvas.drawPath(path, paint); @@ -152,7 +146,7 @@ class CurvePainter extends CustomPainter { ..style = PaintingStyle.stroke ..strokeCap = StrokeCap.round ..strokeJoin = StrokeJoin.round - ..strokeWidth = width - 1; + ..strokeWidth = lineWidth; var length = sqrt((to.dx - from.dx) * (to.dx - from.dx) + (to.dy - from.dy) * (to.dy - from.dy)); @@ -160,8 +154,13 @@ class CurvePainter extends CustomPainter { /// Draw a single arrow. path = Path(); path.moveTo(from.dx, from.dy); - path.relativeCubicTo(0, 0, (from.dx + to.dx) / length + 60, - (from.dy + to.dy) / length + 0, to.dx - from.dx, to.dy - from.dy); + path.relativeCubicTo( + 0, + 0, + (from.dx + to.dx) / (length * 3) + 40, + (from.dy + to.dy) / (length * 2) + 40, + to.dx - from.dx, + to.dy - from.dy); path = ArrowPath.make( path: path, isDoubleSided: doubleSided, @@ -179,10 +178,10 @@ class CurvePainter extends CustomPainter { if (d == i.num) { drawSelfConnect(canvas, off[d]!); } else { - if (gr.getDoubleSidedBool()) { + if (graphData.getDoubleSidedBool()) { if (d > i.num) { - drawLowArrow(canvas, size, beg!, off[d]!, false); - if (gr.getUseLengthBool()) { + drawHArrow(canvas, size, beg!, off[d]!, false); + if (graphData.getUseLengthBool()) { drawDotNames( canvas, Offset((off[d]!.dx + beg.dx) / 2 - 18, @@ -191,7 +190,7 @@ class CurvePainter extends CustomPainter { } } else { drawHighArrow(canvas, size, beg!, off[d]!, false); - if (gr.getUseLengthBool()) { + if (graphData.getUseLengthBool()) { drawDotNames( canvas, Offset((off[d]!.dx + beg.dx) / 2 + 30, @@ -201,7 +200,7 @@ class CurvePainter extends CustomPainter { } } else { drawLine(canvas, beg!, off[d]!); - if (gr.getUseLengthBool()) { + if (graphData.getUseLengthBool()) { drawDotNames( canvas, Offset((off[d]!.dx + beg.dx) / 2, (off[d]!.dy + beg.dy) / 2), @@ -227,15 +226,16 @@ class CurvePainter extends CustomPainter { //gr = getGraph(); //int higest = getHighConnections(); //if (higest > -1) { - var off = getDotPos(gr.getDotAmount(), size); //, higest); + var off = getDotPos(graphData.getDotAmount(), size); //, higest); //off[higest] = Offset(size.width / 2, size.height / 2); for (int i in off.keys) { drawDot(canvas, off[i]!); - drawDotNames(canvas, off[i]!, "${gr.getDots()[i - 1].getName()}:[${i}]"); + drawDotNames( + canvas, off[i]!, "${graphData.getDots()[i - 1].getName()}:[$i]"); } //var g = gr.getNoRepeatDots(); //print(g); - var g = gr.getDots(); + var g = graphData.getDots(); drawConnections(canvas, size, g, off); //pringArr(canvas, size); //drawArrow(canvas, Offset(size.width / 2, size.height / 2), diff --git a/flutter/lib/pages/drawing_page.dart b/flutter/lib/pages/drawing_page.dart index 2505808..449eaa6 100644 --- a/flutter/lib/pages/drawing_page.dart +++ b/flutter/lib/pages/drawing_page.dart @@ -23,7 +23,8 @@ class DrawingPage extends StatefulWidget { class _DrawingPageState extends State { double screenSize = 0; - Graphs data = getGraph(); + Graphs graphData = getGraph(); + List? bfsPath; final _textNameController = TextEditingController(); final _textNumbController = TextEditingController(); @@ -41,7 +42,7 @@ class _DrawingPageState extends State { @override Widget build(BuildContext context) { screenSize = MediaQuery.of(context).size.width; - _textGrNmController.text = data.getName(); + _textGrNmController.text = graphData.getName(); return MaterialApp( home: Scaffold( appBar: AppBar( @@ -58,23 +59,24 @@ class _DrawingPageState extends State { child: Column(children: [ const SizedBox(height: 5), Row(children: [ - addSpaceW(screenSize / 8 + 21), + addSpaceW(screenSize / 8 + 19), createButton("\nAdd dot\n", addDotPushed), createInputBox("Dot name", screenSize / 4 - 25, Icons.label, _textNameController), - addSpaceW(18), - createButton("\nDel dot \n", delDotPushed), - createInputBox("Dot number", screenSize / 4 - 25, - Icons.fiber_manual_record, _textNumbController), - ]), - addSpaceH(3), - Row(children: [ - createInputBox( - "Name", screenSize / 8 - 25, null, _textGrNmController), - //addSpaceW(screenSize / 8 - 4), + addSpaceW(8), createButton("\nAdd path\n", addPathPushed), createInputBox("Input length", screenSize / 4 - 25, Icons.arrow_right_alt_outlined, _textLnthController), + ]), + addSpaceH(3), + Row(children: [ + addSpaceW(5), + createInputBox( + "Name", screenSize / 8 - 25, null, _textGrNmController), + //addSpaceW(screenSize / 8 - 4), + createButton("\nDel dot \n", delDotPushed), + createInputBox("Dot number", screenSize / 4 - 25, + Icons.fiber_manual_record, _textNumbController), addSpaceW(13), createButton("\nDel path\n", delPathPushed), createInputBox("Destination number", screenSize / 4 - 25, @@ -90,21 +92,45 @@ class _DrawingPageState extends State { ), ]), body: CustomPaint( - painter: CurvePainter(gr: data), + painter: CurvePainter(graphData: graphData, bfsPath: bfsPath), child: Align( alignment: Alignment.topRight, child: ButtonBar( mainAxisSize: MainAxisSize.min, children: [ - createButton("Bfs", () {}), + createButton("Bfs", bfsPushed), createButton("Dfs", () {}), - createButton(data.getUseLengthStr(), changeLength), - createButton(data.getDoubleSidedStr(), changeOriented), + createButton("Clear dfs or bfs", () => bfsPath = null), + createButton(graphData.getUseLengthStr(), changeLength), + createButton(graphData.getDoubleSidedStr(), changeOriented), /*Text(_textGrNmController.text, style: TextStyle(fontSize: 15, color: Colors.blueGrey.shade900)),*/ createButton("Save to file", fileSaver), createButton("Load from file", fileOpener), + createButton("Help", () { + String out = + " В поле \"Graph name\" можно сменить имя графу.\n"; + out += + " Для добавления точки необходимо ввести имя в \"Dot name\" и нажать на \"Add dot\".\n"; + out += + " Для удаления точки необходимо ввести номер в \"Dot number\" и нажать на \"Del dot\".\n"; + out += + " Для добавления пути необходимо ввести: номер выходной вершины в \"Dot number\", номер входной вершины в \"Destination number\" "; + out += + "и, если граф взвешенный, то ввести длину пути в \"Input length\". Затем нажать \"Add path\".\n"; + out += + " Для удаления пути необходимо ввести номер выходной вершины в \"Dot number\" и номер входной вершины в \"Destination number\". Затем нажать \"Del path\".\n\n"; + out += + " Кнопки \"Bfs\" и \"Dfs\" нумеруют точки в зависимости от послежовательности, в которой они будут пройдены.\n"; + out += + " Кнопки \"Взвешенный\" и \"Ориентированный\" позволяют сменить эти значения перед построением графа (т.е. для их работы граф должен быть пустым).\n"; + out += + " Кнопки \"Save to file\" и \"Load from file\" позволяют вывести информацию в файл и загрузить информацию из файла соответственно.\n"; + out += + " Кнопка \"Help\" описывает работу с интерфейсом программы."; + showPopUp("Help:", out); + }) ], ), ), @@ -135,7 +161,7 @@ class _DrawingPageState extends State { child: TextField( controller: controller, textAlign: TextAlign.center, - onChanged: (name) => data.setName(name), + onChanged: (name) => graphData.setName(name), decoration: InputDecoration( contentPadding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10), @@ -176,6 +202,21 @@ class _DrawingPageState extends State { return SizedBox(width: w); } + void showPopUp(String alertTitle, String err) => showDialog( + context: context, + builder: (BuildContext context) => AlertDialog( + title: Text(alertTitle), + content: Text(err), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context, 'OK'), + child: const Text('OK'), + ), + ], + ), + ); + +//*********ButtonsFunctions********* void addDotPushed() { //showPopUp("Test", "Test message"); //var inp = int.tryParse(_textNameController.text); @@ -183,7 +224,7 @@ class _DrawingPageState extends State { if (_textNameController.text == "") { showPopUp("Error", "No name in \"Dot name\" box"); } else { - String? res = data.addIsolated(_textNameController.text); + String? res = graphData.addIsolated(_textNameController.text); if (res != null) { showPopUp("Error", res); } @@ -197,8 +238,9 @@ class _DrawingPageState extends State { if (_textNumbController.text == "") { showPopUp("Error", "No number in \"Dot number\" box"); } else if (_textDestController.text == "") { - showPopUp("Error", "No name in \"Dot name\" box"); - } else if (_textLnthController.text == "" && data.getUseLengthBool()) { + showPopUp("Error", "No name in \"Destination number\" box"); + } else if (_textLnthController.text == "" && + graphData.getUseLengthBool()) { showPopUp("Error", "No length in \"Input length\" box"); } else { int? from = int.tryParse(_textNumbController.text); @@ -206,12 +248,12 @@ class _DrawingPageState extends State { int? len = int.tryParse(_textLnthController.text); if (from == null || to == null || - (len == null && data.getUseLengthBool())) { + (len == null && graphData.getUseLengthBool())) { showPopUp("Error", "Can't parse input.\nInts only allowed in \"Dot number\", \"Destination number\" and \"Input length\""); } else { - if (data.getUseLengthBool() && len == null) len = 0; - String? res = data.addPath(from, to, len!); + len ??= 0; + String? res = graphData.addPath(from, to, len); if (res != null) { showPopUp("Error", res); } @@ -221,23 +263,16 @@ class _DrawingPageState extends State { }); } - void addLenPushed() { - //data.delDot() - print( - "${_textNumbController.text} -> ${_textDestController.text} = ${_textLnthController.text}"); - clearTextControllers(); - } - void changeOriented() { setState(() { - String? res = data.flipUseOrientation(); + String? res = graphData.flipUseOrientation(); if (res != null) showPopUp("Error", res); }); } void changeLength() { setState(() { - String? res = data.flipUseLength(); + String? res = graphData.flipUseLength(); if (res != null) showPopUp("Error", res); }); } @@ -255,7 +290,7 @@ class _DrawingPageState extends State { showPopUp("Error", "Can't parse input.\nInts only allowed in \"Dot number\" and \"Destination number\""); } else { - String? res = data.delPath(from, to); + String? res = graphData.delPath(from, to); if (res != null) { showPopUp("Error", res); } @@ -274,7 +309,7 @@ class _DrawingPageState extends State { if (dot == null) { showPopUp("Error", "Can't parse input.\nInts only allowed"); } else { - String? res = data.delDot(dot); + String? res = graphData.delDot(dot); if (res != null) { showPopUp("Error", res); } @@ -284,20 +319,6 @@ class _DrawingPageState extends State { }); } - void showPopUp(String alertTitle, String err) => showDialog( - context: context, - builder: (BuildContext context) => AlertDialog( - title: Text(alertTitle), - content: Text(err), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context, 'OK'), - child: const Text('OK'), - ), - ], - ), - ); - void fileOpener() async { FilePickerResult? result = await FilePicker.platform.pickFiles(allowedExtensions: ["txt"]); @@ -307,7 +328,8 @@ class _DrawingPageState extends State { showPopUp("Error", "Can open only \".txt\" files"); } else { //print(result.files.single.path!); - String? res = data.replaceDataFromFile(result.files.single.path!); + String? res = + graphData.replaceDataFromFile(result.files.single.path!); if (res != null) showPopUp("Error", res); } } else { @@ -329,9 +351,34 @@ class _DrawingPageState extends State { if (!outputFile.endsWith(".txt")) { outputFile += ".txt"; } - data.printToFile(outputFile); + graphData.printToFile(outputFile); } } - void clearScreen() => data.flushData(); + void bfsPushed() { + setState(() { + if (_textNumbController.text == "") { + showPopUp("Error", "No number in \"Dot number\" box"); + } else if (_textDestController.text == "") { + showPopUp("Error", "No name in \"Destination number\" box"); + } else { + int? from = int.tryParse(_textNumbController.text); + int? to = int.tryParse(_textDestController.text); + if (from == null || to == null) { + showPopUp("Error", + "Can't parse input.\nInts only allowed in \"Dot number\" and \"Destination number\""); + } else { + bfsPath = graphData.bfsPath(from, to); + if (bfsPath == null) { + showPopUp("Info", "There is no path"); + } + print(bfsPath); + } + } + clearTextControllers(); + }); + } + + void clearScreen() => graphData.flushData(); + //*********ButtonsFunctions********* } diff --git a/flutter/star.txt b/flutter/star.txt new file mode 100644 index 0000000..6e645bb --- /dev/null +++ b/flutter/star.txt @@ -0,0 +1,10 @@ +star +Ориентированный +НеВзвешенный +1: 2|0 +2: 4|0 +3: 5|0 +4: 6|0 +5: 2|0 +6: 3|0 +END \ No newline at end of file