Compare commits

...

54 Commits
v0.5 ... master

Author SHA1 Message Date
Морозов Андрей 07ba24ee83 Удалить 'tex/task.pl' 2021-12-29 09:37:54 +00:00
Морозов Андрей 34f612970b Удалить 'tex/tex.zip' 2021-12-29 09:37:45 +00:00
Морозов Андрей 92b07e6d48 Удалить 'tex/example-work.toc' 2021-12-29 09:37:32 +00:00
Морозов Андрей 575674fae0 Удалить 'tex/example-work.run.xml' 2021-12-29 09:37:17 +00:00
Морозов Андрей 7d5758d3af Исправления 2021-12-29 09:36:19 +00:00
Морозов Андрей 1b8c38dd89 другая кафедра 2021-12-29 04:00:35 +04:00
Морозов Андрей 9d5520e866 picture label 2021-11-25 10:46:50 +04:00
Морозов Андрей 5175d15194 final1 2021-11-24 21:35:55 +04:00
Морозов Андрей 682c4df7bd Загрузил(а) файлы в 'tex/example' 2021-11-23 12:55:58 +00:00
Морозов Андрей a3bd0cc3fe Загрузил(а) файлы в 'tex' 2021-11-23 12:53:52 +00:00
Морозов Андрей 3ff5b62054 Загрузил(а) файлы в 'tex/pic' 2021-11-23 12:53:32 +00:00
Морозов Андрей a990295f7e source for examples 2021-11-23 12:53:01 +00:00
Морозов Андрей f295e933be gitignore 2021-11-23 03:25:17 +04:00
Морозов Андрей 6e07f084d1 Merge remote-tracking branch 'Graphs_dart/master' 2021-11-23 03:22:16 +04:00
Морозов Андрей f8f94c5400 gitgnore update 2021-11-23 03:19:32 +04:00
Морозов Андрей 4a4ae94bb1 Удалить 'tex/example-work.pdf' 2021-11-22 23:14:26 +00:00
Морозов Андрей fd8446c2e1 Удалить 'tex/example-work.out' 2021-11-22 23:12:35 +00:00
Морозов Андрей 1456df8bbf Удалить 'tex/example-work.synctex.gz' 2021-11-22 23:12:07 +00:00
Морозов Андрей a9c9ee1eca Удалить 'tex/example-work.blg' 2021-11-22 23:11:53 +00:00
Морозов Андрей 4da4910b71 Удалить 'tex/example-work.bbl' 2021-11-22 23:11:43 +00:00
Морозов Андрей d5eaade116 Удалить 'tex/example-work.aux' 2021-11-22 23:11:30 +00:00
Морозов Андрей fc45c57510 latex update 2021-11-23 03:10:14 +04:00
Морозов Андрей a3f0d3cbc5 добавил латэх, для возможности бэкапов файлов 2021-11-22 02:49:20 +04:00
Морозов Андрей 6668b0eeae Kruskal fix+implement 2021-11-15 22:04:08 +04:00
Морозов Андрей b19011ed8b Алгоритм Крускала. Работоспособность под вопросом 2021-11-15 21:22:16 +04:00
Морозов Андрей cec41fc63c Попытки добавить алгоритмы 2021-11-12 03:28:15 +04:00
Морозов Андрей 4953a9ad19 Set min screen size 2021-11-12 02:02:22 +04:00
Морозов Андрей b853664230 Загрузил(а) файлы в 'flutter/lib/src' 2021-11-11 10:35:55 +00:00
Морозов Андрей 44c4c47872 Загрузил(а) файлы в 'flutter/lib/pages' 2021-11-11 10:35:34 +00:00
Морозов Андрей d28f08679e min window size (disabled) 2021-11-11 10:35:06 +00:00
Морозов Андрей 1f180172ec min window size (disabled) 2021-11-11 10:34:50 +00:00
Морозов Андрей 6fc7bbb2e5 Загрузил(а) файлы в 'flutter/lib/src' 2021-11-11 08:11:51 +00:00
Морозов Андрей 8fea8c95fc Загрузил(а) файлы в 'flutter/lib/pages' 2021-11-11 08:11:35 +00:00
Морозов Андрей e1bca55bcf Файл удален для перемещения в другое место 2021-11-11 08:11:05 +00:00
Морозов Андрей d4c57c6aad Работа с алгоритмами 2021-11-11 08:10:21 +00:00
Морозов Андрей 7945800116 Алгоритм Дейкстры 2021-11-11 08:09:49 +00:00
Морозов Андрей 9024c24c4c Merge remote-tracking branch 'Graphs_dart/bfs&dfs' 2021-11-10 19:48:00 +04:00
Морозов Андрей 49ce4aa12f Загрузил(а) файлы в 'flutter/lib/pages' 2021-11-10 12:28:58 +00:00
Морозов Андрей daddfeed6f Загрузил(а) файлы в 'flutter/lib/src' 2021-11-10 12:28:38 +00:00
Морозов Андрей 0a0698bd01 Загрузил(а) файлы в 'flutter/lib' 2021-11-10 12:28:24 +00:00
Морозов Андрей 749afe3e6e option for bfs 2021-11-10 12:26:46 +00:00
Морозов Андрей 442288141d function reordering 2021-11-10 12:05:16 +00:00
Морозов Андрей 4b3542edad src from flutter 2021-11-10 12:04:06 +00:00
Морозов Андрей 77c2f02a96 src from flutter 2021-11-10 12:03:49 +00:00
Морозов Андрей f20cb1fef0 fix null check 2021-11-10 11:37:25 +00:00
Морозов Андрей 41de1ba314 Загрузил(а) файлы в 'flutter/lib/src' 2021-11-10 11:26:41 +00:00
Морозов Андрей 276e7d36ed Загрузил(а) файлы в 'flutter/lib/pages' 2021-11-10 11:26:25 +00:00
Морозов Андрей 3950fabb86 Загрузил(а) файлы в 'flutter/lib' 2021-11-10 11:26:10 +00:00
Морозов Андрей 7bbb414212 Удалить 'flutter/lib/graph.dart' 2021-11-10 11:25:57 +00:00
Морозов Андрей 364495be65 Удалить 'flutter/lib/drawing_page.dart' 2021-11-10 11:25:47 +00:00
Морозов Андрей 3e0be6b7db Удалить 'flutter/lib/curve_painter.dart' 2021-11-10 11:25:17 +00:00
Морозов Андрей a83c683e72 Загрузил(а) файлы в 'flutter/lib' 2021-11-10 11:24:31 +00:00
Морозов Андрей 2422274ea3 работа с цветом 2021-11-09 04:15:36 +04:00
Морозов Андрей 5fc5f8b3d8 Bfs and Dfs implemented. No visuals yet 2021-11-08 22:37:55 +04:00
53 changed files with 6192 additions and 744 deletions

27
.gitignore vendored
View File

@ -44,3 +44,30 @@ app.*.map.json
*/android/app/debug
*/android/app/profile
*/android/app/release
*.aux
*.glo
*.idx
*.log
*.toc
*.ist
*.acn
*.acr
*.alg
*.bbl
*.blg
*.dvi
*.glg
*.gls
*.ilg
*.ind
*.lof
*.lot
*.maf
*.mtc
*.mtc1
*.out
*.synctex.gz
*/_minted-example-work/
*/tex/example-work.pdf
tex/example-work.pdf

View File

@ -13,7 +13,7 @@ void printGraphsName(List<Graphs> inp) {
}
void main(List<String> arguments) {
/*Map<int, int> x = {2: 10};
/* Map<int, int> x = {2: 10};
var p = Dot.fromMap("Т1", x);
p.printD();
x = <int, int>{};
@ -31,10 +31,10 @@ void main(List<String> arguments) {
x1.printToFile("o.txt");
var x2 = Graphs.fromFile("o.txt");
x2.printG();
print(x2.bfsHasPath(1, 4));
print(x2.bfsPath(1, 3));
Graphs x1 = Graphs("Gt", false, true);
print(x2.bfsPath(1, 3));
*/
/*var x1 = Graphs("Gt", false, true);
x1.addIsolated("T1");
x1.addIsolated("T2");
x1.addIsolated("T3");
@ -42,19 +42,27 @@ void main(List<String> arguments) {
x1.addPath(1, 3, 1);
x1.addPath(3, 2, 5);
x1.addPath(2, 4, 10);
x1.printG();
print(x1.bfsPath(1, 4));
print(x1.bfsHasPath(1, 4));
print(x1.dfsIterative(1));
*/
x1.printG();*/
var x1 = Graphs.fromFile("star.txt");
for (var i in x1.getSortedPathList()) {
print("${i.l} ${i.d} ${i.p}");
}
x1.kruskal()!.printG();
//print(x1.getSortedPathList());
//print(x1.bfsPath(1, 4));
//print(x1.dfsIterative(1));
//print(x1.dijkstra(2));
/* var x1 = Graphs.fromFile("star.txt");
print(x1.prim());
int deistvie = 1;
List<Graphs> graphs = <Graphs>[];
String name;
String str = "";
Separators sep = Separators();
while (deistvie != 0) {
stdout.write(
"1 - создать граф, 2 - удалить граф, 3 - добавить в граф вершину,\n4 - удалить вершину, 5 - добавить ребро/дугу, 6 - удалить ребро/дугу,\n7 - вывести граф на экран, 8 - вывести граф в файл, 0 - выход\nDeistvie: ");
"1 - создать граф, 2 - удалить граф, 3 - добавить в граф вершину,\n4 - удалить вершину, 5 - добавить ребро/дугу, 6 - удалить ребро/дугу,\n7 - вывести граф на экран, 8 - вывести граф в файл, 9 - алгоритмы,\n0 - выход\nDeistvie: ");
deistvie = getIntLine();
switch (deistvie) {
case 1:
@ -112,9 +120,9 @@ void main(List<String> arguments) {
List<int> inn = <int>[];
List<int> len = <int>[];
List<String> a = str.split(sep.space);
List<String> a = str.split(Separators.space);
for (var splitted in a) {
var dt = splitted.split(sep.dotToLength);
var dt = splitted.split(Separators.dotToLength);
if (dt.length == 2) {
inn.add(int.parse(dt[0]));
len.add(int.parse(dt[1]));
@ -157,13 +165,13 @@ void main(List<String> arguments) {
stdout.write("Имя вершины: ");
String dotName = getStrLine();
stdout.write(
/*stdout.write(
"Ввод: *куда*|*вес* через пробел. Если граф не взвешенный, то *вес* можно не указывать.\nВершина cмежна с: ");
str = getStrLine();
List<String> a = str.split(sep.space);
List<String> a = str.split(Separators.space);
for (var splitted in a) {
var dt = splitted.split(sep.dotToLength);
var dt = splitted.split(Separators.dotToLength);
if (dt.length == 2) {
inn.add(int.parse(dt[0]));
len.add(int.parse(dt[1]));
@ -171,12 +179,12 @@ void main(List<String> arguments) {
inn.add(int.parse(splitted));
len.add(0);
}
}
}*/
printGraphsName(graphs);
stdout.write("Вставка в графа с номером: ");
int y = getIntLine();
if (y >= 0 && y < graphs.length) {
graphs[y].addDotFromToLists(dotName, inn, len);
graphs[y].addIsolated(dotName);
} else {
print("Не найден граф с таким номером");
}
@ -244,7 +252,7 @@ void main(List<String> arguments) {
num = graphs[y].getDotAmount();
stdout.write(
"Количество вершин: $num. Введите через пробел 2 вершины, между которыми удалить ребро: ");
List<String> x = getStrLine().split(sep.space);
List<String> x = getStrLine().split(Separators.space);
x1 = int.parse(x[0]);
x2 = int.parse(x[1]);
if (x1 >= 0 && x1 < num && x2 >= 0 && x2 < num) {
@ -286,6 +294,67 @@ void main(List<String> arguments) {
}
break;
}
case 9:
{
print("1 - BFS, 2 - DFS, 3 - Дейкстра. Действие:");
int d = getIntLine();
printGraphsName(graphs);
int num = graphs.length;
stdout.write("Работаем в графе с номером: ");
int y = getIntLine();
graphs[y].printG();
if (y >= 0 && y < num) {
num = graphs[y].getDotAmount();
stdout.write("Вершина, из которой выходят: ");
int x1 = getIntLine();
switch (d) {
case 1:
{
stdout.write("Вершина, в которую идут: ");
int x2 = getIntLine();
if (x1 >= 0 && x1 <= num && x2 >= 0 && x2 <= num) {
print("BFS: ${graphs[y].bfsPath(x1, x2)}");
} else {
print("Не найдены введеные вершины в графе\n");
}
break;
}
case 2:
{
if (x1 >= 0 && x1 <= num) {
print("DFS: ${graphs[y].dfsIterative(x1)}");
} else {
print("Не найдена введеныая вершина в графе\n");
}
break;
}
case 3:
{
if (x1 >= 0 && x1 <= num) {
print("Dijkstra: ${graphs[y].dijkstra(x1)}");
if (!graphs[y].getUseLengthBool()) {
print("*Т.к. граф не взвешенный, то вес пути = 1.\n");
}
} else {
print("Не найдена введеная вершина в графе\n");
}
break;
}
default:
{
break;
}
}
} else {
print("Не найден граф с таким номером");
}
break;
}
case 10:
{
print(graphs[0].prim());
break;
}
case 0:
getStrLine();
exit(0);
@ -293,5 +362,5 @@ void main(List<String> arguments) {
print("Действие не распознано");
break;
}
}
}*/
}

View File

@ -1,15 +1,15 @@
import 'dart:io';
class Separators {
final String dotToConnections = ": ";
final String dotToLength = "|";
final String space = " ";
final String hasLength = "Взвешенный\n";
final String hasNoLength = "НеВзвешенный\n";
final String isOriented = "Ориентированный\n";
final String isNotOriented = "НеОриентированный\n";
final String nL = "\n";
final String end = "END";
static const String dotToConnections = ": ";
static const String dotToLength = "|";
static const String space = " ";
static const String hasLength = "Взвешенный";
static const String hasNoLength = "НеВзвешенный";
static const String isOriented = "Ориентированный";
static const String isNotOriented = "НеОриентированный";
static const String nL = "\n";
static const String end = "END";
Separators();
}
@ -25,6 +25,7 @@ class Dot {
String getName() => _name;
bool hasConnection(int n) => _ln.containsKey(n);
Map<int, int> getL() => _ln;
int getLength(int x) {
if (hasConnection(x)) {
return _ln[x]!;
@ -53,9 +54,9 @@ class Dot {
}
//******Constructor******
Dot() {
_name = "Undefined";
num = -1;
Dot([String name = "Undefined", int n = -1]) {
_name = name;
num = n;
_ln = <int, int>{};
}
Dot.fromTwoLists(String name, List<int> num0, List<int> length,
@ -88,6 +89,7 @@ class Dot {
}
class Graphs {
static const int intMax = 0x7fffffffffffffff;
//Data
String _name = "Undefined"; //Имя
int _amount = 0; //Количество вершин
@ -97,11 +99,9 @@ class Graphs {
bool _oriented = false; //Ориентированность
//*********************Add************************
bool addDot(Dot a) {
String? addDot(Dot a) {
if (getNumByName(a.getName()) != null) {
print(
"Dot name ${a.getName()} already in use. Change name or use addPath");
return false;
return ("Dot name \"${a.getName()}\" already in use. Change name or use addPath");
}
_amount++;
a.num = _amount;
@ -109,7 +109,7 @@ class Graphs {
_syncNameTable();
checkDots(false);
if (!_oriented) _fullFix();
return true;
return null;
}
bool addDotFromToLists(String name, List<int> num0, List<int> length,
@ -129,37 +129,43 @@ class Graphs {
return true;
}
bool addIsolated(String name) {
var o = addDot(Dot.fromTwoLists(name, [], []));
String? addIsolated(String name) {
var res = addDot(Dot.fromTwoLists(name, [], []));
_syncNameTable();
return o;
return res;
}
bool addPath(int from, int to, [int len = 0]) {
String? addPath(int from, int to, [int len = 0]) {
if (from <= 0 || from > _amount || to <= 0 && to > _amount) {
return false;
return "Index out of range. Have dots 1..$_amount";
}
_dots[from - 1].addPath(to, len);
if (!_oriented) {
_dots[to - 1].addPath(from, len);
}
return true;
return null;
}
//*********************Add************************
//*********Delete*********
bool delPath(int from, int to) {
String? delPath(int from, int to) {
if (from <= 0 || from > _amount || to <= 0 && to > _amount) {
return false;
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);
}
return true;
return null;
}
bool delDot(int inn) {
String? delDot(int inn) {
if (inn > _amount || inn < 1) {
return "Index out of range. Allowed 1..$_amount";
}
List<int> toDel = <int>[];
for (int i in _dots[inn - 1].getL().keys) {
toDel.add(i);
@ -171,7 +177,13 @@ class Graphs {
_syncNum();
_syncNameTable();
_fixAfterDel(inn);
return true;
return null;
}
void flushData() {
_dots = <Dot>[];
_amount = 0;
_nameTable = <int, String>{};
}
//*********Delete*********
@ -241,12 +253,121 @@ class Graphs {
}
//******Helper*******
//*****Setters*******
void setName(String name) => _name = name;
String? flipUseOrientation() {
if (_amount != 0) {
return "Can change use of orientation only in empty graph";
}
_oriented = !_oriented;
return null;
}
String? flipUseLength() {
if (_amount != 0) {
return "Can change use of length only in empty graph";
}
_useLength = !_useLength;
return null;
}
String? replaceDataFromFile(String path) {
File file = File(path);
List<String> lines = file.readAsLinesSync();
if (lines.length < 3) {
return "Not enough lines in file";
}
String name = lines.removeAt(0);
bool oriented;
switch (lines.removeAt(0)) {
case Separators.isOriented:
oriented = true;
break;
case Separators.isNotOriented:
oriented = false;
break;
default:
return "Error on parsing \"IsOriented\"";
}
bool useLength;
switch (lines.removeAt(0).trim()) {
case Separators.hasLength:
useLength = true;
break;
case Separators.hasNoLength:
useLength = false;
break;
default:
return "Error on parsing \"HasLength\"";
}
List<Dot> dots = <Dot>[];
for (var l in lines) {
l = l.trimRight();
if (l != Separators.end) {
var spl = l.split(Separators.space);
List<int> dot = <int>[];
List<int> len = <int>[];
String name = spl.removeAt(0);
name = name.substring(0, name.length - 1);
for (var splitted in spl) {
if (splitted != "") {
var dt = splitted.split(Separators.dotToLength);
if (dt.length == 2) {
int? parsed = int.tryParse(dt[0]);
if (parsed == null) {
return "Error while parsing file\nin parsing int in \"${dt[0]}\"";
}
dot.add(parsed);
if (useLength) {
parsed = int.tryParse(dt[1]);
if (parsed == null) {
return "Error while parsing file\nin parsing int in \"${dt[1]}\"";
}
len.add(parsed);
} else {
len.add(0);
}
} else if (dt.length == 1) {
int? parsed = int.tryParse(splitted);
if (parsed == null) {
return "Error while parsing file\nin parsing int in \"$splitted\"";
}
dot.add(parsed);
len.add(0);
}
}
}
dots.add(Dot.fromTwoLists(name, dot, len));
}
}
_name = name;
_oriented = oriented;
_useLength = useLength;
_dots = dots;
_syncNum();
_syncNameTable();
if (!_oriented) _fullFix();
return null;
}
//*****Setters*******
//*****Getters*******
bool getDoubleSided() => _oriented;
bool getUseLength() => _useLength;
bool getDoubleSidedBool() => _oriented;
String getDoubleSidedStr() {
if (_oriented) return Separators.isOriented;
return Separators.isNotOriented;
}
bool getUseLengthBool() => _useLength;
String getUseLengthStr() {
if (_useLength) return Separators.hasLength;
return Separators.hasNoLength;
}
List<Dot> getDots() => _dots;
String getName() => _name;
String? getNameByNum(int n) => _nameTable[n];
Map<int, String> getNameTable() => _nameTable;
int getDotAmount() => _dots.length;
int? getNumByName(String n) {
for (var i in _nameTable.keys) {
@ -282,6 +403,69 @@ class Graphs {
}
return out;
}
List<int>? getLongestPath([int start = 1]) {
start--;
if (start < 0 || start >= _amount) {
return null;
}
int max = -1;
int inD = -1;
int out = -1;
List<int>? res = <int>[];
for (int i = start; i < _amount; i++) {
var lens = _dots[i].getL();
for (var d in lens.keys) {
if (lens[d]! > max) {
max = lens[d]!;
inD = i + 1;
out = d;
}
}
}
if (inD == -1) {
return null;
}
res.add(inD);
res.add(out);
res.add(max);
return res;
}
List<LenDotPath> getSortedPathList() {
int max = -1;
int inD = -1;
int out = -1;
List<LenDotPath> result = <LenDotPath>[];
for (int i = 0; i < _amount; i++) {
var lens = _dots[i].getL();
for (var d in lens.keys) {
max = lens[d]!;
inD = i + 1;
out = d;
result.add(LenDotPath(max, inD, out));
}
}
result.sort((a, b) => a.l.compareTo(b.l));
return result;
}
/*List<Dot> getNoRepeatDots() {
List<Dot> ret = <Dot>[];
for (int i = 0; i < _amount; i++) {
ret.add(Dot(_dots[i].getName(), _dots[i].num));
}
for (int i = 0; i < _amount; i++) {
for (int j in _dots[i].getL().keys) {
if (!ret[j - 1].hasConnection(i + 1) && !ret[i].hasConnection(j) ||
i == j) {
var len = _dots[i].getLength(j);
ret[i].addPath(j, len);
}
}
}
return ret;
}*/
//*****Getters*******
//******Print******
@ -303,31 +487,34 @@ class Graphs {
}
void printToFile(String name) {
Separators sep = Separators();
var file = File(name);
file.writeAsStringSync("$_name\n");
if (_oriented) {
file.writeAsStringSync(sep.isOriented, mode: FileMode.append);
file.writeAsStringSync("${Separators.isOriented}\n",
mode: FileMode.append);
} else {
file.writeAsStringSync(sep.isNotOriented, mode: FileMode.append);
file.writeAsStringSync("${Separators.isNotOriented}\n",
mode: FileMode.append);
}
if (_useLength) {
file.writeAsStringSync(sep.hasLength, mode: FileMode.append);
file.writeAsStringSync("${Separators.hasLength}\n",
mode: FileMode.append);
} else {
file.writeAsStringSync(sep.hasNoLength, mode: FileMode.append);
file.writeAsStringSync("${Separators.hasNoLength}\n",
mode: FileMode.append);
}
for (int i = 0; i < _amount; i++) {
file.writeAsStringSync((i + 1).toString() + sep.dotToConnections,
file.writeAsStringSync(_dots[i].getName() + Separators.dotToConnections,
mode: FileMode.append);
var d = _dots[i].getL();
for (var j in d.keys) {
file.writeAsStringSync(
j.toString() + sep.dotToLength + d[j].toString() + " ",
j.toString() + Separators.dotToLength + d[j].toString() + " ",
mode: FileMode.append);
}
file.writeAsStringSync(sep.nL, mode: FileMode.append);
file.writeAsStringSync(Separators.nL, mode: FileMode.append);
}
file.writeAsStringSync(sep.end, mode: FileMode.append);
file.writeAsStringSync(Separators.end, mode: FileMode.append);
}
//******Print******
@ -353,22 +540,22 @@ class Graphs {
if (!_oriented) _fullFix();
}
Graphs.fromFile(String path) {
Separators sep = Separators();
File file = File(path);
replaceDataFromFile(path);
/*File file = File(path);
List<String> lines = file.readAsLinesSync();
_name = lines.removeAt(0);
_oriented = lines.removeAt(0) == sep.isOriented.trim();
_useLength = lines.removeAt(0) == sep.hasLength.trim();
_oriented = lines.removeAt(0) == Separators.isOriented.trim();
_useLength = lines.removeAt(0) == Separators.hasLength.trim();
_dots = <Dot>[];
for (var l in lines) {
if (l != sep.end) {
var spl = l.split(sep.space);
if (l != Separators.end) {
var spl = l.split(Separators.space);
List<int> dot = <int>[];
List<int> len = <int>[];
String name = spl.removeAt(0);
name = name.substring(0, name.length - 1);
for (var splitted in spl) {
var dt = splitted.split(sep.dotToLength);
var dt = splitted.split(Separators.dotToLength);
if (dt.length == 2) {
dot.add(int.parse(dt[0]));
if (_useLength) {
@ -386,7 +573,7 @@ class Graphs {
}
_syncNum();
_syncNameTable();
if (!_oriented) _fullFix();
if (!_oriented) _fullFix();*/
}
//*******Constructor********
@ -394,14 +581,14 @@ class Graphs {
Graphs.clone(Graphs a) {
_name = a.getName();
_dots = a.getDots();
_oriented = a.getDoubleSided();
_useLength = a.getUseLength();
_oriented = a.getDoubleSidedBool();
_useLength = a.getUseLengthBool();
_amount = _dots.length;
_syncNameTable();
}
//************Алгоритмы************
bool bfsHasPath(int startDot, int goalDot) {
/* bool bfsHasPath(int startDot, int goalDot) {
// обход в ширину
startDot--;
goalDot--;
@ -428,9 +615,10 @@ class Graphs {
}
}
return false; // Целевой узел недостижим
}
}*/
List<int>? bfsPath(int startDot, int goalDot) {
if (startDot == goalDot) return [startDot];
//if (!bfsHasPath(startDot, goalDot)) return null;
startDot--;
goalDot--;
@ -470,7 +658,6 @@ class Graphs {
//Восстановим кратчайший путь
//Для восстановления пути пройдём его в обратном порядке, и развернём.
List<int> path = <int>[];
int cur = goalDot; //текущая вершина пути
@ -493,46 +680,128 @@ class Graphs {
List<bool>? dfsIterative(int v) {
v--;
//List<int>? pos = <int>[];
List<bool> label = <bool>[];
for (int i = 0; i < _amount; i++) {
label.add(false);
}
List<int> stack = <int>[];
stack.add(v);
//pos.add(v);
while (stack.isNotEmpty) {
v = stack.removeLast();
if (!label[v]) {
label[v] = true;
for (int i in _dots[v].getL().keys) {
stack.add(i - 1);
//pos.add(i);
}
}
}
//print(pos);
return label;
}
void dijkstra(int source) {
/*
create vertex set Q;
List<int?> dijkstra(int from) {
List<int?> d = List<int?>.filled(_amount, intMax);
List<int> p = List<int>.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<bool> u = List<bool>.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;
}
List<int?>? prim() {
// работоспособность?
List<bool> used = List<bool>.filled(_amount, false);
List<int> minE = List<int>.filled(_amount, intMax);
List<int> selE = List<int>.filled(_amount, -1);
minE[0] = 0;
for (int i = 0; i < _amount; ++i) {
int v = -1;
for (int j = 0; j < _amount; ++j) {
if (!used[j] && (v == -1 || minE[j] < minE[v])) {
v = j;
}
}
if (minE[v] == intMax) {
return null;
}
used[v] = true;
if (selE[v] != -1) {
print("${v + 1} ${selE[v]}");
}
for (int to in _dots[v].getL().keys) {
if (_dots[v].getL()[to]! < minE[to - 1]) {
minE[to] = _dots[v].getL()[to]!;
selE[to] = v;
}
}
}
}
Graphs? kruskal() {
//List<pair<int,pair<int,int>>> g (m); // вес - вершина 1 - вершина 2
List<LenDotPath> g = getSortedPathList();
//int cost = 0;
List<Dot> res = <Dot>[];
for (int i = 0; i < _amount; i++) {
res.add(Dot(_dots[i].getName(), _dots[i].num));
}
List<int> treeId = List<int>.filled(_amount, 0);
for (int i = 0; i < _amount; ++i) {
treeId[i] = i;
}
for (int i = 0; i < g.length; ++i) {
int a = g[i].d - 1, b = g[i].p - 1;
int l = g[i].l;
if (treeId[a] != treeId[b]) {
//cost += l;
res[a].addPath(b + 1, l);
int oldId = treeId[b], newId = treeId[a];
for (int j = 0; j < _amount; ++j) {
if (treeId[j] == oldId) {
treeId[j] = newId;
}
}
}
}
//_dots = res;
return Graphs.fromList(_name, res, _useLength, _oriented);
}
//************Алгоритмы************
}
class LenDotPath {
late int l;
late int d;
late int p;
LenDotPath(int len, dot, path) {
l = len;
d = dot;
p = path;
}
}

10
cmd/star.txt Normal file
View File

@ -0,0 +1,10 @@
star
Ориентированный
НеВзвешенный
1: 2|0
2: 4|0
3: 5|0
4: 6|0
5: 2|0
6: 3|0
END

11
flutter/afterKruskal.txt Normal file
View File

@ -0,0 +1,11 @@
1
НеОриентированный
Взвешенный
A: 4|5 2|7
B: 5|7 1|7
C: 5|5
D: 6|6 1|5
E: 7|9 2|7 3|5
F: 4|6
G: 5|9
END

View File

@ -1,395 +0,0 @@
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,
});
List<int>? 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 = TextStyle(
color: Colors.green.shade900,
fontSize: 20,
);
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) {
var p = Paint();
p.color = Colors.yellow.shade900;
p.strokeWidth = lineWidth + 2;
canvas.drawCircle(p1, dotRad, 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 drawAboveText(Canvas canvas, Offset size, String s) {
var textPainter = _getTextPainter(s);
textPainter.layout();
textPainter.paint(
canvas,
Offset((size.dx - textPainter.width),
(size.dy - textPainter.height) - aboveHeight));
}
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<int, Offset> getDotPos(int dotsAm, Size size, [int? exclude]) {
Map<int, Offset> off = <int, Offset>{};
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 * 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);
}
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 * 3) + 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<Dot> dots, Map<int, Offset> 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());
}
}
}
}
}
}
@override
void paint(Canvas canvas, Size size) {
if (size.width > size.height) {
circleRad = size.height / 3;
} else {
circleRad = size.width / 3;
}
//var paint = Paint();
//drawLine(canvas, Offset(0, size.height / 2),
//Offset(size.width, size.height / 2));
//gr = getGraph();
//int higest = getHighConnections();
//if (higest > -1) {
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]!, "${graphData.getDots()[i - 1].getName()}:[$i]");
}
//var g = gr.getNoRepeatDots();
//print(g);
var g = graphData.getDots();
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));
//}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
/*void _drawTextAt(String txt, Offset position, Canvas canvas) {
final textPainter = getTextPainter(txt);
textPainter.layout(minWidth: 0, maxWidth: 0);
Offset drawPosition =
Offset(position.dx, position.dy - (textPainter.height / 2));
textPainter.paint(canvas, drawPosition);
}*/
}
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));
/*
/// Draw a double sided arrow.
path = Path();
path.moveTo(size.width * 0.25, size.height * 0.2);
path.relativeCubicTo(0, 0, size.width * 0.25, 50, size.width * 0.5, 0);
path = ArrowPath.make(path: path, isDoubleSided: true);
canvas.drawPath(path, paint..color = Colors.cyan);
textSpan = const TextSpan(
text: 'Double sided arrow',
style: TextStyle(color: Colors.cyan),
);
textPainter = TextPainter(
text: textSpan,
textAlign: TextAlign.center,
textDirection: TextDirection.ltr,
);
textPainter.layout(minWidth: size.width);
textPainter.paint(canvas, Offset(0, size.height * 0.16));
/// Use complex path.
path = Path();
path.moveTo(size.width * 0.25, size.height * 0.3);
path.relativeCubicTo(0, 0, size.width * 0.25, 0, size.width * 0.5, 50);
path.relativeCubicTo(0, 0, -size.width * 0.25, 50, -size.width * 0.5, 50);
//path.relativeCubicTo(0, 0, size.width * 0.125, 10, size.width * 0.25, -10);
path = ArrowPath.make(path: path, isDoubleSided: true);
canvas.drawPath(path, paint..color = Colors.blue);
textSpan = const TextSpan(
text: 'Complex path',
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.28));
/// Draw several arrows on the same path.
path = Path();
path.moveTo(size.width * 0.25, size.height * 0.53);
path.relativeCubicTo(0, 0, size.width * 0.25, 50, size.width * 0.5, 50);
path = ArrowPath.make(path: path);
path.relativeCubicTo(0, 0, -size.width * 0.25, 0, -size.width * 0.5, 50);
path = ArrowPath.make(path: path);
Path subPath = Path();
subPath.moveTo(size.width * 0.375, size.height * 0.53 + 100);
subPath.relativeCubicTo(0, 0, size.width * 0.125, 10, size.width * 0.25, -10);
subPath = ArrowPath.make(path: subPath, isDoubleSided: true);
path.addPath(subPath, Offset.zero);
canvas.drawPath(path, paint..color = Colors.cyan);
textSpan = const TextSpan(
text: 'Several arrows on the same path',
style: TextStyle(color: Colors.cyan),
);
textPainter = TextPainter(
text: textSpan,
textAlign: TextAlign.center,
textDirection: TextDirection.ltr,
);
textPainter.layout(minWidth: size.width);
textPainter.paint(canvas, Offset(0, size.height * 0.49));
/// Adjusted
path = Path();
path.moveTo(size.width * 0.1, size.height * 0.8);
path.relativeCubicTo(0, 0, size.width * 0.3, 50, size.width * 0.25, 75);
path = ArrowPath.make(path: path, isAdjusted: true);
canvas.drawPath(path, paint..color = Colors.blue);
textSpan = const TextSpan(
text: 'Adjusted',
style: TextStyle(color: Colors.blue),
);
textPainter = TextPainter(
text: textSpan,
textAlign: TextAlign.left,
textDirection: TextDirection.ltr,
);
textPainter.layout();
textPainter.paint(canvas, Offset(size.width * 0.2, size.height * 0.77));
/// Non adjusted.
path = Path();
path.moveTo(size.width * 0.6, size.height * 0.8);
path.relativeCubicTo(0, 0, size.width * 0.3, 50, size.width * 0.25, 75);
path = ArrowPath.make(path: path, isAdjusted: false);
canvas.drawPath(path, paint..color = Colors.blue);
textSpan = const TextSpan(
text: 'Non adjusted',
style: TextStyle(color: Colors.blue),
);
textPainter = TextPainter(
text: textSpan,
textAlign: TextAlign.left,
textDirection: TextDirection.ltr,
);
textPainter.layout();
textPainter.paint(canvas, Offset(size.width * 0.65, size.height * 0.77));*/
}

View File

@ -1,15 +1,21 @@
import 'package:graphs/pages/drawing_page.dart';
import 'package:desktop_window/desktop_window.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
Future setupWindow() async {
await DesktopWindow.setMinWindowSize(const Size(850, 700));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
setupWindow();
return const MaterialApp(
title: "Graphs",
home: DrawingPage(),

View File

@ -1,17 +1,17 @@
import 'package:graphs/src/graph.dart';
import 'package:graphs/curve_painter.dart';
import 'package:graphs/src/curve_painter.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
Graphs getGraph() {
List<Dot> d = <Dot>[];
d.add(Dot.fromTwoLists("1", [2, 3], [5, 1]));
d.add(Dot.fromTwoLists("2", [1, 3], [1, 1]));
d.add(Dot.fromTwoLists("3", [1, 2], [1, 2]));
d.add(Dot.fromTwoLists("1", [2], [3]));
d.add(Dot.fromTwoLists("2", [3], [1]));
d.add(Dot.fromTwoLists("3", [1], [2]));
//d.add(Dot.fromTwoLists("Name1", [], []));
//d.add(Dot.fromTwoLists("Name2", [], []));
return Graphs.fromList("UMYA", d, true, true);
return Graphs.fromList("Имя графа", d, true, true);
}
class DrawingPage extends StatefulWidget {
@ -24,138 +24,90 @@ class DrawingPage extends StatefulWidget {
class _DrawingPageState extends State<DrawingPage> {
double screenSize = 0;
Graphs graphData = getGraph();
List<int>? bfsPath;
List<int?>? intListPath;
List<bool>? dfsAccessTable;
//List<int?>? dijkstraTable;
String op = Operations.none;
int? startDot;
int? endDot;
String? dropdownValue1;
String? dropdownValue2;
String currOp = "";
final _textNameController = TextEditingController();
final _textNumbController = TextEditingController();
final _textDestController = TextEditingController();
final _textLnthController = TextEditingController();
final _textGrNmController = TextEditingController();
void clearTextControllers() {
_textDestController.clear();
_textNumbController.clear();
_textLnthController.clear();
_textNameController.clear();
void clearInputData() {
setState(() {
_textLnthController.clear();
_textNameController.clear();
dropdownValue1 = null;
dropdownValue2 = null;
});
}
@override
Widget build(BuildContext context) {
screenSize = MediaQuery.of(context).size.width;
_textGrNmController.text = graphData.getName();
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Align(
alignment: Alignment.topLeft,
child: Text("Graph name:\n",
style: TextStyle(
fontSize: 18,
color: Colors.white,
))),
toolbarHeight: 110,
flexibleSpace: Container(
color: Colors.blue,
child: Column(children: <Widget>[
const SizedBox(height: 5),
Row(children: [
addSpaceW(screenSize / 8 + 19),
createButton("\nAdd dot\n", addDotPushed),
createInputBox("Dot name", screenSize / 4 - 25, Icons.label,
_textNameController),
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,
Icons.fiber_manual_record, _textDestController),
]),
]),
),
actions: [
IconButton(
onPressed: clearScreen,
icon: const Icon(Icons.delete_sweep),
iconSize: 60,
),
]),
body: CustomPaint(
painter: CurvePainter(graphData: graphData, bfsPath: bfsPath),
child: Align(
alignment: Alignment.topRight,
child: ButtonBar(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
createButton("Bfs", bfsPushed),
createButton("Dfs", () {}),
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);
})
],
),
),
),
));
void clearDropDownVals() {
setState(() {
startDot = null;
intListPath = null;
dfsAccessTable = null;
endDot = null;
currOp = "";
op = Operations.none;
});
}
// ignore: avoid_types_as_parameter_names, non_constant_identifier_names, use_function_type_syntax_for_parameters
ElevatedButton createButton(String txt, void onPressing()) {
// *************buttons*************
ElevatedButton createButton(String txt, void Function() onPressing) {
return ElevatedButton(
onPressed: onPressing,
style: ButtonStyle(
backgroundColor: MaterialStateProperty.resolveWith<Color>(
(states) => Colors.green.shade700)),
child: Text(txt,
style: const TextStyle(
fontSize: 15,
color: Colors.white,
color: Colors.white70,
height: 1,
)),
);
}
Container createInputBox(String text, double wid, IconData? icon,
void showPopUp(String alertTitle, String err) => showDialog<String>(
context: context,
builder: (BuildContext context) => AlertDialog(
title: Text(alertTitle, style: const TextStyle(fontSize: 26)),
content: Text(
err,
style: const TextStyle(fontSize: 18),
),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, 'OK'),
child: const Text('OK'),
),
],
),
);
// *************buttons*************
// ***addSpace***
SizedBox addSpaceH(double h) {
return SizedBox(height: h);
}
SizedBox addSpaceW(double w) {
return SizedBox(width: w);
}
// ***addSpace***
// *************inputs*************
Container createInputBox(String text, double width, IconData? icon,
TextEditingController? controller) {
if (icon == null) {
return Container(
width: wid,
width: width,
height: 40,
margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 5),
child: TextField(
@ -175,7 +127,7 @@ class _DrawingPageState extends State<DrawingPage> {
));
}
return Container(
width: wid,
width: width,
height: 40,
margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 5),
child: TextField(
@ -194,73 +146,116 @@ class _DrawingPageState extends State<DrawingPage> {
));
}
SizedBox addSpaceH(double h) {
return SizedBox(height: h);
SizedBox dropList1(double width) {
var button = DropdownButton(
hint: const Text(
'Select dot',
style: TextStyle(color: Colors.white, fontSize: 13),
),
//icon: const Icon(Icons.fiber_manual_record, color: Colors.white),
alignment: AlignmentDirectional.bottomEnd,
value: dropdownValue1,
isDense: true,
borderRadius: const BorderRadius.all(Radius.circular(20)),
dropdownColor: Colors.green.shade800,
style: const TextStyle(
color: Colors.white,
fontSize: 18,
),
onChanged: (String? newValue) {
setState(() {
dropdownValue1 = newValue;
});
},
items: graphData.getDots().map((location) {
return DropdownMenuItem(
child: Text(location.getName()),
value: location.num.toString(),
);
}).toList(),
);
return SizedBox(
child: button,
width: width,
);
}
SizedBox addSpaceW(double w) {
return SizedBox(width: w);
}
SizedBox dropList2(double width) {
var button = DropdownButton(
hint: const Text(
'Select Dot',
style: TextStyle(color: Colors.white, fontSize: 13),
),
alignment: AlignmentDirectional.centerEnd,
value: dropdownValue2,
isDense: true,
borderRadius: const BorderRadius.all(Radius.circular(20)),
dropdownColor: Colors.green.shade800,
style: const TextStyle(
color: Colors.white,
fontSize: 18,
),
onChanged: (String? newValue) {
setState(() {
dropdownValue2 = newValue;
});
},
items: graphData.getDots().map((location) {
return DropdownMenuItem(
child: Text(location.getName()),
value: location.num.toString(),
);
}).toList(),
);
void showPopUp(String alertTitle, String err) => showDialog<String>(
context: context,
builder: (BuildContext context) => AlertDialog(
title: Text(alertTitle),
content: Text(err),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, 'OK'),
child: const Text('OK'),
),
],
),
);
return SizedBox(
child: button,
width: width,
);
}
// *************inputs*************
//*********ButtonsFunctions*********
void addDotPushed() {
//showPopUp("Test", "Test message");
//var inp = int.tryParse(_textNameController.text);
setState(() {
if (_textNameController.text == "") {
showPopUp("Error", "No name in \"Dot name\" box");
} else {
clearDropDownVals();
if (_textNameController.text == "") {
showPopUp("Error", "No name in \"Dot name\" box");
} else {
setState(() {
String? res = graphData.addIsolated(_textNameController.text);
if (res != null) {
showPopUp("Error", res);
}
}
clearTextControllers();
});
});
}
clearInputData();
}
void addPathPushed() {
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 if (_textLnthController.text == "" &&
graphData.getUseLengthBool()) {
showPopUp("Error", "No length in \"Input length\" box");
clearDropDownVals();
if (dropdownValue1 == null) {
showPopUp("Error", "No dot in first box selected");
} else if (dropdownValue2 == null) {
showPopUp("Error", "No dot in second box selected");
} else if (_textLnthController.text == "" && graphData.getUseLengthBool()) {
showPopUp("Error", "No length in \"Input length\" box");
} else {
int? from = int.parse(dropdownValue1!);
int? to = int.parse(dropdownValue2!);
int? len = int.tryParse(_textLnthController.text);
if (len == null && graphData.getUseLengthBool()) {
showPopUp("Error",
"Can't parse input.\nInts only allowed in \"Input length\"");
} else {
int? from = int.tryParse(_textNumbController.text);
int? to = int.tryParse(_textDestController.text);
int? len = int.tryParse(_textLnthController.text);
if (from == null ||
to == null ||
(len == null && graphData.getUseLengthBool())) {
showPopUp("Error",
"Can't parse input.\nInts only allowed in \"Dot number\", \"Destination number\" and \"Input length\"");
} else {
len ??= 0;
String? res = graphData.addPath(from, to, len);
if (res != null) {
showPopUp("Error", res);
}
}
len ??= 0;
setState(() {
String? res = graphData.addPath(from, to, len!);
if (res != null) showPopUp("Error", res);
});
}
clearTextControllers();
});
}
clearInputData();
}
void changeOriented() {
@ -278,65 +273,64 @@ class _DrawingPageState extends State<DrawingPage> {
}
void delPathPushed() {
setState(() {
if (_textNumbController.text == "") {
showPopUp("Error", "No number in \"Dot number\" box");
} else if (_textDestController.text == "") {
showPopUp("Error", "No name in \"Dot name\" box");
clearDropDownVals();
if (dropdownValue1 == null) {
showPopUp("Error", "No dot in first box selected");
} else if (dropdownValue2 == null) {
showPopUp("Error", "No dot in second box selected");
} else {
int? from = int.tryParse(dropdownValue1!);
int? to = int.tryParse(dropdownValue2!);
if (from == null || to == null) {
showPopUp("Error",
"Can't parse input.\nInts only allowed in \"Dot number\" and \"Destination number\"");
} 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 {
setState(() {
String? res = graphData.delPath(from, to);
if (res != null) {
showPopUp("Error", res);
}
}
});
}
clearTextControllers();
});
}
clearInputData();
}
void delDotPushed() {
setState(() {
if (_textNumbController.text == "") {
showPopUp("Error", "No number in \"Dot number\" box");
} else {
int? dot = int.tryParse(_textNumbController.text);
if (dot == null) {
showPopUp("Error", "Can't parse input.\nInts only allowed");
} else {
String? res = graphData.delDot(dot);
if (res != null) {
showPopUp("Error", res);
}
if (dropdownValue1 != null) {
setState(() {
String? res = graphData.delDot(int.parse(dropdownValue1!));
if (res != null) {
showPopUp("Error", res);
}
}
clearTextControllers();
});
});
} else {
showPopUp("Error", "Nothing in input");
}
clearDropDownVals();
clearInputData();
}
void fileOpener() async {
FilePickerResult? result =
await FilePicker.platform.pickFiles(allowedExtensions: ["txt"]);
setState(() {
if (result != null) {
if (!result.files.single.path!.endsWith(".txt")) {
showPopUp("Error", "Can open only \".txt\" files");
} else {
//print(result.files.single.path!);
if (result != null) {
if (!result.files.single.path!.endsWith(".txt")) {
showPopUp("Error", "Can open only \".txt\" files");
} else {
setState(() {
String? res =
graphData.replaceDataFromFile(result.files.single.path!);
if (res != null) showPopUp("Error", res);
}
} else {
showPopUp("Error", "No file selected");
// User canceled the picker
});
}
});
} else {
showPopUp("Error", "No file selected");
}
clearDropDownVals();
clearInputData();
}
void fileSaver() async {
@ -346,39 +340,208 @@ class _DrawingPageState extends State<DrawingPage> {
allowedExtensions: ["txt"]);
if (outputFile == null) {
showPopUp("Error", "Save cancelled");
// User canceled the picker
} else {
if (!outputFile.endsWith(".txt")) {
outputFile += ".txt";
}
graphData.printToFile(outputFile);
}
clearDropDownVals();
clearInputData();
}
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);
}
clearDropDownVals();
if (dropdownValue1 == null) {
showPopUp("Error", "No dot in first box selected");
} else if (dropdownValue2 == null) {
showPopUp("Error", "No dot in second box selected");
} else {
setState(() {
startDot = int.parse(dropdownValue1!);
endDot = int.parse(dropdownValue2!);
currOp = "OP: BFS from $startDot to $endDot";
op = Operations.bfs;
intListPath = graphData.bfsPath(startDot!, endDot!);
});
if (intListPath == null) {
showPopUp("Info", "There is no path");
}
clearTextControllers();
});
//print(intListPath);
}
clearInputData();
}
void clearScreen() => graphData.flushData();
void dfsPushed() {
clearDropDownVals();
if (dropdownValue1 == null) {
showPopUp("Error", "No dot in first box selected");
} else {
setState(() {
startDot = int.parse(dropdownValue1!);
op = Operations.dfs;
currOp = "OP: DFS from $startDot";
dfsAccessTable = graphData.dfsIterative(startDot!);
});
if (dfsAccessTable == null) {
showPopUp("Err", "report this error.");
}
//print(dfsAccessTable);
}
clearInputData();
}
void dijkstraPushed() {
clearDropDownVals();
if (dropdownValue1 == null) {
showPopUp("Error", "No number in \"Dot number\" box");
} else {
setState(() {
startDot = int.parse(dropdownValue1!);
currOp = "OP: Dijkstra from $startDot";
op = Operations.dijkstra;
intListPath = graphData.dijkstra(startDot!);
});
if (intListPath == null) {
showPopUp("Err", "report this error.");
}
//print(intListPath);
}
clearInputData();
}
//*********ButtonsFunctions*********
// build
@override
Widget build(BuildContext context) {
screenSize = MediaQuery.of(context).size.width;
_textGrNmController.text = graphData.getName();
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Align(
alignment: Alignment.topLeft,
child: Text("Graph name:\n",
style: TextStyle(
fontSize: 18,
color: Colors.white,
))),
toolbarHeight: 110,
flexibleSpace: Container(
color: Colors.green.shade900,
child: Column(children: <Widget>[
const SizedBox(height: 5),
Row(children: [
addSpaceW(screenSize / 8 + 19),
createButton("\nAdd dot\n", addDotPushed),
createInputBox("Dot name", screenSize / 4 - 25, Icons.label,
_textNameController),
addSpaceW(8),
createButton("\nAdd path\n", addPathPushed),
createInputBox("Input length", screenSize / 4 - 25,
Icons.arrow_right_alt_outlined, _textLnthController),
]),
addSpaceH(3),
Row(children: [
addSpaceW(6),
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(54),
dropList1(screenSize / 4 - 80),
addSpaceW(53),
createButton("\nDel path\n", delPathPushed),
addSpaceW(54),
dropList2(screenSize / 4 - 80),
//createInputBox("Destination number", screenSize / 4 - 25, Icons.fiber_manual_record, _textDestController),
]),
]),
),
actions: [
IconButton(
onPressed: () {
setState(() {
clearDropDownVals();
graphData.flushData();
clearInputData();
});
},
icon: const Icon(Icons.delete_sweep),
iconSize: 60,
),
]),
body: CustomPaint(
painter: CurvePainter(
graphData: graphData,
intListPath: intListPath,
dfsAccessTable: dfsAccessTable,
start: startDot,
end: endDot,
op: op),
child: Align(
alignment: Alignment.topRight,
child: ButtonBar(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
currOp,
style: const TextStyle(fontSize: 14, color: Colors.black),
),
createButton("Bfs", bfsPushed),
createButton("Dfs", dfsPushed),
createButton("Dijkstra", dijkstraPushed),
createButton("Kruskal", () {
clearDropDownVals();
setState(() {
currOp = "OP: Kruscal algo";
graphData.kruskal();
});
clearInputData();
}),
createButton("Clear OP", () {
clearDropDownVals();
clearInputData();
}),
createButton(graphData.getUseLengthStr(), changeLength),
createButton(graphData.getDoubleSidedStr(), changeOriented),
createButton("Save to file", fileSaver),
createButton("Load from file", fileOpener),
createButton("Help", () {
String out =
" В поле \"Graph name\" можно сменить имя графу.\n";
out +=
" Для добавления точки необходимо ввести имя в \"Dot name\" и нажать на \"Add dot\".\n";
out +=
" Для удаления точки необходимо выбрать номер в левом выпадающем списке и нажать на \"Del dot\".\n";
out +=
" Для добавления пути необходимо выбрать: первую вершину в левом выпадающем списке, вторую вершину в правом выпадающем списке, ";
out +=
"и, если граф взвешенный, то ввести длину пути в \"Input length\". Затем нажать \"Add path\".\n";
out +=
" Для удаления пути необходимо выбрать первую вершину из левого списка и вторую вершину в правом списке. Затем нажать \"Del path\".\n\n";
out +=
" Кнопка \"Bfs\" отмечает точки в пути из вершины, выбранной слева, в вершину, выбранную справа.\n";
out +=
" Кнопка \"Dfs\" помечает точки, которые доступны из вершины, выбранной слева.";
out +=
" Кнопка \"Dijkstra\" ищет кратчайший путь из вершины, выбранной слева. во все другие.";
out +=
" Кнопка \"Clear op\" отменяет \"Bfs\", \"Dfs\" и \"Dijkstra\". Все эти отображения так же отменяются при любых действиях с графом.";
out +=
" Кнопки \"Взвешенный\" и \"Ориентированный\" позволяют сменить эти значения перед построением графа (т.е. для их работы граф должен быть пустым).\n";
out +=
" Кнопки \"Save to file\" и \"Load from file\" позволяют вывести информацию в файл и загрузить информацию из файла соответственно.\n";
out +=
" Кнопка \"Help\" описывает работу с интерфейсом программы.";
showPopUp("Help:", out);
})
],
),
),
),
));
}
}

View File

@ -0,0 +1,334 @@
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<int?>? intListPath;
List<bool>? 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<int, Offset> _off = <int, Offset>{};
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<int, Offset> _getDotPos(int dotsAm, Size size) {
Map<int, Offset> off = <int, Offset>{};
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<Dot> dots, Map<int, Offset> 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) {
//print("${size.width}, ${size.height}");
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;
}
}

View File

@ -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);
@ -363,6 +367,7 @@ class Graphs {
List<Dot> getDots() => _dots;
String getName() => _name;
String? getNameByNum(int n) => _nameTable[n];
Map<int, String> getNameTable() => _nameTable;
int getDotAmount() => _dots.length;
int? getNumByName(String n) {
for (var i in _nameTable.keys) {
@ -399,6 +404,52 @@ class Graphs {
return out;
}
List<int>? getLongestPath([int start = 1]) {
start--;
if (start < 0 || start >= _amount) {
return null;
}
int max = -1;
int inD = -1;
int out = -1;
List<int>? res = <int>[];
for (int i = start; i < _amount; i++) {
var lens = _dots[i].getL();
for (var d in lens.keys) {
if (lens[d]! > max) {
max = lens[d]!;
inD = i + 1;
out = d;
}
}
}
if (inD == -1) {
return null;
}
res.add(inD);
res.add(out);
res.add(max);
return res;
}
List<LenDotPath> getSortedPathList() {
int max = -1;
int inD = -1;
int out = -1;
List<LenDotPath> result = <LenDotPath>[];
for (int i = 0; i < _amount; i++) {
var lens = _dots[i].getL();
for (var d in lens.keys) {
max = lens[d]!;
inD = i + 1;
out = d;
result.add(LenDotPath(max, inD, out));
}
}
result.sort((a, b) => a.l.compareTo(b.l));
return result;
}
/*List<Dot> getNoRepeatDots() {
List<Dot> ret = <Dot>[];
for (int i = 0; i < _amount; i++) {
@ -453,7 +504,7 @@ class Graphs {
mode: FileMode.append);
}
for (int i = 0; i < _amount; i++) {
file.writeAsStringSync((i + 1).toString() + Separators.dotToConnections,
file.writeAsStringSync(_dots[i].getName() + Separators.dotToConnections,
mode: FileMode.append);
var d = _dots[i].getL();
for (var j in d.keys) {
@ -537,7 +588,7 @@ class Graphs {
}
//************Алгоритмы************
/* bool bfsHasPath(int startDot, int goalDot) {
/* bool bfsHasPath(int startDot, int goalDot) {
// обход в ширину
startDot--;
goalDot--;
@ -567,6 +618,7 @@ class Graphs {
}*/
List<int>? bfsPath(int startDot, int goalDot) {
if (startDot == goalDot) return [startDot];
//if (!bfsHasPath(startDot, goalDot)) return null;
startDot--;
goalDot--;
@ -606,7 +658,6 @@ class Graphs {
//Восстановим кратчайший путь
//Для восстановления пути пройдём его в обратном порядке, и развернём.
List<int> path = <int>[];
int cur = goalDot; //текущая вершина пути
@ -629,46 +680,128 @@ class Graphs {
List<bool>? dfsIterative(int v) {
v--;
//List<int>? pos = <int>[];
List<bool> label = <bool>[];
for (int i = 0; i < _amount; i++) {
label.add(false);
}
List<int> stack = <int>[];
stack.add(v);
//pos.add(v);
while (stack.isNotEmpty) {
v = stack.removeLast();
if (!label[v]) {
label[v] = true;
for (int i in _dots[v].getL().keys) {
stack.add(i - 1);
//pos.add(i);
}
}
}
//print(pos);
return label;
}
void dijkstra(int source) {
/*
create vertex set Q;
List<int?> dijkstra(int from) {
List<int?> d = List<int?>.filled(_amount, intMax);
List<int> p = List<int>.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<bool> u = List<bool>.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;
}
List<int?>? prim() {
// работоспособность?
List<bool> used = List<bool>.filled(_amount, false);
List<int> minE = List<int>.filled(_amount, intMax);
List<int> selE = List<int>.filled(_amount, -1);
minE[0] = 0;
for (int i = 0; i < _amount; ++i) {
int v = -1;
for (int j = 0; j < _amount; ++j) {
if (!used[j] && (v == -1 || minE[j] < minE[v])) {
v = j;
}
}
if (minE[v] == intMax) {
return null;
}
used[v] = true;
if (selE[v] != -1) {
print("${v + 1} ${selE[v]}");
}
for (int to in _dots[v].getL().keys) {
if (_dots[v].getL()[to]! < minE[to - 1]) {
minE[to] = _dots[v].getL()[to]!;
selE[to] = v;
}
}
}
}
Graphs? kruskal() {
//List<pair<int,pair<int,int>>> g (m); // вес - вершина 1 - вершина 2
List<LenDotPath> g = getSortedPathList();
//int cost = 0;
List<Dot> res = <Dot>[];
for (int i = 0; i < _amount; i++) {
res.add(Dot(_dots[i].getName(), _dots[i].num));
}
List<int> treeId = List<int>.filled(_amount, 0);
for (int i = 0; i < _amount; ++i) {
treeId[i] = i;
}
for (int i = 0; i < g.length; ++i) {
int a = g[i].d - 1, b = g[i].p - 1;
int l = g[i].l;
if (treeId[a] != treeId[b]) {
//cost += l;
res[a].addPath(b + 1, l);
int oldId = treeId[b], newId = treeId[a];
for (int j = 0; j < _amount; ++j) {
if (treeId[j] == oldId) {
treeId[j] = newId;
}
}
}
}
_dots = res;
return Graphs.fromList(_name, res, _useLength, _oriented);
}
//************Алгоритмы************
}
class LenDotPath {
late int l;
late int d;
late int p;
LenDotPath(int len, dot, path) {
l = len;
d = dot;
p = path;
}
}

View File

@ -57,6 +57,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
desktop_window:
dependency: "direct main"
description:
name: desktop_window
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.0"
fake_async:
dependency: transitive
description:

View File

@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1
version: 0.8.0+1
environment:
sdk: ">=2.15.0-100.0.dev<3.0.0"
@ -35,6 +35,7 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
desktop_window: ^0.4.0
dev_dependencies:
flutter_test:

11
flutter/wikiKruskal.txt Normal file
View File

@ -0,0 +1,11 @@
1
НеОриентированный
Взвешенный
A: 2|7 4|5
B: 1|7 4|9 3|8 5|7
C: 2|8 5|5
D: 1|5 2|9 5|15 6|6
E: 3|5 2|7 4|15 6|8 7|9
F: 4|6 5|8 7|11
G: 6|11 5|9
END

View File

@ -6,6 +6,9 @@
#include "generated_plugin_registrant.h"
#include <desktop_window/desktop_window_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
DesktopWindowPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("DesktopWindowPlugin"));
}

View File

@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
desktop_window
)
set(PLUGIN_BUNDLED_LIBRARIES)

View File

@ -27,7 +27,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
FlutterWindow window(project);
Win32Window::Point origin(10, 10);
Win32Window::Size size(1280, 720);
if (!window.CreateAndShow(L"grafs", origin, size)) {
if (!window.CreateAndShow(L"graphs", origin, size)) {
return EXIT_FAILURE;
}
window.SetQuitOnClose(true);

915
tex/SCWorks.cls Normal file
View File

@ -0,0 +1,915 @@
\LoadClass[14pt]{extarticle}
%\RequirePackage[14pt]{extsizes}
\RequirePackage[
a4paper, mag=1000,
left=2.5cm, right=1.5cm, top=2cm, bottom=2cm, bindingoffset=0cm,
headheight=0cm, footskip=1cm, headsep=0cm
]{geometry}
\RequirePackage{setspace}
\RequirePackage{calc}
\RequirePackage{titlesec}
\RequirePackage{titletoc}
\RequirePackage{caption}
\RequirePackage[inline]{enumitem}
% --------------------------------------------------------------------------%
% Input data
% --------------------------------------------------------------------------%
\def\chair#1{\gdef\@chair{#1}}\chair{\hbox to 3cm{\hrulefill}}
\def\worktype#1{\gdef\@worktype{#1}}
\def\worktitle{\@title}
\def\typework#1{\gdef\@typework{#1}}
\def\disserform#1{\gdef\@disserform{#1}}
\def\disserformP#1{\gdef\@disserformP{#1}}
\def\disserformR#1{\gdef\@disserformR{#1}}
\def\disserformV#1{\gdef\@disserformV{#1}}
\def\course#1{\gdef\@course{#1}}\course{2}
\def\group#1{\gdef\@group{#1}}\group{211}
\def\department#1{\gdef\@department{#1}}\department{\cyr\cyrf\cyra\cyrk%
\cyru\cyrl\cyrsftsn\cyrt\cyre\cyrt\cyra\ \CYRK\CYRN\cyri\CYRI\CYRT}
\def\otdelenie#1{\gdef\@otdelenie{#1}}
\def\studentName{\@author}
%\def\studentName#1{\gdef\@studentName{#1}}
\def\satitle#1{\gdef\@satitle{#1}}\satitle{\hbox to 3cm{\hrulefill}}
\def\saname#1{\gdef\@saname{#1}}\saname{\hbox to 3cm{\hrulefill}}
\def\critictitle#1{\gdef\@critictitle{#1}}\critictitle{\hbox to 3cm{\hrulefill}}
\def\criticname#1{\gdef\@criticname{#1}}\criticname{\hbox to 3cm{\hrulefill}}
\def\secrname#1{\gdef\@secrname{#1}}\secrname{\hbox to 3cm{\hrulefill}}
\def\chtitle#1{\gdef\@chtitle{#1}}\chtitle{\hbox to 3cm{\hrulefill}}
\def\chname#1{\gdef\@chname{#1}}\chname{\hbox to 3cm{\hrulefill}}
%\def\year#1{\gdef\@year{#1}}
\def\spectype#1{\gdef\@spectype{#1}}
\def\spectyperod#1{\gdef\@spectyperod{#1}}
\def\workform#1{\gdef\@workform{#1}}
\def\practtype#1{\gdef\@practtype{#1}}\practtype{\cyr\cyru\cyrch\cyre\cyrb%
\cyrn\cyra\cyrya}
\def\term#1{\gdef\@term{#1}}\term{2}
\def\duration#1{\gdef\@duration{#1}}\duration{2}
\def\protnum#1{\gdef\@protnum{#1}}\protnum{\hbox to 1cm{\hrulefill}}
\def\protdate#1{\gdef\@protdate{#1}}\protdate{\hbox to 3cm{\hrulefill}}
\def\practStart#1{\gdef\@practStart{#1}}\practStart{\hbox to 3cm{\hrulefill}}
\def\practFinish#1{\gdef\@practFinish{#1}}\practFinish{\hbox to 3cm{\hrulefill}}
\def\reviewtype#1{\gdef\@reviewtype{#1}}\reviewtype{\CYRO\CYRT%
\CYRZ\CYRERY\CYRV}
\def\patitle#1{\gdef\@patitle{#1}}\patitle{\@satitle}
\def\paname#1{\gdef\@paname{#1}}\paname{\@saname}
\def\napravlenie#1{\gdef\@napravlenie{#1}}\napravlenie{\hbox to 3cm{\hrulefill}}
\def\Napravlenie{\@napravlenie}
\def\studenttitle#1{\gdef\@studenttitle{#1}}\studenttitle{\cyr\cyrs\cyrt%
\cyru\cyrd\cyre\cyrn\cyrt\cyra}
\def\studentdone#1{\gdef\@studentdone{#1}}\studentdone{\cyrp\cyrr\cyro%
\cyrsh\cyre\cyrd\cyrsh\cyre\cyrg\cyro}
\def\studentfemale{\studenttitle{\cyrs\cyrt\cyru\cyrd\cyre\cyrn\cyrt%
\cyrk\cyri}\studentdone{\cyrp\cyrr\cyro\cyrsh\cyre\cyrd\cyrsh%
\cyre\cyrishrt}}
%\newcommand{\MakeTitle}{}
\def\workname#1{\gdef\@workname{#1}}
%\hbox to 3cm{\hrulefill}
% --------------------------------------------------------------------------%
\newcommand{\signature}[2]{
\hbox to 7cm{#1\hfill} \hbox to 3cm{\hrulefill} \hbox to 6cm{\hfill #2}}
\newcommand{\inlinesignature}[2]{%
#1\qquad \hbox to 3cm{\hrulefill}\quad #2}
\newcommand{\signatureline}{}
% --------------------------------------------------------------------------%
\newcommand{\scaleUnivName}{0.97}
\DeclareOption{times}{%
\renewcommand{\rmdefault}{ftm}
\renewcommand{\scaleUnivName}{1.0}
}
\DeclareOption{spec}{%
\spectype{\cyr\cyrs\cyrp\cyre\cyrc\cyri\cyra\cyrl\cyrsftsn\cyrn\cyro%
\cyrs\cyrt\cyri}
\spectyperod{\cyrs\cyrp\cyre\cyrc\cyri\cyra\cyrl\cyrsftsn\cyrn\cyro%
\cyrs\cyrt\cyri}
\workform{\cyr\CYRS\CYRP\CYRE\CYRC\CYRI\CYRA\CYRL\CYRI\CYRS\CYRT\CYRA}
\disserform{\CYRD\CYRI\CYRP\CYRL\CYRO\CYRM\CYRN\CYRA\CYRYA\ \CYRR\CYRA%
\CYRB\CYRO\CYRT\CYRA}
\disserformP{\CYRD\CYRI\CYRP\CYRL\CYRO\CYRM\CYRN\CYRO\CYRISHRT\ \CYRR%
\CYRA\CYRB\CYRO\CYRT\CYRE}
\disserformR{\CYRD\CYRI\CYRP\CYRL\CYRO\CYRM\CYRN\CYRO\CYRISHRT\ \CYRR%
\CYRA\CYRB\CYRO\CYRT\CYRERY}
\disserformV{\CYRD\CYRI\CYRP\CYRL\CYRO\CYRM\CYRN\CYRU\CYRYU\ \CYRR\CYRA%
\CYRB\CYRO\CYRT\CYRU}
}
\DeclareOption{bachelor}{%
\spectype{\cyr\cyrn\cyra\cyrp\cyrr\cyra\cyrv\cyrl\cyre\cyrn\cyri\cyrya}
\spectyperod{\cyr\cyrn\cyra\cyrp\cyrr\cyra\cyrv\cyrl\cyre\cyrn\cyri \cyryu}
\workform{\cyr\CYRB\CYRA\CYRK\CYRA\CYRL\CYRA\CYRV\CYRR\CYRA}
\disserform{\CYRB\CYRA\CYRK\CYRA\CYRL\CYRA\CYRV\CYRR\CYRS\CYRK\CYRA%
\CYRYA\ \CYRR\CYRA\CYRB\CYRO\CYRT\CYRA}
\disserformP{\CYRB\CYRA\CYRK\CYRA\CYRL\CYRA\CYRV\CYRR\CYRS\CYRK\CYRO%
\CYRISHRT\ \CYRR\CYRA\CYRB\CYRO\CYRT\CYRE}
\disserformR{\CYRB\CYRA\CYRK\CYRA\CYRL\CYRA\CYRV\CYRR\CYRS\CYRK\CYRO%
\CYRISHRT\ \CYRR\CYRA\CYRB\CYRO\CYRT\CYRERY}
\disserformV{\CYRB\CYRA\CYRK\CYRA\CYRL\CYRA\CYRV\CYRR\CYRS\CYRK\CYRU%
\CYRYU\ \CYRR\CYRA\CYRB\CYRO\CYRT\CYRU}
}
\DeclareOption{master}{%
\spectype{\cyr\cyrn\cyra\cyrp\cyrr\cyra\cyrv\cyrl\cyre\cyrn\cyri\cyrya}
\spectyperod{\cyr\cyrn\cyra\cyrp\cyrr\cyra\cyrv\cyrl\cyre\cyrn\cyri \cyryu}
\workform{\cyr\CYRM\CYRA\CYRG\CYRI\CYRS\CYRT\CYRR\CYRA}
\disserform{\CYRM\CYRA\CYRG\CYRI\CYRS\CYRT\CYRE\CYRR\CYRS\CYRK\CYRA%
\CYRYA\ \CYRR\CYRA\CYRB\CYRO\CYRT\CYRA}
\disserformP{\CYRM\CYRA\CYRG\CYRI\CYRS\CYRT\CYRE\CYRR\CYRS\CYRK\CYRO%
\CYRISHRT\ \CYRR\CYRA\CYRB\CYRO\CYRT\CYRE}
\disserformR{\CYRM\CYRA\CYRG\CYRI\CYRS\CYRT\CYRE\CYRR\CYRS\CYRK\CYRO%
\CYRISHRT\ \CYRR\CYRA\CYRB\CYRO\CYRT\CYRERY}
\disserformV{\CYRM\CYRA\CYRG\CYRI\CYRS\CYRT\CYRE\CYRR\CYRS\CYRK\CYRU%
\CYRYU\ \CYRR\CYRA\CYRB\CYRO\CYRT\CYRU}
}
\DeclareOption{coursework}{%
\worktype{\cyr\CYRK\cyru\cyrr\cyrs\cyro\cyrv\cyra\cyrya\ \cyrr\cyra\cyrb%
\cyro\cyrt\cyra}
\renewcommand{\maketitle}{\CDMakeTitle}
\workname{\MakeUppercase{\@worktype}}
\typework{\cyr\cyrn\cyra\cyrp\cyri\cyrs\cyra\cyrn\cyra}
}
\DeclareOption{documentation}{%
\worktype{\cyr\CYRK\cyru\cyrr\cyrs\cyro\cyrv\cyra\cyrya\ \cyrr\cyra\cyrb%
\cyro\cyrt\cyra}
\renewcommand{\maketitle}{\CDMakeTitle}
\workname{\MakeUppercase{}}
\typework{\cyr\cyrn\cyra\cyrp\cyri\cyrs\cyra\cyrn\cyra}
}
\DeclareOption{diploma}{%
\worktype{\cyr\CYRV\cyrery\cyrp\cyru\cyrs\cyrk\cyrn\cyra\cyrya\ \cyrk\cyrv%
\cyra\cyrl\cyri\cyrf\cyri\cyrk\cyra\cyrc\cyri\cyro\cyrn\cyrn\cyra%
\cyrya\ \cyrr\cyra\cyrb\cyro\cyrt\cyra}
\worktype{\ \cyrr\cyra\cyrb\cyro\cyrt\cyra}
\renewcommand{\maketitle}{\CDMakeTitle}
\workname{\MakeUppercase{\@disserform}}
\typework{\cyr\cyrn\cyra\cyrp\cyri\cyrs\cyra\cyrn\cyra}
}
\DeclareOption{autoref}{%
\workname{\cyr\CYRA\CYRV\CYRT\CYRO\CYRR\CYRE\CYRF\CYRE\CYRR\CYRA\CYRT\ %
\MakeUppercase{\@disserformR}}
\worktype{\ \cyrr\cyra\cyrb\cyro\cyrt\cyra}
\renewcommand{\maketitle}{\CDMakeTitle}
%\workname{\MakeUppercase{\@disserform}}
\typework{\cyr\cyrn\cyra\cyrp\cyri\cyrs\cyra\cyrn\cyra}
}
\DeclareOption{nir}{%
\workname{\cyr\CYRO\CYRT\CYRCH\CYRE\CYRT\ \CYRO\ \CYRN\CYRA\CYRU\CYRCH%
\CYRN\CYRO-\CYRI\CYRS\CYRS\CYRL\CYRE\CYRD\CYRO\CYRV\CYRA\CYRT\CYRE\CYRL%
\CYRSFTSN\CYRS\CYRK\CYRO\CYRISHRT\ \CYRR\CYRA\CYRB\CYRO\CYRT\CYRE}
\worktype{\ \cyrr\cyra\cyrb\cyro\cyrt\cyra}
\renewcommand{\maketitle}{\CDMakeTitle}
%\workname{\MakeUppercase{\@disserform}}
\typework{\cyr\cyrn\cyra\cyrp\cyri\cyrs\cyra\cyrn\cyra}
}
\DeclareOption{pract}{%
\worktype{\cyr\CYRO\cyrt\cyrch\cyre\cyrt\ \cyro\ \cyrp\cyrr\cyra\cyrk\cyrt%
\cyri\cyrk\cyre}
\renewcommand{\maketitle}{\MakeTitlePr}
\typework{\cyr\cyrn\cyra\cyrp\cyri\cyrs\cyra\cyrn}
}
\DeclareOption{review}{%
\reviewtype{\CYRO\CYRT\CYRZ\CYRERY\CYRV}
\worktype{\cyrn\cyra\cyru\cyrch\cyrn\cyro\cyrg\cyro\ \cyrr\cyru\cyrk%
\cyro\cyrv\cyro\cyrd\cyri\cyrt\cyre\cyrl\cyrya\ \cyro\ \cyrv\cyrery%
\cyrp\cyru\cyrs\cyrk\cyrn\cyro\cyrishrt\ \cyrk\cyrv\cyra\cyrl\cyri\cyrf%
\cyri\cyrk\cyra\cyrc\cyri\cyro\cyrn\cyrn\cyro\cyrishrt\ \cyrr\cyra\cyrb%
\cyro\cyrt\cyre}
\workname{\cyr\cyrn\cyra\cyru\cyrch\cyrn\cyro\cyrg\cyro\ \cyrr\cyru\cyrk%
\cyro\cyrv\cyro\cyrd\cyri\cyrt\cyre\cyrl\cyrya\ \cyro\ \MakeLowercase{\@disserformP}}
%\workname{\@worktype\ \MakeLowercase{\@workform}}
\renewcommand{\maketitle}{\MakeTitleReview}
\renewcommand{\signatureline}{%
\par\noindent%
\CYRN\cyra\cyru\cyrch\cyrn\cyrery\cyrishrt\ \cyrr\cyru\cyrk\cyro\cyrv%
\cyro\cyrd\cyri\cyrt\cyre\cyrl\cyrsftsn\\%
\signature{\@satitle}{\@saname}\\%
}
}
\DeclareOption{assignment}{%
\reviewtype{\CYRZ\CYRA\CYRD\CYRA\CYRN\CYRI\CYRE}
\worktype{\cyrn\cyra\ \cyrv\cyrery\cyrp\cyru\cyrs\cyrk\cyrn\cyru%
\cyryu\ \cyrk\cyrv\cyra\cyrl\cyri\cyrf\cyri\cyrk\cyra\cyrc\cyri\cyro%
\cyrn\cyrn\cyru\cyryu\ \cyrr\cyra\cyrb\cyro\cyrt\cyru}
\workname{\cyr\cyrn\cyra\ \MakeLowercase{\@disserformV}}
%\workname{\@worktype\ \MakeLowercase{\@workform}}
\renewcommand{\maketitle}{\MakeTitleAssign}
\renewcommand{\signatureline}{%
\vfill%
\noindent%
\textbf{\CYRS\cyrr\cyro\cyrk\ \cyrp\cyrr\cyre\cyrd\cyro\cyrs\cyrt\cyra%
\cyrv\cyrl\cyre\cyrn\cyri\cyrya\ \cyrr\cyra\cyrb\cyro\cyrt\cyrery:}\ \@practFinish
\vspace{2em}\raggedright
\noindent \CYRR\cyra\cyrs\cyrs\cyrm\cyro\cyrt\cyrr\cyre\cyrn\cyro\ %
\cyrn\cyra\ \cyrz\cyra\cyrs\cyre\cyrd\cyra\cyrn\cyri\cyri\ \cyrk\cyra%
\cyrf\cyre\cyrd\cyrr\cyrery\ \@chair
\vspace{1em}
\CYRP\cyrr\cyro\cyrt\cyro\cyrk\cyro\cyrl\ \textnumero\ \@protnum\ \cyro%
\cyrt\ \@protdate
\vspace{1em}
\raggedright
\noindent
\inlinesignature{\CYRS\cyre\cyrk\cyrr\cyre\cyrt\cyra\cyrr\cyrsftsn}{\@secrname}
\vspace{2em}
\noindent\raggedright
\CYRD\cyra\cyrt\cyra\ \cyrv\cyrery\cyrd\cyra\cyrch\cyri\ \cyrz\cyra%
\cyrd\cyra\cyrn\cyri\cyrya\ \@practStart
\vspace{1em}
\noindent\raggedright
\inlinesignature{\CYRZ\cyra\cyrd\cyra\cyrn\cyri\cyre\ \cyrp\cyro\cyrl%
\cyru\cyrch\cyri\cyrl}{\hbox to 3cm{\hrulefill}}
\vspace{1cm}
}
}
\DeclareOption{critique}{%
\reviewtype{\CYRR\CYRE\CYRC\CYRE\CYRN\CYRZ\CYRI\CYRYA}
\worktype{\cyrn\cyra\ \cyrv\cyrery\cyrp\cyru\cyrs\cyrk\cyrn\cyru%
\cyryu\ \cyrk\cyrv\cyra\cyrl\cyri\cyrf\cyri\cyrk\cyra\cyrc\cyri\cyro%
\cyrn\cyrn\cyru\cyryu\ \cyrr\cyra\cyrb\cyro\cyrt\cyru}
\workname{\cyr\cyrn\cyra\ \MakeLowercase{\@disserformV}}
%\workname{\@worktype\ \MakeLowercase{\@workform}}
\renewcommand{\maketitle}{\MakeTitleReview}
\renewcommand{\signatureline}{%
\par\noindent%
\CYRR\cyre\cyrc\cyre\cyrn\cyrz\cyre\cyrn\cyrt\\%
\signature{\@critictitle}{\@criticname}\\%
}
}
\DeclareOption{referat}{%
\worktype{\cyr\CYRR\cyre\cyrf\cyre\cyrr\cyra\cyrt}
\workname{\MakeUppercase{\@worktype}}
\renewcommand{\maketitle}{\RefMakeTitle}
\typework{\cyr\cyrn\cyra\cyrp\cyri\cyrs\cyra\cyrn}
}
\DeclareOption{och}{%
\otdelenie{\cyr\cyro\cyrch\cyrn\cyro\cyrishrt\ \cyrf\cyro\cyrr\cyrm%
\cyrery\ \cyro\cyrb\cyru\cyrch\cyre\cyrn\cyri\cyrya}
}
\DeclareOption{zaoch}{%
\otdelenie{\cyr\cyrz\cyra\cyro\cyrch\cyrn\cyro\cyrishrt\ \cyrf\cyro\cyrr%
\cyrm\cyrery\ \cyro\cyrb\cyru\cyrch\cyre\cyrn\cyri\cyrya}
}
\ExecuteOptions{coursework,och,bachelor}
\ProcessOptions
% --------------------------------------------------------------------------%
\newcommand*{\hm}[1]{#1\nobreak\discretionary{}%
{\hbox{$\mathsurround=0pt #1$}}{}}
% --------------------------------------------------------------------------%
% --------------------------------------------------------------------------%
\onehalfspacing
\parindent=1.25cm
\pagestyle{headings}
\renewcommand{\@oddhead}{}
\renewcommand{\@oddfoot}{\hfil \thepage}
% --------------------------------------------------------------------------%
% Table and figure numbering by sections
% --------------------------------------------------------------------------%
\newif\if@secNumbering\@secNumberingfalse
\newcommand{\secNumbering}{
\renewcommand{\thefigure}{\arabic{section}.\arabic{figure}}
\renewcommand{\thetable}{\arabic{section}.\arabic{table}}
\renewcommand{\theequation}{\arabic{section}.\arabic{equation}}
\@addtoreset{figure}{section}
\@addtoreset{table}{section}
\@addtoreset{equation}{section}
\@secNumberingtrue
}
% --------------------------------------------------------------------------%
% --------------------------------------------------------------------------%
% Table and figure captions
% --------------------------------------------------------------------------%
\def\CaptionName#1{\gdef\@captionname{#1}}
\newlength\tmp %10cm
\setlength{\tmp}{1ex}
\setlength{\belowcaptionskip}{1ex}
\setlength{\abovecaptionskip}{1ex}
\captionsetup[figure]{name=\CYRR\cyri\cyrs\cyru\cyrn\cyro\cyrk, labelsep=endash,
justification=centering, font={small}, skip=\abovecaptionskip, position=below}
\captionsetup[table]{name=\CYRT\cyra\cyrb\cyrl\cyri\cyrc\cyra, labelsep=endash, format=plain,
justification=RaggedRight, singlelinecheck=false, font={small}, position=top}
% --------------------------------------------------------------------------%
% Table of contents
% --------------------------------------------------------------------------%
\renewcommand{\tableofcontents}%
{\structformat\section*{\uppercase{\cyr\CYRS\CYRO\CYRD\CYRE\CYRR\CYRZH\CYRA%
\CYRN\CYRI\CYRE}}\secformat\@starttoc{toc}
\thispagestyle{empty}}
\renewcommand{\@dotsep}{1.5}
\renewcommand{\@pnumwidth}{1.0em}
\newcommand{\l@abcd}[2]{{\@dottedtocline{0}{0pt}{0pt}{#1}{#2}}}
\renewcommand{\l@section}{\@dottedtocline{1}{0em}{1.5em}}
\renewcommand{\l@subsection}{\@dottedtocline{2}{1.5em}{2.3em}}
% --------------------------------------------------------------------------%
% --------------------------------------------------------------------------%
% Sections, subsections
% --------------------------------------------------------------------------%
% Numbering
\renewcommand{\thesection}{\arabic{section}}
\renewcommand{\thesubsection}{\arabic{section}.\arabic{subsection}}
\renewcommand{\thesubsubsection}{\arabic{section}.\arabic{subsection}.\arabic{subsubsection}}
\newcommand{\sectionbreak}{\clearpage}
% Contents, intro, conclusion
\newcommand{\structformat}
{
\titlespacing{\section}
{0cm}{3ex plus 1ex minus .2ex}{1.4ex plus.2ex}
\titleformat{\section}[block]
{\centering\bfseries}
{\thesection}{0ex}{}
}
% Sections, subsections
\newcommand{\secformat}
{
\titlespacing{\section}
{0cm}{3ex plus 1ex minus .2ex}{0.4ex plus.2ex}
\titleformat{\section}[block]
{\hspace{1.25cm}\raggedright\bfseries}
{\thesection}{1ex}{}
}
\newif\if@hyperrefloaded\@hyperrefloadedfalse
\AtBeginDocument{\@ifpackageloaded{hyperref}%
{\@hyperrefloadedtrue}{\@hyperrefloadedfalse}%
}
%\RequirePackage{ifthen}
\newcommand{\starsection}[1]{
\structformat
\section*{#1}%
\if@hyperrefloaded
\phantomsection
\fi
\addcontentsline{toc}{section}{#1}
\setcounter{section}{0}
\secformat
}
\setcounter{section}{0}
\secformat
\newcommand{\intro}{\starsection{\cyr\CYRV\CYRV\CYRE\CYRD\CYRE%
\CYRN\CYRI\CYRE}}
\newcommand{\abbreviations}{\starsection{\CYRO\CYRB\CYRO\CYRZ\CYRN\CYRA%
\CYRCH\CYRE\CYRN\CYRI\CYRYA\ \CYRI\ \CYRS\CYRO\CYRK\CYRR\CYRA\CYRSHCH%
\CYRE\CYRN\CYRI\CYRYA}}
\newcommand{\definitions}{\starsection{\CYRO\CYRP\CYRR\CYRE\CYRD\CYRE%
\CYRL\CYRE\CYRN\CYRI\CYRYA}}
\newcommand{\defabbr}{\starsection{\CYRO\CYRP\CYRR\CYRE\CYRD\CYRE\CYRL%
\CYRE\CYRN\CYRI\CYRYA, \CYRO\CYRB\CYRO\CYRZ\CYRN\CYRA\CYRCH\CYRE\CYRN%
\CYRI\CYRYA\ \CYRI\ \CYRS\CYRO\CYRK\CYRR\CYRA\CYRSHCH\CYRE\CYRN\CYRI\CYRYA}}
\newcommand{\conclusion}{\starsection{\cyr\CYRZ\CYRA\CYRK\CYRL\CYRYU%
\CYRCH\CYRE\CYRN\CYRI\CYRE}}
% Section and subsection parameters
\titlespacing{\section}
{0cm}{3ex plus 1ex minus .2ex}{0.4ex plus.2ex}
\titleformat{\subsection}[block]
{\hspace{1.25cm}\normalfont\bfseries}
{\thesubsection}{1ex}{}
\titlespacing{\subsection}
{0cm}{2ex plus 1ex minus .2ex}{.4ex plus.2ex}
\titleformat{\subsubsection}[block]
{\hspace{1.25cm}\normalfont}
{\thesubsubsection}{1ex}{}
\titlespacing{\subsubsection}
{0cm}{2ex plus 1ex minus .2ex}{.4ex plus.2ex}
% --------------------------------------------------------------------------%
% --------------------------------------------------------------------------%
%\AddEnumerateCounter{\Asbuk}{\@Asbuk}{\CYRM}
%\AddEnumerateCounter{\asbuk}{\@asbuk}{\cyrm}
\makeatletter
\def\redeflsection{\def\l@section{\@dottedtocline{1}{0em}{10em}}}
\renewcommand{\appendix}{\par%
\renewcommand{\secNumbering}{
\renewcommand{\thefigure}{\Asbuk{section}.\arabic{figure}}
\renewcommand{\thetable}{\Asbuk{section}.\arabic{table}}
\renewcommand{\theequation}{\Asbuk{section}.\arabic{equation}}
\@addtoreset{figure}{section}
\@addtoreset{table}{section}
\@addtoreset{equation}{section}
}
\if@secNumbering
\secNumbering
\fi
\setcounter{section}{0}%
\setcounter{subsection}{0}%
\renewcommand{\appendixname}{\cyr\CYRP\CYRR\CYRI\CYRL\CYRO\CYRZH\CYRE%
\CYRN\CYRI\CYRE}%
\def\sectionname{\appendixname}%
\addtocontents{toc}{\protect\redeflsection}%
\gdef\thesection{\Asbuk{section}}%
\titlespacing{\section}
%{0cm}{1ex plus 0.1ex minus .2ex}{1.1ex plus.1ex}
{0cm}{3ex plus 1ex minus .2ex}{0.4ex plus.2ex}
\titleformat{\section}[display]
{\centering\normalfont\bfseries}
{\appendixname\hspace{1ex}\thesection}{0ex}{}
\titlecontents{section}
[3ex]
{\hspace{-3ex}}
{\appendixname~\thecontentslabel\hspace{2ex}}
{\hspace{2.3em}}
{\titlerule*[0.98ex]{.}\contentspage}
}
% --------------------------------------------------------------------------%
% Title pages
% --------------------------------------------------------------------------%
%\newcommand{\shapka}{{\centering \CYRM\CYRI\CYRN\CYRO\CYRB\CYRR\CYRN\CYRA%
%\CYRU\CYRK\CYRI\ \CYRR\CYRO\CYRS\CYRS\CYRI\CYRI\\ %
%\CYRF\cyre\cyrd\cyre\cyrr\cyra\cyrl\cyrsftsn\cyrn\cyro\cyre\ \cyrg\cyro%
%\cyrs\cyru\cyrd\cyra\cyrr\cyrs\cyrt\cyrv\cyre\cyrn\cyrn\cyro\cyre\ %
%\cyrb\cyryu\cyrd\cyrzh\cyre\cyrt\cyrn\cyro\cyre\ \cyro\cyrb\cyrr\cyra%
%\cyrz\cyro\cyrv\cyra\cyrt\cyre\cyrl\cyrsftsn\cyrn\cyro\cyre\ \cyru%
%\cyrch\cyrr\cyre\cyrzh\cyrd\cyre\cyrn\cyri\cyre\ \cyrv\cyrery\cyrs%
%\cyrsh\cyre\cyrg\cyro\ \cyro\cyrb\cyrr\cyra\cyrz%
%\cyro\cyrv\cyra\cyrn\cyri\cyrya\\
%\textbf{<<\CYRS\CYRA\CYRR\CYRA\CYRT\CYRO\CYRV\CYRS\CYRK\CYRI\CYRISHRT\ %
%\CYRN\CYRA\CYRC\CYRI\CYRO\CYRN\CYRA\CYRL\CYRSFTSN\CYRN\CYRERY%
%\CYRISHRT\ \CYRI\CYRS\CYRS\CYRL\CYRE\CYRD\CYRO\CYRV\CYRA\CYRT\CYRE\CYRL%
%\CYRSFTSN\CYRS\CYRK\CYRI\CYRISHRT\ %
%\CYRG\CYRO\CYRS\CYRU\CYRD\CYRA\CYRR\CYRS\CYRT\CYRV\CYRE\CYRN\CYRN\CYRERY%
%\CYRISHRT\ \CYRU\CYRN\CYRI\CYRV\CYRE\CYRR\CYRS\CYRI\CYRT\CYRE\CYRT\ %
%\CYRI\CYRM\CYRE\CYRN\CYRI~\CYRN.\,\CYRG.\,\CYRCH\CYRE\CYRR\CYRN\CYRERY%
%\CYRSH\CYRE\CYRV\CYRS\CYRK\CYRO\CYRG\CYRO>>}\\}}
%\newcommand{\shapka}{{\centering \CYRM\CYRI\CYRN\CYRO\CYRB\CYRR\CYRN\CYRA%
%\CYRU\CYRK\CYRI\ \CYRR\CYRO\CYRS\CYRS\CYRI\CYRI\\ \hspace{-1em}%
%\CYRF\cyre\cyrd\cyre\cyrr\cyra\cyrl\cyrsftsn\cyrn\cyro\cyre\ \cyrg\cyro%
%\cyrs\cyru\cyrd\cyra\cyrr\cyrs\cyrt\cyrv\cyre\cyrn\cyrn\cyro\cyre\ %
%\cyrb\cyryu\cyrd\cyrzh\cyre\cyrt\cyrn\cyro\cyre\ \cyro\cyrb\cyrr\cyra%
%\cyrz\cyro\cyrv\cyra\cyrt\cyre\cyrl\cyrsftsn\cyrn\cyro\cyre\ \cyru%
%\cyrch\cyrr\cyre\cyrzh\cyrd\cyre\cyrn\cyri\cyre\ \\\cyrv\cyrery\cyrs%
%\cyrsh\cyre\cyrg\cyro\ \cyro\cyrb\cyrr\cyra\cyrz%
%\cyro\cyrv\cyra\cyrn\cyri\cyrya\\\hspace{-2em}
%{
%\textbf{<<\CYRS\CYRA\CYRR\CYRA\CYRT\CYRO\CYRV\CYRS\CYRK\CYRI\CYRISHRT\ %
%\CYRN\CYRA\CYRC\CYRI\CYRO\CYRN\CYRA\CYRL\CYRSFTSN\CYRN\CYRERY%
%\CYRISHRT\ \CYRI\CYRS\CYRS\CYRL\CYRE\CYRD\CYRO\CYRV\CYRA\CYRT\CYRE\CYRL%
%\CYRSFTSN\CYRS\CYRK\CYRI\CYRISHRT}} \\%
%{\textbf{\CYRG\CYRO\CYRS\CYRU\CYRD\CYRA\CYRR\CYRS\CYRT\CYRV\CYRE\CYRN\CYRN\CYRERY%
%\CYRISHRT\ \CYRU\CYRN\CYRI\CYRV\CYRE\CYRR\CYRS\CYRI\CYRT\CYRE\CYRT}} \\%
%{\textbf{\CYRI\CYRM\CYRE\CYRN\CYRI~\CYRN.\,\CYRG.\,\CYRCH\CYRE\CYRR\CYRN\CYRERY%
%\CYRSH\CYRE\CYRV\CYRS\CYRK\CYRO\CYRG\CYRO>>}}\\}}
\newcommand{\shapka}{{\centering \CYRM\CYRI\CYRN\CYRO\CYRB\CYRR\CYRN\CYRA%
\CYRU\CYRK\CYRI\ \CYRR\CYRO\CYRS\CYRS\CYRI\CYRI\\ %
\CYRF\cyre\cyrd\cyre\cyrr\cyra\cyrl\cyrsftsn\cyrn\cyro\cyre\ \cyrg\cyro%
\cyrs\cyru\cyrd\cyra\cyrr\cyrs\cyrt\cyrv\cyre\cyrn\cyrn\cyro\cyre\ %
\cyrb\cyryu\cyrd\cyrzh\cyre\cyrt\cyrn\cyro\cyre\ \cyro\cyrb\cyrr\cyra%
\cyrz\cyro\cyrv\cyra\cyrt\cyre\cyrl\cyrsftsn\cyrn\cyro\cyre\ \cyru%
\cyrch\cyrr\cyre\cyrzh\cyrd\cyre\cyrn\cyri\cyre\ \\\cyrv\cyrery\cyrs%
\cyrsh\cyre\cyrg\cyro\ \cyro\cyrb\cyrr\cyra\cyrz%
\cyro\cyrv\cyra\cyrn\cyri\cyrya\\[0.2em]
\centerline{\scalebox{\scaleUnivName}[1.0]{\parbox[t]{1.1\textwidth}
{\centering
\textbf{<<\CYRS\CYRA\CYRR\CYRA\CYRT\CYRO\CYRV\CYRS\CYRK\CYRI\CYRISHRT\ %
\CYRN\CYRA\CYRC\CYRI\CYRO\CYRN\CYRA\CYRL\CYRSFTSN\CYRN\CYRERY%
\CYRISHRT\ \CYRI\CYRS\CYRS\CYRL\CYRE\CYRD\CYRO\CYRV\CYRA\CYRT\CYRE\CYRL%
\CYRSFTSN\CYRS\CYRK\CYRI\CYRISHRT\ \\%
\CYRG\CYRO\CYRS\CYRU\CYRD\CYRA\CYRR\CYRS\CYRT\CYRV\CYRE\CYRN\CYRN\CYRERY%
\CYRISHRT\ \CYRU\CYRN\CYRI\CYRV\CYRE\CYRR\CYRS\CYRI\CYRT\CYRE\CYRT\ \\%
\CYRI\CYRM\CYRE\CYRN\CYRI~\CYRN.\,\CYRG.\,\CYRCH\CYRE\CYRR\CYRN\CYRERY%
\CYRSH\CYRE\CYRV\CYRS\CYRK\CYRO\CYRG\CYRO>>}}}}}}
\newcommand{\CDMakeTitle}
{
\thispagestyle{empty}
\shapka
%\vspace{0.5cm}
\begin{center}
%\parbox{8cm}{
%\raggedright
\CYRK\cyra\cyrf\cyre\cyrd\cyrr\cyra\ \@chair
%}
\end{center}
\vspace{14pt}
\vspace{1cm}
{\centering
\textbf{\MakeUppercase{\@title}}
\\[0.3cm]
{\@workname}
}
\vspace{1.5cm}
\begin{flushleft}
\@studenttitle\ \@course\ \cyrk\cyru\cyrr\cyrs\cyra\ \@group\ \cyrg%
\cyrr\cyru\cyrp\cyrp\cyrery\\
\@spectype\ \@napravlenie\\
\@department\\
\@author
\end{flushleft}
\vfill
\noindent
\CYRN\cyra\cyru\cyrch\cyrn\cyrery\cyrishrt\ \cyrr\cyru\cyrk\cyro\cyrv%
\cyro\cyrd\cyri\cyrt\cyre\cyrl\cyrsftsn\\
\signature{\@satitle}{\@saname}\\[14pt]
\CYRZ\cyra\cyrv\cyre\cyrd\cyru\cyryu\cyrshch\cyri\cyrishrt\ \cyrk\cyra%
\cyrf\cyre\cyrd\cyrr\cyro\cyrishrt\\
\signature{\@chtitle}{\@chname}
\vfill
{\centering{\cyr\CYRS\cyra\cyrr\cyra\cyrt\cyro\cyrv\ \@date}
}
\newpage
}
% --------------------------------------------------------------------------%
% --------------------------------------------------------------------------%
% Title page of internship
% --------------------------------------------------------------------------%
\newcommand{\MakeTitlePr}
{
\thispagestyle{empty}
\begin{center}
\shapka
\end{center}
\vspace{0.5cm}
\begin{flushright}
\parbox{7cm}{
\begin{flushleft}
\CYRU\CYRT\CYRV\CYRE\CYRR\CYRZH\CYRD\CYRA\CYRYU\\
\CYRZ\cyra\cyrv.\cyrk\cyra\cyrf\cyre\cyrd\cyrr\cyro\cyrishrt,\\
\@chtitle\\
\hbox to 7cm{\hrulefill\ \@chname}
\end{flushleft}
}
\end{flushright}
\vspace{1cm}
\begin{center}\textbf{\MakeUppercase{\@worktype}}\end{center}
\begin{flushleft}
\vspace{12pt}
\@studenttitle\ \@course\ \cyrk\cyru\cyrr\cyrs\cyra\ \@group\ \cyrg%
\cyrr\cyru\cyrp\cyrp\cyrery\ \@department\\
\@author
\vspace{0.5cm}
\cyrv\cyri\cyrd\ \cyrp\cyrr\cyra\cyrk\cyrt\cyri\cyrk\cyri: \@practtype\\
\cyrk\cyra\cyrf\cyre\cyrd\cyrr\cyra: \@chair\\
\cyrk\cyru\cyrr\cyrs: \@course\\
\cyrs\cyre\cyrm\cyre\cyrs\cyrt\cyrr: \@term\\
\cyrp\cyrr\cyro\cyrd\cyro\cyrl\cyrzh\cyri\cyrt\cyre\cyrl\cyrsftsn\cyrn%
\cyro\cyrs\cyrt\cyrsftsn: \@duration\ \cyrn\cyre\cyrd., \cyrs\ \@practStart\ \cyrg. \cyrp\cyro\ \@practFinish\ \cyrg.
\end{flushleft}
%\parindent=-0.2cm
\vspace{1cm}
\noindent
\CYRR\cyru\cyrk\cyro\cyrv\cyro\cyrd\cyri\cyrt\cyre\cyrl\cyrsftsn\ \cyrp%
\cyrr\cyra\cyrk\cyrt\cyri\cyrk\cyri\ \cyro\cyrt\ \cyru\cyrn\cyri\cyrv%
\cyre\cyrr\cyrs\cyri\cyrt\cyre\cyrt\cyra,\\[12pt]%
\signature{\@satitle}{\@saname}\\[14pt]
\CYRR\cyru\cyrk\cyro\cyrv\cyro\cyrd\cyri\cyrt\cyre\cyrl\cyrsftsn\ \cyrp%
\cyrr\cyra\cyrk\cyrt\cyri\cyrk\cyri\ \cyro\cyrt\ \cyro\cyrr\cyrg\cyra%
\cyrn\cyri\cyrz\cyra\cyrc\cyri\cyri\ (\cyru\cyrch\cyrr\cyre\cyrzh\cyrd%
\cyre\cyrn\cyri\cyrya, \cyrp\cyrr\cyre\cyrd\cyrp\cyrr\cyri\cyrya\cyrt%
\cyri\cyrya),\\[12pt]%
\signature{\@patitle}{\@paname}
\newpage
\thispagestyle{empty}
\vspace*{11cm}
\CYRT\cyre\cyrm\cyra\ \cyrp\cyrr\cyra\cyrk\cyrt\cyri\cyrk\cyri:<<\@title>>
\parindent=1.25cm
\newpage
}
% --------------------------------------------------------------------------%
% --------------------------------------------------------------------------%
% Title page of review
% --------------------------------------------------------------------------%
\newcommand{\MakeTitleReview}
{
\pagestyle{empty}
\begin{center}
\shapka
\end{center}
{
\centering
\textbf{\MakeUppercase{\@reviewtype}}\\[-0.3em]
\textbf{\@workname}\\[0.3em]
<<{\MakeUppercase{\@title}}>>
\@studenttitle\ \@course\ \cyrk\cyru\cyrr\cyrs\cyra\ %
\@department\\
\centering
{\@author}\\
\centering
\@studentdone\ \cyro\cyrb\cyru\cyrch\cyre\cyrn\cyri\cyre\ \cyrp%
\cyro\ \@spectyperod\ \@napravlenie
}
\vspace{2em}
}
% --------------------------------------------------------------------------%
% --------------------------------------------------------------------------%
% Title page of assignment
% --------------------------------------------------------------------------%
\newcommand{\MakeTitleAssign}
{
\pagestyle{empty}
\begin{center}
\shapka
\end{center}
{
\centering
\CYRK\cyra\cyrf\cyre\cyrd\cyrr\cyra\ \@chair
\vspace{6em}
\centering
\textbf{\MakeUppercase{\@reviewtype}\\%[-0.3em]
\@workname}
\vspace{0.3em}
\raggedright
\cyrp\cyro\ \@spectyperod\ \@napravlenie\\
\@studenttitle\ \@course\ \cyrk\cyru\cyrr\cyrs\cyra\ %
\@department\\
\MakeUppercase{\@author}\\
\textbf{\CYRT\cyre\cyrm\cyra\ \cyrr\cyra\cyrb\cyro\cyrt\cyrery:} <<{\MakeUppercase{\@title}}>>
}
\vfill
\noindent
\CYRN\cyra\cyru\cyrch\cyrn\cyrery\cyrishrt\ \cyrr\cyru\cyrk\cyro\cyrv%
\cyro\cyrd\cyri\cyrt\cyre\cyrl\cyrsftsn\\
\signature{\@satitle}{\@saname}\\[14pt]
\CYRZ\cyra\cyrv\cyre\cyrd\cyru\cyryu\cyrshch\cyri\cyrishrt\ \cyrk\cyra%
\cyrf\cyre\cyrd\cyrr\cyro\cyrishrt\\
\signature{\@chtitle}{\@chname}
\vfill
{\centering{\cyr\CYRS\cyra\cyrr\cyra\cyrt\cyro\cyrv\ \@date}
}
\newpage
\begin{center}\bf
C\cyro\cyrd\cyre\cyrr\cyrzh\cyra\cyrn\cyri\cyre\ \cyrr\cyra\cyrb%
\cyro\cyrt\cyrery
\end{center}
}
% --------------------------------------------------------------------------%
% --------------------------------------------------------------------------%
% Referat title page
% --------------------------------------------------------------------------%
\newcommand{\RefMakeTitle}
{
\thispagestyle{empty}
\shapka
\vspace{3cm}
{\centering
\textbf{\MakeUppercase{\@title}}
\\[0.3cm]
{\@workname}
}
\vspace{1.5cm}
\begin{flushleft}
\@studenttitle\ \@course\ \cyrk\cyru\cyrr\cyrs\cyra\ \@group\ \cyrg%
\cyrr\cyru\cyrp\cyrp\cyrery\\
\@spectype\ \@napravlenie\\
\@department\\
\@author
\end{flushleft}
\vfill
\noindent
\CYRP\cyrr\cyro\cyrv\cyre\cyrr\cyri\cyrl\\
\signature{\@satitle}{\@saname}
\vfill
{\centering{\cyr\CYRS\cyra\cyrr\cyra\cyrt\cyro\cyrv\ \@date}
}
\newpage
}
% --------------------------------------------------------------------------%
% --------------------------------------------------------------------------%
% Last page
% --------------------------------------------------------------------------%
\newcommand{\lastpage}
{
\newpage
\thispagestyle{empty}
\vspace*{11cm}
\@worktype\ <<\@title>>\ \@typework\ \cyrm\cyrn\cyro\cyrishrt\ %
\cyrs\cyra\cyrm\cyro\cyrs\cyrt\cyro\cyrya\cyrt\cyre\cyrl\cyrsftsn\cyrn%
\cyro, \cyri\ \cyrn\cyra\ \cyrv\cyrs\cyre\ \cyri\cyrs\cyrt\cyro\cyrch%
\cyrn\cyri\cyrk\cyri, \cyri\cyrm\cyre\cyryu\cyrshch\cyri\cyre\cyrs%
\cyrya\ \cyrv\ \cyrr\cyra\cyrb\cyro\cyrt\cyre, \cyrd\cyra\cyrn\cyrery\ %
\cyrs\cyro\cyro\cyrt\cyrv\cyre\cyrs\cyrt\cyrv\cyru\cyryu\cyrshch\cyri%
\cyre\ \cyrs\cyrs\cyrery\cyrl\cyrk\cyri.\par
\parindent=9cm
\parbox{8cm}{
\begin{flushleft}
\hbox to 6cm{\hbox to 3.5cm{\hrulefill}/\hbox to 3.5cm{\hrulefill}/}
\end{flushleft}
}
}
\AddEnumerateCounter{\Asbuk}{\@Asbuk}{\CYRM}
\AddEnumerateCounter{\asbuk}{\@asbuk}{\cyrm}
% --------------------------------------------------------------------------%
% enumerations
% --------------------------------------------------------------------------%
\setlist{noitemsep}
%\setlist[1]{labelindent=\parindent} % < Usually a good idea
\setlist[itemize]{
%leftmargin=52pt,
rightmargin=0pt,
labelsep=7pt,
labelwidth=20pt,
itemindent=0pt,
listparindent=0pt,
topsep=0pt,%4pt plus 2pt minus 4pt,
partopsep=0pt,% plus 1pt minus 1pt,
parsep=0pt,% plus 1pt,
itemsep=0 pt%\parsep
}
\setlist[enumerate]{
%leftmargin=52pt,
rightmargin=0pt,
labelsep=5pt,
labelwidth=20pt,
itemindent=0pt,
listparindent=0pt,
topsep=0pt,%4pt plus 2pt minus 4pt,
partopsep=0pt,% plus 1pt minus 1pt,
parsep=0pt,% plus 1pt,
itemsep=0pt%\parsep
}
\setlist[itemize,1]{label={\normalfont\bfseries\textemdash}}
%\setlist[enumerate]{labelsep=*, leftmargin=1.5pc}
\setlist[enumerate,1]{label=\arabic*., ref=\arabic*}
\setlist[enumerate,2]{label=\emph{\asbuk*}), ref=\theenumi.\emph{\asbuk*}}
\setlist[enumerate,3]{label=\roman*., ref=\theenumii.\roman*}
\setlist[enumerate,4]{label=\Asbuk*., ref=\theenumiii.\Asbuk*}
%\setlist[description]{font=\sffamily\bfseries}
%%%\renewcommand{\@listI}{%
%%%\leftmargin=52pt
%%%\rightmargin=0pt
%%%\labelsep=7pt
%%%\labelwidth=20pt
%%%\itemindent=0pt
%%%\listparindent=0pt
%%%\topsep=4pt plus 2pt minus 4pt
%%%\partopsep=0pt plus 1pt minus 1pt
%%%\parsep=0pt plus 1pt
%%%\itemsep=\parsep}
%%%\renewcommand\theenumi {\@arabic\c@enumi}
%%%\renewcommand\theenumii {\asbuk{enumii}}
%%%\renewcommand\theenumiii{\@roman\c@enumiii}
%%%\renewcommand\theenumiv {\Asbuk{enumiv}}
%%%\newcommand\atheenumi{\asbuk{enumi}}
%%%\newcommand\atheenumii{\asbuk{enumii}}
%%%\renewcommand\labelenumi {\theenumi.}
%%%\renewcommand\labelenumii {\theenumii.}
%%%\renewcommand\labelenumiii{\theenumiii.}
%%%\renewcommand\labelenumiv {\theenumiv.}
%%%\renewcommand\p@enumii {\theenumi}
%%%\renewcommand\p@enumiii {\theenumi.\theenumii}
%%%\renewcommand\p@enumiv {\p@enumiii.\theenumiii}
%%%\renewcommand\labelitemi {\normalfont\bfseries\textemdash}
%%%\renewcommand\labelitemii {\normalfont\bfseries\textendash}
%%%\renewcommand\labelitemiii{\textperiodcentered}
%%%\renewcommand\labelitemiv {\textasteriskcentered}
%%%
%%%\renewcommand{\@listI}{%
%%%\leftmargin=52pt
%%%\rightmargin=0pt
%%%\labelsep=7pt
%%%\labelwidth=20pt
%%%\itemindent=0pt
%%%\listparindent=0pt
%%%\topsep=4pt plus 2pt minus 4pt
%%%\partopsep=0pt plus 1pt minus 1pt
%%%\parsep=0pt plus 1pt
%%%\itemsep=\parsep}
% --------------------------------------------------------------------------%
% --------------------------------------------------------------------------%
% References
% --------------------------------------------------------------------------%
\makeatletter
\def\@biblabel#1{#1 }
\renewenvironment{thebibliography}[1]
{
\starsection{\cyr\CYRS\CYRP\CYRI\CYRS\CYRO\CYRK\ \CYRI\CYRS\CYRP\CYRO\CYRL%
\CYRSFTSN\CYRZ\CYRO\CYRV\CYRA\CYRN\CYRN\CYRERY\CYRH\ \CYRI\CYRS\CYRT%
\CYRO\CYRCH\CYRN\CYRI\CYRK\CYRO\CYRV}
\list{\@biblabel{\@arabic\c@enumiv}}%
{\settowidth\labelwidth{\@biblabel{#1}}%
\leftmargin\labelwidth
\advance\leftmargin\labelsep
\setlength{\itemsep}{0pt}
\@openbib@code
\usecounter{enumiv}%
\let\p@enumiv\@empty
\renewcommand\theenumiv{\@arabic\c@enumiv}}%
\sloppy
\clubpenalty4000
\@clubpenalty \clubpenalty
\widowpenalty4000%
\sfcode`\.\@m}
{\def\@noitemerr
{\@latex@warning{Empty `thebibliography' environment}}%
\endlist}
\makeatother
% --------------------------------------------------------------------------%

25
tex/dart/flutter.dart Normal file
View File

@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter Demo'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const <Widget>[
Text('Hello World'),
]),
)),
);
}
}

52
tex/dart/graph/bfs.dart Normal file
View File

@ -0,0 +1,52 @@
List<int>? bfsPath(int startDot, int goalDot) {
if (startDot == goalDot) return [startDot];
startDot--;
goalDot--;
List<List<int>>? graph = getLenTable();
List<bool> used = <bool>[];
List<int> dst = <int>[];
List<int> pr = <int>[];
for (int i = 0; i < _amount; i++) {
dst.add(-1);
used.add(false);
pr.add(0);
}
List<int> q = <int>[];
q.add(startDot);
used[startDot] = true;
dst[startDot] = 0;
pr[startDot] = -1; //ó âåðøèíû íåò ïðåäûäóùåé.
while (q.isNotEmpty) {
int cur = q.removeAt(0);
int x = 0;
for (int neighbor in graph![cur]) {
if (neighbor != -1) {
if (!used[x]) {
q.add(x);
used[x] = true;
dst[x] = dst[cur] + 1;
pr[x] = cur; //ñîõðàíåíèå ïðåäûäóùåé âåðøèíû
}
}
x++;
}
}
//Âîññòàíîâëåíèå êðàò÷àéøèåãî ïóòüè
List<int> path = <int>[];
int cur = goalDot;
path.add(cur + 1);
while (pr[cur] != -1) {
cur = pr[cur];
path.add(cur + 1);
}
path = path.reversed.toList();
if (path[0] == (startDot + 1) &&
path[1] == (goalDot + 1) &&
!_dots[startDot].hasConnection(goalDot + 1)) return null;
return path;
}

19
tex/dart/graph/dfs.dart Normal file
View File

@ -0,0 +1,19 @@
List<bool>? dfsIterative(int v) {
v--;
List<bool> label = <bool>[];
for (int i = 0; i < _amount; i++) {
label.add(false);
}
List<int> stack = <int>[];
stack.add(v);
while (stack.isNotEmpty) {
v = stack.removeLast();
if (!label[v]) {
label[v] = true;
for (int i in _dots[v].getL().keys) {
stack.add(i - 1);
}
}
}
return label;
}

View File

@ -0,0 +1,29 @@
List<int?> dijkstra(int from) {
List<int?> d = List<int?>.filled(_amount, intMax);
List<int> p = List<int>.filled(_amount, -1);
d[from - 1] = 0;
List<bool> u = List<bool>.filled(_amount, false);
for (int i = 0; i < _amount; ++i) {
int v = -1;
for (int j = 0; j < _amount; ++j) {
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;
}

6
tex/dart/graph/dot.dart Normal file
View File

@ -0,0 +1,6 @@
class Dot {
//Data
String _name = "";
int num = -1;
Map<int, int> _ln = <int, int>{};
***

View File

@ -0,0 +1,24 @@
Dot([String name = "Undefined", int n = -1]) {
_name = name;
num = n;
_ln = <int, int>{};
}
Dot.fromTwoLists(String name, List<int> num0, List<int> length,
[int n = -1]) {
_name = name;
num = n;
Map<int, int> nw = <int, int>{};
if (num0.length != length.length) {
print("Error in lists");
} else {
for (var i = 0; i < num0.length; i++) {
nw[num0[i]] = length[i];
_ln = nw;
}
}
}
Dot.fromMap(String name, Map<int, int> l, [int n = -1]) {
_name = name;
num = n;
_ln = l;
}

View File

@ -0,0 +1,5 @@
void setName(String n) => _name = n;
void addPath(int inp, int length) => _ln[inp] = length;
void delPath(int n) => _ln.removeWhere((key, value) => key == n);

View File

@ -0,0 +1,6 @@
String _name = "Undefined"; //Èìÿ
int _amount = 0; //Êîëè÷åñòâî âåðøèí
List<Dot> _dots = <Dot>[]; //Ñïèñîê ñìåæíîñòè âåðøèí
Map<int, String> _nameTable = <int, String>{}; //Ñïèñîê âåðøèí ïî èìåíàì
bool _useLength = false; //Âçâåøåííîñòü
bool _oriented = false; //Îðèåíòèðîâàííîñòü

View File

@ -0,0 +1,28 @@
Graphs? kruskal() {
List<LenDotPath> g = getSortedPathList();
//int cost = 0;
List<Dot> res = <Dot>[];
for (int i = 0; i < _amount; i++) {
res.add(Dot(_dots[i].getName(), _dots[i].num));
}
List<int> treeId = List<int>.filled(_amount, 0);
for (int i = 0; i < _amount; ++i) {
treeId[i] = i;
}
for (int i = 0; i < g.length; ++i) {
int a = g[i].d - 1, b = g[i].p - 1;
int l = g[i].l;
if (treeId[a] != treeId[b]) {
//cost += l;
res[a].addPath(b + 1, l);
int oldId = treeId[b], newId = treeId[a];
for (int j = 0; j < _amount; ++j) {
if (treeId[j] == oldId) {
treeId[j] = newId;
}
}
}
}
_dots = res;
return Graphs.fromList(_name, res, _useLength, _oriented);
}

1
tex/dart/hW.dart Normal file
View File

@ -0,0 +1 @@
void main() => print("Hello World");

3
tex/dart/helloWorld.dart Normal file
View File

@ -0,0 +1,3 @@
void main() {
print("Hello World");
}

58
tex/dart/page/build.dart Normal file
View File

@ -0,0 +1,58 @@
@override
Widget build(BuildContext context) {
screenSize = MediaQuery.of(context).size.width;
_textGrNmController.text = graphData.getName();
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Align(
alignment: Alignment.topLeft,
child: Text("Graph name:\n",
style: TextStyle(
fontSize: 18,
color: Colors.white,
))),
toolbarHeight: 110,
flexibleSpace: Container(
color: Colors.green.shade900,
child: Column(children: <Widget>[
/*
Îïèñàíèå ýëåìåíòîâ íà ïàíåëå ïðèëîæåíèÿ
*/
]),
),
actions: [
IconButton(
onPressed: () {
setState(() {
clearDropDownVals();
graphData.flushData();
clearInputData();
});
},
icon: const Icon(Icons.delete_sweep),
iconSize: 60,
),
]),
body: CustomPaint(
painter: CurvePainter(
graphData: graphData,
intListPath: intListPath,
dfsAccessTable: dfsAccessTable,
start: startDot,
end: endDot,
op: op),
child: Align(
alignment: Alignment.topRight,
child: ButtonBar(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
/*
Îïèñàíèå êíîïîê äåéñòâèÿ
*/
],
),
),
),
));
}

14
tex/dart/page/button.dart Normal file
View File

@ -0,0 +1,14 @@
ElevatedButton createButton(String txt, void Function() onPressing) {
return ElevatedButton(
onPressed: onPressing,
style: ButtonStyle(
backgroundColor: MaterialStateProperty.resolveWith<Color>(
(states) => Colors.green.shade700)),
child: Text(txt,
style: const TextStyle(
fontSize: 15,
color: Colors.white70,
height: 1,
)),
);
}

View File

@ -0,0 +1,22 @@
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 = ... // çàäàåòñÿ ñòèëü
var length = sqrt((to.dx - from.dx) * (to.dx - from.dx) +
(to.dy - from.dy) * (to.dy - from.dy));
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);
}

View File

@ -0,0 +1,9 @@
CurvePainter({
Key? key,
required this.graphData,
required this.intListPath,
required this.dfsAccessTable,
required this.start,
required this.end,
required this.op,
});

24
tex/dart/painter/pos.dart Normal file
View File

@ -0,0 +1,24 @@
Map<int, Offset> _getDotPos(int dotsAm, Size size) {
Map<int, Offset> off = <int, Offset>{};
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");
}
}
return off;
}

10
tex/dart/pseudoBFS.txt Normal file
View File

@ -0,0 +1,10 @@
BFS(G, s):
queue Q
s <- visited
Q.add(s)
while (Q.not_empty):
u = Q.pop
for a in u.connections:
if a is unvisited:
Q.add(a)
a <- visited

12
tex/dart/pseudoDFS.txt Normal file
View File

@ -0,0 +1,12 @@
function doDfs(G[n]: Graph):
visited = array[n, false]
function DFS(u: int):
visited[u] = true
for v: (u, v) in G:
if not visited[v]:
DFS(v)
for i = 1 to n:
if not visited[i]:
DFS(i)

View File

@ -0,0 +1,9 @@
dijkstra (G, w, s):
source(G,s)
S <- null
Q <- V[G]
while Q!=null:
u <- Extract_min(Q)
s <- S + u
äë˙ ęŕćäîé âĺđřčíű v čç Adj[u]:
relax(u, v, w)

View File

@ -0,0 +1,10 @@
kruscal(G, w):
A <- null
for v èç V[G]:
create_set(v)
sort(E)
for (u, v) èç E:
if find_set(u) != find_set(v):
A <- A + {(u,v)}
return A

491
tex/example-work.tex Normal file
View File

@ -0,0 +1,491 @@
\documentclass[bachelor, och, coursework]{SCWorks}
% параметр - тип обучения - одно из значений:
% spec - специальность
% bachelor - бакалавриат (по умолчанию)
% master - магистратура
% параметр - форма обучения - одно из значений:
% och - очное (по умолчанию)
% zaoch - заочное
% параметр - тип работы - одно из значений:
% referat - реферат
% coursework - курсовая работа (по умолчанию)
% diploma - дипломная работа
% pract - отчет по практике
% pract - отчет о научно-исследовательской работе
% autoref - автореферат выпускной работы
% assignment - задание на выпускную квалификационную работу
% review - отзыв руководителя
% critique - рецензия на выпускную работу
% параметр - включение шрифта
% times - включение шрифта Times New Roman (если установлен)
% по умолчанию выключен
\usepackage[T2A]{fontenc}
\usepackage[cp1251]{inputenc}
\usepackage{graphicx}
\usepackage{minted}
\usepackage{float}
\usepackage[sort,compress]{cite}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{amsthm}
\usepackage{fancyvrb}
\usepackage{longtable}
\usepackage{array}
\usepackage[english,russian]{babel}
\usepackage[colorlinks=true]{hyperref}
\usepackage{caption}
\captionsetup[figure]{font=normalsize,labelfont=normalsize}
%\newcommand{\eqdef}{\stackrel {\rm def}{=}}
%\newtheorem{lem}{Лемма}
\begin{document}
% Кафедра (в родительном падеже)
\chair{дискретной математики и информационных технологий}
% Тема работы
\title{Создание приложения для отрисовки графов и алгоритмов для работы с ними}
% Курс
\course{2}
% Группа
\group{221}
% Факультет (в родительном падеже) (по умолчанию "факультета КНиИТ")
%\department{факультета КНиИТ}
% Специальность/направление код - наименование
%\napravlenie{02.03.02 "--- Фундаментальная информатика и информационные технологии}
%\napravlenie{02.03.01 "--- Математическое обеспечение и администрирование информационных систем}
\napravlenie{09.03.01 "--- Информатика и вычислительная техника}
%\napravlenie{09.03.04 "--- Программная инженерия}
%\napravlenie{10.05.01 "--- Компьютерная безопасность}
% Для студентки. Для работы студента следующая команда не нужна.
%\studenttitle{Студентки}
% Фамилия, имя, отчество в родительном падеже
\author{Морозова Андрея Денисовича}
% Заведующий кафедрой
\chtitle{к.\,ф.-м.\,н., доцент} % степень, звание
\chname{Л.\,Б.\,Тяпаев}
%Научный руководитель (для реферата преподаватель проверяющий работу)
\satitle{доцент каф. ДМиИТ} %должность, степень, звание
\saname{О.\,В.\,Мещерякова}
% Семестр (только для практики, для остальных
% типов работ не используется)
\term{2}
% Год выполнения отчета
\date{2021}
\maketitle
% Включение нумерации рисунков, формул и таблиц по разделам
% (по умолчанию - нумерация сквозная)
% (допускается оба вида нумерации)
%\secNumbering
\tableofcontents
% Раздел "Обозначения и сокращения". Может отсутствовать в работе
%\abbreviations
%\begin{description}
% \item SQL "--- англ. structured query language — «язык структурированных запросов;
% \item $\det B$ "--- определитель матрицы $B$;
% \item ИНС "--- Искусственная нейронная сеть;
% \item FANN "--- Feedforward Artifitial Neural Network
%\end{description}
% Раздел "Определения". Может отсутствовать в работе
%\definitions
% Раздел "Определения, обозначения и сокращения". Может отсутствовать в работе.
% Если присутствует, то заменяет собой разделы "Обозначения и сокращения" и "Определения"
%\defabbr
% Раздел "Введение"
\intro
Целью настоящей работы является изучение работы фреймворка для кроссплатформенной разработки ''Flutter'' и разработка приложения для создания графов и взаимодействия с ними.
Поставлены задачи:
\begin{itemize}
\item разбор алгоритмов на графах;
\item разбор работы с Flutter;
\item построение приложения.
\end{itemize}
\section{Введение}
\subsection{Графы}
Граф --- математический объект, состоящий из двух множеств. Одно из
них --- любое конечное множество, его элементы называются \textit{вершинами}
графа. Другое множество состоит из пар вершин, эти пары называются
\textit{ребрами} графа~\cite{IITMMM_2017}.
\subsection{Основные определения}
\textbf{Ориентированный граф} определяется как пара \textit{(V, E)}, где \textit{V} --- конечное множество, а \textit{E} --- бинарное отношение на \textit{V}, т.~е. подмножество множества ${V \times V}$. Ориентированный граф для краткости называют \textbf{орграфом}. Множество $V$ называют \textbf{множеством вершин графа}, а его элемент называют \textbf{вершиной} графа. Множество $E$ называют \textbf{множеством рёбер}, а его элементы называют \textbf{рёбрами}. Граф может содержать \textbf{рёбра-циклы}, соединяющие вершину с собой. На рисунке~\ref{fig:orgrapf_example} изображен ориентированный граф с множеством вершин \{0, 1, 2, 3, 4\}.~\cite{Algo_2013}
\begin{figure}[!ht]
\centering
\includegraphics[width=9cm]{./pic/orgraph.png}
\caption{\label{fig:orgrapf_example}
Пример орграфа}
\end{figure}
В \textbf{неориентированном} графе $G = (V, E)$ множество ребер состоит из \textbf{неупорядоченных} пар вершин: парами являются множества $\{u, v\}$, где $u, v \in V$ и $u \neq v$. Для неориентированного графа $\{u, v\}$ и $\{v, u\}$ обозначают одно и то же ребро. Неориентированный граф не может содержать рёбер-циклов, и каждое ребро состоит из двух различных вершин. На рисунке~\ref{fig:grapf_example} изображен неориентированный граф с множеством вершин \{0, 1, 2, 2, 4\}
\begin{figure}[!ht]
\centering
\includegraphics[width=9cm]{./pic/graph.png}
\caption{\normalsize\label{fig:grapf_example}
Пример неориентированного графа}
\end{figure}
Вершина \textit{v} \textbf{смежна} с вершиной \textit{u}, если в графе имеется ребро $\{u, v\}$. Для неориентированных графов отношение смежности является симметричным, но для ориентированных графов это не обязательно.
\textbf{Степенью} вершины в неориентированном графе называется число инцидентных ей рёбер. Для ориентированного графа различают исходящую степень, определяемую как число выходящих из неё рёбер, и входящую степень, определяемую как число входящих в неё рёбер. Сумма исходящей и входящей степеней называется степенью вершины.
\textbf{Маршрутом} в графе называется конечная чередующаяся последовательность смежных вершин и ребер, соединяющих эти вершины.
Маршрут называется открытым, если его начальная и конечная вершины различны, в противном случае он называется замкнутым.
Маршрут называется \textbf{цепью}, если все его ребра различны. Открытая цепь называется \textbf{путем}, если все ее вершины различны.
Замкнутая цепь называется \textbf{циклом}, если различны все ее вершины, за исключением концевых.
Граф называется \textbf{связным}, если для любой пары вершин существует соединяющий их путь.~\cite{intuit}
\subsection{Представление графа}
Граф можно описать несколькими способами.
\begin{enumerate}
\item Перечисление элементов:~\ref{ref:list}.
\item Матрица смежности:~\ref{ref:smej}.
\item Матрица инцидентности:~\ref{ref:incident}.
\item Список смежности:~\ref{ref:spisok}.
\end{enumerate}
Рассмотрим на примере графа на рисунке~\ref{fig:graph_prim1}.
\begin{figure}
\centering
\includegraphics[width=9cm]{./pic/prim1.png}
\caption{\label{fig:graph_prim1}
Рассматриваемый граф}
\end{figure}
\subsubsection{Перечисление элементов}\label{ref:list}
Исходя из определения, для того, чтобы задать граф, достаточно перечислить его вершины и ребра (т.е. пары вершин).
Пример:
$$ V = \{a, b, c, d, e\} $$
$$ E = \{(a, b), (a, c), (a, e), (b, c), (b, d), (c, e), (d, e)\}$$
\subsubsection{Матрица смежности}\label{ref:smej}
Пусть $G$ --- граф с $n$ вершинами, пронумерованными числами от 1 до $n$. \textbf{Матрица смежности} --- это таблица с $n$ строками и $n$ столбцами, в которой элемент, стоящий на пересечении строки с номером $i$ и столбца с номером $j$, равен 1, если вершины с номерами $i$ и $j$ смежны, и 0 в противоположном случае.
\begin{table}[!ht]
%\centering
\caption{Пример матрицы смежности}
\begin{tabular}{|l|c|c|c|c|}
\hline 0 & 1 & 1 & 0 & 1\\
\hline 1 & 0 & 1 & 1 & 0\\
\hline 1 & 1 & 0 & 0 & 1\\
\hline 0 & 1 & 0 & 0 & 1\\
\hline 1 & 0 & 1 & 1 & 0\\
\hline
\end{tabular}
\end{table}
\subsubsection{Матрица инцидентности}\label{ref:incident}
Пусть $G$ --- граф с вершинами, пронумерованными числами от 1 до $n$, и ребрами, пронумерованными от 1 до $m$. В матрице инцидентности строки соответствуют вершинам, а столбцы рёбрам. На пересечении строки с номером $i$ и столбца с номером $j$ стоит 1, если вершина с номером $i$ инцидентна ребру с номером $j$, и 0 в противном случае.
\begin{table}[!ht]
%\centering
\caption{Пример матрицы инцидентности}
\begin{tabular}{|c|c|c|c|c|c|c|}
\hline 1 & 1 & 1 & 0 & 0 & 0 & 0\\
\hline 1 & 0 & 0 & 1 & 1 & 0 & 0\\
\hline 0 & 1 & 0 & 1 & 0 & 1 & 0\\
\hline 0 & 0 & 0 & 0 & 1 & 0 & 1\\
\hline 0 & 0 & 1 & 0 & 0 & 1 & 1\\
\hline
\end{tabular}
\end{table}
\subsubsection{Списки смежности}\label{ref:spisok}
Списки смежности часто используются для компьютерного представления графов. Для каждой вершины задается список всех смежных с ней вершин. В структурах данных, применяемых в программировании, списки смежности могут быть реализованы как массив линейных списков.
Пример:
\begin{itemize}
\item[1] : 2, 3, 5
\item[2] : 1, 3, 4
\item[3] : 1, 2, 5
\item[4] : 2, 5
\item[5] : 1, 3, 4
\end{itemize}
\subsection{Алгоритмы}
\subsubsection{Обход в ширину}
Обход в ширину --- один из основных алгоритмов на графах.
Пусть задан граф $G = (V, E)$ и выделена исходная вершина $s$. Алгоритм поиска в ширину систематически обходит все ребра $G$ для «открытия» всех вершин, достижимых из $s$, вычисляя минимальное количество рёбер от $s$ s до каждой достижимой из $s$ вершины.
Поиск в ширину имеет такое название потому, что в процессе обхода мы идём вширь, то есть перед тем как приступить к поиску вершин на расстоянии $k + 1$, выполняется обход вершин на расстоянии $k$~\cite{Algo_2013}.
Приведенная ниже процедура поиска в ширину BFS предполагает, что входной граф $G = (V, E)$ представлен при помощи списков смежности. Псевдокод алгоритма:
\inputminted[fontsize=\footnotesize, linenos]{python}{./dart/pseudoBFS.txt}
Воспользуемся групповым анализом. Операции внесения в очередь и удаление из нее требует $O(1)$ времени. Следовательно на очередь потребуется $O(V)$ времени. Т.к. максимальная длина списков смежности $\theta(E)$, то время, необходимое для сканирования списков, равно $O(E)$. Расходы на инициализацию: $O(V)$. Таким образом, общее время работы алгоритма: $O(V + E)$. Время поиска в ширину линейно зависит от размера представления графа.
\subsubsection{Обход в глубину}
Стратегия обхода в ширину состоит в том, чтобы идти ''вглубь'' графа. Сначала исследуются ребра, выходящие из вершины, открытой последней, и покидаем вершину, когда не остается неисследованных ребер --- при этом происходит возврат в вершину, из которой была открыта вершина $v$. Процесс продолжается, пока не будут открыты все вершины, достижимые из исходной.
Псевдо код алгоритма:
\inputminted[fontsize=\footnotesize, linenos]{python}{./dart/pseudoDFS.txt}
\subsubsection{Алгоритм Дейкстры}
Алгоритм был предложен голландским исследователем Эдсгером Дейкстрой в 1959 году. Используется для поиска кратчайшего пути от одной вершины до всех остальных. Используется только если вес ребер неотрицательный.
В алгоритме поддерживается множество вершин $U$, для которых уже вычислены длины кратчайших путей до них из $s$. На каждой итерации основного цикла выбирается вершина, которой на текущий момент соответствует минимальная оценка кратчайшего пути. Вершина $u$ добавляется в множество $U$ и производится релаксация всех исходящих из неё рёбер.
Псевдокод алгоритма:
\inputminted[fontsize=\footnotesize, linenos]{python}{./dart/pseudoDijkstra.txt}
\subsubsection{Алгоритм Краскала}
Алгоритм Краскала — алгоритм поиска минимального остовного дерева во взвешенном неориентированном связном графе. Был описан Джозефом Краскалом в 1956 году.
Алгоритм Краскала изначально помещает каждую вершину в своё дерево, а затем постепенно объединяет эти деревья, объединяя на каждой итерации два некоторых дерева некоторым ребром. Перед началом выполнения алгоритма, все рёбра сортируются по весу (в порядке неубывания). Затем начинается процесс объединения: перебираются все рёбра от первого до последнего (в порядке сортировки), и если у текущего ребра его концы принадлежат разным поддеревьям, то эти поддеревья объединяются, а ребро добавляется к ответу. По окончании перебора всех рёбер, все вершины окажутся принадлежащими одному поддереву.\cite{krusc}
\inputminted[fontsize=\footnotesize, linenos]{python}{./dart/pseudoKruscal.txt}
\section{Инструменты}
Рассмотрим используемый язык и библиотеку для отрисовки.
\subsection{Dart}
В качестве основы используется язык \textbf{Dart}, разработанный компанией Google, и широко используемый для кросс-платформенной разработки~\cite{dart_web}.
Dart позиционируется в качестве замены/альтернативы JavaScript. Один из разработчиков языка Марк Миллер (Mark S. Miller) писал, что JavaScript «имеет фундаментальные изъяны»\cite{futureOfJavascript}, которые невозможно исправить.
Версия 1.0 вышла в 14 ноября 2013 года.
Вторая версия была выпущена в августе 2018 года. В языке появилась надежная система типов, т.~е. во время выполнении программы все переменные будут гарантированно указанному типу~\cite{dart_sound}.
В Dart используется сборщик мусора. Синтаксис похож на языки: JavaScript, C\#, Java~\cite{dartInAction}.
Пример HelloWorld на Dart:
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/helloWorld.dart}
Dart поддерживает сокращенную запись. Код примера HelloWorld можно записать так:
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/hW.dart}
Концепты языка~\cite{dart_tour}:
\begin{itemize}
\item Все, что можно поместить в переменную, является объектом, а каждый объект является частью класса.
\item Dart -- язык со строгой типизацией, но объявления типов опциональны, т.к. поддерживается определение типа при компиляции.
\item В версии языка 2.12 появилась Null безопасность. Каждый объект не может быть Null, если не указано обратное.
\item Поддерживаются дженерики.
\item Для объявления локальных функций и переменных необходимо начать имя со знака ''\_''.
\end{itemize}
.
\subsection{Flutter}
Для отрисовки информации используется фреймворк c открытым исходным кодом ''Flutter'', разработанный компанией Google.
Flutter не использует нативные компоненты для отрисовки интерфейса. В его основе лежит графический движок ''Skia'', написанный преимущественно на С++.
Skia --- библиотека для работы с 2D графикой с открытым исходным кодом, поддерживающая работу на большом количестве платформ~\cite{skia_docs}
Первая версия выпущена в 2015 году под названием ''Sky'', работала только для Android-приложений. Основная заявленная особенность --- высокая графическая производительность (возможность отображения 120 кадров в секунду). Полная поддержка создания веб-приложений появилась в версии 2.0 (март 2021 года), также разрабатывается поддержка создания настольных приложений для Windows, macOS и Linux и Google Fuchsia.
Простейший пример приложения с использованием Flutter:
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/flutter.dart}
Из кода видно, что интерфейс описывается с помощью виджетов.
Результат приложения изображен на рисунке~\ref{fig:flutter_example}.
\newpage
\begin{figure}[!ht]
\centering
\includegraphics[width=11cm]{./pic/flutter0.png}
\caption{\label{fig:flutter_example}
Простейшее приложение на Flutter}
\end{figure}
\section{Реализация}
Структура программы разбита на 4 файла:
\begin{itemize}
\item \textbf{main.dart} --- точка входа в программу;
\item \textbf{drawing\_page.dart} --- страница с описанием работы кнопок;
\item \textbf{curve\_painter.dart} --- функционал для отрисовки графа;
\item \textbf{graph.dart} --- класс для хранения графа и манипуляции с ним.
\end{itemize}
\subsection{Графы}
Информация хранится в виде списков смежности.
Полный код можно посмотреть в приложении~\ref{graph}.
\subsubsection{Класс для хранения вершины}
Класс \textbf{Dot} используется для хранения информации о ребрах, исходящих из данной вершины.
Основная информация:
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/dot.dart}
\textit{String \_name} используется для хранения имени, \textit{int num} используется для хранения номера вершины.
Информация о ребрах задается в виде \textit{Map<int, int> \_ln}, где каждой вершине можно присвоить вес ребра.
Создать вершину можно тремя способами:
\begin{enumerate}
\item пустая точка;
\item из двух списков, где в первом список вершин, а во втором - длины пути;
\item из \textit{Map<int, int>}.
\end{enumerate}
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/dot_constr.dart}
Для манипуляций с информацией доступно:
\begin{enumerate}
\item изменение имени;
\item добавление пути;
\item удаление пути.
\end{enumerate}
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/dot_manip.dart}
\subsubsection{Класс для графов}
Класс \textbf{Graphs} используется для хранения точек и манипуляции с ними.
Основная информация:
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/graph.dart}
\textit{String \_name} исользуется для имени графа, \textit{int \_amount} --- количество вершин, \textit{List<Dot> \_dots} --- массив точек, \textit{Map<int, String> \_nameTable} --- отслеживание имени каждой вершины, \textit{bool \_useLength} --- отслеживание взвешенности графа, \textit{bool \_oriented} --- отслеживание ориентированности графа.
Для работы графом доступны функции:
\begin{enumerate}
\item \textbf{String? addDot(Dot a)} --- добавление вершины;
\item \textbf{String? addIsolated(String name)} --- добавление изолированной точки;
\item \textbf{String? addPath(int from, int to, [int len = 0])} --- добавление пути;
\item \textbf{String? delPath(int from, int to)} --- удаление пути;
\item \textbf{String? delDot(int inn)} --- удаление вершины;
\item \textbf{void flushData()} --- удаление информации из графа;
\item \textbf{bool checkDots([bool verbose = false])} --- проверка графа на ошибки;
\item \textbf{void \_fixAfterDel(int inn}) --- испралвение нумерации после удаления;
\item \textbf{void \_syncNameTable()} --- синхронизация таблицы имен вершин;
\item \textbf{void setName(String name)} --- изменение имени;
\item \textbf{String? replaceDataFromFile(String path)} --- заменяет информацию графа на информацию из файла;
\item \textbf{List<LenDotPath> getSortedPathList()} --- возвращает список всех путей, отсортированный в порядке неубывания;
\item \textbf{void printG()} --- выводит информацию о графе в консоль;
\item \textbf{void printToFile(String name)} --- выводит информацию о графе в файл.
\end{enumerate}
Создать граф можно тремя способами:
\begin{enumerate}
\item пустой граф;
\item из списка смежности;
\item из файла.
\end{enumerate}
\subsubsection{Алгоритмы}
Алгоритмы описаны в классе графа в приложении~\ref{graph} .\newline
Обход в ширину:
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/bfs.dart}
Обход в глубину:
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/dfs.dart}
Алгоритм Дейкстры:
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/dijkstra.dart}
Алгоритм Краскала:
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/graph/kruscal.dart}
\subsubsection{Интерфейс}
Интерфейс описан в файле drawing\_page.dart. Полный код доступен в приложении~\ref{buttons}.
Интерфейс программы изображен на рисунке~\ref{fig:programm}.
\begin{figure}[!ht]
\centering
\includegraphics[width=13cm]{./pic/grafs.png}
\caption{\label{fig:programm}
Интерфейс программы}
\end{figure}
\newpage
В качестве основы выбран виджет, сохраняющий состояние.
Построение интерфейса:
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/page/build.dart}
Запуск работы алгоритмов происходит по нажатию соответствующих кнопок, затем страница отрисовки, используя полученные данные, выводит на экране информацию.
\subsubsection{Отрисовка графа}
Отрисовка элементов графа описана в файле curve\_painter.dart. Код доступен в приложении~\ref{painter}.
Для отрисовки информации используется виджет CustomPaint~\cite{custompaint}.
Для передачи информации графа используется конструктор CurvePainter.
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/painter/constr.dart}
Отрисовка соединений происходит с помощью пакета ''arrow\_path''.
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/painter/arrow.dart}
Для упрощения отрисовки, точки располагаются на окружности с центом, совпадающим с центром экрана. Сначала вычисляется их местоположение на экране, точка с максимальным количеством соединений располагается в центре:
\inputminted[fontsize=\footnotesize, linenos]{dart}{./dart/painter/pos.dart}
Процесс вывода на экран:
\begin{enumerate}
\item вывод вершин на экран;
\item определение текущей операции и вывод работы текущего алгоритма;
\item вывод ребер;
\item вывод длин ребер;
\item вывод имен вершин.
\end{enumerate}
\subsubsection{Итоговый вид приложения}
Итоговый вид приложения отображен на картинке~\ref{fig:result}
\begin{figure}[!ht]
\centering
\includegraphics[width=15cm]{./pic/result.png}
\caption{\label{fig:result}
Интерфейс программы}
\end{figure}
\newpage
% Раздел "Заключение"
\conclusion
Было разработано простое приложение для создания и работы с графами с использованием Dart и Flutter.
В приложении возможно с помощью графического интерфейса:
\begin{itemize}
\item создать пустой граф;
\item добавить вершину;
\item удалить вершину;
\item добавить/изменить путь;
\item удалить путь;
\item сохранить граф;
\item загрузить граф;
\item построить минимальное остовное дерево с помощью алгоритма Краскала;
\item найти минимальный путь из выбранной вершины в другие с помощью алгоритма Дейкстры;
\item проверить доступность вершин с помощью обхода в глубину;
\item построить путь из одной вершины в другую с помощью обхода в ширину.
\end{itemize}
%Библиографический список, составленный с помощью BibTeX
%
\bibliographystyle{gost780uv}
\bibliography{thesis}
% Окончание основного документа и начало приложений
% Каждая последующая секция документа будет являться приложением
\appendix
\section{Код main.dart}\label{mainD}
Точка входа в программу.
\inputminted[fontsize=\footnotesize, linenos]{dart}{./lib/main.dart}
\section{Код страницы отрисовки}\label{buttons}
Описание интерфейса и базовые функции для взаимодействия с информацией.
\inputminted[fontsize=\footnotesize, linenos]{dart}{./lib/pages/drawing_page.dart}
\section{Код отрисовки графа}\label{painter}
Вывод на экран информации из графа.
\inputminted[fontsize=\footnotesize, linenos]{dart}{./lib/src/curve_painter.dart}
\section{Код класса для работы с графом}\label{graph}
Основной класс для хранения и взаимодействия с информацией.
\inputminted[fontsize=\footnotesize, linenos]{dart}{./lib/src/graph.dart}
\noindent
\end{document}

BIN
tex/example/ex1.7z Normal file

Binary file not shown.

BIN
tex/fig2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

1408
tex/gost780uv.bst Normal file

File diff suppressed because it is too large Load Diff

24
tex/lib/main.dart Normal file
View File

@ -0,0 +1,24 @@
import 'package:graphs/pages/drawing_page.dart';
import 'package:desktop_window/desktop_window.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
Future setupWindow() async {
await DesktopWindow.setMinWindowSize(const Size(850, 700));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
setupWindow();
return const MaterialApp(
title: "Graphs",
home: DrawingPage(),
);
}
}

View File

@ -0,0 +1,508 @@
import 'package:graphs/src/graph.dart';
import 'package:graphs/src/curve_painter.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
Graphs getGraph() {
List<Dot> d = <Dot>[];
d.add(Dot.fromTwoLists("1", [2], [3]));
d.add(Dot.fromTwoLists("2", [3], [1]));
d.add(Dot.fromTwoLists("3", [1], [2]));
return Graphs.fromList("Èìÿ ãðàôà", d, true, true);
}
class DrawingPage extends StatefulWidget {
const DrawingPage({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() => _DrawingPageState();
}
class _DrawingPageState extends State<DrawingPage> {
double screenSize = 0;
Graphs graphData = getGraph();
List<int?>? intListPath;
List<bool>? dfsAccessTable;
String op = Operations.none;
int? startDot;
int? endDot;
String? dropdownValue1;
String? dropdownValue2;
String currOp = "";
final _textNameController = TextEditingController();
final _textLnthController = TextEditingController();
final _textGrNmController = TextEditingController();
void clearInputData() {
setState(() {
_textLnthController.clear();
_textNameController.clear();
dropdownValue1 = null;
dropdownValue2 = null;
});
}
void clearDropDownVals() {
setState(() {
startDot = null;
intListPath = null;
dfsAccessTable = null;
endDot = null;
currOp = "";
op = Operations.none;
});
}
// *************buttons*************
ElevatedButton createButton(String txt, void Function() onPressing) {
return ElevatedButton(
onPressed: onPressing,
style: ButtonStyle(
backgroundColor: MaterialStateProperty.resolveWith<Color>(
(states) => Colors.green.shade700)),
child: Text(txt,
style: const TextStyle(
fontSize: 15,
color: Colors.white70,
height: 1,
)),
);
}
void showPopUp(String alertTitle, String err) => showDialog<String>(
context: context,
builder: (BuildContext context) => AlertDialog(
title: Text(alertTitle, style: const TextStyle(fontSize: 26)),
content: Text(
err,
style: const TextStyle(fontSize: 18),
),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, 'OK'),
child: const Text('OK'),
),
],
),
);
// *************buttons*************
// ***addSpace***
SizedBox addSpaceH(double h) {
return SizedBox(height: h);
}
SizedBox addSpaceW(double w) {
return SizedBox(width: w);
}
// ***addSpace***
// *************inputs*************
Container createInputBox(String text, double width, IconData? icon,
TextEditingController? controller) {
if (icon == null) {
return Container(
width: width,
height: 40,
margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 5),
child: TextField(
controller: controller,
textAlign: TextAlign.center,
onChanged: (name) => graphData.setName(name),
decoration: InputDecoration(
contentPadding:
const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
filled: true,
fillColor: Colors.white,
//prefixIcon: Icon(icon, color: Colors.black),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(40))),
hintStyle: const TextStyle(color: Colors.black38),
hintText: text),
));
}
return Container(
width: width,
height: 40,
margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 5),
child: TextField(
controller: controller,
textAlign: TextAlign.center,
decoration: InputDecoration(
contentPadding:
const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
filled: true,
fillColor: Colors.white,
prefixIcon: Icon(icon, color: Colors.black),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(40))),
hintStyle: const TextStyle(color: Colors.black38),
hintText: text),
));
}
SizedBox dropList1(double width) {
var button = DropdownButton(
hint: const Text(
'Select dot',
style: TextStyle(color: Colors.white, fontSize: 13),
),
alignment: AlignmentDirectional.bottomEnd,
value: dropdownValue1,
isDense: true,
borderRadius: const BorderRadius.all(Radius.circular(20)),
dropdownColor: Colors.green.shade800,
style: const TextStyle(
color: Colors.white,
fontSize: 18,
),
onChanged: (String? newValue) {
setState(() {
dropdownValue1 = newValue;
});
},
items: graphData.getDots().map((location) {
return DropdownMenuItem(
child: Text(location.getName()),
value: location.num.toString(),
);
}).toList(),
);
return SizedBox(
child: button,
width: width,
);
}
SizedBox dropList2(double width) {
var button = DropdownButton(
hint: const Text(
'Select Dot',
style: TextStyle(color: Colors.white, fontSize: 13),
),
alignment: AlignmentDirectional.centerEnd,
value: dropdownValue2,
isDense: true,
borderRadius: const BorderRadius.all(Radius.circular(20)),
dropdownColor: Colors.green.shade800,
style: const TextStyle(
color: Colors.white,
fontSize: 18,
),
onChanged: (String? newValue) {
setState(() {
dropdownValue2 = newValue;
});
},
items: graphData.getDots().map((location) {
return DropdownMenuItem(
child: Text(location.getName()),
value: location.num.toString(),
);
}).toList(),
);
return SizedBox(
child: button,
width: width,
);
}
// *************inputs*************
//*********ButtonsFunctions*********
void addDotPushed() {
clearDropDownVals();
if (_textNameController.text == "") {
showPopUp("Error", "No name in \"Dot name\" box");
} else {
setState(() {
String? res = graphData.addIsolated(_textNameController.text);
if (res != null) {
showPopUp("Error", res);
}
});
}
clearInputData();
}
void addPathPushed() {
clearDropDownVals();
if (dropdownValue1 == null) {
showPopUp("Error", "No dot in first box selected");
} else if (dropdownValue2 == null) {
showPopUp("Error", "No dot in second box selected");
} else if (_textLnthController.text == "" && graphData.getUseLengthBool()) {
showPopUp("Error", "No length in \"Input length\" box");
} else {
int? from = int.parse(dropdownValue1!);
int? to = int.parse(dropdownValue2!);
int? len = int.tryParse(_textLnthController.text);
if (len == null && graphData.getUseLengthBool()) {
showPopUp("Error",
"Can't parse input.\nInts only allowed in \"Input length\"");
} else {
len ??= 0;
setState(() {
String? res = graphData.addPath(from, to, len!);
if (res != null) showPopUp("Error", res);
});
}
}
clearInputData();
}
void changeOriented() {
setState(() {
String? res = graphData.flipUseOrientation();
if (res != null) showPopUp("Error", res);
});
}
void changeLength() {
setState(() {
String? res = graphData.flipUseLength();
if (res != null) showPopUp("Error", res);
});
}
void delPathPushed() {
clearDropDownVals();
if (dropdownValue1 == null) {
showPopUp("Error", "No dot in first box selected");
} else if (dropdownValue2 == null) {
showPopUp("Error", "No dot in second box selected");
} else {
int? from = int.tryParse(dropdownValue1!);
int? to = int.tryParse(dropdownValue2!);
if (from == null || to == null) {
showPopUp("Error",
"Can't parse input.\nInts only allowed in \"Dot number\" and \"Destination number\"");
} else {
setState(() {
String? res = graphData.delPath(from, to);
if (res != null) {
showPopUp("Error", res);
}
});
}
}
clearInputData();
}
void delDotPushed() {
if (dropdownValue1 != null) {
setState(() {
String? res = graphData.delDot(int.parse(dropdownValue1!));
if (res != null) {
showPopUp("Error", res);
}
});
} else {
showPopUp("Error", "Nothing in input");
}
clearDropDownVals();
clearInputData();
}
void fileOpener() async {
FilePickerResult? result =
await FilePicker.platform.pickFiles(allowedExtensions: ["txt"]);
if (result != null) {
if (!result.files.single.path!.endsWith(".txt")) {
showPopUp("Error", "Can open only \".txt\" files");
} else {
setState(() {
String? res =
graphData.replaceDataFromFile(result.files.single.path!);
if (res != null) showPopUp("Error", res);
});
}
} else {
showPopUp("Error", "No file selected");
}
clearDropDownVals();
clearInputData();
}
void fileSaver() async {
String? outputFile = await FilePicker.platform.saveFile(
dialogTitle: 'Please select an output file:',
fileName: 'output-file.txt',
allowedExtensions: ["txt"]);
if (outputFile == null) {
showPopUp("Error", "Save cancelled");
} else {
if (!outputFile.endsWith(".txt")) {
outputFile += ".txt";
}
graphData.printToFile(outputFile);
}
clearDropDownVals();
clearInputData();
}
void bfsPushed() {
clearDropDownVals();
if (dropdownValue1 == null) {
showPopUp("Error", "No dot in first box selected");
} else if (dropdownValue2 == null) {
showPopUp("Error", "No dot in second box selected");
} else {
setState(() {
startDot = int.parse(dropdownValue1!);
endDot = int.parse(dropdownValue2!);
currOp = "OP: BFS from $startDot to $endDot";
op = Operations.bfs;
intListPath = graphData.bfsPath(startDot!, endDot!);
});
if (intListPath == null) {
showPopUp("Info", "There is no path");
}
}
clearInputData();
}
void dfsPushed() {
clearDropDownVals();
if (dropdownValue1 == null) {
showPopUp("Error", "No dot in first box selected");
} else {
setState(() {
startDot = int.parse(dropdownValue1!);
op = Operations.dfs;
currOp = "OP: DFS from $startDot";
dfsAccessTable = graphData.dfsIterative(startDot!);
});
if (dfsAccessTable == null) {
showPopUp("Err", "report this error.");
}
}
clearInputData();
}
void dijkstraPushed() {
clearDropDownVals();
if (dropdownValue1 == null) {
showPopUp("Error", "No number in \"Dot number\" box");
} else {
setState(() {
startDot = int.parse(dropdownValue1!);
currOp = "OP: Dijkstra from $startDot";
op = Operations.dijkstra;
intListPath = graphData.dijkstra(startDot!);
});
if (intListPath == null) {
showPopUp("Err", "report this error.");
}
}
clearInputData();
}
//*********ButtonsFunctions*********
// build
@override
Widget build(BuildContext context) {
screenSize = MediaQuery.of(context).size.width;
_textGrNmController.text = graphData.getName();
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Align(
alignment: Alignment.topLeft,
child: Text("Graph name:\n",
style: TextStyle(
fontSize: 18,
color: Colors.white,
))),
toolbarHeight: 110,
flexibleSpace: Container(
color: Colors.green.shade900,
child: Column(children: <Widget>[
const SizedBox(height: 5),
Row(children: [
addSpaceW(screenSize / 8 + 19),
createButton("\nAdd dot\n", addDotPushed),
createInputBox("Dot name", screenSize / 4 - 25, Icons.label,
_textNameController),
addSpaceW(8),
createButton("\nAdd path\n", addPathPushed),
createInputBox("Input length", screenSize / 4 - 25,
Icons.arrow_right_alt_outlined, _textLnthController),
]),
addSpaceH(3),
Row(children: [
addSpaceW(6),
createInputBox(
"Name", screenSize / 8 - 25, null, _textGrNmController),
createButton("\nDel dot \n", delDotPushed),
addSpaceW(54),
dropList1(screenSize / 4 - 80),
addSpaceW(53),
createButton("\nDel path\n", delPathPushed),
addSpaceW(54),
dropList2(screenSize / 4 - 80),
]),
]),
),
actions: [
IconButton(
onPressed: () {
setState(() {
clearDropDownVals();
graphData.flushData();
clearInputData();
});
},
icon: const Icon(Icons.delete_sweep),
iconSize: 60,
),
]),
body: CustomPaint(
painter: CurvePainter(
graphData: graphData,
intListPath: intListPath,
dfsAccessTable: dfsAccessTable,
start: startDot,
end: endDot,
op: op),
child: Align(
alignment: Alignment.topRight,
child: ButtonBar(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
currOp,
style: const TextStyle(fontSize: 14, color: Colors.black),
),
createButton("Bfs", bfsPushed),
createButton("Dfs", dfsPushed),
createButton("Dijkstra", dijkstraPushed),
createButton("Kruskal", () {
clearDropDownVals();
setState(() {
currOp = "OP: Kruscal algo";
graphData.kruskal();
});
clearInputData();
}),
createButton("Clear OP", () {
clearDropDownVals();
clearInputData();
}),
createButton(graphData.getUseLengthStr(), changeLength),
createButton(graphData.getDoubleSidedStr(), changeOriented),
createButton("Save to file", fileSaver),
createButton("Load from file", fileOpener),
],
),
),
),
));
}
}

View File

@ -0,0 +1,327 @@
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<int?>? intListPath;
List<bool>? 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<int, Offset> _off = <int, Offset>{};
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<int, Offset> _getDotPos(int dotsAm, Size size) {
Map<int, Offset> off = <int, Offset>{};
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");
}
}
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;
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<Dot> dots, Map<int, Offset> 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: <20>${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]!);
}
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) {
_drawDotNames(
canvas, _off[i]!, "${graphData.getDots()[i - 1].getName()}:[$i]");
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}

685
tex/lib/src/graph.dart Normal file
View File

@ -0,0 +1,685 @@
import 'dart:io';
class Separators {
static const String dotToConnections = ": ";
static const String dotToLength = "|";
static const String space = " ";
static const String hasLength = "Âçâåøåííûé";
static const String hasNoLength = "ÍåÂçâåøåííûé";
static const String isOriented = "Îðèåíòèðîâàííûé";
static const String isNotOriented = "ÍåÎðèåíòèðîâàííûé";
static const String nL = "\n";
static const String end = "END";
Separators();
}
class Dot {
//Data
// ignore: prefer_final_fields
String _name = "";
int num = -1;
Map<int, int> _ln = <int, int>{};
//****Get****
String getName() => _name;
bool hasConnection(int n) => _ln.containsKey(n);
Map<int, int> getL() => _ln;
int getLength(int x) {
if (hasConnection(x)) {
return _ln[x]!;
}
return -1;
}
//****Get****
//Set
void setName(String n) => _name = n;
//Add
void addPath(int inp, int length) => _ln[inp] = length;
//Del
void delPath(int n) => _ln.removeWhere((key, value) =>
key == n); // óäàëèòü îáðàòíûé ïóòü åñëè íå îðèåíòèðîâàííûé
//Print
void printD() {
stdout.write("$_name: ¹$num => ");
for (var i in _ln.keys) {
stdout.write("$i|${_ln[i]} ");
}
stdout.write("\n");
}
//******Constructor******
Dot([String name = "Undefined", int n = -1]) {
_name = name;
num = n;
_ln = <int, int>{};
}
Dot.fromTwoLists(String name, List<int> num0, List<int> length,
[int n = -1]) {
_name = name;
num = n;
Map<int, int> nw = <int, int>{};
if (num0.length != length.length) {
print("Error in lists");
} else {
for (var i = 0; i < num0.length; i++) {
nw[num0[i]] = length[i];
_ln = nw;
}
}
}
Dot.fromMap(String name, Map<int, int> l, [int n = -1]) {
_name = name;
num = n;
_ln = l;
}
//******Constructor******
//Copy
Dot.clone(Dot a) {
_name = a.getName();
num = a.num;
_ln = a.getL();
}
}
class Graphs {
static const int intMax = 0x7fffffffffffffff;
//Data
String _name = "Undefined"; //Èìÿ
int _amount = 0; //Êîëè÷åñòâî âåðøèí
List<Dot> _dots = <Dot>[]; //Ñïèñîê ñìåæíîñòè âåðøèí
Map<int, String> _nameTable = <int, String>{}; //Ñïèñîê âåðøèí ïî èìåíàì
bool _useLength = false; //Âçâåøåííîñòü
bool _oriented = false; //Îðèåíòèðîâàííîñòü
//*********************Add************************
String? addDot(Dot a) {
if (getNumByName(a.getName()) != null) {
return ("Dot name \"${a.getName()}\" already in use. Change name or use addPath");
}
_amount++;
a.num = _amount;
_dots.add(a);
_syncNameTable();
checkDots(false);
if (!_oriented) _fullFix();
return null;
}
bool addDotFromToLists(String name, List<int> num0, List<int> length,
[int n = -1]) {
var a = Dot.fromTwoLists(name, num0, length, n);
if (getNumByName(a.getName()) != null) {
print(
"Dot name ${a.getName()} already in use. Change name or use addPath");
return false;
}
_amount++;
a.num = _amount;
_dots.add(a);
_syncNameTable();
checkDots(false);
if (!_oriented) _fixPathAfterInsert(a);
return true;
}
String? addIsolated(String name) {
var res = addDot(Dot.fromTwoLists(name, [], []));
_syncNameTable();
return res;
}
String? addPath(int from, int to, [int len = 0]) {
if (from <= 0 || from > _amount || to <= 0 && to > _amount) {
return "Index out of range. Have dots 1..$_amount";
}
_dots[from - 1].addPath(to, len);
if (!_oriented) {
_dots[to - 1].addPath(from, len);
}
return null;
}
//*********************Add************************
//*********Delete*********
String? delPath(int from, int to) {
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);
}
return null;
}
String? delDot(int inn) {
if (inn > _amount || inn < 1) {
return "Index out of range. Allowed 1..$_amount";
}
List<int> toDel = <int>[];
for (int i in _dots[inn - 1].getL().keys) {
toDel.add(i);
}
for (int i in toDel) {
delPath(i, inn);
}
_dots.removeAt(inn - 1);
_syncNum();
_syncNameTable();
_fixAfterDel(inn);
return null;
}
void flushData() {
_dots = <Dot>[];
_amount = 0;
_nameTable = <int, String>{};
}
//*********Delete*********
//******Helper*******
bool checkDots([bool verbose = false]) {
for (var a in _dots) {
for (var i in a.getL().keys) {
try {
if (!_dots[i - 1].getL().containsKey(a.num)) {
if (verbose) print("Can't find ${a.num}");
return false;
}
} catch (e) {
if (verbose) {
print("Can't find Dot $i for path ${a.num}->$i. Exception $e");
}
_dots[a.num - 1].getL().remove(i);
return false;
}
}
}
return true;
}
void _fixAfterDel(int inn) {
for (int i = 0; i < _dots.length; i++) {
Map<int, int> l = <int, int>{};
for (int j in _dots[i].getL().keys) {
if (j >= inn) {
l[j - 1] = _dots[i].getL()[j]!;
} else {
l[j] = _dots[i].getL()[j]!;
}
}
_dots[i] = Dot.fromMap(_dots[i].getName(), l, _dots[i].num);
}
}
void _fixPathAfterInsert(Dot a) {
//Äëÿ íåîðèåíòèðîâàííîãî
for (var i in a.getL().keys) {
if (!_dots[i - 1].getL().containsKey(a.num)) {
addPath(i, a.num, a.getL()[i]!);
}
}
}
void _fullFix() {
for (var i in _dots) {
_fixPathAfterInsert(i);
}
}
void _syncNameTable() {
_nameTable = <int, String>{};
for (var i in _dots) {
_nameTable[i.num] = i.getName();
}
}
void _syncNum() {
_amount = 0;
for (var i in _dots) {
i.num = ++_amount;
}
_syncNameTable();
}
//******Helper*******
//*****Setters*******
void setName(String name) => _name = name;
String? flipUseOrientation() {
if (_amount != 0) {
return "Can change use of orientation only in empty graph";
}
_oriented = !_oriented;
return null;
}
String? flipUseLength() {
if (_amount != 0) {
return "Can change use of length only in empty graph";
}
_useLength = !_useLength;
return null;
}
String? replaceDataFromFile(String path) {
File file = File(path);
List<String> lines = file.readAsLinesSync();
if (lines.length < 3) {
return "Not enough lines in file";
}
String name = lines.removeAt(0);
bool oriented;
switch (lines.removeAt(0)) {
case Separators.isOriented:
oriented = true;
break;
case Separators.isNotOriented:
oriented = false;
break;
default:
return "Error on parsing \"IsOriented\"";
}
bool useLength;
switch (lines.removeAt(0).trim()) {
case Separators.hasLength:
useLength = true;
break;
case Separators.hasNoLength:
useLength = false;
break;
default:
return "Error on parsing \"HasLength\"";
}
List<Dot> dots = <Dot>[];
for (var l in lines) {
l = l.trimRight();
if (l != Separators.end) {
var spl = l.split(Separators.space);
List<int> dot = <int>[];
List<int> len = <int>[];
String name = spl.removeAt(0);
name = name.substring(0, name.length - 1);
for (var splitted in spl) {
if (splitted != "") {
var dt = splitted.split(Separators.dotToLength);
if (dt.length == 2) {
int? parsed = int.tryParse(dt[0]);
if (parsed == null) {
return "Error while parsing file\nin parsing int in \"${dt[0]}\"";
}
dot.add(parsed);
if (useLength) {
parsed = int.tryParse(dt[1]);
if (parsed == null) {
return "Error while parsing file\nin parsing int in \"${dt[1]}\"";
}
len.add(parsed);
} else {
len.add(0);
}
} else if (dt.length == 1) {
int? parsed = int.tryParse(splitted);
if (parsed == null) {
return "Error while parsing file\nin parsing int in \"$splitted\"";
}
dot.add(parsed);
len.add(0);
}
}
}
dots.add(Dot.fromTwoLists(name, dot, len));
}
}
_name = name;
_oriented = oriented;
_useLength = useLength;
_dots = dots;
_syncNum();
_syncNameTable();
if (!_oriented) _fullFix();
return null;
}
//*****Setters*******
//*****Getters*******
bool getDoubleSidedBool() => _oriented;
String getDoubleSidedStr() {
if (_oriented) return Separators.isOriented;
return Separators.isNotOriented;
}
bool getUseLengthBool() => _useLength;
String getUseLengthStr() {
if (_useLength) return Separators.hasLength;
return Separators.hasNoLength;
}
List<Dot> getDots() => _dots;
String getName() => _name;
String? getNameByNum(int n) => _nameTable[n];
Map<int, String> getNameTable() => _nameTable;
int getDotAmount() => _dots.length;
int? getNumByName(String n) {
for (var i in _nameTable.keys) {
if (_nameTable[i] == n) return i;
}
return null;
}
List<List<int>>? getLenTable() {
List<List<int>>? out = <List<int>>[];
for (int i = 0; i < _amount; i++) {
List<int> xx = <int>[];
for (int j = 1; j <= _amount; j++) {
xx.add(_dots[i].getLength(j));
}
out.add(xx);
}
return out;
}
List<List<int>>? getPathTable() {
List<List<int>>? out = <List<int>>[];
for (int i = 0; i < _amount; i++) {
List<int> xx = <int>[];
for (int j = 1; j <= _amount; j++) {
if (_dots[i].getLength(j) != -1) {
xx.add(i);
} else {
xx.add(-1);
}
}
out.add(xx);
}
return out;
}
List<int>? getLongestPath([int start = 1]) {
start--;
if (start < 0 || start >= _amount) {
return null;
}
int max = -1;
int inD = -1;
int out = -1;
List<int>? res = <int>[];
for (int i = start; i < _amount; i++) {
var lens = _dots[i].getL();
for (var d in lens.keys) {
if (lens[d]! > max) {
max = lens[d]!;
inD = i + 1;
out = d;
}
}
}
if (inD == -1) {
return null;
}
res.add(inD);
res.add(out);
res.add(max);
return res;
}
List<LenDotPath> getSortedPathList() {
int max = -1;
int inD = -1;
int out = -1;
List<LenDotPath> result = <LenDotPath>[];
for (int i = 0; i < _amount; i++) {
var lens = _dots[i].getL();
for (var d in lens.keys) {
max = lens[d]!;
inD = i + 1;
out = d;
result.add(LenDotPath(max, inD, out));
}
}
result.sort((a, b) => a.l.compareTo(b.l));
return result;
}
//*****Getters*******
//******Print******
void printG() {
stdout.write("$_name: ");
if (_oriented) {
stdout.write("Îðèåíòèðîâàííûé, ");
} else {
stdout.write("Íå îðèåíòèðîâàííûé, ");
}
if (_useLength) {
print("Âçâåøåííûé");
} else {
print("Íå âçâåøåííûé");
}
for (var i in _dots) {
i.printD();
}
}
void printToFile(String name) {
var file = File(name);
file.writeAsStringSync("$_name\n");
if (_oriented) {
file.writeAsStringSync("${Separators.isOriented}\n",
mode: FileMode.append);
} else {
file.writeAsStringSync("${Separators.isNotOriented}\n",
mode: FileMode.append);
}
if (_useLength) {
file.writeAsStringSync("${Separators.hasLength}\n",
mode: FileMode.append);
} else {
file.writeAsStringSync("${Separators.hasNoLength}\n",
mode: FileMode.append);
}
for (int i = 0; i < _amount; i++) {
file.writeAsStringSync(_dots[i].getName() + Separators.dotToConnections,
mode: FileMode.append);
var d = _dots[i].getL();
for (var j in d.keys) {
file.writeAsStringSync(
j.toString() + Separators.dotToLength + d[j].toString() + " ",
mode: FileMode.append);
}
file.writeAsStringSync(Separators.nL, mode: FileMode.append);
}
file.writeAsStringSync(Separators.end, mode: FileMode.append);
}
//******Print******
//*******Constructor********
Graphs(
[String name = "Undefined",
bool hasLen = false,
bool isOriented = false]) {
_name = name;
_dots = <Dot>[];
_useLength = hasLen;
_oriented = isOriented;
_amount = 0;
_nameTable = <int, String>{};
}
Graphs.fromList(String name, List<Dot> dots, bool hasLen, bool oriented) {
_name = name;
_dots = dots;
_useLength = hasLen;
_amount = _dots.length;
_oriented = oriented;
_syncNum();
if (!_oriented) _fullFix();
}
Graphs.fromFile(String path) {
replaceDataFromFile(path);
}
//*******Constructor********
//Copy
Graphs.clone(Graphs a) {
_name = a.getName();
_dots = a.getDots();
_oriented = a.getDoubleSidedBool();
_useLength = a.getUseLengthBool();
_amount = _dots.length;
_syncNameTable();
}
//************Àëãîðèòìû************
List<int>? bfsPath(int startDot, int goalDot) {
if (startDot == goalDot) return [startDot];
startDot--;
goalDot--;
List<List<int>>? graph = getLenTable();
List<bool> used = <bool>[];
List<int> dst = <int>[];
List<int> pr = <int>[];
for (int i = 0; i < _amount; i++) {
dst.add(-1);
used.add(false);
pr.add(0);
}
List<int> q = <int>[];
q.add(startDot);
used[startDot] = true;
dst[startDot] = 0;
pr[startDot] = -1; //ó âåðøèíû íåò ïðåäûäóùåé.
while (q.isNotEmpty) {
int cur = q.removeAt(0);
int x = 0;
for (int neighbor in graph![cur]) {
if (neighbor != -1) {
if (!used[x]) {
q.add(x);
used[x] = true;
dst[x] = dst[cur] + 1;
pr[x] = cur; //ñîõðàíåíèå ïðåäûäóùåé âåðøèíû
}
}
x++;
}
}
//Âîññòàíîâëåíèå êðàò÷àéøèåãî ïóòüè
List<int> path = <int>[];
int cur = goalDot;
path.add(cur + 1);
while (pr[cur] != -1) {
cur = pr[cur];
path.add(cur + 1);
}
path = path.reversed.toList();
if (path[0] == (startDot + 1) &&
path[1] == (goalDot + 1) &&
!_dots[startDot].hasConnection(goalDot + 1)) return null;
return path;
}
List<bool>? dfsIterative(int v) {
v--;
List<bool> label = <bool>[];
for (int i = 0; i < _amount; i++) {
label.add(false);
}
List<int> stack = <int>[];
stack.add(v);
while (stack.isNotEmpty) {
v = stack.removeLast();
if (!label[v]) {
label[v] = true;
for (int i in _dots[v].getL().keys) {
stack.add(i - 1);
}
}
}
return label;
}
List<int?> dijkstra(int from) {
List<int?> d = List<int?>.filled(_amount, intMax);
List<int> p = List<int>.filled(_amount, -1);
d[from - 1] = 0;
List<bool> u = List<bool>.filled(_amount, false);
for (int i = 0; i < _amount; ++i) {
int v = -1;
for (int j = 0; j < _amount; ++j) {
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;
}
Graphs? kruskal() {
List<LenDotPath> g = getSortedPathList();
//int cost = 0;
List<Dot> res = <Dot>[];
for (int i = 0; i < _amount; i++) {
res.add(Dot(_dots[i].getName(), _dots[i].num));
}
List<int> treeId = List<int>.filled(_amount, 0);
for (int i = 0; i < _amount; ++i) {
treeId[i] = i;
}
for (int i = 0; i < g.length; ++i) {
int a = g[i].d - 1, b = g[i].p - 1;
int l = g[i].l;
if (treeId[a] != treeId[b]) {
//cost += l;
res[a].addPath(b + 1, l);
int oldId = treeId[b], newId = treeId[a];
for (int j = 0; j < _amount; ++j) {
if (treeId[j] == oldId) {
treeId[j] = newId;
}
}
}
}
_dots = res;
return Graphs.fromList(_name, res, _useLength, _oriented);
}
//************Àëãîðèòìû************
}
class LenDotPath {
late int l;
late int d;
late int p;
LenDotPath(int len, dot, path) {
l = len;
d = dot;
p = path;
}
}

BIN
tex/pic/flutter0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
tex/pic/grafs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
tex/pic/graph.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
tex/pic/orgraph.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
tex/pic/prim1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
tex/pic/result.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

74
tex/thesis.bib Normal file
View File

@ -0,0 +1,74 @@
% Encoding: windows-1251
@Book{IITMMM_2017,
author = {Çàõàðîâà, Â. Å. and Àëåêñååâ, Ä. Â.},
title = {ÒÅÎÐÈß ÃÐÀÔÎÂ: Ó÷åáíîå ïîñîáèå},
adress = {Íèæíèé Íîâãîðîä},
publisher = {Íèæåãîðîäñêèé óíèâåðñèòåò},
year = {2017},
pages = {119},
}
@BOOK{Algo_2013,
author={Êîðìåí, Òîìàñ Õ. and Ëåéçåðñîí, ×àðëüç È. and Ðèâåñò, Ðîíàëä Ë. and Øòàéí, Êëèôôîðä},
title={Àëãîðèòìû: ïîñòðîåíèå è àíàëèç, 2-å èçäàíèå},
year={2013},
pages={1296},
publisher={Èçäàòåëüñêèé äîì ''Âèëüÿìñ''},
}
@Misc{intuit,
title={Ëåêöèÿ 45: Àëãîðèòìû íà ãðàôàõ. Àëãîðèòìû îáõîäà ãðàôa. [{Ý}ëåêòðîííûé ðåñóðñ]},
note={URL:~\url{https://intuit.ru/studies/courses/648/504/lecture/11474} (Äàòà îáðàùåíèÿ 1.11.2021). Çàãë. ñ ýêð. ßç. ðóñ.},
}
@TechReport{repo,
title={Ðåïîçèòîðèé ñ èõîäíûì êîäîì ïðîãðàììû. [{Ý}ëåêòðîííûé ðåñóðñ]},
note={URL:~\url{https://morozovad.ddns.net/lnd212/Graphs_dart}}
}
@Manual{dart_web,
title={Dart overview. [{Ý}ëåêòðîííûé ðåñóðñ]},
note={URL:~\url{https://dart.dev/overview} (Äàòà îáðàùåíèÿ 11.11.2021). Çàãë. ñ ýêð. ßç. àíãë.}
}
@Book{dartInAction,
author={Áàêêåò, Ê.},
title={Dart â äåéñòâèè},
year={2013},
pages={528},
publisher={ÄÌÊ Ïðåññ},
}
@TechReport{futureOfJavascript,
title={Future of Javascript. [{Ý}ëåêòðîííîå ïèñüìî]},
note={URL:~\url{https://markmail.org/message/uro3jtoitlmq6x7t}},
year={2010}
}
@Manual{dart_sound,
title={The Dart type system. [{Ý}ëåêòðîííûé ðåñóðñ]},
note={URL:~\url{https://dart.dev/guides/language/type-system} (Äàòà îáðàùåíèÿ 2.11.2021). Çàãë. ñ ýêð. ßç. àíãë.},
}
@Manual{dart_tour,
title={A tour of the Dart language. [{Ý}ëåêòðîííûé ðåñóðñ]},
note={URL:~\url{https://dart.dev/guides/language/language-tour} (Äàòà îáðàùåíèÿ 4.11.2021). Çàãë. ñ ýêð. ßç. àíãë.},
}
@Manual{skia_docs,
title={Skia documentation. [{Ý}ëåêòðîííûé ðåñóðñ]},
note={URL:~\url{https://skia.org/docs/} (Äàòà îáðàùåíèÿ 3.11.2021). Çàãë. ñ ýêð. ßç. àíãë.},
}
@Manual{krusc,
title={Ìèíèìàëüíîå îñòîâíîå äåðåâî. Àëãîðèòì Êðóñêàëà. [{Ý}ëåêòðîííûé ðåñóðñ]},
note={URL:~\url{https://e-maxx.ru/algo/mst_kruskal} (Äàòà îáðàùåíèÿ 27.10.2021). Çàãë. ñ ýêð. ßç. ðóñ.},
}
@Manual{custompaint,
title={CustomPaint class. [{Ý}ëåêòðîííûé ðåñóðñ]},
note={URL:~\url{https://api.flutter.dev/flutter/widgets/CustomPaint-class.html} (Äàòà îáðàùåíèÿ 6.11.2021). Çàãë. ñ ýêð. ßç. àíãë.},
}
@Comment{jabref-meta: databaseType:bibtex;}