Compare commits
54 Commits
Author | SHA1 | Date |
---|---|---|
Морозов Андрей | 07ba24ee83 | |
Морозов Андрей | 34f612970b | |
Морозов Андрей | 92b07e6d48 | |
Морозов Андрей | 575674fae0 | |
Морозов Андрей | 7d5758d3af | |
Морозов Андрей | 1b8c38dd89 | |
Морозов Андрей | 9d5520e866 | |
Морозов Андрей | 5175d15194 | |
Морозов Андрей | 682c4df7bd | |
Морозов Андрей | a3bd0cc3fe | |
Морозов Андрей | 3ff5b62054 | |
Морозов Андрей | a990295f7e | |
Морозов Андрей | f295e933be | |
Морозов Андрей | 6e07f084d1 | |
Морозов Андрей | f8f94c5400 | |
Морозов Андрей | 4a4ae94bb1 | |
Морозов Андрей | fd8446c2e1 | |
Морозов Андрей | 1456df8bbf | |
Морозов Андрей | a9c9ee1eca | |
Морозов Андрей | 4da4910b71 | |
Морозов Андрей | d5eaade116 | |
Морозов Андрей | fc45c57510 | |
Морозов Андрей | a3f0d3cbc5 | |
Морозов Андрей | 6668b0eeae | |
Морозов Андрей | b19011ed8b | |
Морозов Андрей | cec41fc63c | |
Морозов Андрей | 4953a9ad19 | |
Морозов Андрей | b853664230 | |
Морозов Андрей | 44c4c47872 | |
Морозов Андрей | d28f08679e | |
Морозов Андрей | 1f180172ec | |
Морозов Андрей | 6fc7bbb2e5 | |
Морозов Андрей | 8fea8c95fc | |
Морозов Андрей | e1bca55bcf | |
Морозов Андрей | d4c57c6aad | |
Морозов Андрей | 7945800116 | |
Морозов Андрей | 9024c24c4c | |
Морозов Андрей | 49ce4aa12f | |
Морозов Андрей | daddfeed6f | |
Морозов Андрей | 0a0698bd01 | |
Морозов Андрей | 749afe3e6e | |
Морозов Андрей | 442288141d | |
Морозов Андрей | 4b3542edad | |
Морозов Андрей | 77c2f02a96 | |
Морозов Андрей | f20cb1fef0 | |
Морозов Андрей | 41de1ba314 | |
Морозов Андрей | 276e7d36ed | |
Морозов Андрей | 3950fabb86 | |
Морозов Андрей | 7bbb414212 | |
Морозов Андрей | 364495be65 | |
Морозов Андрей | 3e0be6b7db | |
Морозов Андрей | a83c683e72 | |
Морозов Андрей | 2422274ea3 | |
Морозов Андрей | 5fc5f8b3d8 |
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
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;
|
||||
}
|
||||
|
||||
while Q is not empty{
|
||||
u ← vertex in Q with min dist[u]
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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[]*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
star
|
||||
Ориентированный
|
||||
НеВзвешенный
|
||||
1: 2|0
|
||||
2: 4|0
|
||||
3: 5|0
|
||||
4: 6|0
|
||||
5: 2|0
|
||||
6: 3|0
|
||||
END
|
|
@ -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
|
|
@ -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));*/
|
||||
}
|
|
@ -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(),
|
||||
|
|
|
@ -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();
|
||||
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,74 +146,117 @@ class _DrawingPageState extends State<DrawingPage> {
|
|||
));
|
||||
}
|
||||
|
||||
SizedBox addSpaceH(double h) {
|
||||
return SizedBox(height: h);
|
||||
}
|
||||
|
||||
SizedBox addSpaceW(double w) {
|
||||
return SizedBox(width: w);
|
||||
}
|
||||
|
||||
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'),
|
||||
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 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() {
|
||||
//showPopUp("Test", "Test message");
|
||||
//var inp = int.tryParse(_textNameController.text);
|
||||
setState(() {
|
||||
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()) {
|
||||
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.tryParse(_textNumbController.text);
|
||||
int? to = int.tryParse(_textDestController.text);
|
||||
int? from = int.parse(dropdownValue1!);
|
||||
int? to = int.parse(dropdownValue2!);
|
||||
int? len = int.tryParse(_textLnthController.text);
|
||||
if (from == null ||
|
||||
to == null ||
|
||||
(len == null && graphData.getUseLengthBool())) {
|
||||
if (len == null && graphData.getUseLengthBool()) {
|
||||
showPopUp("Error",
|
||||
"Can't parse input.\nInts only allowed in \"Dot number\", \"Destination number\" and \"Input length\"");
|
||||
"Can't parse input.\nInts only allowed in \"Input length\"");
|
||||
} else {
|
||||
len ??= 0;
|
||||
String? res = graphData.addPath(from, to, len);
|
||||
if (res != null) {
|
||||
showPopUp("Error", res);
|
||||
}
|
||||
}
|
||||
}
|
||||
clearTextControllers();
|
||||
setState(() {
|
||||
String? res = graphData.addPath(from, to, len!);
|
||||
if (res != null) showPopUp("Error", res);
|
||||
});
|
||||
}
|
||||
}
|
||||
clearInputData();
|
||||
}
|
||||
|
||||
void changeOriented() {
|
||||
setState(() {
|
||||
|
@ -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(_textNumbController.text);
|
||||
int? to = int.tryParse(_textDestController.text);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
clearTextControllers();
|
||||
});
|
||||
}
|
||||
}
|
||||
clearInputData();
|
||||
}
|
||||
|
||||
void delDotPushed() {
|
||||
if (dropdownValue1 != null) {
|
||||
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);
|
||||
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!);
|
||||
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
|
||||
}
|
||||
});
|
||||
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() {
|
||||
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(() {
|
||||
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) {
|
||||
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");
|
||||
}
|
||||
print(bfsPath);
|
||||
//print(intListPath);
|
||||
}
|
||||
}
|
||||
clearTextControllers();
|
||||
});
|
||||
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);
|
||||
})
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
@ -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;
|
||||
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;
|
||||
}
|
||||
|
||||
while Q is not empty{
|
||||
u ← vertex in Q with min dist[u]
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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[]*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
|
@ -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"));
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
desktop_window
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
% --------------------------------------------------------------------------%
|
|
@ -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'),
|
||||
]),
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
class Dot {
|
||||
//Data
|
||||
String _name = "";
|
||||
int num = -1;
|
||||
Map<int, int> _ln = <int, int>{};
|
||||
***
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -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; //Îðèåíòèðîâàííîñòü
|
|
@ -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);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
void main() => print("Hello World");
|
|
@ -0,0 +1,3 @@
|
|||
void main() {
|
||||
print("Hello World");
|
||||
}
|
|
@ -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>[
|
||||
/*
|
||||
Îïèñàíèå êíîïîê äåéñòâèÿ
|
||||
*/
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
|
@ -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,
|
||||
)),
|
||||
);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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,
|
||||
});
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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)
|
|
@ -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)
|
|
@ -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
|
|
@ -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}
|
After Width: | Height: | Size: 8.6 KiB |
|
@ -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(),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 8.3 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 97 KiB |
|
@ -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;}
|