diff --git a/.extra/GeoGebra/2 circles test.ggb b/.extra/GeoGebra/2 circles test.ggb
new file mode 100644
index 0000000..474adc8
Binary files /dev/null and b/.extra/GeoGebra/2 circles test.ggb differ
diff --git a/.extra/GeoGebra/3 circles test.ggb b/.extra/GeoGebra/3 circles test.ggb
new file mode 100644
index 0000000..3cab36f
Binary files /dev/null and b/.extra/GeoGebra/3 circles test.ggb differ
diff --git a/.extra/GeoGebra/Many circles test.ggb b/.extra/GeoGebra/Many circles test.ggb
new file mode 100644
index 0000000..a3f27a4
Binary files /dev/null and b/.extra/GeoGebra/Many circles test.ggb differ
diff --git a/.extra/GeoGebra/no intersection with obstacle test.ggb b/.extra/GeoGebra/no intersection with obstacle test.ggb
new file mode 100644
index 0000000..15d1fc7
Binary files /dev/null and b/.extra/GeoGebra/no intersection with obstacle test.ggb differ
diff --git a/.extra/GeoGebra/no obstacle test.ggb b/.extra/GeoGebra/no obstacle test.ggb
new file mode 100644
index 0000000..0f3557e
Binary files /dev/null and b/.extra/GeoGebra/no obstacle test.ggb differ
diff --git a/.extra/GeoGebra/one circle test.ggb b/.extra/GeoGebra/one circle test.ggb
new file mode 100644
index 0000000..e49fd82
Binary files /dev/null and b/.extra/GeoGebra/one circle test.ggb differ
diff --git a/.extra/GeoGebra/one poly and one circle.ggb b/.extra/GeoGebra/one poly and one circle.ggb
new file mode 100644
index 0000000..caec069
Binary files /dev/null and b/.extra/GeoGebra/one poly and one circle.ggb differ
diff --git a/.extra/GeoGebra/one poly test.ggb b/.extra/GeoGebra/one poly test.ggb
new file mode 100644
index 0000000..42a355a
Binary files /dev/null and b/.extra/GeoGebra/one poly test.ggb differ
diff --git a/.extra/GeoGebra/three polys test.ggb b/.extra/GeoGebra/three polys test.ggb
new file mode 100644
index 0000000..26daa94
Binary files /dev/null and b/.extra/GeoGebra/three polys test.ggb differ
diff --git a/.extra/GeoGebra/two circles touch.ggb b/.extra/GeoGebra/two circles touch.ggb
new file mode 100644
index 0000000..693ed90
Binary files /dev/null and b/.extra/GeoGebra/two circles touch.ggb differ
diff --git a/.extra/GeoGebra/two polys test.ggb b/.extra/GeoGebra/two polys test.ggb
new file mode 100644
index 0000000..9ca3b4f
Binary files /dev/null and b/.extra/GeoGebra/two polys test.ggb differ
diff --git a/.extra/images/AA.png b/.extra/images/AA.png
index 233e29c..c696cb8 100644
Binary files a/.extra/images/AA.png and b/.extra/images/AA.png differ
diff --git a/.extra/images/beautify.png b/.extra/images/beautify.png
new file mode 100644
index 0000000..c78c2ce
Binary files /dev/null and b/.extra/images/beautify.png differ
diff --git a/.extra/images/flying_robot_pixel.png b/.extra/images/flying_robot_pixel.png
new file mode 100644
index 0000000..f8a956c
Binary files /dev/null and b/.extra/images/flying_robot_pixel.png differ
diff --git a/.extra/images/help.png b/.extra/images/help.png
new file mode 100644
index 0000000..7535529
Binary files /dev/null and b/.extra/images/help.png differ
diff --git a/.extra/images/home_scale.png b/.extra/images/home_scale.png
new file mode 100644
index 0000000..4f524e5
Binary files /dev/null and b/.extra/images/home_scale.png differ
diff --git a/.extra/images/images_source.html b/.extra/images/images_source.html
index e0e64ed..bfb8b4c 100644
--- a/.extra/images/images_source.html
+++ b/.extra/images/images_source.html
@@ -1,15 +1,42 @@
-Document icons created by Freepik - Flaticon
+Document icons created by Freepik -
+ Flaticon
Save icons created by Freepik - Flaticon
-Contract icons created by Freepik - Flaticon
+Contract icons created by Freepik -
+ Flaticon
Folder icons created by Freepik - Flaticon
Request icons by Freepik - Flaticon
-Указатель мыши иконки от Syahrul Ramadhany - Flaticon
+Указатель мыши иконки от Syahrul
+ Ramadhany - Flaticon
-Указатель мыши иконки от Syahrul Ramadhany - Flaticon
+Указатель мыши иконки от Syahrul
+ Ramadhany - Flaticon
Ui иконки от PLANBSTUDIO - Flaticon
+
+Route icons created by Freepik - Flaticon
+
+Triangle icons created by Freepik -
+ Flaticon
+
+Circle icons created by Freepik - Flaticon
+
+Line segment icons created by
+ Freepik - Flaticon
+
+Target icons created by Freepik - Flaticon
+
+Diamond icons created by Freepik -
+ Flaticon
+
+Star icons created by Freepik - Flaticon
+
+Question icons created by Freepik -
+ Flaticon
+
+Search icons created by Freepik - Flaticon
+
\ No newline at end of file
diff --git a/.extra/images/play_triangle.png b/.extra/images/play_triangle.png
index 22a0655..c470529 100644
Binary files a/.extra/images/play_triangle.png and b/.extra/images/play_triangle.png differ
diff --git a/.extra/images/route.png b/.extra/images/route.png
new file mode 100644
index 0000000..f36eb2d
Binary files /dev/null and b/.extra/images/route.png differ
diff --git a/.extra/images/stop_square.png b/.extra/images/stop_square.png
new file mode 100644
index 0000000..23f8145
Binary files /dev/null and b/.extra/images/stop_square.png differ
diff --git a/.extra/images/target.png b/.extra/images/target.png
index 6cd4e9a..51cd719 100644
Binary files a/.extra/images/target.png and b/.extra/images/target.png differ
diff --git a/.extra/images/trappy_line.png b/.extra/images/trappy_line.png
new file mode 100644
index 0000000..eae8513
Binary files /dev/null and b/.extra/images/trappy_line.png differ
diff --git a/.extra/json/beauty_cycle.json b/.extra/json/beauty_cycle.json
new file mode 100644
index 0000000..f0e952c
--- /dev/null
+++ b/.extra/json/beauty_cycle.json
@@ -0,0 +1,98 @@
+{
+ "Hills": [
+ {
+ "Id": 40000,
+ "Vertices": [
+ {
+ "X": 3.1579493209203786,
+ "Y": 2.443013223818448
+ },
+ {
+ "X": 2.7150427465259144,
+ "Y": 1.626404227278654
+ },
+ {
+ "X": 5.372482192892702,
+ "Y": 2.3461274106696584
+ },
+ {
+ "X": 3.6977417084636315,
+ "Y": 2.581421528316718
+ }
+ ]
+ },
+ {
+ "Id": 40001,
+ "Vertices": [
+ {
+ "X": 1.1371880752456347,
+ "Y": 4.394570317244057
+ },
+ {
+ "X": 0.5697140268027274,
+ "Y": 3.923982081949938
+ },
+ {
+ "X": 0.4313057223044572,
+ "Y": 4.42225197814371
+ },
+ {
+ "X": 0.5558731963529002,
+ "Y": 4.865158552538174
+ }
+ ]
+ }
+ ],
+ "Targets": [
+ {
+ "Id": 10000,
+ "X": 0.5,
+ "Y": 3
+ },
+ {
+ "Id": 10001,
+ "X": 2,
+ "Y": 4.5
+ },
+ {
+ "Id": 10002,
+ "X": 2,
+ "Y": 1.5
+ },
+ {
+ "Id": 10003,
+ "X": 3.5,
+ "Y": 3
+ },
+ {
+ "Id": 10004,
+ "X": 4.528191535453253,
+ "Y": 1.626404227278654
+ },
+ {
+ "Id": 10005,
+ "X": 0.23753409600687925,
+ "Y": 5.086611839735407
+ }
+ ],
+ "Trappy_Circles": [
+ {
+ "Id": 20000,
+ "Radius": 0.9939142797825369,
+ "X": 1.9985569985569984,
+ "Y": 2.9899999999999998
+ }
+ ],
+ "Trappy_Lines": [
+ {
+ "Id": 30000,
+ "Id_P1": 10001,
+ "Id_P2": 10003
+ },
+ {
+ "Id": 30001,
+ "Id_P1": 10000,
+ "Id_P2": 10002
+ }
+ ]
+}
diff --git a/.extra/json/circles.json b/.extra/json/circles.json
new file mode 100644
index 0000000..609d964
--- /dev/null
+++ b/.extra/json/circles.json
@@ -0,0 +1,75 @@
+{
+ "Hills": [
+ ],
+ "Targets": [
+ {
+ "Id": 10001,
+ "X": 7.895199011753746,
+ "Y": 12.435481915630191
+ },
+ {
+ "Id": 10002,
+ "X": 26.75436318143683,
+ "Y": 52.62890246889562
+ },
+ {
+ "Id": 10003,
+ "X": 21.702801350271713,
+ "Y": -56.12976491052849
+ },
+ {
+ "Id": 10004,
+ "X": 142.06093294699355,
+ "Y": -11.607958582541585
+ },
+ {
+ "Id": 10006,
+ "X": -178.96002688324305,
+ "Y": -192.18383722750727
+ },
+ {
+ "Id": 10007,
+ "X": -189.32250761172395,
+ "Y": 31.49966748419456
+ },
+ {
+ "Id": 10000,
+ "X": -8.091031861133459,
+ "Y": 49.83226513258863
+ }
+ ],
+ "Trappy_Circles": [
+ {
+ "Id": 20000,
+ "Radius": 9.214915824274225,
+ "X": -2.822531340034921,
+ "Y": 31.13386646881319
+ },
+ {
+ "Id": 20001,
+ "Radius": 4.849347615884043,
+ "X": 8.613362718751027,
+ "Y": 20.88454422773051
+ },
+ {
+ "Id": 20002,
+ "Radius": 15.417775569285356,
+ "X": 25.888862254363836,
+ "Y": 33.330149806188054
+ },
+ {
+ "Id": 20003,
+ "Radius": 52.744563723068566,
+ "X": 74.55224122792106,
+ "Y": -18.16049288115593
+ },
+ {
+ "Id": 20004,
+ "Radius": 104.17904309976987,
+ "X": -94.65664061006035,
+ "Y": -40.93241800644881
+ }
+ ],
+ "Trappy_Lines": [
+ ]
+}
diff --git a/.extra/json/shapes.json b/.extra/json/shapes.json
new file mode 100644
index 0000000..6ebac9b
--- /dev/null
+++ b/.extra/json/shapes.json
@@ -0,0 +1,127 @@
+{
+ "Hills": [
+ {
+ "Id": 40001,
+ "Vertices": [
+ {
+ "X": -25.8437,
+ "Y": 3.59211
+ },
+ {
+ "X": -10.220724087645344,
+ "Y": 46.575086389491545
+ },
+ {
+ "X": 8.858454099199534,
+ "Y": 47.67159088298838
+ },
+ {
+ "X": 18.5077,
+ "Y": 24.4257
+ }
+ ]
+ },
+ {
+ "Id": 40000,
+ "Vertices": [
+ {
+ "X": 23.167871749486935,
+ "Y": 36.31120476458979
+ },
+ {
+ "X": 22.000234305563637,
+ "Y": 0.2643686507672598
+ },
+ {
+ "X": 55.51142894616228,
+ "Y": -4.042111769910655
+ },
+ {
+ "X": 35.1945374218969,
+ "Y": 30.888229420032417
+ }
+ ]
+ }
+ ],
+ "Targets": [
+ {
+ "Id": 10000,
+ "X": 7.919552991488025,
+ "Y": 16.55916271383264
+ },
+ {
+ "Id": 10001,
+ "X": -13.889921384163955,
+ "Y": 47.129707992524295
+ },
+ {
+ "Id": 10002,
+ "X": 19.663116116839085,
+ "Y": 24.947422089083396
+ },
+ {
+ "Id": 10003,
+ "X": -23.396615342781484,
+ "Y": -3.3862540228747164
+ },
+ {
+ "Id": 10004,
+ "X": 59.92676111804275,
+ "Y": -2.08141367561349
+ },
+ {
+ "Id": 10005,
+ "X": 12.20688556106063,
+ "Y": -10.096861523075326
+ },
+ {
+ "Id": 10006,
+ "X": 18.122479668419928,
+ "Y": 62.43950441037006
+ },
+ {
+ "Id": 10007,
+ "X": 46.11954787154053,
+ "Y": -26.576930968956088
+ }
+ ],
+ "Trappy_Circles": [
+ {
+ "Id": 20000,
+ "Radius": 10.14584,
+ "X": 0.883362,
+ "Y": 3.76028
+ },
+ {
+ "Id": 20004,
+ "Radius": 7.098120017227275,
+ "X": 14.343409181715266,
+ "Y": 52.865085174213014
+ },
+ {
+ "Id": 20002,
+ "Radius": 1.2906186630113354,
+ "X": 58.37746878313216,
+ "Y": -3.005475947971121
+ },
+ {
+ "Id": 20001,
+ "Radius": 6.871468522371515,
+ "X": 15.009247865260352,
+ "Y": 14.817571670134896
+ },
+ {
+ "Id": 20003,
+ "Radius": 9.108004552405431,
+ "X": 45.597530738654505,
+ "Y": -16.452272507642146
+ }
+ ],
+ "Trappy_Lines": [
+ {
+ "Id": 30001,
+ "Id_P1": 10006,
+ "Id_P2": 10004
+ }
+ ]
+}
diff --git a/.extra/json/tessaract.json b/.extra/json/tessaract.json
new file mode 100644
index 0000000..ecb613d
--- /dev/null
+++ b/.extra/json/tessaract.json
@@ -0,0 +1,560 @@
+{
+ "Hills": [
+ ],
+ "Targets": [
+ {
+ "Id": 10000,
+ "X": -5,
+ "Y": 10
+ },
+ {
+ "Id": 10001,
+ "X": -5,
+ "Y": 5
+ },
+ {
+ "Id": 10002,
+ "X": 0,
+ "Y": 5
+ },
+ {
+ "Id": 10003,
+ "X": 0,
+ "Y": 10
+ },
+ {
+ "Id": 10004,
+ "X": 2.5,
+ "Y": 11
+ },
+ {
+ "Id": 10005,
+ "X": 2.5,
+ "Y": 6
+ },
+ {
+ "Id": 10006,
+ "X": -2.5,
+ "Y": 6
+ },
+ {
+ "Id": 10007,
+ "X": -2.5,
+ "Y": 11
+ },
+ {
+ "Id": 10008,
+ "X": -10,
+ "Y": 12.5
+ },
+ {
+ "Id": 10009,
+ "X": -10,
+ "Y": 7.5
+ },
+ {
+ "Id": 10010,
+ "X": -5,
+ "Y": 7.5
+ },
+ {
+ "Id": 10011,
+ "X": -5,
+ "Y": 12.5
+ },
+ {
+ "Id": 10012,
+ "X": -2.5,
+ "Y": 13.5
+ },
+ {
+ "Id": 10013,
+ "X": -2.5,
+ "Y": 8.5
+ },
+ {
+ "Id": 10014,
+ "X": -7.5,
+ "Y": 13.5
+ },
+ {
+ "Id": 10015,
+ "X": -7.5,
+ "Y": 8.5
+ }
+ ],
+ "Trappy_Circles": [
+ ],
+ "Trappy_Lines": [
+ {
+ "Id": 30001,
+ "Id_P1": 10000,
+ "Id_P2": 10002
+ },
+ {
+ "Id": 30003,
+ "Id_P1": 10000,
+ "Id_P2": 10004
+ },
+ {
+ "Id": 30004,
+ "Id_P1": 10000,
+ "Id_P2": 10005
+ },
+ {
+ "Id": 30005,
+ "Id_P1": 10000,
+ "Id_P2": 10006
+ },
+ {
+ "Id": 30006,
+ "Id_P1": 10000,
+ "Id_P2": 10007
+ },
+ {
+ "Id": 30007,
+ "Id_P1": 10000,
+ "Id_P2": 10008
+ },
+ {
+ "Id": 30008,
+ "Id_P1": 10000,
+ "Id_P2": 10009
+ },
+ {
+ "Id": 30011,
+ "Id_P1": 10000,
+ "Id_P2": 10012
+ },
+ {
+ "Id": 30012,
+ "Id_P1": 10000,
+ "Id_P2": 10013
+ },
+ {
+ "Id": 30013,
+ "Id_P1": 10000,
+ "Id_P2": 10014
+ },
+ {
+ "Id": 30014,
+ "Id_P1": 10000,
+ "Id_P2": 10015
+ },
+ {
+ "Id": 30016,
+ "Id_P1": 10001,
+ "Id_P2": 10003
+ },
+ {
+ "Id": 30017,
+ "Id_P1": 10001,
+ "Id_P2": 10004
+ },
+ {
+ "Id": 30018,
+ "Id_P1": 10001,
+ "Id_P2": 10005
+ },
+ {
+ "Id": 30020,
+ "Id_P1": 10001,
+ "Id_P2": 10007
+ },
+ {
+ "Id": 30021,
+ "Id_P1": 10001,
+ "Id_P2": 10008
+ },
+ {
+ "Id": 30022,
+ "Id_P1": 10001,
+ "Id_P2": 10009
+ },
+ {
+ "Id": 30025,
+ "Id_P1": 10001,
+ "Id_P2": 10012
+ },
+ {
+ "Id": 30026,
+ "Id_P1": 10001,
+ "Id_P2": 10013
+ },
+ {
+ "Id": 30027,
+ "Id_P1": 10001,
+ "Id_P2": 10014
+ },
+ {
+ "Id": 30028,
+ "Id_P1": 10001,
+ "Id_P2": 10015
+ },
+ {
+ "Id": 30030,
+ "Id_P1": 10002,
+ "Id_P2": 10004
+ },
+ {
+ "Id": 30031,
+ "Id_P1": 10002,
+ "Id_P2": 10005
+ },
+ {
+ "Id": 30032,
+ "Id_P1": 10002,
+ "Id_P2": 10006
+ },
+ {
+ "Id": 30033,
+ "Id_P1": 10002,
+ "Id_P2": 10007
+ },
+ {
+ "Id": 30034,
+ "Id_P1": 10002,
+ "Id_P2": 10008
+ },
+ {
+ "Id": 30035,
+ "Id_P1": 10002,
+ "Id_P2": 10009
+ },
+ {
+ "Id": 30036,
+ "Id_P1": 10002,
+ "Id_P2": 10010
+ },
+ {
+ "Id": 30037,
+ "Id_P1": 10002,
+ "Id_P2": 10011
+ },
+ {
+ "Id": 30038,
+ "Id_P1": 10002,
+ "Id_P2": 10012
+ },
+ {
+ "Id": 30039,
+ "Id_P1": 10002,
+ "Id_P2": 10013
+ },
+ {
+ "Id": 30040,
+ "Id_P1": 10002,
+ "Id_P2": 10014
+ },
+ {
+ "Id": 30041,
+ "Id_P1": 10002,
+ "Id_P2": 10015
+ },
+ {
+ "Id": 30042,
+ "Id_P1": 10003,
+ "Id_P2": 10004
+ },
+ {
+ "Id": 30043,
+ "Id_P1": 10003,
+ "Id_P2": 10005
+ },
+ {
+ "Id": 30044,
+ "Id_P1": 10003,
+ "Id_P2": 10006
+ },
+ {
+ "Id": 30045,
+ "Id_P1": 10003,
+ "Id_P2": 10007
+ },
+ {
+ "Id": 30046,
+ "Id_P1": 10003,
+ "Id_P2": 10008
+ },
+ {
+ "Id": 30047,
+ "Id_P1": 10003,
+ "Id_P2": 10009
+ },
+ {
+ "Id": 30048,
+ "Id_P1": 10003,
+ "Id_P2": 10010
+ },
+ {
+ "Id": 30049,
+ "Id_P1": 10003,
+ "Id_P2": 10011
+ },
+ {
+ "Id": 30050,
+ "Id_P1": 10003,
+ "Id_P2": 10012
+ },
+ {
+ "Id": 30051,
+ "Id_P1": 10003,
+ "Id_P2": 10013
+ },
+ {
+ "Id": 30052,
+ "Id_P1": 10003,
+ "Id_P2": 10014
+ },
+ {
+ "Id": 30053,
+ "Id_P1": 10003,
+ "Id_P2": 10015
+ },
+ {
+ "Id": 30055,
+ "Id_P1": 10004,
+ "Id_P2": 10006
+ },
+ {
+ "Id": 30057,
+ "Id_P1": 10004,
+ "Id_P2": 10008
+ },
+ {
+ "Id": 30058,
+ "Id_P1": 10004,
+ "Id_P2": 10009
+ },
+ {
+ "Id": 30059,
+ "Id_P1": 10004,
+ "Id_P2": 10010
+ },
+ {
+ "Id": 30060,
+ "Id_P1": 10004,
+ "Id_P2": 10011
+ },
+ {
+ "Id": 30061,
+ "Id_P1": 10004,
+ "Id_P2": 10012
+ },
+ {
+ "Id": 30062,
+ "Id_P1": 10004,
+ "Id_P2": 10013
+ },
+ {
+ "Id": 30063,
+ "Id_P1": 10004,
+ "Id_P2": 10014
+ },
+ {
+ "Id": 30064,
+ "Id_P1": 10004,
+ "Id_P2": 10015
+ },
+ {
+ "Id": 30066,
+ "Id_P1": 10005,
+ "Id_P2": 10007
+ },
+ {
+ "Id": 30067,
+ "Id_P1": 10005,
+ "Id_P2": 10008
+ },
+ {
+ "Id": 30068,
+ "Id_P1": 10005,
+ "Id_P2": 10009
+ },
+ {
+ "Id": 30069,
+ "Id_P1": 10005,
+ "Id_P2": 10010
+ },
+ {
+ "Id": 30070,
+ "Id_P1": 10005,
+ "Id_P2": 10011
+ },
+ {
+ "Id": 30071,
+ "Id_P1": 10005,
+ "Id_P2": 10012
+ },
+ {
+ "Id": 30072,
+ "Id_P1": 10005,
+ "Id_P2": 10013
+ },
+ {
+ "Id": 30073,
+ "Id_P1": 10005,
+ "Id_P2": 10014
+ },
+ {
+ "Id": 30074,
+ "Id_P1": 10005,
+ "Id_P2": 10015
+ },
+ {
+ "Id": 30076,
+ "Id_P1": 10006,
+ "Id_P2": 10008
+ },
+ {
+ "Id": 30077,
+ "Id_P1": 10006,
+ "Id_P2": 10009
+ },
+ {
+ "Id": 30078,
+ "Id_P1": 10006,
+ "Id_P2": 10010
+ },
+ {
+ "Id": 30079,
+ "Id_P1": 10006,
+ "Id_P2": 10011
+ },
+ {
+ "Id": 30082,
+ "Id_P1": 10006,
+ "Id_P2": 10014
+ },
+ {
+ "Id": 30083,
+ "Id_P1": 10006,
+ "Id_P2": 10015
+ },
+ {
+ "Id": 30084,
+ "Id_P1": 10007,
+ "Id_P2": 10008
+ },
+ {
+ "Id": 30085,
+ "Id_P1": 10007,
+ "Id_P2": 10009
+ },
+ {
+ "Id": 30086,
+ "Id_P1": 10007,
+ "Id_P2": 10010
+ },
+ {
+ "Id": 30087,
+ "Id_P1": 10007,
+ "Id_P2": 10011
+ },
+ {
+ "Id": 30090,
+ "Id_P1": 10007,
+ "Id_P2": 10014
+ },
+ {
+ "Id": 30091,
+ "Id_P1": 10007,
+ "Id_P2": 10015
+ },
+ {
+ "Id": 30093,
+ "Id_P1": 10008,
+ "Id_P2": 10010
+ },
+ {
+ "Id": 30095,
+ "Id_P1": 10008,
+ "Id_P2": 10012
+ },
+ {
+ "Id": 30096,
+ "Id_P1": 10008,
+ "Id_P2": 10013
+ },
+ {
+ "Id": 30097,
+ "Id_P1": 10008,
+ "Id_P2": 10014
+ },
+ {
+ "Id": 30098,
+ "Id_P1": 10008,
+ "Id_P2": 10015
+ },
+ {
+ "Id": 30100,
+ "Id_P1": 10009,
+ "Id_P2": 10011
+ },
+ {
+ "Id": 30101,
+ "Id_P1": 10009,
+ "Id_P2": 10012
+ },
+ {
+ "Id": 30102,
+ "Id_P1": 10009,
+ "Id_P2": 10013
+ },
+ {
+ "Id": 30103,
+ "Id_P1": 10009,
+ "Id_P2": 10014
+ },
+ {
+ "Id": 30104,
+ "Id_P1": 10009,
+ "Id_P2": 10015
+ },
+ {
+ "Id": 30106,
+ "Id_P1": 10010,
+ "Id_P2": 10012
+ },
+ {
+ "Id": 30107,
+ "Id_P1": 10010,
+ "Id_P2": 10013
+ },
+ {
+ "Id": 30108,
+ "Id_P1": 10010,
+ "Id_P2": 10014
+ },
+ {
+ "Id": 30109,
+ "Id_P1": 10010,
+ "Id_P2": 10015
+ },
+ {
+ "Id": 30111,
+ "Id_P1": 10011,
+ "Id_P2": 10013
+ },
+ {
+ "Id": 30112,
+ "Id_P1": 10011,
+ "Id_P2": 10014
+ },
+ {
+ "Id": 30113,
+ "Id_P1": 10011,
+ "Id_P2": 10015
+ },
+ {
+ "Id": 30116,
+ "Id_P1": 10012,
+ "Id_P2": 10015
+ },
+ {
+ "Id": 30117,
+ "Id_P1": 10013,
+ "Id_P2": 10014
+ }
+ ]
+}
diff --git a/.gitmodules b/.gitmodules
index fb170f7..a915d31 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -3,7 +3,7 @@
url = https://github.com/renatoGarcia/icecream-cpp
[submodule "qcustomplot"]
path = qcustomplot
- url = https://github.com/legerch/QCustomPlot-library
+ url = https://github.com/UmbrellaLeaf5/qcustomplot
[submodule "doxygen_awesome"]
path = .extra/doxygen/theme
- url = https://github.com/jothepro/doxygen-awesome-css.git
+ url = https://github.com/jothepro/doxygen-awesome-css
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a46bdc0..efc8f82 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -43,17 +43,14 @@ move_icecream_hpp_to_build_dir(${CMAKE_CURRENT_SOURCE_DIR})
# and add it to CMAKE_PREFIX_PATH manually
list(APPEND CMAKE_PREFIX_PATH "${CMAKE_BINARY_DIR}/icecream-cpp")
-find_package(IcecreamCpp) # here finding icecream-cpp
add_subdirectory(${ICECREAM-CPP_DIR}) # here adding icecream-cpp subfolder
+find_package(IcecreamCpp) # here finding icecream-cpp
-# because of qcustomplot [module] CMake settings we need to change include in autogen
+# the command QCUSTOMPLOT_USE_LIBRARY does not work for an unknown reason
include(${CMAKESCRIPTS_DIR}/scripts_for_qcustomplot.cmake)
-replace_include_qcustomplot_in_autogen(${CMAKE_BINARY_DIR})
+load_qcustomplot_dll(${CMAKE_CURRENT_SOURCE_DIR}) # copy one dll file
add_subdirectory(${QCUSTOMPLOT_DIR}) # here adding qcustomplot subfolder
-# copy one dll file: the command QCUSTOMPLOT_USE_LIBRARY does not work for an unknown reason
-load_qcustomplot_dll(${CMAKE_CURRENT_SOURCE_DIR})
-
# --------------------------------- Our code subdirs ---------------------------------
add_subdirectory(lib)
add_subdirectory(tests)
@@ -61,5 +58,5 @@ add_subdirectory(main)
# ------------------------------------------------------------------------------------------
-# add images to build directory
+# copy images to build directory
file(COPY ${IMAGES_DIR} DESTINATION ${CMAKE_BINARY_DIR})
diff --git a/cmake_scripts/scripts_for_qcustomplot.cmake b/cmake_scripts/scripts_for_qcustomplot.cmake
index 494b6c2..b1589b0 100644
--- a/cmake_scripts/scripts_for_qcustomplot.cmake
+++ b/cmake_scripts/scripts_for_qcustomplot.cmake
@@ -1,23 +1,5 @@
# language: CMake
-function(replace_include_qcustomplot_in_autogen BUILD_DIR)
- if(EXISTS "${BUILD_DIR}/main/main_autogen/include/ui_mainwindow.h")
- message(STATUS "Found ${BUILD_DIR}/main/main_autogen/include/ui_mainwindow.h , replacing qcustomplot include")
-
- # open the file where you need to replace the line in FILE_CONTENT
- file(READ "${BUILD_DIR}/main/main_autogen/include/ui_mainwindow.h" FILE_CONTENT)
-
- # replace the string using REGEX and write it to MODIFIED_CONTENT
- string(REGEX REPLACE "#include \"../qcustomplot/qcustomplot.h\""
- "#include \"../qcustomplot/lib/qcustomplot.h\"" MODIFIED_CONTENT "${FILE_CONTENT}")
-
- # rewrite the contents of MODIFIED_CONTENT to the file
- file(WRITE "${BUILD_DIR}/main/main_autogen/include/ui_mainwindow.h" "${MODIFIED_CONTENT}")
- else()
- message(STATUS "warning: ${BUILD_DIR}/main/main_autogen/include does not exist, please build and reconfigure the project")
- endif()
-endfunction()
-
function(load_qcustomplot_dll START_DIR)
if(EXISTS "${START_DIR}/qcustomplot")
# add needed files to QCUSTOMPLOTDLLFILES
@@ -44,6 +26,6 @@ function(load_qcustomplot_dll START_DIR)
endforeach()
return()
else()
- message(STATUS "warning: ${START_DIR}/qcustomplot does not exist, please install submodule 'https://github.com/legerch/QCustomPlot-library'")
+ message(STATUS "warning: ${START_DIR}/qcustomplot does not exist, please install submodule 'https://github.com/UmbrellaLeaf5/qcustomplot'")
endif()
endfunction()
diff --git a/data_tools/data_manager/data_manager.cpp b/data_tools/data_manager/data_manager.cpp
index 5496615..b642ec0 100644
--- a/data_tools/data_manager/data_manager.cpp
+++ b/data_tools/data_manager/data_manager.cpp
@@ -56,7 +56,17 @@ void DataManager::Add(gui::Target* t) {
RemoveLastDuplicate();
}
-void DataManager::Add(lib::Target data) {
+void DataManager::Add(lib::Target& data) {
+ if (targets_.empty())
+ targets_.emplace_back(new gui::Airport(data));
+ else
+ targets_.emplace_back(new gui::Target(data));
+
+ CheckErrorValues();
+ RemoveLastDuplicate();
+}
+
+void DataManager::Add(const lib::Target& data) {
if (targets_.empty())
targets_.emplace_back(new gui::Airport(data));
else
@@ -76,7 +86,14 @@ void DataManager::Add(std::vector new_targets) {
RemoveLastDuplicate();
}
-void DataManager::Add(std::vector new_targets) {
+void DataManager::Add(std::vector& new_targets) {
+ for (const auto& target : new_targets) Add(target);
+
+ CheckErrorValues();
+ RemoveLastDuplicate();
+}
+
+void DataManager::Add(const std::vector& new_targets) {
for (const auto& target : new_targets) Add(target);
CheckErrorValues();
@@ -91,7 +108,15 @@ void DataManager::Set(std::vector targets) {
RemoveAllDuplicates();
}
-void DataManager::Set(std::vector targets) {
+void DataManager::Set(std::vector& targets) {
+ targets_.clear();
+ Add(targets);
+
+ CheckErrorValues();
+ RemoveAllDuplicates();
+}
+
+void DataManager::Set(const std::vector& targets) {
targets_.clear();
Add(targets);
@@ -127,7 +152,14 @@ void DataManager::Add(gui::Hill* h) {
RemoveLastDuplicate();
}
-void DataManager::Add(lib::Hill data) {
+void DataManager::Add(lib::Hill& data) {
+ hills_.emplace_back(new gui::Hill(data));
+
+ CheckErrorValues();
+ RemoveLastDuplicate();
+}
+
+void DataManager::Add(const lib::Hill& data) {
hills_.emplace_back(new gui::Hill(data));
CheckErrorValues();
@@ -144,7 +176,14 @@ void DataManager::Add(std::vector new_hills) {
RemoveLastDuplicate();
}
-void DataManager::Add(std::vector new_hills) {
+void DataManager::Add(std::vector& new_hills) {
+ for (const auto& hill : new_hills) Add(hill);
+
+ CheckErrorValues();
+ RemoveLastDuplicate();
+}
+
+void DataManager::Add(const std::vector& new_hills) {
for (const auto& hill : new_hills) Add(hill);
CheckErrorValues();
@@ -159,7 +198,15 @@ void DataManager::Set(std::vector hills) {
RemoveAllDuplicates();
}
-void DataManager::Set(std::vector hills) {
+void DataManager::Set(std::vector& hills) {
+ hills_.clear();
+ Add(hills);
+
+ CheckErrorValues();
+ RemoveAllDuplicates();
+}
+
+void DataManager::Set(const std::vector& hills) {
hills_.clear();
Add(hills);
@@ -195,7 +242,14 @@ void DataManager::Add(gui::TrappyCircle* tr_c) {
RemoveLastDuplicate();
}
-void DataManager::Add(lib::TrappyCircle data) {
+void DataManager::Add(lib::TrappyCircle& data) {
+ tr_circles_.emplace_back(new gui::TrappyCircle(data));
+
+ CheckErrorValues();
+ RemoveLastDuplicate();
+}
+
+void DataManager::Add(const lib::TrappyCircle& data) {
tr_circles_.emplace_back(new gui::TrappyCircle(data));
CheckErrorValues();
@@ -212,7 +266,14 @@ void DataManager::Add(std::vector new_tr_circles) {
RemoveLastDuplicate();
}
-void DataManager::Add(std::vector new_tr_circles) {
+void DataManager::Add(std::vector& new_tr_circles) {
+ for (const auto& tr_circle : new_tr_circles) Add(tr_circle);
+
+ CheckErrorValues();
+ RemoveLastDuplicate();
+}
+
+void DataManager::Add(const std::vector& new_tr_circles) {
for (const auto& tr_circle : new_tr_circles) Add(tr_circle);
CheckErrorValues();
@@ -227,7 +288,15 @@ void DataManager::Set(std::vector tr_circles) {
RemoveAllDuplicates();
}
-void DataManager::Set(std::vector tr_circles) {
+void DataManager::Set(std::vector& tr_circles) {
+ tr_circles_.clear();
+ Add(tr_circles);
+
+ CheckErrorValues();
+ RemoveAllDuplicates();
+}
+
+void DataManager::Set(const std::vector& tr_circles) {
tr_circles_.clear();
Add(tr_circles);
@@ -263,7 +332,14 @@ void DataManager::Add(gui::TrappyLine* tr_l) {
RemoveLastDuplicate();
}
-void DataManager::Add(lib::TrappyLine data) {
+void DataManager::Add(lib::TrappyLine& data) {
+ tr_lines_.emplace_back(new gui::TrappyLine(data));
+
+ CheckErrorValues();
+ RemoveLastDuplicate();
+}
+
+void DataManager::Add(const lib::TrappyLine& data) {
tr_lines_.emplace_back(new gui::TrappyLine(data));
CheckErrorValues();
@@ -280,7 +356,14 @@ void DataManager::Add(std::vector new_tr_lines) {
RemoveLastDuplicate();
}
-void DataManager::Add(std::vector new_tr_lines) {
+void DataManager::Add(std::vector& new_tr_lines) {
+ for (auto& tr_line : new_tr_lines) Add(tr_line);
+
+ CheckErrorValues();
+ RemoveLastDuplicate();
+}
+
+void DataManager::Add(const std::vector& new_tr_lines) {
for (auto& tr_line : new_tr_lines) Add(tr_line);
CheckErrorValues();
@@ -295,7 +378,15 @@ void DataManager::Set(std::vector tr_lines) {
RemoveAllDuplicates();
}
-void DataManager::Set(std::vector tr_lines) {
+void DataManager::Set(std::vector& tr_lines) {
+ tr_lines_.clear();
+ Add(tr_lines);
+
+ CheckErrorValues();
+ RemoveAllDuplicates();
+}
+
+void DataManager::Set(const std::vector& tr_lines) {
tr_lines_.clear();
Add(tr_lines);
diff --git a/data_tools/data_manager/data_manager.h b/data_tools/data_manager/data_manager.h
index bf54447..0f5f398 100644
--- a/data_tools/data_manager/data_manager.h
+++ b/data_tools/data_manager/data_manager.h
@@ -28,9 +28,14 @@ class DataManager {
void Set(std::vector);
// for lib::Target
- void Add(lib::Target data);
- void Add(std::vector);
- void Set(std::vector);
+ void Add(lib::Target& data);
+ void Add(const lib::Target& data);
+
+ void Add(std::vector&);
+ void Add(const std::vector&);
+
+ void Set(std::vector&);
+ void Set(const std::vector&);
/**
* @brief Возвращает значение Targets
@@ -52,9 +57,14 @@ class DataManager {
void Set(std::vector);
// for lib::Hill
- void Add(lib::Hill data);
- void Add(std::vector);
- void Set(std::vector);
+ void Add(lib::Hill& data);
+ void Add(const lib::Hill& data);
+
+ void Add(std::vector&);
+ void Add(const std::vector&);
+
+ void Set(std::vector&);
+ void Set(const std::vector&);
/**
* @brief Возвращает значение Hills
@@ -76,21 +86,27 @@ class DataManager {
void Set(std::vector);
// for lib::TrappyCircle
- void Add(lib::TrappyCircle data);
- void Add(std::vector);
- void Set(std::vector);
+ void Add(lib::TrappyCircle& data);
+ void Add(const lib::TrappyCircle& data);
+
+ void Add(std::vector&);
+ void Add(const std::vector&);
+
+ void Set(std::vector&);
+ void Set(const std::vector&);
/**
- * @brief Возвращает значение Trappy Circles
+ * @brief Возвращает значение TrappyCircles
* @return std::vector: указатели на объекты опасной зоны
*/
std::vector GetTrappyCirclesPtrs();
/**
- * @brief Возвращает значение Trappy Circles
+ * @brief Возвращает значение TrappyCircles
* @return std::vector: объекты опасной зоны
*/
std::vector GetTrappyCircles() const;
+
// ---------------------- TrappyLine methods ----------------------
// for gui::TrappyLine
@@ -99,18 +115,23 @@ class DataManager {
void Set(std::vector);
// for lib::TrappyLine
- void Add(lib::TrappyLine data);
- void Add(std::vector);
- void Set(std::vector);
+ void Add(lib::TrappyLine& data);
+ void Add(const lib::TrappyLine& data);
+
+ void Add(std::vector&);
+ void Add(const std::vector&);
+
+ void Set(std::vector&);
+ void Set(const std::vector&);
/**
- * @brief Возвращает значение Trappy Lines
+ * @brief Возвращает значение TrappyLines
* @return std::vector: указатели на объекты оп. перелета
*/
std::vector GetTrappyLinesPtrs();
/**
- * @brief Возвращает значение Trappy Lines
+ * @brief Возвращает значение TrappyLines
* @return std::vector: объекты оп. перелета
*/
std::vector GetTrappyLines() const;
diff --git a/data_tools/plot_area/check_errors.cpp b/data_tools/plot_area/check_errors.cpp
new file mode 100644
index 0000000..a72c569
--- /dev/null
+++ b/data_tools/plot_area/check_errors.cpp
@@ -0,0 +1,186 @@
+// header file:
+#include "plot_area.h"
+
+// our code libs:
+#include "math/optimal_way/helpers_functions.h"
+
+namespace data_tools {
+
+bool IsPointInsideHill(lib::Point point, std::vector vertices) {
+ bool inside = false;
+ int j = vertices.size() - 1;
+ for (size_t i = 0; i < vertices.size(); i++) {
+ if (((vertices[i].y < point.y && vertices[j].y >= point.y) ||
+ (vertices[j].y < point.y && vertices[i].y >= point.y)) &&
+ (vertices[i].x + (point.y - vertices[i].y) /
+ (vertices[j].y - vertices[i].y) *
+ (vertices[j].x - vertices[i].x) <
+ point.x))
+ inside = !inside;
+ j = i;
+ }
+ return inside;
+}
+
+void PlotArea::CheckIntersectionsBetweenTrappyCircles() {
+ if (manager_->GetTrappyCircles().empty()) return;
+
+ for (size_t i = 0; i < manager_->GetTrappyCircles().size() - 1; i++) {
+ const auto trc = manager_->GetTrappyCircles()[i];
+
+ for (size_t j = i + 1; j < manager_->GetTrappyCircles().size(); j++) {
+ const auto trc2 = manager_->GetTrappyCircles()[j];
+
+ if (lib::DistanceBetweenPoints(trc2.GetCenter(), trc.GetCenter()) <
+ trc2.GetRadius() + trc.GetRadius()) {
+ std::string text =
+ "There are intersections between TrappyCircles! Id's: ";
+ text += std::to_string(trc.GetData().GetId());
+ text += " and ";
+ text += std::to_string(trc2.GetData().GetId());
+ throw std::invalid_argument(text);
+ }
+ }
+ }
+}
+
+void PlotArea::CheckIntersectionsBetweenHills() {
+ if (manager_->GetHillsPtrs().empty()) return;
+
+ for (size_t i = 0; i < manager_->GetHills().size() - 1; i++) {
+ std::vector vec;
+ for (const auto& v : manager_->GetHillsPtrs()[i]->GetVertices())
+ vec.push_back(math::Point(v));
+
+ for (size_t j = i + 1; j < manager_->GetHillsPtrs().size(); j++) {
+ std::vector vec2;
+ for (const auto& v : manager_->GetHillsPtrs()[j]->GetVertices())
+ vec2.push_back(math::Point(v));
+
+ for (size_t k = 0; k < vec2.size(); k++) {
+ if (math::AreThereIntersections(math::PolygonObstacle(vec), vec2[k],
+ vec2[(k + 1) % vec2.size()]) ||
+ IsPointInsideHill(manager_->GetHillsPtrs()[j]->GetVertices()[k],
+ manager_->GetHillsPtrs()[i]->GetVertices())) {
+ std::string text = "There are intersections between Hills! Id's: ";
+ text +=
+ std::to_string(manager_->GetHillsPtrs()[i]->GetData().GetId());
+ text += " and ";
+ text +=
+ std::to_string(manager_->GetHillsPtrs()[j]->GetData().GetId());
+ throw std::invalid_argument(text);
+ }
+ }
+ }
+ }
+}
+
+void PlotArea::CheckTrappyCircles() {
+ // Проверка на пересечения с другими TrappyCircle
+ CheckIntersectionsBetweenTrappyCircles();
+
+ if (manager_->GetTrappyCircles().empty()) return;
+
+ for (const auto& trc : manager_->GetTrappyCircles()) {
+ // Проверка на то, что какой-то Target находится внутри TrappyCircle
+ for (const auto& target : manager_->GetTargets()) {
+ if (lib::DistanceBetweenPoints(target.GetPoint(), trc.GetCenter()) <=
+ trc.GetRadius()) {
+ std::string text = "There is Target in TrappyCircle! Id's: ";
+ text += std::to_string(target.GetData().GetId());
+ text += " and ";
+ text += std::to_string(trc.GetData().GetId());
+ throw std::invalid_argument(text);
+ }
+ }
+
+ // Проверка на пересечения с Hill
+ for (const auto& hill : manager_->GetHillsPtrs()) {
+ std::vector vec;
+ for (const auto& v : hill->GetVertices()) vec.push_back(math::Point(v));
+
+ for (size_t k = 0; k < vec.size(); k++) {
+ if (math::AreThereIntersections(
+ math::CircleObstacle(trc.GetCenter(), trc.GetRadius()), vec[k],
+ vec[(k + 1) % vec.size()]) ||
+ IsPointInsideHill(trc.GetCenter(), hill->GetVertices()) ||
+ lib::DistanceBetweenPoints(
+ trc.GetCenter(), hill->GetVertices()[k]) < trc.GetRadius()) {
+ std::string text =
+ "There are intersections between TrappyCircle and Hill! Id's: ";
+ text += std::to_string(trc.GetData().GetId());
+ text += " and ";
+ text += std::to_string(hill->GetData().GetId());
+ throw std::invalid_argument(text);
+ }
+ }
+ }
+ }
+}
+
+void PlotArea::CheckTrappyLines() {
+ auto l = manager_->GetTrappyLinesPtrs().size();
+ auto n = manager_->GetTargetsPtrs().size() + amount_of_robots_ - 1;
+ if (l == 0) return;
+
+ // прекрасная формула
+ if ((pow(n, 2) - n) / 2 - l < n)
+ throw std::invalid_argument("There are too many TrappyLines here!");
+}
+
+void PlotArea::CheckTargets() {
+ if (manager_->GetTargetsPtrs().size() > 30) {
+ std::string text = "There are too many Targets: ";
+ text += std::to_string(manager_->GetTargetsPtrs().size());
+ text += " > 30 \n";
+ text += "(sorry, but our algo is now to slow for more)";
+ throw std::invalid_argument(text);
+ }
+}
+
+void PlotArea::CheckHills() {
+ for (const auto& hill : manager_->GetHills()) {
+ // Проверка на пересечения с другими Hill
+ CheckIntersectionsBetweenHills();
+
+ // Проверка на выпуклость многоугольника
+ // Определяем знак векторного произведения
+ QVector2D v1 = QVector2D(hill.GetVertices()[1].x - hill.GetVertices()[0].x,
+ hill.GetVertices()[1].y - hill.GetVertices()[0].y);
+ QVector2D v2 = QVector2D(hill.GetVertices()[2].x - hill.GetVertices()[1].x,
+ hill.GetVertices()[2].y - hill.GetVertices()[1].y);
+ short sign = (v1.x() * v2.y() - v1.y() * v2.x()) /
+ abs(v1.x() * v2.y() - v1.y() * v2.x());
+
+ size_t sz = hill.GetVertices().size();
+ for (size_t i = 1; i < sz; i++) {
+ QVector2D vec1 = QVector2D(
+ hill.GetVertices()[(i + 1) % sz].x - hill.GetVertices()[i % sz].x,
+ hill.GetVertices()[(i + 1) % sz].y - hill.GetVertices()[i % sz].y);
+ QVector2D vec2 = QVector2D(hill.GetVertices()[(i + 2) % sz].x -
+ hill.GetVertices()[(i + 1) % sz].x,
+ hill.GetVertices()[(i + 2) % sz].y -
+ hill.GetVertices()[(i + 1) % sz].y);
+
+ double product = vec1.x() * vec2.y() - vec1.y() * vec2.x();
+
+ if (product == 0 || product / abs(product) != sign) {
+ std::string text = "There is non-convex polygon! Id: ";
+ text += std::to_string(hill.GetData().GetId());
+ throw std::invalid_argument(text);
+ }
+ }
+
+ // Проверка на то, что какой-то Target находится внутри или на Hill
+ for (const auto& target : manager_->GetTargets())
+ if (IsPointInsideHill(target.GetPoint(), hill.GetVertices())) {
+ std::string text = "There is Target in Hill! Id's: ";
+ text += std::to_string(target.GetData().GetId());
+ text += " and ";
+ text += std::to_string(hill.GetData().GetId());
+ throw std::invalid_argument(text);
+ }
+ }
+}
+
+} // namespace data_tools
diff --git a/data_tools/plot_area/plot_area.cpp b/data_tools/plot_area/plot_area.cpp
index 2ab0fe8..a0bef65 100644
--- a/data_tools/plot_area/plot_area.cpp
+++ b/data_tools/plot_area/plot_area.cpp
@@ -5,7 +5,7 @@ namespace data_tools {
void PlotArea::Setup(DataManager* manager) { manager_.reset(manager); }
-void PlotArea::Redraw() {
+void PlotArea::ReDraw() {
plot_->clearPlottables();
plot_->clearItems();
@@ -19,7 +19,50 @@ void PlotArea::Redraw() {
for (auto& tr_line : manager_->GetTrappyLinesPtrs())
tr_line->Draw(plot_.get());
+ trajectory_->Clear();
+
plot_->replot();
}
-} // namespace data_tools
\ No newline at end of file
+void PlotArea::ReDrawTrajectory() {
+ ReDraw();
+
+ try {
+ CheckTrappyCircles();
+ CheckHills();
+ CheckTrappyLines();
+ CheckTargets();
+
+ CalculateTrajectory();
+ trajectory_->Draw(plot_.get());
+ plot_->replot();
+ } catch (const std::exception& e) {
+ QMessageBox::warning(plot_.get(), "Cannot calculate trajectory!", e.what());
+ }
+}
+
+void PlotArea::CalculateTrajectory() {
+ auto lib_targets = std::vector(manager_->GetTargets().size());
+ for (size_t i = 0; i < lib_targets.size(); i++)
+ lib_targets[i] = manager_->GetTargets()[i].GetData();
+
+ auto lib_hills = std::vector(manager_->GetHills().size());
+ for (size_t i = 0; i < lib_hills.size(); i++)
+ lib_hills[i] = manager_->GetHills()[i].GetData();
+
+ auto lib_tr_lines =
+ std::vector(manager_->GetTrappyLines().size());
+ for (size_t i = 0; i < lib_tr_lines.size(); i++)
+ lib_tr_lines[i] = manager_->GetTrappyLines()[i].GetData();
+
+ auto lib_tr_circles =
+ std::vector(manager_->GetTrappyCircles().size());
+ for (size_t i = 0; i < lib_tr_circles.size(); i++)
+ lib_tr_circles[i] = manager_->GetTrappyCircles()[i].GetData();
+
+ trajectory_->Calculate(lib_targets, lib_hills, lib_tr_circles, lib_tr_lines,
+ amount_of_robots_);
+ if (lib_targets.size() > 1) robot_->SetTrajectory(trajectory_.get());
+}
+
+} // namespace data_tools
diff --git a/data_tools/plot_area/plot_area.h b/data_tools/plot_area/plot_area.h
index ede6123..f697645 100644
--- a/data_tools/plot_area/plot_area.h
+++ b/data_tools/plot_area/plot_area.h
@@ -21,13 +21,40 @@ class PlotArea {
void SetPlot(QCustomPlot* plot) { plot_.reset(plot); }
/// @brief Перерисовывает на полотне все объекты и обновляет данные
- void Redraw();
+ void ReDraw();
+
+ gui::FlyingRobot* GetRobot() { return robot_.get(); }
+
+ gui::Trajectory* GetTrajectory() { return trajectory_.get(); }
+
+ /// @brief Перерисовывает на полотне траекторию
+ void ReDrawTrajectory();
+
+ void ClearTrajectory() { trajectory_->Clear(); }
+
+ size_t TrajectorySize() const { return trajectory_->Segments().size(); }
+
+ void SetAmountOfRobots(unsigned short amount) { amount_of_robots_ = amount; }
private:
+ /// @brief Расчет вектора сегментов по заданным объектам на полотне
+ void CalculateTrajectory();
+
// i love unique_ptr's, i love logic schemes
std::unique_ptr plot_;
-
std::unique_ptr manager_;
+
+ std::unique_ptr trajectory_{new gui::Trajectory()};
+ std::unique_ptr robot_{new gui::FlyingRobot()};
+
+ unsigned short amount_of_robots_ = 1;
+
+ void CheckHills();
+ void CheckTrappyCircles();
+ void CheckTrappyLines();
+ void CheckTargets();
+ void CheckIntersectionsBetweenTrappyCircles();
+ void CheckIntersectionsBetweenHills();
};
} // namespace data_tools
diff --git a/data_tools/tables_connection/tables_connection.cpp b/data_tools/tables_connection/tables_connection.cpp
index ba3db36..24eecb1 100644
--- a/data_tools/tables_connection/tables_connection.cpp
+++ b/data_tools/tables_connection/tables_connection.cpp
@@ -170,7 +170,8 @@ void TablesConnection::UpdateTable(
"Wrong targets numbers in TrappyLines!");
manager_->Remove(gui::ObjectType::TrappyLines, i);
- area_->Redraw();
+ tr_lines_table_->removeColumn(i);
+ area_->ReDraw();
}
}
@@ -249,10 +250,10 @@ void TablesConnection::UpdateTable(gui::ObjectType obj_type) {
}
void TablesConnection::UpdateTables() {
- UpdateTable(manager_->GetTargets());
- UpdateTable(manager_->GetHills());
- UpdateTable(manager_->GetTrappyLines());
- UpdateTable(manager_->GetTrappyCircles());
+ UpdateTable(gui::ObjectType::Targets);
+ UpdateTable(gui::ObjectType::Hills);
+ UpdateTable(gui::ObjectType::TrappyCircles);
+ UpdateTable(gui::ObjectType::TrappyLines);
UpdateTablesConnections();
}
@@ -273,7 +274,7 @@ void TablesConnection::TargetsItemChanged(int row, int column) {
manager_->GetTargetsPtrs()[column]->SetPoint(x_item->text().toDouble(),
y_item->text().toDouble());
- area_->Redraw();
+ area_->ReDraw();
}
} catch (const std::exception& e) {
@@ -284,7 +285,7 @@ void TablesConnection::TargetsItemChanged(int row, int column) {
// (в случае удаления к.т., которая была привязана, надо обновить)
UpdateTable(gui::ObjectType::TrappyLines);
- area_->Redraw();
+ area_->ReDraw();
}
}
@@ -319,7 +320,7 @@ void TablesConnection::HillsItemChanged(int row, int column) {
manager_->GetHillsPtrs()[column]->GetVertices()[p_index].y =
y_item->text().toDouble();
- area_->Redraw();
+ area_->ReDraw();
}
} catch (const std::exception& e) {
@@ -327,7 +328,7 @@ void TablesConnection::HillsItemChanged(int row, int column) {
manager_->Remove(gui::ObjectType::Hills, static_cast(column));
UpdateTable(gui::ObjectType::Hills);
- area_->Redraw();
+ area_->ReDraw();
}
}
@@ -348,7 +349,7 @@ void TablesConnection::TrappyCirclesItemChanged(int row, int column) {
manager_->GetTrappyCirclesPtrs()[column]->SetRadius(
r_item->text().toDouble());
- area_->Redraw();
+ area_->ReDraw();
}
} catch (const std::exception& e) {
@@ -357,7 +358,7 @@ void TablesConnection::TrappyCirclesItemChanged(int row, int column) {
manager_->Remove(gui::ObjectType::TrappyCircles,
static_cast(column));
UpdateTable(gui::ObjectType::TrappyCircles);
- area_->Redraw();
+ area_->ReDraw();
}
}
@@ -382,18 +383,18 @@ void TablesConnection::TrappyLinesItemChanged(int row, int column) {
manager_->GetTargetsPtrs()[t_1_index],
manager_->GetTargetsPtrs()[t_2_index]);
- area_->Redraw();
+ area_->ReDraw();
} else
throw std::invalid_argument("Wrong targets numbers in TrappyLines!");
}
} catch (const std::exception& e) {
- QMessageBox::warning(targets_table_.get(), "Error!", e.what());
+ QMessageBox::warning(tr_lines_table_.get(), "Error!", e.what());
manager_->Remove(gui::ObjectType::TrappyLines, static_cast(column));
UpdateTable(gui::ObjectType::TrappyLines);
- area_->Redraw();
+ area_->ReDraw();
}
}
@@ -403,9 +404,9 @@ void TablesConnection::RemoveTargetItem() {
DisableTablesConnections();
manager_->Remove(gui::ObjectType::Targets, selected_column_);
- area_->Redraw();
- UpdateTable(manager_->GetTargets());
- UpdateTable(manager_->GetTrappyLines());
+ area_->ReDraw();
+ UpdateTable(gui::ObjectType::Targets);
+ UpdateTable(gui::ObjectType::TrappyLines);
UpdateTablesConnections();
}
@@ -414,8 +415,8 @@ void TablesConnection::RemoveHillItem() {
DisableTablesConnections();
manager_->Remove(gui::ObjectType::Hills, selected_column_);
- area_->Redraw();
- UpdateTable(manager_->GetHills());
+ area_->ReDraw();
+ UpdateTable(gui::ObjectType::Hills);
UpdateTablesConnections();
}
@@ -424,8 +425,8 @@ void TablesConnection::RemoveTrappyCircleItem() {
DisableTablesConnections();
manager_->Remove(gui::ObjectType::TrappyCircles, selected_column_);
- area_->Redraw();
- UpdateTable(manager_->GetTrappyCircles());
+ area_->ReDraw();
+ UpdateTable(gui::ObjectType::TrappyCircles);
UpdateTablesConnections();
}
@@ -434,8 +435,8 @@ void TablesConnection::RemoveTrappyLineItem() {
DisableTablesConnections();
manager_->Remove(gui::ObjectType::TrappyLines, selected_column_);
- area_->Redraw();
- UpdateTable(manager_->GetTrappyLines());
+ area_->ReDraw();
+ UpdateTable(gui::ObjectType::TrappyLines);
UpdateTablesConnections();
}
diff --git a/gui/_objects.h b/gui/_objects.h
index 02b5287..21c8773 100644
--- a/gui/_objects.h
+++ b/gui/_objects.h
@@ -8,7 +8,6 @@
#include "airport.h"
#include "flying_robot.h"
#include "hill.h"
-#include "target.h"
#include "trajectory.h"
#include "trappy_circle.h"
#include "trappy_line.h"
diff --git a/gui/flying_robot.cpp b/gui/flying_robot.cpp
index 8254aab..92168ab 100644
--- a/gui/flying_robot.cpp
+++ b/gui/flying_robot.cpp
@@ -1,35 +1,121 @@
+// header file:
#include "flying_robot.h"
-// Константа, которая нужна для того, чтобы определить размер отрезков, на
-// которые будет делиться каждый Segment
-constexpr double distribution = 0.03;
+namespace gui {
-void gui::FlyingRobot::SetNewSegment() {
+void FlyingRobot::SetTrajectory(Trajectory* trj) {
+ trajectory_ = trj;
+ if (!trajectory_) return;
+
+ curr_point_ = trj->Segments()[0].Start();
+ index_of_segment_ = 0;
+
+ if (trajectory_->Segments()[0].IsArc())
+ UpdateCircleFields();
+ else
+ UpdateLineFields();
+}
+
+void FlyingRobot::SetNewPositionOnLine() {
+ if (count_of_partitions_ == 0) {
+ UpdateSegment();
+ } else {
+ count_of_partitions_--;
+
+ // Полотно обновляется раз в 5 миллисекунд, скорость робота такая: смещение
+ // на 1 происходит за 20 вызовов функции, поэтому мы и умножаем на 1/20
+ curr_point_.x += line_const_ * cos_of_line_;
+ curr_point_.y += line_const_ * sin_of_line_;
+ }
+}
+
+void FlyingRobot::SetNewPositionOnCircle() {
+ if (count_of_partitions_ == 0)
+ UpdateSegment();
+ else {
+ count_of_partitions_--;
+
+ curr_point_.x = trajectory_->Segments()[index_of_segment_].Radius() *
+ cos(curr_angle_on_circle_) +
+ trajectory_->Segments()[index_of_segment_].Center().x;
+ curr_point_.y = trajectory_->Segments()[index_of_segment_].Radius() *
+ sin(curr_angle_on_circle_) +
+ trajectory_->Segments()[index_of_segment_].Center().y;
+
+ if (clockwise_)
+ curr_angle_on_circle_ -= distribution_of_angle_;
+ else
+ curr_angle_on_circle_ += distribution_of_angle_;
+ }
+}
+
+void FlyingRobot::UpdateSegment() {
index_of_segment_++;
- if (index_of_segment_ == trajectory_.Segments().size()) index_of_segment_ = 0;
+ if (index_of_segment_ == trajectory_->Segments().size())
+ index_of_segment_ = 0;
- curr_point_ = trajectory_.Segments()[index_of_segment_].Start();
+ curr_point_ = trajectory_->Segments()[index_of_segment_].Start();
- if (trajectory_.Segments()[index_of_segment_].IsArc())
- SetStartAngleAndClockwise();
+ if (trajectory_->Segments()[index_of_segment_].IsArc())
+ UpdateCircleFields();
else
- SetAnglesOfLine();
+ UpdateLineFields();
}
-void gui::FlyingRobot::SetAnglesOfLine() {
- lib::Point p = trajectory_.Segments()[index_of_segment_].End() -
- trajectory_.Segments()[index_of_segment_].Start();
+void FlyingRobot::UpdateLineFields() {
+ lib::Point p = trajectory_->Segments()[index_of_segment_].End() -
+ trajectory_->Segments()[index_of_segment_].Start();
double R = sqrt(p.x * p.x + p.y * p.y);
+ double dis = lib::DistanceBetweenPoints(
+ trajectory_->Segments()[index_of_segment_].Start(),
+ trajectory_->Segments()[index_of_segment_].End());
+
+ switch (speed_) {
+ case SpeedOfRobot::Low:
+ line_const_ = 0.01;
+ break;
+
+ case SpeedOfRobot::Medium:
+ line_const_ = 0.05;
+ break;
+
+ case SpeedOfRobot::High:
+ line_const_ = 0.375;
+ break;
+ }
+
+ count_of_partitions_ = dis / line_const_;
+
cos_of_line_ = p.x / R;
sin_of_line_ = p.y / R;
}
-void gui::FlyingRobot::SetStartAngleAndClockwise() {
- auto angles = trajectory_.Segments()[index_of_segment_].ToAnglesOnCircle();
+void FlyingRobot::UpdateCircleFields() {
+ auto angles = trajectory_->Segments()[index_of_segment_].ToAnglesOnCircle();
+
+ double len_sector = trajectory_->Segments()[index_of_segment_].Radius() *
+ (abs(angles.second - angles.first) * M_PI / 180);
+
+ switch (speed_) {
+ case SpeedOfRobot::Low: {
+ count_of_partitions_ = len_sector * 100;
+ break;
+ }
+ case SpeedOfRobot::Medium: {
+ count_of_partitions_ = len_sector * 20;
+ break;
+ }
+ case SpeedOfRobot::High: {
+ count_of_partitions_ = len_sector * 20 / 7.5;
+ break;
+ }
+ }
curr_angle_on_circle_ = angles.first * M_PI / 180;
+ distribution_of_angle_ =
+ abs(angles.second - angles.first) / count_of_partitions_ * M_PI / 180;
if (angles.second * M_PI / 180 - curr_angle_on_circle_ < 0)
clockwise_ = true;
@@ -37,58 +123,27 @@ void gui::FlyingRobot::SetStartAngleAndClockwise() {
clockwise_ = false;
}
-bool gui::FlyingRobot::IsCloseToEndPoint() {
- lib::Point new_p =
- curr_point_ - trajectory_.Segments()[index_of_segment_].End();
-
- return sqrt(new_p.x * new_p.x + new_p.y * new_p.y) < 0.1;
-}
-
-void gui::FlyingRobot::SetNewPositionOnLine() {
- if (IsCloseToEndPoint())
- SetNewSegment();
- else {
- curr_point_.x += distribution * cos_of_line_;
- curr_point_.y += distribution * sin_of_line_;
- }
-}
-
-void gui::FlyingRobot::SetNewPositionOnCircle() {
- if (IsCloseToEndPoint())
- SetNewSegment();
- else {
- curr_point_.x = trajectory_.Segments()[index_of_segment_].Radius() *
- cos(curr_angle_on_circle_) +
- trajectory_.Segments()[index_of_segment_].Center().x;
- curr_point_.y = trajectory_.Segments()[index_of_segment_].Radius() *
- sin(curr_angle_on_circle_) +
- trajectory_.Segments()[index_of_segment_].Center().y;
-
- if (clockwise_)
- curr_angle_on_circle_ -= distribution / 3;
- else
- curr_angle_on_circle_ += distribution / 3;
- }
-}
+void FlyingRobot::Draw(QCustomPlot* plot) {
+ if (!trajectory_) throw std::invalid_argument("The trajectory is not built!");
-void gui::FlyingRobot::Draw(QCustomPlot* plot) {
graph_ = plot->addGraph(plot->xAxis, plot->yAxis);
-
graph_->setPen(QColor(50, 50, 50, 255));
graph_->setLineStyle(QCPGraph::lsNone);
graph_->setScatterStyle(
- QCPScatterStyle(QPixmap("../images/flying_robot.png")
- .scaled(QSize(24, 24), Qt::KeepAspectRatio)));
-
+ QCPScatterStyle(QPixmap("../images/flying_robot_pixel.png")
+ .scaled(QSize(36, 16), Qt::KeepAspectRatio)));
graph_->setData({curr_point_.x}, {curr_point_.y});
}
-void gui::FlyingRobot::ReDraw(QCustomPlot* plot) {
- if (trajectory_.Segments()[index_of_segment_].IsArc())
+void FlyingRobot::ReDraw(QCustomPlot* plot) {
+ if (trajectory_->Segments()[index_of_segment_].IsArc())
SetNewPositionOnCircle();
else
SetNewPositionOnLine();
+
graph_->setData({curr_point_.x}, {curr_point_.y});
plot->replot();
}
+
+} // namespace gui
diff --git a/gui/flying_robot.h b/gui/flying_robot.h
index 68f8bf7..ab57701 100644
--- a/gui/flying_robot.h
+++ b/gui/flying_robot.h
@@ -1,6 +1,8 @@
#pragma once
#include "trajectory.h"
+enum class SpeedOfRobot { Low, Medium, High };
+
namespace gui {
/**
@@ -14,22 +16,24 @@ class FlyingRobot : public Drawable {
/**
* @brief Инициализирует новый экземпляр FlyingRobot
- * @param gui::Trajectory: траетория
+ * @param gui::Trajectory: траектория
*/
- FlyingRobot(gui::Trajectory trj)
- : trajectory_{trj}, curr_point_{trj.Segments()[0].Start()} {
+ FlyingRobot(gui::Trajectory* trj)
+ : trajectory_{trj}, curr_point_{trj->Segments()[0].Start()} {
// Сразу задаем нужные приватные поля при инициализации экземпляра класса, в
// зависимости от того, lib::Segment является аркой или прямой линией
- if (trajectory_.Segments()[0].IsArc())
- SetStartAngleAndClockwise();
+ if (trajectory_->Segments()[0].IsArc())
+ UpdateCircleFields();
else
- SetAnglesOfLine();
+ UpdateLineFields();
}
+
/**
* @brief Отрисовывает png картинку
* @param plot: указатель на полотно
*/
void Draw(QCustomPlot* plot) override;
+
/**
* @brief Эта функция нужна для того, чтобы обновлять позицию картинки на
* полотне
@@ -37,47 +41,61 @@ class FlyingRobot : public Drawable {
*/
void ReDraw(QCustomPlot* plot);
+ void SetTrajectory(gui::Trajectory* trj);
+
+ gui::Trajectory* GetTrajectory() const { return trajectory_; }
+
+ void SetSpeed(SpeedOfRobot speed) { speed_ = speed; }
+
private:
- gui::Trajectory trajectory_;
+ gui::Trajectory* trajectory_;
QCPGraph* graph_{nullptr};
+ SpeedOfRobot speed_ = SpeedOfRobot::Medium;
size_t index_of_segment_ = 0;
lib::Point curr_point_;
+
+ // Количество отрезков, на которые будет делиться lib::Segment
+ int count_of_partitions_;
+
+ // Поля для положения на линии
+ double line_const_;
double cos_of_line_;
double sin_of_line_;
+
+ // Поля для положения на окружности
+
double curr_angle_on_circle_;
+ // Величина смещения угла
+ double distribution_of_angle_;
+ // Если true, что двигаемся по часовой
bool clockwise_;
/**
- * @brief Обновляет поля cos_of_line_ и sin_of_line_, где
- * cos_of_line_ - косинус, sin_of_line_ - синус между линией и осью Ox
- * соответственно
+ * @brief Обновляет поля cos_of_line_ и sin_of_line_
*/
- void SetAnglesOfLine();
+ void UpdateLineFields();
+
/**
- * @brief Обновляет поля curr_angle_on_circle_ и clockwise_, где
- * curr_angle_on_circle_ - текущее положение точки на окружности в радианах,
- * clockwise_ - если true, то двигаемся по часовой, если false - то против
+ * @brief Обновляет поля curr_angle_on_circle_, clockwise_ и
+ * distribution_of_angle_
*/
- void SetStartAngleAndClockwise();
+ void UpdateCircleFields();
+
/**
* @brief Берет следующий Segment, обновляя приватные поля
*/
- void SetNewSegment();
+ void UpdateSegment();
+
/**
* @brief Обновляет текущее положение точки на линии
*/
void SetNewPositionOnLine();
+
/**
* @brief Обновляет текущее положение точки на окружности
*/
void SetNewPositionOnCircle();
- /**
- * @brief Проверяет, что текущая точка находится близко к конечной точке
- * Segment'a
- * @return true, если точка находится близко
- */
- bool IsCloseToEndPoint();
};
} // namespace gui
diff --git a/gui/hill.h b/gui/hill.h
index b6623fe..36fd749 100644
--- a/gui/hill.h
+++ b/gui/hill.h
@@ -21,11 +21,12 @@ class Hill : public Drawable {
* @param points: список точек
* @param color: цвет
*/
- Hill(std::initializer_list points,
+ Hill(const std::initializer_list& points,
QColor color = QColor(50, 200, 50, 255))
: data_(points), color_{color} {}
- Hill(std::vector points, QColor color = QColor(50, 200, 50, 255))
+ Hill(const std::vector& points,
+ QColor color = QColor(50, 200, 50, 255))
: data_(points), color_{color} {}
Hill(const lib::Hill& data, QColor color = QColor(50, 200, 50, 255))
diff --git a/gui/segment.h b/gui/segment.h
index f901caf..4e1e412 100644
--- a/gui/segment.h
+++ b/gui/segment.h
@@ -14,6 +14,8 @@ class Segment : private Drawable {
public:
Segment() = default;
+ Segment(const lib::Segment& data) : data_{data} {}
+
Segment(const lib::Point& start, const lib::Point& end) : data_(start, end) {}
Segment(const lib::Point& start, const lib::Point& end,
diff --git a/gui/trajectory.cpp b/gui/trajectory.cpp
index 73e9233..cdde042 100644
--- a/gui/trajectory.cpp
+++ b/gui/trajectory.cpp
@@ -1,2 +1,26 @@
// header file
#include "trajectory.h"
+
+namespace gui {
+
+void Trajectory::Draw(QCustomPlot* plot) {
+ for (auto& segment : segments_) segment.Draw(plot);
+}
+
+void Trajectory::Calculate(const std::vector& targets,
+ const std::vector& hills,
+ const std::vector& tr_circles,
+ const std::vector& tr_lines,
+ unsigned short amount_of_robots) {
+ segments_.clear();
+
+ if (targets.empty()) return;
+
+ for (auto seg : math::TrajectoryCalculator(targets, tr_lines, tr_circles,
+ hills, amount_of_robots)
+ .GetTrajectory()) {
+ segments_.push_back(gui::Segment(seg));
+ }
+}
+
+} // namespace gui
diff --git a/gui/trajectory.h b/gui/trajectory.h
index 18ba8ef..d450abb 100644
--- a/gui/trajectory.h
+++ b/gui/trajectory.h
@@ -2,6 +2,7 @@
// our code libs:
#include "lib/_objects.h"
+#include "math/trajectory.h"
#include "segment.h"
namespace gui {
@@ -14,13 +15,25 @@ class Trajectory : private Drawable {
public:
Trajectory() = default;
- Trajectory(std::vector segments) : segments_{segments} {}
+ /**
+ * @brief Инициализирует новый экземпляр Trajectory
+ * @param segments: вектор отрезков траектории
+ */
+ Trajectory(const std::vector& segments) : segments_{segments} {}
- Trajectory(std::vector targets, std::vector hills,
- std::vector tr_circles,
- std::vector tr_lines) {
- Calculate(targets, hills, tr_circles, tr_lines);
- }
+ /**
+ * @brief Расчет вектора сегментов по заданным объектам на полотне
+ * @param targets: контрольные точки
+ * @param hills: многоугольные препятствия
+ * @param tr_circles: круговые препятствия
+ * @param tr_lines: запрещённые перелеты
+ * @param amount_of_robots: кол-во летающих роботов
+ */
+ void Calculate(const std::vector& targets,
+ const std::vector& hills,
+ const std::vector& tr_circles,
+ const std::vector& tr_lines,
+ unsigned short amount_of_robots);
/**
* @brief Возвращает вектор сегментов
@@ -38,30 +51,12 @@ class Trajectory : private Drawable {
* @brief Отрисовывает фигуру на полотне
* @param plot: указатель на полотно
*/
- void Draw(QCustomPlot* plot) override {
- for (auto& segment : segments_) segment.Draw(plot);
- }
+ void Draw(QCustomPlot* plot) override;
+
+ void Clear() { segments_.clear(); }
private:
std::vector segments_;
-
- /// @brief Расчет вектора сегментов по заданным объектам на полотне
- void Calculate(std::vector targets, std::vector hills,
- std::vector tr_circles,
- std::vector tr_lines) {
- Q_UNUSED(targets);
- Q_UNUSED(hills);
- Q_UNUSED(tr_circles);
- Q_UNUSED(tr_lines);
-
- /**
- * Здесь должно быть использование функционала из math
-
- * Они должны нам вернуть std::vector,
- * которые мы переведём в std::vector
- * и запишем в segments_
- */
- }
};
} // namespace gui
diff --git a/gui/trappy_circle.h b/gui/trappy_circle.h
index a0b80f4..ba54fec 100644
--- a/gui/trappy_circle.h
+++ b/gui/trappy_circle.h
@@ -63,7 +63,6 @@ class TrappyCircle : public Drawable {
private:
lib::TrappyCircle data_;
QColor color_;
- size_t item_index_{ULLONG_MAX};
QCPItemEllipse* ellipse_{nullptr};
};
diff --git a/lib/hill.cpp b/lib/hill.cpp
index a70f6bd..15d7462 100644
--- a/lib/hill.cpp
+++ b/lib/hill.cpp
@@ -6,13 +6,6 @@
namespace lib {
-Hill::Hill(std::initializer_list points) : vertices_{points} {
- if (points.size() == 0 || points.size() == 1)
- throw std::invalid_argument("Hill cannot consist of one or zero points!");
-
- CheckErrorValues();
-}
-
QJsonObject Hill::GetJsonInfo() const {
QVariantMap hill_map;
hill_map.insert("Id", GetId());
diff --git a/lib/hill.h b/lib/hill.h
index 97da1e4..bf62a08 100644
--- a/lib/hill.h
+++ b/lib/hill.h
@@ -18,10 +18,10 @@ class Hill : public JSONable {
* @brief Инициализирует новый экземпляр Hill
* (так как рельеф является многоугольником, его можно построить по точкам)
* @param points: список точек
- * @throw std::invalid_argument: если кол-во вершин 1 или 0
*/
- Hill(std::initializer_list points);
- Hill(std::vector points) : vertices_{points} { CheckErrorValues(); }
+ Hill(const std::vector& points) : vertices_{points} {
+ CheckErrorValues();
+ }
Hill(const Hill&) = default;
Hill(Hill&&) = default;
diff --git a/lib/segment.cpp b/lib/segment.cpp
index 010fd92..b524bb1 100644
--- a/lib/segment.cpp
+++ b/lib/segment.cpp
@@ -41,7 +41,9 @@ std::pair Segment::ToAnglesOnCircle() {
auto angle_pairs = std::array{
std::make_pair(start_angles.positive_angle, end_angles.positive_angle),
- std::make_pair(start_angles.positive_angle, end_angles.negative_angle)};
+ std::make_pair(start_angles.positive_angle, end_angles.negative_angle),
+ std::make_pair(start_angles.negative_angle, end_angles.positive_angle),
+ std::make_pair(start_angles.negative_angle, end_angles.negative_angle)};
return *std::min_element(angle_pairs.begin(), angle_pairs.end(),
[](const auto& pair_1, const auto& pair_2) {
diff --git a/lib/segment.h b/lib/segment.h
index 35bdbcf..eb3ff0d 100644
--- a/lib/segment.h
+++ b/lib/segment.h
@@ -11,6 +11,8 @@ namespace lib {
*/
class Segment {
public:
+ Segment() = default;
+
/**
* @brief Инициализирует новый экземпляр сегмента,
* как отрезок с началом и концом
diff --git a/lib/trappy_circle.cpp b/lib/trappy_circle.cpp
index 67c568a..a070717 100644
--- a/lib/trappy_circle.cpp
+++ b/lib/trappy_circle.cpp
@@ -10,7 +10,7 @@ TrappyCircle::TrappyCircle(Point center, double radius)
: center_{center}, radius_{radius} {
if (radius < 0)
throw std::invalid_argument(
- "Trappy circle cannot have of negative radius!");
+ "TrappyCircles cannot have of negative radius!");
CheckErrorValues();
}
diff --git a/main/add_objects_forms/add_hill_form.cpp b/main/add_objects_forms/add_hill_form.cpp
index 56ceaf4..cbeb5f8 100644
--- a/main/add_objects_forms/add_hill_form.cpp
+++ b/main/add_objects_forms/add_hill_form.cpp
@@ -52,7 +52,7 @@ void AddHillForm::AddNewInputFields(size_t amount) {
ui->gridLayout->addItem(
ui->buttonsLayout, ui->gridLayout->count() + static_cast(amount), 0);
- // (-1, так как buttonLayout считается за отдельный уже находящийся элемент)
+ // (-1, так как buttonsLayout считается за отдельный уже находящийся элемент)
auto old_size = ui->gridLayout->count() - 1;
auto new_size = ui->gridLayout->count() - 1 + amount;
diff --git a/main/add_objects_forms/add_hill_form.h b/main/add_objects_forms/add_hill_form.h
index a782446..4054140 100644
--- a/main/add_objects_forms/add_hill_form.h
+++ b/main/add_objects_forms/add_hill_form.h
@@ -38,7 +38,7 @@ class AddHillForm : public QDialog {
~AddHillForm();
signals:
- void AddHill(std::vector> points);
+ void AddHill(const std::vector>& points);
private slots:
void on_createPushButton_clicked();
diff --git a/main/add_objects_forms/add_trappy_circle_form.ui b/main/add_objects_forms/add_trappy_circle_form.ui
index d183d62..0c7a281 100644
--- a/main/add_objects_forms/add_trappy_circle_form.ui
+++ b/main/add_objects_forms/add_trappy_circle_form.ui
@@ -29,7 +29,7 @@
- Add Trappy Circle
+ Add TrappyCircle
diff --git a/main/add_objects_forms/add_trappy_line_form.ui b/main/add_objects_forms/add_trappy_line_form.ui
index 232ab8d..d93862c 100644
--- a/main/add_objects_forms/add_trappy_line_form.ui
+++ b/main/add_objects_forms/add_trappy_line_form.ui
@@ -29,11 +29,11 @@
- Add Trappy Line
+ Add TrappyLine
- ../images/flying_robot.png../images/flying_robot.png
+ ../images/trappy_line.png../images/trappy_line.png
true
diff --git a/main/gui_json_file/open.cpp b/main/gui_json_file/open.cpp
index b3c2640..e525817 100644
--- a/main/gui_json_file/open.cpp
+++ b/main/gui_json_file/open.cpp
@@ -31,7 +31,7 @@ std::vector GetHillsFromFile(QJsonArray arr) {
return hills;
}
-bool IsExistId(std::vector ids, unsigned short curr_id) {
+bool IsExistId(const std::vector& ids, unsigned short curr_id) {
for (const auto& id : ids) {
if (curr_id == id) return true;
}
diff --git a/main/mainwindow.cpp b/main/mainwindow.cpp
index fa0b3b7..45607cd 100644
--- a/main/mainwindow.cpp
+++ b/main/mainwindow.cpp
@@ -36,6 +36,14 @@ MainWindow::MainWindow(QWidget* parent)
ui->trappyCircleRemovePushButton, ui->trappyLineRemovePushButton);
json_file_.SetUntitledFile();
+
+ QIntValidator* int_validator{new QIntValidator()};
+ int_validator->setRange(1, 9);
+ ui->robotsAmountLineEdit->setValidator(int_validator);
+
+ connect(
+ ui->plotSettingsDockWidget, &QDockWidget::visibilityChanged, this,
+ [this](bool visible) { ui->flyRobotPushButton->setEnabled(!visible); });
}
MainWindow::~MainWindow() { delete ui; }
diff --git a/main/mainwindow.h b/main/mainwindow.h
index 5991b30..82c34cc 100644
--- a/main/mainwindow.h
+++ b/main/mainwindow.h
@@ -39,12 +39,11 @@ class MainWindow : public QMainWindow {
~MainWindow();
private slots:
-
// Slots for files:
- bool on_actionSave_as_triggered();
- bool on_actionSave_triggered();
- void on_actionOpen_triggered();
- void on_actionNew_triggered();
+ bool on_saveAsFileAction_triggered();
+ bool on_saveFileAction_triggered();
+ void on_openFileAction_triggered();
+ void on_newFileAction_triggered();
// Slots for interaction objects adding:
void mousePressObjectsButton(QMouseEvent* mouse_event);
@@ -55,23 +54,23 @@ class MainWindow : public QMainWindow {
void keyPressEvent(QKeyEvent* key_event) override;
// Slots for Target:
- void on_pushButtonAddTarget_clicked();
- void on_actionTarget_triggered();
+ void on_addTargetPushButton_clicked();
+ void on_targetAction_triggered();
// Slots for TrappyCircle:
- void on_pushButtonAddTrappyCircle_clicked();
- void on_actionTrappy_Circle_triggered();
+ void on_addTrappyCirclePushButton_clicked();
+ void on_trappyCircleAction_triggered();
void mousePressSetRadiusFromPlot(QMouseEvent* mouse_event);
void mouseMoveSetRadiusFromPlot(QMouseEvent* mouse_event);
// Slots for TrappyLine:
- void on_pushButtonAddTrappyLine_clicked();
- void on_actionTrappy_Line_triggered();
- void on_actionHill_triggered();
+ void on_addTrappyLinePushButton_clicked();
+ void on_trappyLineAction_triggered();
void mousePressSelectSecondTarget(QMouseEvent* mouse_event);
// Slots for Hill:
- void on_pushButtonAddHill_clicked();
+ void on_addHillPushButton_clicked();
+ void on_hillAction_triggered();
void mousePressAddVertice(QMouseEvent* mouse_event);
void mousePressDeleteLastVertice(QMouseEvent* mouse_event);
void mouseMoveAddVertice(QMouseEvent* mouse_event);
@@ -81,7 +80,7 @@ class MainWindow : public QMainWindow {
void on_hillAddFromTablePushButton_clicked();
void on_trappyCircleAddFromTablePushButton_clicked();
void on_trappyLineAddFromTablePushButton_clicked();
- void on_pushButtonEditObjects_clicked();
+ void on_editObjectsPushButton_clicked();
// Extra slots:
void on_actionBeautify_triggered();
@@ -89,17 +88,27 @@ class MainWindow : public QMainWindow {
void on_xAxis_rangeChanged(QCPRange range);
void on_yAxis_rangeChanged(QCPRange range);
+ void moveRobot();
+ void on_calcTrajectoryPushButton_clicked();
+ void on_flyRobotPushButton_clicked();
+ void on_homeScalePushButton_clicked();
+ void on_actionHelp_triggered();
+
+ void on_robotsApplyAmountPushButton_clicked();
+
+ void on_LowSpeedButton_clicked();
+ void on_MediumSpeedButton_clicked();
+ void on_HighSpeedButton_clicked();
+
public slots:
void AddTrappyCircle(double x, double y, double radius);
void AddTarget(double x, double y);
void AddTrappyLine(double x1, double y1, double x2, double y2);
- void AddHill(std::vector> points);
+ void AddHill(const std::vector>& points);
private:
Ui::MainWindow* ui;
- QTimer* timer;
- gui::Trajectory* trj;
- gui::FlyingRobot* robot;
+ QTimer* timer_{new QTimer(this)};
std::unique_ptr area_;
std::unique_ptr manager_;
@@ -109,7 +118,16 @@ class MainWindow : public QMainWindow {
CursorType cursor_{CursorType::DefaultCursor};
WhatObjectAddition what_obj_addition_{WhatObjectAddition::Nothing};
+ bool is_robot_flying_ = false;
+ bool is_drown_trajectory_ = false;
+
bool OpenMessageWindow();
gui::ObjectType GetObjType() const;
void DeleteLastAddedObject();
+
+ void CalcTrajectory();
+ void DeCalcTrajectory();
+
+ void StopRobot();
+ void FlyRobot();
};
diff --git a/main/mainwindow.ui b/main/mainwindow.ui
index 244fbc8..ade13ca 100644
--- a/main/mainwindow.ui
+++ b/main/mainwindow.ui
@@ -20,13 +20,16 @@
-
-
+
-
-
+
+
+ false
+
- 0
- 0
+ 50
+ 50
@@ -35,6 +38,12 @@
50
+
+
+ 50
+ 50
+
+
PointingHandCursor
@@ -59,11 +68,54 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
-
-
+
+
+
+ 50
+ 50
+
+
+
+
+ 50
+ 50
+
+
+
+
+ 50
+ 50
+
+
+
+ PointingHandCursor
+
+
+ QPushButton::hover{background-color: rgb(184,184,184);
+border: 1px solid #6f6f6f;}
+QPushButton::pressed{background-color: rgb(135, 135, 135);}
+
+
+
+
+
+
+ ../images/route.png../images/route.png
+
+
+
+ 40
+ 40
+
+
+
+
+ -
+
- 0
- 0
+ 50
+ 50
@@ -72,6 +124,12 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
50
+
+
+ 50
+ 50
+
+
PointingHandCursor
@@ -95,11 +153,11 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
-
-
+
- 0
- 0
+ 50
+ 50
@@ -108,6 +166,12 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
50
+
+
+ 50
+ 50
+
+
PointingHandCursor
@@ -131,11 +195,11 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
-
-
+
- 0
- 0
+ 50
+ 50
@@ -144,6 +208,12 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
50
+
+
+ 50
+ 50
+
+
PointingHandCursor
@@ -156,7 +226,7 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
- ../images/flying_robot.png../images/flying_robot.png
+ ../images/trappy_line.png../images/trappy_line.png
@@ -167,11 +237,11 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
-
-
+
- 0
- 0
+ 50
+ 50
@@ -180,6 +250,12 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
50
+
+
+ 50
+ 50
+
+
PointingHandCursor
@@ -203,11 +279,11 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
-
-
+
- 0
- 0
+ 50
+ 50
@@ -216,6 +292,12 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
50
+
+
+ 50
+ 50
+
+
PointingHandCursor
@@ -251,6 +333,48 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
+ -
+
+
+
+ 50
+ 50
+
+
+
+
+ 50
+ 50
+
+
+
+
+ 50
+ 50
+
+
+
+ PointingHandCursor
+
+
+ QPushButton::hover{background-color: rgb(184, 184, 184);border: 1px solid #6f6f6f;}
+QPushButton::pressed{background-color: rgb(135, 135, 135);}
+
+
+
+
+
+
+ ../images/home_scale.png../images/home_scale.png
+
+
+
+ 40
+ 40
+
+
+
+
-
@@ -277,7 +401,7 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
0
0
800
- 25
+ 26
@@ -288,24 +412,18 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
File 🗂
-
-
-
-
+
+
+
+
-
-
@@ -329,7 +446,7 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
175
- 130
+ 186
@@ -395,6 +512,81 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
5
+
-
+
+
-
+
+
+ QPushButton::hover{background-color: rgb(184, 184, 184);border: 1px solid #6f6f6f;}
+QPushButton::pressed{background-color: rgb(135, 135, 135);}
+
+
+
+ Apply
+
+
+
+ -
+
+
+ 1
+
+
+
+ -
+
+
+
+ true
+
+
+
+ Robots amount:
+
+
+
+
+
+ -
+
+
-
+
+
+ High
+
+
+
+ -
+
+
+ Medium
+
+
+ true
+
+
+
+ -
+
+
+ Low
+
+
+
+ -
+
+
+
+ true
+
+
+
+ Speed of robot:
+
+
+
+
+
-
@@ -417,8 +609,8 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
0
0
- 300
- 1000
+ 338
+ 933
@@ -553,7 +745,7 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
-
-
+
0
@@ -671,7 +863,7 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
-
-
+
0
@@ -810,7 +1002,7 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
-
-
+
0
@@ -944,7 +1136,7 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
-
+
../images/new_file.png../images/new_file.png
@@ -956,7 +1148,7 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
Ctrl+N
-
+
../images/open_file.png../images/open_file.png
@@ -968,7 +1160,7 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
Ctrl+O
-
+
../images/save_file.png../images/save_file.png
@@ -980,7 +1172,7 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
Ctrl+S
-
+
../images/save_file_as.png../images/save_file_as.png
@@ -992,20 +1184,11 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
Ctrl+Shift+S
-
-
- Format
-
-
- F1
-
-
-
-
- Style
-
-
+
+
+ ../images/beautify.png../images/beautify.png
+
Beautify
@@ -1013,7 +1196,7 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
F5
-
+
false
@@ -1028,7 +1211,7 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
Target
-
+
../images/AA.png../images/AA.png
@@ -1037,16 +1220,16 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
Trappy Circle
-
+
- ../images/flying_robot.png../images/flying_robot.png
+ ../images/trappy_line.png../images/trappy_line.png
Trappy Line
-
+
../images/high_hills.png../images/high_hills.png
@@ -1055,6 +1238,15 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);}
Hill
+
+
+
+ ../images/help.png../images/help.png
+
+
+ Help
+
+
diff --git a/main/mainwindow_connections/interaction_buttons.cpp b/main/mainwindow_connections/interaction_buttons.cpp
index 2dad0ce..a43939e 100644
--- a/main/mainwindow_connections/interaction_buttons.cpp
+++ b/main/mainwindow_connections/interaction_buttons.cpp
@@ -33,6 +33,7 @@ void MainWindow::DisconnectObject(gui::ObjectType obj_type) {
disconnect(ui->plot, &QCustomPlot::mouseDoubleClick, this,
&MainWindow::mousePressObjectsButton);
+
connect(ui->plot, &QCustomPlot::mousePress, this,
&MainWindow::mousePressContextMenu);
@@ -70,7 +71,8 @@ void MainWindow::DeleteLastAddedObject() {
break;
}
- area_->Redraw();
+ area_->ReDraw();
+ DeCalcTrajectory();
what_obj_addition_ = WhatObjectAddition::Nothing;
}
@@ -85,6 +87,8 @@ void MainWindow::keyPressEvent(QKeyEvent* key_event) {
void MainWindow::mousePressObjectsButton(QMouseEvent* mouse_event) {
if (mouse_event->button() != Qt::LeftButton) return;
+ disconnect(ui->plot, &QCustomPlot::mouseDoubleClick, this,
+ &MainWindow::mousePressObjectsButton);
double x = ui->plot->xAxis->pixelToCoord(mouse_event->pos().x());
double y = ui->plot->yAxis->pixelToCoord(mouse_event->pos().y());
@@ -99,6 +103,7 @@ void MainWindow::mousePressObjectsButton(QMouseEvent* mouse_event) {
// после финального добавления обновляем таблицу
t_connection_->UpdateTable(gui::ObjectType::Targets);
+
break;
}
@@ -163,7 +168,7 @@ void MainWindow::mousePressObjectsButton(QMouseEvent* mouse_event) {
break;
}
- area_->Redraw();
+ area_->ReDraw();
} catch (const std::exception& e) {
QMessageBox::critical(this, "Error!", e.what());
@@ -175,7 +180,7 @@ void MainWindow::mousePressSetRadiusFromPlot(QMouseEvent* mouse_event) {
DisconnectObject(gui::ObjectType::TrappyCircles);
what_obj_addition_ = WhatObjectAddition::Nothing;
- area_->Redraw();
+ area_->ReDraw();
// после финального добавления обновляем таблицу
t_connection_->UpdateTable(gui::ObjectType::TrappyCircles);
@@ -197,7 +202,7 @@ void MainWindow::mouseMoveSetRadiusFromPlot(QMouseEvent* mouse_event) {
double r = sqrt(pow(x, 2) + pow(y, 2));
manager_->GetTrappyCirclesPtrs()[last]->SetRadius(r);
- area_->Redraw();
+ area_->ReDraw();
}
void MainWindow::mousePressSelectSecondTarget(QMouseEvent* mouse_event) {
@@ -218,7 +223,7 @@ void MainWindow::mousePressSelectSecondTarget(QMouseEvent* mouse_event) {
DisconnectObject(gui::ObjectType::TrappyLines);
what_obj_addition_ = WhatObjectAddition::Nothing;
- area_->Redraw();
+ area_->ReDraw();
// после финального добавления обновляем таблицу
t_connection_->UpdateTable(gui::ObjectType::TrappyLines);
@@ -249,7 +254,7 @@ void MainWindow::mousePressAddVertice(QMouseEvent* mouse_event) {
// пикселей. Если это так, то мы считаем, что он завершил создание Hill
if (sqrt(pow(x_pixels - x2_pixels, 2) + pow(y_pixels - y2_pixels, 2)) <
10 &&
- manager_->GetHills()[last].GetVertices().size() > 2) {
+ manager_->GetHills()[last].GetVertices().size() > 3) {
size_t last_vertice =
manager_->GetHillsPtrs()[last]->GetVertices().size() - 1;
manager_->GetHillsPtrs()[last]->GetVertices().erase(
@@ -269,7 +274,7 @@ void MainWindow::mousePressAddVertice(QMouseEvent* mouse_event) {
} else
manager_->GetHillsPtrs()[last]->AddVertice({x, y});
- area_->Redraw();
+ area_->ReDraw();
}
}
@@ -290,7 +295,7 @@ void MainWindow::mousePressDeleteLastVertice(QMouseEvent* mouse_event) {
DeleteLastAddedObject();
}
- area_->Redraw();
+ area_->ReDraw();
}
}
@@ -307,6 +312,32 @@ void MainWindow::mouseMoveAddVertice(QMouseEvent* mouse_event) {
manager_->GetHillsPtrs()[last]->GetVertices().begin() + last_vertice);
manager_->GetHillsPtrs()[last]->AddVertice({x, y});
- area_->Redraw();
+ area_->ReDraw();
}
}
+
+void MainWindow::on_homeScalePushButton_clicked() {
+ lib::Point first_target = {0, 0};
+ double max_distance = 10;
+
+ if (!manager_->GetTargets().empty()) {
+ first_target = manager_->GetTargets()[0].GetPoint();
+ if (manager_->GetTargets().size() > 1)
+ for (size_t i = 1; i < manager_->GetTargets().size(); i++) {
+ double distance = lib::DistanceBetweenPoints(
+ first_target, manager_->GetTargets()[i].GetPoint());
+ if (max_distance < distance) max_distance = distance;
+ }
+ }
+
+ if (first_target.x - max_distance <= -max_scale ||
+ first_target.x + max_distance >= max_scale ||
+ first_target.y - max_distance <= -max_scale ||
+ first_target.y + max_distance >= max_scale)
+ max_distance /= 2.;
+ ui->plot->xAxis->setRange(first_target.x - max_distance,
+ first_target.x + max_distance);
+ ui->plot->yAxis->setRange(first_target.y - max_distance,
+ first_target.y + max_distance);
+ on_actionBeautify_triggered();
+}
diff --git a/main/mainwindow_connections/menu_add_actions.cpp b/main/mainwindow_connections/menu_add_actions.cpp
index 2bec882..acd0017 100644
--- a/main/mainwindow_connections/menu_add_actions.cpp
+++ b/main/mainwindow_connections/menu_add_actions.cpp
@@ -4,7 +4,7 @@
void MainWindow::AddTarget(double x, double y) {
try {
manager_->Add(new gui::Target(x, y));
- area_->Redraw();
+ area_->ReDraw();
t_connection_->UpdateTable(gui::ObjectType::Targets);
} catch (const std::exception& e) {
@@ -15,7 +15,7 @@ void MainWindow::AddTarget(double x, double y) {
void MainWindow::AddTrappyCircle(double x, double y, double radius) {
try {
manager_->Add(new gui::TrappyCircle(x, y, radius));
- area_->Redraw();
+ area_->ReDraw();
t_connection_->UpdateTable(gui::ObjectType::TrappyCircles);
} catch (const std::exception& e) {
@@ -32,7 +32,7 @@ void MainWindow::AddTrappyLine(double x1, double y1, double x2, double y2) {
manager_->GetTargetsPtrs()[manager_->GetTargets().size() - 2],
manager_->GetTargetsPtrs()[manager_->GetTargets().size() - 1]));
- area_->Redraw();
+ area_->ReDraw();
t_connection_->UpdateTable(gui::ObjectType::TrappyLines);
} catch (const std::exception& e) {
@@ -40,7 +40,7 @@ void MainWindow::AddTrappyLine(double x1, double y1, double x2, double y2) {
}
}
-void MainWindow::AddHill(std::vector> points) {
+void MainWindow::AddHill(const std::vector>& points) {
try {
std::vector lib_points;
for (const auto& point : points) {
@@ -49,7 +49,7 @@ void MainWindow::AddHill(std::vector> points) {
manager_->Add(new gui::Hill(lib_points));
- area_->Redraw();
+ area_->ReDraw();
t_connection_->UpdateTable(gui::ObjectType::Hills);
} catch (const std::exception& e) {
@@ -57,7 +57,7 @@ void MainWindow::AddHill(std::vector> points) {
}
}
-void MainWindow::on_actionTarget_triggered() {
+void MainWindow::on_targetAction_triggered() {
DeleteLastAddedObject();
AddTargetForm* atf = new AddTargetForm;
@@ -65,7 +65,7 @@ void MainWindow::on_actionTarget_triggered() {
connect(atf, &AddTargetForm::AddTarget, this, &MainWindow::AddTarget);
}
-void MainWindow::on_actionTrappy_Circle_triggered() {
+void MainWindow::on_trappyCircleAction_triggered() {
DeleteLastAddedObject();
AddTrappyCircleForm* adf = new AddTrappyCircleForm;
@@ -74,7 +74,7 @@ void MainWindow::on_actionTrappy_Circle_triggered() {
&MainWindow::AddTrappyCircle);
}
-void MainWindow::on_actionTrappy_Line_triggered() {
+void MainWindow::on_trappyLineAction_triggered() {
DeleteLastAddedObject();
AddTrappyLineForm* adl = new AddTrappyLineForm;
@@ -83,7 +83,7 @@ void MainWindow::on_actionTrappy_Line_triggered() {
&MainWindow::AddTrappyLine);
}
-void MainWindow::on_actionHill_triggered() {
+void MainWindow::on_hillAction_triggered() {
DeleteLastAddedObject();
AddHillForm* adh = new AddHillForm;
diff --git a/main/mainwindow_connections/menu_file_actions.cpp b/main/mainwindow_connections/menu_file_actions.cpp
index 6a67f9a..7d470ba 100644
--- a/main/mainwindow_connections/menu_file_actions.cpp
+++ b/main/mainwindow_connections/menu_file_actions.cpp
@@ -14,7 +14,7 @@ bool MainWindow::OpenMessageWindow() {
switch (ret) {
case QMessageBox::Save: {
- bool is_closed = on_actionSave_triggered();
+ bool is_closed = on_saveFileAction_triggered();
if (is_closed) return true;
break;
}
@@ -54,7 +54,7 @@ void MainWindow::closeEvent(QCloseEvent* event) {
}
/// @brief Кнопка "New"
-void MainWindow::on_actionNew_triggered() {
+void MainWindow::on_newFileAction_triggered() {
DeleteLastAddedObject();
bool is_closed = false;
@@ -71,13 +71,13 @@ void MainWindow::on_actionNew_triggered() {
if (!is_closed) {
manager_->Clear();
json_file_.SetUntitledFile();
- area_->Redraw();
+ area_->ReDraw();
t_connection_->UpdateTables();
}
}
/// @brief Кнопка "Open"
-void MainWindow::on_actionOpen_triggered() {
+void MainWindow::on_openFileAction_triggered() {
DeleteLastAddedObject();
bool is_closed = false;
@@ -113,7 +113,7 @@ void MainWindow::on_actionOpen_triggered() {
json_file_.Open(manager_.get());
}
- area_->Redraw();
+ area_->ReDraw();
t_connection_->UpdateTables();
}
@@ -121,11 +121,11 @@ void MainWindow::on_actionOpen_triggered() {
* @brief Кнопка "Save"
* @return true: закрыто окно сохранения файла
*/
-bool MainWindow::on_actionSave_triggered() {
+bool MainWindow::on_saveFileAction_triggered() {
DeleteLastAddedObject();
if (!json_file_.IsExistsFile())
- return on_actionSave_as_triggered();
+ return on_saveAsFileAction_triggered();
else {
json_file_.Save(manager_.get());
return false;
@@ -136,11 +136,11 @@ bool MainWindow::on_actionSave_triggered() {
* @brief Кнопка "Save as"
* @return true: закрыто окно сохранения файла
*/
-bool MainWindow::on_actionSave_as_triggered() {
+bool MainWindow::on_saveAsFileAction_triggered() {
DeleteLastAddedObject();
QString file_name = QFileDialog::getSaveFileName(
- this, tr("Save as"), json_file_.GetParentPath(), tr("File (*.json)"));
+ this, tr("Save as"), json_file_.GetAbsolutePath(), tr("File (*.json)"));
if (!file_name.isEmpty()) {
json_file_.SetFile(file_name);
diff --git a/main/mainwindow_connections/other_connections.cpp b/main/mainwindow_connections/other_connections.cpp
index 4145994..5534217 100644
--- a/main/mainwindow_connections/other_connections.cpp
+++ b/main/mainwindow_connections/other_connections.cpp
@@ -1,4 +1,5 @@
// header file:
+#include "gui/flying_robot.h"
#include "main/mainwindow.h"
gui::ObjectType MainWindow::GetObjType() const {
@@ -14,7 +15,7 @@ gui::ObjectType MainWindow::GetObjType() const {
}
}
-void MainWindow::on_pushButtonAddTarget_clicked() {
+void MainWindow::on_addTargetPushButton_clicked() {
DeleteLastAddedObject();
connect(ui->plot, &QCustomPlot::mouseDoubleClick, this,
&MainWindow::mousePressObjectsButton);
@@ -24,7 +25,7 @@ void MainWindow::on_pushButtonAddTarget_clicked() {
cursor_ = CursorType::TargetCursor;
}
-void MainWindow::on_pushButtonAddTrappyCircle_clicked() {
+void MainWindow::on_addTrappyCirclePushButton_clicked() {
DeleteLastAddedObject();
connect(ui->plot, &QCustomPlot::mouseDoubleClick, this,
&MainWindow::mousePressObjectsButton);
@@ -34,7 +35,7 @@ void MainWindow::on_pushButtonAddTrappyCircle_clicked() {
cursor_ = CursorType::TrCircleCursor;
}
-void MainWindow::on_pushButtonAddTrappyLine_clicked() {
+void MainWindow::on_addTrappyLinePushButton_clicked() {
DeleteLastAddedObject();
connect(ui->plot, &QCustomPlot::mouseDoubleClick, this,
&MainWindow::mousePressObjectsButton);
@@ -44,7 +45,7 @@ void MainWindow::on_pushButtonAddTrappyLine_clicked() {
cursor_ = CursorType::TrLineCursor;
}
-void MainWindow::on_pushButtonAddHill_clicked() {
+void MainWindow::on_addHillPushButton_clicked() {
DeleteLastAddedObject();
connect(ui->plot, &QCustomPlot::mouseDoubleClick, this,
&MainWindow::mousePressObjectsButton);
@@ -54,33 +55,32 @@ void MainWindow::on_pushButtonAddHill_clicked() {
cursor_ = CursorType::HillCursor;
}
-void MainWindow::on_pushButtonEditObjects_clicked() {
- manager_->RemoveAllDuplicates();
- t_connection_->UpdateTables();
-
+void MainWindow::on_editObjectsPushButton_clicked() {
ui->plotSettingsDockWidget->setVisible(true);
on_actionBeautify_triggered();
+
+ ui->flyRobotPushButton->setEnabled(false);
}
void MainWindow::on_actionBeautify_triggered() {
ui->plot->xAxis->setScaleRatio(ui->plot->yAxis);
- area_->Redraw();
+ ui->plot->replot();
}
void MainWindow::on_targetAddFromTablePushButton_clicked() {
- on_actionTarget_triggered();
+ on_targetAction_triggered();
}
void MainWindow::on_hillAddFromTablePushButton_clicked() {
- on_actionHill_triggered();
+ on_hillAction_triggered();
}
void MainWindow::on_trappyCircleAddFromTablePushButton_clicked() {
- on_actionTrappy_Circle_triggered();
+ on_trappyCircleAction_triggered();
}
void MainWindow::on_trappyLineAddFromTablePushButton_clicked() {
- on_actionTrappy_Line_triggered();
+ on_trappyLineAction_triggered();
}
void MainWindow::on_xAxis_rangeChanged(QCPRange range) {
@@ -124,6 +124,8 @@ void MainWindow::on_yAxis_rangeChanged(QCPRange range) {
}
void MainWindow::mousePressRemoveObject() {
+ DeCalcTrajectory();
+
if (ui->plot->selectedGraphs().size() > 0) {
bool is_found = false;
for (size_t i = 0; i < manager_->GetTargets().size(); i++) {
@@ -166,7 +168,7 @@ void MainWindow::mousePressRemoveObject() {
}
}
- area_->Redraw();
+ area_->ReDraw();
}
void MainWindow::mousePressContextMenu(QMouseEvent* mouse_event) {
@@ -185,3 +187,34 @@ void MainWindow::mousePressContextMenu(QMouseEvent* mouse_event) {
menu->exec(mouse_event->globalPosition().toPoint());
}
}
+
+void MainWindow::moveRobot() { area_->GetRobot()->ReDraw(ui->plot); }
+
+void MainWindow::on_actionHelp_triggered() {
+ QDesktopServices::openUrl(
+ QUrl("https://umbrellaleaf5.github.io/locus_no_pilotus/index.html"));
+}
+
+void MainWindow::on_robotsApplyAmountPushButton_clicked() {
+ DeCalcTrajectory();
+ area_->ReDraw();
+ unsigned short amount = ui->robotsAmountLineEdit->displayText().toUShort();
+
+ if (amount > 0)
+ area_->SetAmountOfRobots(amount);
+ else
+ QMessageBox::warning(this, "Zero flying robot!",
+ "Flying robots must be at list 1!");
+}
+
+void MainWindow::on_LowSpeedButton_clicked() {
+ area_->GetRobot()->SetSpeed(SpeedOfRobot::Low);
+}
+
+void MainWindow::on_MediumSpeedButton_clicked() {
+ area_->GetRobot()->SetSpeed(SpeedOfRobot::Medium);
+}
+
+void MainWindow::on_HighSpeedButton_clicked() {
+ area_->GetRobot()->SetSpeed(SpeedOfRobot::High);
+}
diff --git a/main/mainwindow_connections/trajectory_and_robot.cpp b/main/mainwindow_connections/trajectory_and_robot.cpp
new file mode 100644
index 0000000..e01d31b
--- /dev/null
+++ b/main/mainwindow_connections/trajectory_and_robot.cpp
@@ -0,0 +1,81 @@
+// header file:
+#include "main/mainwindow.h"
+
+void MainWindow::StopRobot() {
+ if (is_robot_flying_) {
+ disconnect(timer_, &QTimer::timeout, this, &MainWindow::moveRobot);
+ timer_->stop();
+ is_robot_flying_ = false;
+
+ ui->flyRobotPushButton->setIcon(QPixmap("../images/play_triangle.png"));
+ area_->ReDraw();
+ if (is_drown_trajectory_) area_->ReDrawTrajectory();
+ area_->GetRobot()->SetTrajectory(nullptr);
+ ui->plot->replot();
+
+ ui->editObjectsPushButton->setEnabled(true);
+ }
+}
+
+void MainWindow::FlyRobot() {
+ if (!is_robot_flying_) {
+ connect(timer_, &QTimer::timeout, this, &MainWindow::moveRobot);
+ timer_->start(5);
+ is_robot_flying_ = true;
+
+ ui->flyRobotPushButton->setIcon(QPixmap("../images/stop_square.png"));
+
+ if (!area_->GetRobot()->GetTrajectory())
+ area_->GetRobot()->SetTrajectory(area_->GetTrajectory());
+
+ area_->GetRobot()->Draw(ui->plot);
+ ui->plot->replot();
+
+ ui->plotSettingsDockWidget->setVisible(false);
+ ui->editObjectsPushButton->setEnabled(false);
+ }
+}
+
+void MainWindow::CalcTrajectory() {
+ is_drown_trajectory_ = true;
+
+ area_->ReDrawTrajectory();
+
+ if (area_->TrajectorySize()) ui->calcTrajectoryPushButton->setEnabled(false);
+
+ if (area_->TrajectorySize() && !ui->plotSettingsDockWidget->isVisible())
+ ui->flyRobotPushButton->setEnabled(true);
+}
+
+void MainWindow::DeCalcTrajectory() {
+ is_drown_trajectory_ = false;
+
+ area_->ClearTrajectory();
+
+ ui->calcTrajectoryPushButton->setEnabled(true);
+
+ ui->flyRobotPushButton->setEnabled(false);
+
+ if (is_robot_flying_) StopRobot();
+}
+
+void MainWindow::on_calcTrajectoryPushButton_clicked() {
+ DeleteLastAddedObject();
+ manager_->RemoveAllDuplicates();
+ t_connection_->UpdateTables();
+ area_->ReDraw();
+
+ CalcTrajectory();
+}
+
+void MainWindow::on_flyRobotPushButton_clicked() {
+ try {
+ if (is_robot_flying_)
+ StopRobot();
+ else
+ FlyRobot();
+
+ } catch (const std::exception& e) {
+ QMessageBox::warning(this, "Cannot add Robot!", e.what());
+ }
+}
diff --git a/math/littles_algorithm/adjacency_matrix.cpp b/math/littles_algorithm/adjacency_matrix.cpp
index c7f1979..09216ac 100644
--- a/math/littles_algorithm/adjacency_matrix.cpp
+++ b/math/littles_algorithm/adjacency_matrix.cpp
@@ -1,10 +1,13 @@
// header file:
#include "adjacency_matrix.h"
+// std libs:
+#include
+
namespace math {
AdjacencyMatrix AdjacencyMatrix::WithExtraRowCol(
- std::vector> nums) {
+ const std::vector>& nums) {
AdjacencyMatrix m(nums);
m.matrix_.resize(m.size_ + 1);
m.matrix_[m.size_].resize(m.size_ + 1);
@@ -40,7 +43,7 @@ AdjacencyMatrix AdjacencyMatrix::Reducted() {
return reducted;
}
-AdjacencyMatrix::AdjacencyMatrix(std::vector> nums)
+AdjacencyMatrix::AdjacencyMatrix(const std::vector>& nums)
: size_{nums.size()}, matrix_{nums}, min_numbers_(size_ + size_) {}
Minimums AdjacencyMatrix::FindTwoMinimums(Mins type, std::size_t index) const {
@@ -143,6 +146,9 @@ void AdjacencyMatrix::CalculateData() {
}
void AdjacencyMatrix::ExtendTo(std::size_t num_of_flyers) {
+ if (num_of_flyers < 2)
+ throw std::runtime_error("dev: num_of_flyers < 2 in ExtendTo");
+
for (std::size_t i = 1; i < num_of_flyers; ++i) {
matrix_.insert(matrix_.begin() + size_, matrix_[0]);
for (std::size_t j = 0; j < size_ + 1; ++j) {
diff --git a/math/littles_algorithm/adjacency_matrix.h b/math/littles_algorithm/adjacency_matrix.h
index ad06e53..2074947 100644
--- a/math/littles_algorithm/adjacency_matrix.h
+++ b/math/littles_algorithm/adjacency_matrix.h
@@ -29,7 +29,8 @@ class AdjacencyMatrix {
* @return AdjacencyMatrix: экземпляр AdjacencyMatrix по данной матрице
* смежности
*/
- static AdjacencyMatrix WithExtraRowCol(std::vector> nums);
+ static AdjacencyMatrix WithExtraRowCol(
+ const std::vector>& nums);
enum Mins { Rows, Columns };
@@ -121,7 +122,7 @@ class AdjacencyMatrix {
* @return AdjacencyMatrix: экземпляр AdjacencyMatrix по данной матрице
* смежности
*/
- AdjacencyMatrix(std::vector> nums);
+ AdjacencyMatrix(const std::vector>& nums);
/**
* @brief Находит 2 минимума в строке или столбце
diff --git a/math/littles_algorithm/travelling_salesmans_problem.cpp b/math/littles_algorithm/travelling_salesmans_problem.cpp
index 3610a95..f6e446e 100644
--- a/math/littles_algorithm/travelling_salesmans_problem.cpp
+++ b/math/littles_algorithm/travelling_salesmans_problem.cpp
@@ -6,15 +6,10 @@
namespace math {
-TravellingSalesmansProblem::TravellingSalesmansProblem(AdjacencyMatrix& m) {
- paths_stack_.push_back(std::make_shared(m));
- if (m.GetSize() == 2) CompleteEdgePath(paths_stack_[0]);
-}
-
TravellingSalesmansProblem::TravellingSalesmansProblem(
AdjacencyMatrix& m, std::size_t num_of_flyers)
: num_of_flyers_{num_of_flyers} {
- m.ExtendTo(num_of_flyers);
+ if (num_of_flyers > 1) m.ExtendTo(num_of_flyers);
paths_stack_.push_back(std::make_shared(m));
if (m.GetSize() == 2) CompleteEdgePath(paths_stack_[0]);
}
diff --git a/math/littles_algorithm/travelling_salesmans_problem.h b/math/littles_algorithm/travelling_salesmans_problem.h
index c609b15..577f444 100644
--- a/math/littles_algorithm/travelling_salesmans_problem.h
+++ b/math/littles_algorithm/travelling_salesmans_problem.h
@@ -9,12 +9,6 @@ namespace math {
/// @brief Решение задачи коммивояжера
class TravellingSalesmansProblem {
public:
- /**
- * @brief Инициализирует новый экземпляр TravellingSalesmansProblem
- * @param matrix: матрица смежности графа
- */
- TravellingSalesmansProblem(AdjacencyMatrix& matrix);
-
/**
* @brief Инициализирует новый экземпляр TravellingSalesmansProblem для
* нескольких коммивояжеров
@@ -22,7 +16,7 @@ class TravellingSalesmansProblem {
* @param num_of_flyers: количество коммивояжеров
*/
TravellingSalesmansProblem(AdjacencyMatrix& matrix,
- std::size_t num_of_flyers);
+ std::size_t num_of_flyers = 1);
// Возвращает длину оптимального маршрута
double GetTrajLength() const { return paths_len_; }
@@ -34,7 +28,7 @@ class TravellingSalesmansProblem {
private:
// Количество коммивояжеров
- std::size_t num_of_flyers_;
+ std::size_t num_of_flyers_{1};
// Вектор с указателями на вершины графа
// Отсортирован в порядке возрастания нижней оценки
diff --git a/math/optimal_way/helpers_functions.cpp b/math/optimal_way/helpers_functions.cpp
index e550719..e79d14d 100644
--- a/math/optimal_way/helpers_functions.cpp
+++ b/math/optimal_way/helpers_functions.cpp
@@ -1,5 +1,7 @@
+// header file:
#include "helpers_functions.h"
+// std libs:
#include
namespace math {
@@ -207,8 +209,8 @@ std::vector TangentsBetween(const PolygonObstacle& polygon,
for (auto& tang_pnt : {tang_pnts.first, tang_pnts.second})
if (vertex != tang_pnt) {
LinearFunction line(vertex, tang_pnt);
- if (!AreThereIntersections(polygon, line) &&
- !AreThereIntersections(obstacle, line)) {
+ if ((!AreThereIntersections(polygon, line)) &&
+ (!AreThereIntersections(obstacle, line))) {
bool is_unique = !_isnan(line.a_coef);
for (std::size_t i = 0; i < tangents.size(); ++i)
if (tangents[i] == line) is_unique = false;
@@ -221,31 +223,29 @@ std::vector TangentsBetween(const PolygonObstacle& polygon,
bool AreThereIntersections(const CircleObstacle& cr_obst, const Point& point1,
const Point& point2) {
- double slope = (point2.y - point1.y) / (point2.x - point1.x);
- double b_coef = point1.y - slope * point1.x;
+ double distance = 0;
Point center = cr_obst.GetCenter();
double radius = cr_obst.GetRadius();
- double discriminant = (pow(slope * b_coef - center.x - slope * center.y, 2)) +
- (pow(radius, 2) - pow(center.x, 2) - pow(b_coef, 2) -
- pow(center.y, 2) + 2 * b_coef * center.y) *
- (1 + pow(slope, 2));
- if (discriminant < precision)
- return false;
- else {
- double x_1 =
- (-(slope * b_coef - center.x - slope * center.y) + sqrt(discriminant)) /
- (1 + pow(slope, 2));
- double x_2 =
- (-(slope * b_coef - center.x - slope * center.y) - sqrt(discriminant)) /
- (1 + pow(slope, 2));
- if (((std::min(point1.x, point2.x) <= x_1) &&
- (x_1 <= std::max(point1.x, point2.x))) ||
- ((std::min(point1.x, point2.x) <= x_2) &&
- (x_2 <= std::max(point1.x, point2.x))))
- return true;
- else
- return false;
+ Point vector_p1_c = center - point1;
+ Point vector_p2_c = center - point2;
+ Point vector_p1_p2 = point2 - point1;
+ auto module = [](Point p) {
+ return lib::DistanceBetweenPoints(p, Point(0, 0));
+ };
+ auto scalar = [](Point p1, Point p2) { return p1.x * p2.x + p1.y * p2.y; };
+ auto vect = [](Point p1, Point p2) {
+ return std::abs(p1.x * p2.y - p1.y * p2.x);
+ };
+
+ if (scalar(vector_p1_p2, vector_p2_c) >= precision) {
+ distance = module(vector_p2_c);
+ } else if (scalar(vector_p1_p2, vector_p1_c) <= -precision) {
+ distance = module(vector_p1_c);
+ } else {
+ distance = vect(vector_p1_p2, vector_p1_c) / module(vector_p1_p2);
}
+ if (distance - radius <= -precision) return true;
+ return false;
}
bool AreThereIntersections(const CircleObstacle& cr_obst,
@@ -266,8 +266,8 @@ bool AreThereIntersections(const PolygonObstacle& poly_obst, const Point& pnt1,
LinearFunction v_line(vertexes[i], vertexes[(i + 1) % vertexes.size()]);
if ((line.Substitute(vertexes[i]) *
line.Substitute(vertexes[(i + 1) % vertexes.size()]) <
- 0) &&
- (v_line.Substitute(pnt1) * v_line.Substitute(pnt2) < 0))
+ -precision) &&
+ (v_line.Substitute(pnt1) * v_line.Substitute(pnt2) < -precision))
return true;
}
std::size_t prev = ULONG_LONG_MAX;
@@ -288,7 +288,7 @@ bool AreThereIntersections(const PolygonObstacle& poly_obst,
for (std::size_t i = 0; i < vertexes.size(); ++i)
if ((line.Substitute(vertexes[i]) *
line.Substitute(vertexes[(i + 1) % vertexes.size()]) <
- 0))
+ -precision))
return true;
std::size_t prev = ULONG_LONG_MAX;
diff --git a/math/optimal_way/obstacles.h b/math/optimal_way/obstacles.h
index 60cd77f..2276d8d 100644
--- a/math/optimal_way/obstacles.h
+++ b/math/optimal_way/obstacles.h
@@ -31,7 +31,7 @@ struct LinearFunction {
double a_coef, b_coef, c_coef;
- bool operator==(const LinearFunction& other) {
+ bool operator==(const LinearFunction& other) const {
double proportion = ((std::abs(a_coef) >= precision) &&
(std::abs(other.a_coef) >= precision))
? other.a_coef / a_coef
@@ -113,7 +113,7 @@ class PolygonObstacle {
* @brief Инициализирует экземпляр PolygonObstacle
* @param vertexes: вершины многоугольника
*/
- PolygonObstacle(std::vector vertexes) : vertexes_{vertexes} {
+ PolygonObstacle(const std::vector& vertexes) : vertexes_{vertexes} {
center_ = Point(0, 0);
for (auto& elem : vertexes_) {
center_.x += elem.x;
@@ -131,6 +131,10 @@ class PolygonObstacle {
std::vector GetTangentPoints() { return tangent_points_; }
+ void DeleteTangentPoint(std::size_t index) {
+ tangent_points_.erase(tangent_points_.begin() + index);
+ }
+
void AddTangentLine(const LinearFunction& tangent) {
tangents_.push_back(tangent);
}
diff --git a/math/optimal_way/optimal_way.cpp b/math/optimal_way/optimal_way.cpp
index 222ff34..9b7a0e6 100644
--- a/math/optimal_way/optimal_way.cpp
+++ b/math/optimal_way/optimal_way.cpp
@@ -4,6 +4,8 @@
// our code libs:
#include "helpers_functions.h"
+using lib::Segment;
+
namespace math {
template
@@ -87,110 +89,171 @@ void OptimalWayCalculator::AddCommonTangents() {
void OptimalWayCalculator::AddGraphTangentPoints() {
for (auto& circle : circles_)
for (auto& point : circle.GetTangentPoints()) {
- PathWayNode new_node(point, graph_.nodes.size());
- new_node.circle_ptr = std::make_shared(circle);
- graph_.AddNode(std::make_shared(new_node));
+ bool is_unique = true;
+ for (std::size_t i = 0; i < graph_.nodes.size(); ++i) {
+ if (point == graph_.nodes[i]->point) {
+ is_unique = false;
+ break;
+ }
+ }
+ if (is_unique == false) continue;
+ PathWayNode* new_node = new PathWayNode(point, graph_.nodes.size(), true);
+ new_node->circle_ptr = std::make_shared(circle);
+ graph_.AddNode(new_node);
for (std::size_t i = 0; i < graph_.nodes.size() - 1; ++i) {
if (graph_.nodes[i]->circle_ptr &&
((*graph_.nodes[i]->circle_ptr) == circle)) {
- graph_.AddEdge(graph_.nodes[i]->number, new_node.number,
+ graph_.AddEdge(graph_.nodes[i]->number, new_node->number,
DistanceBetweenPointsOnCircle(
- circle, graph_.nodes[i]->point, new_node.point));
- } else if (new_node.point ==
+ circle, graph_.nodes[i]->point, new_node->point));
+ } else if (new_node->point ==
*graph_.nodes[i]->point.another_tangent_point) {
graph_.AddEdge(
- graph_.nodes[i]->number, new_node.number,
- DistanceBetweenPoints(graph_.nodes[i]->point, new_node.point));
+ graph_.nodes[i]->number, new_node->number,
+ DistanceBetweenPoints(graph_.nodes[i]->point, new_node->point));
}
}
}
-
for (auto& poly : polys_) {
std::vector vertexes = poly.GetVertexes();
for (std::size_t i = 0; i < vertexes.size(); ++i) {
- PathWayNode new_node(vertexes[i], graph_.nodes.size());
- new_node.poly_ptr = std::make_shared(poly);
- graph_.AddNode(std::make_shared(new_node));
- // Проводим ребра в графе только между соседними вершинами многоугольника
+ PathWayNode* new_node =
+ new PathWayNode(vertexes[i], graph_.nodes.size(), true);
+ for (std::size_t j = 0; j < poly.GetTangentPoints().size(); ++j) {
+ if (poly.GetTangentPoints()[j] == vertexes[i])
+ new_node->point = poly.GetTangentPoints()[j];
+ }
+ new_node->poly_ptr = std::make_shared(poly);
+ graph_.AddNode(new_node);
+ // Проводим ребра в графе только между соседними вершинами
+ // многоугольника
if (i != 0)
graph_.AddEdge(
- graph_.nodes[graph_.nodes.size() - 2]->number, new_node.number,
+ graph_.nodes[graph_.nodes.size() - 2]->number, new_node->number,
DistanceBetweenPoints(graph_.nodes[graph_.nodes.size() - 2]->point,
- new_node.point));
+ new_node->point));
if (i == vertexes.size() - 1)
graph_.AddEdge(
graph_.nodes[graph_.nodes.size() - vertexes.size()]->number,
- new_node.number,
+ new_node->number,
DistanceBetweenPoints(
graph_.nodes[graph_.nodes.size() - vertexes.size()]->point,
- new_node.point));
- for (std::size_t i = 0; i < graph_.nodes.size() - 1; ++i) {
- if (new_node.point != *graph_.nodes[i]->point.another_tangent_point)
- continue;
- graph_.AddEdge(
- graph_.nodes[i]->number, new_node.number,
- DistanceBetweenPoints(graph_.nodes[i]->point, new_node.point));
+ new_node->point));
+
+ for (std::size_t j = 0; j < graph_.nodes.size(); ++j) {
+ if ((graph_.nodes[j]->point.another_tangent_point) &&
+ (new_node->point == *graph_.nodes[j]->point.another_tangent_point))
+ graph_.AddEdge(
+ graph_.nodes[j]->number, new_node->number,
+ DistanceBetweenPoints(graph_.nodes[j]->point, new_node->point));
}
}
}
}
-std::size_t OptimalWayCalculator::AddGraphControlPoints(Point point) {
+std::set OptimalWayCalculator::AddGraphControlPoints(Point point) {
+ std::set tangent_points_numbers;
for (auto& circle : circles_) {
std::pair tangent_points = TangentPoints(circle, point);
for (auto& tangent_point : {tangent_points.first, tangent_points.second}) {
if (TangentGoesThroughOtherObstacle(tangent_point, point)) continue;
- PathWayNode new_node(tangent_point, graph_.nodes.size());
- new_node.circle_ptr = std::make_shared(circle);
- graph_.AddNode(std::make_shared(new_node));
- for (std::size_t i = 0; i < graph_.nodes.size() - 1; ++i) {
- if (*graph_.nodes[i]->circle_ptr == circle) {
- graph_.AddEdge(graph_.nodes[i]->number, new_node.number,
+ bool is_unique = true;
+ for (std::size_t i = 0; i < graph_.nodes.size(); ++i) {
+ if (tangent_point == graph_.nodes[i]->point) {
+ is_unique = false;
+ tangent_points_numbers.insert(graph_.nodes[i]->number);
+ break;
+ }
+ }
+ if (!is_unique) continue;
+ PathWayNode* new_node =
+ new PathWayNode(tangent_point, graph_.nodes.size(), false);
+ new_node->circle_ptr = std::make_shared(circle);
+ tangent_points_numbers.insert(graph_.nodes.size());
+ graph_.AddNode(new_node);
+
+ for (std::size_t i = 0; i < graph_.nodes.size(); ++i) {
+ if ((graph_.nodes[i]->circle_ptr) &&
+ (*graph_.nodes[i]->circle_ptr == circle) &&
+ (graph_.nodes[i]->number != new_node->number)) {
+ graph_.AddEdge(graph_.nodes[i]->number, new_node->number,
DistanceBetweenPointsOnCircle(circle, tangent_point,
graph_.nodes[i]->point));
}
}
}
}
+
for (auto& poly : polys_) {
std::pair tangent_points = TangentPoints(poly, point);
for (auto& tangent_point : {tangent_points.first, tangent_points.second}) {
if (TangentGoesThroughOtherObstacle(tangent_point, point)) continue;
- PathWayNode new_node(tangent_point, graph_.nodes.size());
- new_node.poly_ptr = std::make_shared(poly);
- graph_.AddNode(std::make_shared(new_node));
- for (std::size_t i = 0; i < graph_.nodes.size() - 1; ++i) {
- if (*graph_.nodes[i]->poly_ptr == poly) {
- graph_.AddEdge(graph_.nodes[i]->number, new_node.number,
- DistanceBetweenPointsOnPolygon(
- poly, tangent_point, graph_.nodes[i]->point));
+ for (std::size_t i = 0; i < graph_.nodes.size(); ++i) {
+ if (tangent_point == graph_.nodes[i]->point) {
+ tangent_points_numbers.insert(graph_.nodes[i]->number);
+ break;
}
}
}
}
- return graph_.nodes.size();
+
+ return tangent_points_numbers;
}
void OptimalWayCalculator::FindOptimalWay(Point p1, Point p2) {
- std::size_t point1_new_nodes = AddGraphControlPoints(p1);
- std::size_t point2_new_nodes = AddGraphControlPoints(p2);
- PathWayNode new_node1(p1, graph_.nodes.size());
- graph_.AddNode(std::make_shared(new_node1));
- for (std::size_t i = normal_graph_size_; i < point1_new_nodes; ++i)
- graph_.AddEdge(graph_.nodes[i]->number, new_node1.number,
- DistanceBetweenPoints(graph_.nodes[i]->point, p1));
- PathWayNode new_node2(p2, graph_.nodes.size());
- graph_.AddNode(std::make_shared(new_node2));
- for (std::size_t i = point1_new_nodes; i < point2_new_nodes; ++i)
- graph_.AddEdge(graph_.nodes[i]->number, new_node2.number,
- DistanceBetweenPoints(graph_.nodes[i]->point, p2));
- if (!TangentGoesThroughOtherObstacle(p1, p2))
+ ResetInformation();
+ std::set point1_tangents = AddGraphControlPoints(p1);
+ std::set point2_tangents = AddGraphControlPoints(p2);
+
+ PathWayNode* new_node1 = new PathWayNode(p1, graph_.nodes.size(), false);
+ graph_.AddNode(new_node1);
+ for (const auto& index : point1_tangents) {
+ graph_.AddEdge(
+ new_node1->number, graph_.nodes[index]->number,
+ DistanceBetweenPoints(new_node1->point, graph_.nodes[index]->point));
+ }
+ PathWayNode* new_node2 = new PathWayNode(p2, graph_.nodes.size(), false);
+ graph_.AddNode(new_node2);
+ for (const auto& index : point2_tangents) {
+ graph_.AddEdge(
+ new_node2->number, graph_.nodes[index]->number,
+ DistanceBetweenPoints(new_node2->point, graph_.nodes[index]->point));
+ }
+
+ if (!TangentGoesThroughOtherObstacle(p1, p2)) {
graph_.AddEdge(graph_.nodes.size() - 2, graph_.nodes.size() - 1,
DistanceBetweenPoints(p1, p2));
+ }
+
DijkstrasAlgorithm da(graph_);
optimal_way_ = da.Get_Min_Path();
optimal_way_length_ = da.Get_Min_Len();
- graph_.RemoveLastNodes(graph_.nodes.size() - normal_graph_size_);
+ MakeTrajectoryPart();
+ graph_.RemoveNonConstantNodes();
+}
+
+void OptimalWayCalculator::MakeTrajectoryPart() {
+ for (std::size_t i = 0; i < optimal_way_.size() - 1; ++i) {
+ // Если точки лежат на одной окружности, их следует соединить дугой
+ if ((graph_.nodes[optimal_way_[i]]->circle_ptr) &&
+ (graph_.nodes[optimal_way_[i + 1]]->circle_ptr) &&
+ (*(graph_.nodes[optimal_way_[i]]->circle_ptr) ==
+ *(graph_.nodes[optimal_way_[i + 1]]->circle_ptr))) {
+ trajectory_part_.push_back(
+ Segment(graph_.nodes[optimal_way_[i]]->point,
+ graph_.nodes[optimal_way_[i + 1]]->point,
+ graph_.nodes[optimal_way_[i]]->circle_ptr->GetCenter()));
+ } else {
+ trajectory_part_.push_back(
+ Segment(graph_.nodes[optimal_way_[i]]->point,
+ graph_.nodes[optimal_way_[i + 1]]->point));
+ }
+ }
+}
+
+void OptimalWayCalculator::ResetInformation() {
+ trajectory_part_.clear();
+ for (auto& node : graph_.nodes) node->is_visited = false;
}
template bool OptimalWayCalculator::TangentGoesThroughOtherObstacle<
@@ -216,4 +279,5 @@ template void
OptimalWayCalculator::AddTangent(
const LinearFunction& tangent, PolygonObstacle& obstacle1,
PolygonObstacle& obstacle2);
+
} // namespace math
diff --git a/math/optimal_way/optimal_way.h b/math/optimal_way/optimal_way.h
index adee462..5f99ccb 100644
--- a/math/optimal_way/optimal_way.h
+++ b/math/optimal_way/optimal_way.h
@@ -1,6 +1,10 @@
#pragma once
+// std libs:
+#include
+
// our code libs:
+#include "lib/segment.h"
#include "path_graph.h"
namespace math {
@@ -13,25 +17,26 @@ class OptimalWayCalculator {
* @param circles: круговые препятствия
* @param polys: многоугольные препятствия
*/
- OptimalWayCalculator(std::vector circles,
- std::vector polys)
+ OptimalWayCalculator(const std::vector& circles,
+ const std::vector& polys)
: circles_{circles}, polys_{polys} {
AddCommonTangents();
AddGraphTangentPoints();
- normal_graph_size_ = graph_.nodes.size();
}
std::vector> GetGraphNodes() {
return graph_.nodes;
}
- std::vector GetOptimalWay(Point point1, Point point2) {
- FindOptimalWay(point1, point2);
- return optimal_way_;
- }
+ std::vector GetOptimalWay() { return optimal_way_; }
double GetOptimalWayLength() { return optimal_way_length_; }
+ std::vector GetTrajectoryPart() { return trajectory_part_; }
+
+ // Находит оптимальный маршрут
+ void FindOptimalWay(Point p1, Point p2);
+
private:
std::vector circles_;
@@ -40,15 +45,15 @@ class OptimalWayCalculator {
// Граф для алгоритма Дейкстры
PathWayGraph graph_;
- // Количество вершин в графе без связей с контрольными точками
- std::size_t normal_graph_size_;
-
// Оптимальный путь
std::vector optimal_way_;
// Длина оптимального пути
double optimal_way_length_;
+ // Часть траектории
+ std::vector trajectory_part_;
+
/**
* @brief Проверяет, пересекает ли общая касательная двух препятствий другое
* препятствие
@@ -86,10 +91,13 @@ class OptimalWayCalculator {
void AddGraphTangentPoints();
// Добавляет в граф контрольные точки
- std::size_t AddGraphControlPoints(Point point);
+ std::set AddGraphControlPoints(Point point);
- // Находит оптимальный маршрут
- void FindOptimalWay(Point p1, Point p2);
+ // Создать часть траектории
+ void MakeTrajectoryPart();
+
+ // Удаляет информацию от предыдущих точек
+ void ResetInformation();
};
} // namespace math
diff --git a/math/optimal_way/path_graph.h b/math/optimal_way/path_graph.h
index 4e946fe..345e1a6 100644
--- a/math/optimal_way/path_graph.h
+++ b/math/optimal_way/path_graph.h
@@ -15,8 +15,8 @@ struct PathWayNode {
* @param p координаты
* @param n номер вершины
*/
- PathWayNode(Point p, std::size_t n)
- : point{p}, number{n}, is_visited{false} {}
+ PathWayNode(Point p, std::size_t n, bool is_const)
+ : point{p}, number{n}, is_visited{false}, is_constant{is_const} {}
// Ребра данной вершины
std::vector> edges;
@@ -37,6 +37,9 @@ struct PathWayNode {
// Была ли уже посещена вершина
// в алгоритме Дейкстры
bool is_visited;
+
+ // Явлеяется ли точка постоянной в графе
+ bool is_constant;
};
/// @brief Граф вершин между контрольными точками
@@ -45,21 +48,29 @@ struct PathWayGraph {
std::vector> nodes;
// Добавить новую вершину
- void AddNode(std::shared_ptr new_node) {
- nodes.push_back(new_node);
- }
-
- /**
- * @brief Удалить последние n вершин
- * @param n: количество вершин
- */
- void RemoveLastNodes(std::size_t n) {
- for (std::size_t i = 0; i < n; ++i) {
- for (auto& other_node : nodes[nodes.size() - 1]->edges)
- for (std::size_t j = 0; j < n; ++j)
- if (other_node->edges[j] == nodes[nodes.size() - 1])
- other_node->edges.erase(other_node->edges.begin() + j);
- nodes.erase(nodes.begin() + nodes.size() - 1);
+ void AddNode(PathWayNode* new_node) { nodes.emplace_back(new_node); }
+
+ // Удалить временные вершины
+ void RemoveNonConstantNodes() {
+ std::size_t i = 0;
+ while (i < nodes.size()) {
+ if (nodes[i]->is_constant) {
+ ++i;
+ continue;
+ }
+ for (const auto& other_node : nodes[i]->edges) {
+ std::size_t j = 0;
+ while (j < other_node->edges.size()) {
+ if (nodes[i]->point != other_node->edges[j]->point) {
+ ++j;
+ continue;
+ }
+ other_node->edges.erase(other_node->edges.begin() + j);
+ other_node->edges_lens.erase(other_node->edges_lens.begin() + j);
+ break;
+ }
+ }
+ nodes.erase(nodes.begin() + i);
}
}
diff --git a/math/trajectory.cpp b/math/trajectory.cpp
index e9cacf8..a797180 100644
--- a/math/trajectory.cpp
+++ b/math/trajectory.cpp
@@ -9,27 +9,6 @@ using lib::TrappyLine;
namespace math {
-std::vector TrajectoryCalculator::GetTrajectoryPart(
- std::vector optimal_way,
- const std::vector>& nodes) {
- std::vector trajectory_part;
- for (std::size_t i = 0; i < optimal_way.size() - 1; ++i) {
- // Если точки ляжат на одной окружности, их следует соединить дугой
- if ((nodes[optimal_way[i]]->circle_ptr) &&
- (nodes[optimal_way[i] + 1]->circle_ptr) &&
- (nodes[optimal_way[i]]->circle_ptr ==
- nodes[optimal_way[i] + 1]->circle_ptr))
- trajectory_part.push_back(Segment(
- nodes[optimal_way[i]]->point, nodes[optimal_way[i] + 1]->point,
- nodes[optimal_way[i]]->circle_ptr->GetCenter()));
- else {
- trajectory_part.push_back(Segment(nodes[optimal_way[i]]->point,
- nodes[optimal_way[i] + 1]->point));
- }
- }
- return trajectory_part;
-}
-
void TrajectoryCalculator::CalculateTrajectory() {
// Матрица смежности контрольных точек
std::vector> matrix(targets_.size());
@@ -42,15 +21,21 @@ void TrajectoryCalculator::CalculateTrajectory() {
// Заполнение матриц
OptimalWayCalculator owc(circles_, polys_);
- for (std::size_t i = 0; i < targets_.size() - 1; ++i) {
+ for (std::size_t i = 0; i < targets_.size(); ++i) {
matrix[i][i] = inf;
- for (std::size_t j = i + 1; i < targets_.size(); ++j) {
- std::vector optimal_way =
- owc.GetOptimalWay(targets_[i], targets_[j]);
- std::vector> nodes = owc.GetGraphNodes();
- std::vector segment_way = GetTrajectoryPart(optimal_way, nodes);
+ for (std::size_t j = 0; j < i; ++j) {
+ owc.FindOptimalWay(targets_[i], targets_[j]);
+ std::vector segment_way = owc.GetTrajectoryPart();
+
optimal_ways[i][j] = segment_way;
- std::reverse(segment_way.begin(), segment_way.begin());
+ std::reverse(segment_way.begin(), segment_way.end());
+ for (auto& seg_part : segment_way) {
+ if (seg_part.IsArc())
+ seg_part =
+ Segment(seg_part.End(), seg_part.Start(), seg_part.Center());
+ else
+ seg_part = Segment(seg_part.End(), seg_part.Start());
+ }
optimal_ways[j][i] = segment_way;
matrix[i][j] = owc.GetOptimalWayLength();
@@ -65,14 +50,15 @@ void TrajectoryCalculator::CalculateTrajectory() {
// Просчет кратчайшей траектории по алгоритму Литтла
AdjacencyMatrix adj_matrix = AdjacencyMatrix::WithExtraRowCol(matrix);
- TravellingSalesmansProblem tsp(adj_matrix);
+ TravellingSalesmansProblem tsp(adj_matrix, number_of_flyers_);
std::vector traj = tsp.GetTrajectory();
// Объединение частей траектории
- for (std::size_t i = 0; i < traj.size() - 1; ++i) {
- trajectory_.insert(trajectory_.end(),
- optimal_ways[traj[i]][traj[i + 1]].begin(),
- optimal_ways[traj[i]][traj[i + 1]].end());
+ for (std::size_t i = 0; i < traj.size(); ++i) {
+ trajectory_.insert(
+ trajectory_.end(),
+ optimal_ways[traj[i]][traj[(i + 1) % traj.size()]].begin(),
+ optimal_ways[traj[i]][traj[(i + 1) % traj.size()]].end());
}
}
diff --git a/math/trajectory.h b/math/trajectory.h
index d9b59d2..fd55c4c 100644
--- a/math/trajectory.h
+++ b/math/trajectory.h
@@ -19,7 +19,9 @@ class TrajectoryCalculator {
TrajectoryCalculator(const std::vector& targets,
const std::vector& lines,
const std::vector& circles,
- const std::vector& hills) {
+ const std::vector& hills,
+ unsigned short num_of_flyers)
+ : number_of_flyers_{num_of_flyers} {
for (auto& target : targets) targets_.push_back(Point(target.GetPoint()));
for (auto& line : lines) {
@@ -50,6 +52,9 @@ class TrajectoryCalculator {
std::vector GetTrajectory() { return trajectory_; }
private:
+ // Количество роботов
+ unsigned short number_of_flyers_;
+
// Контрольные точки
std::vector targets_;
@@ -75,7 +80,7 @@ class TrajectoryCalculator {
std::vector optimal_way,
const std::vector>& nodes);
- /// @brief Расcчитывает траекторию
+ /// @brief Рассчитывает траекторию
void CalculateTrajectory();
};
diff --git a/qcustomplot b/qcustomplot
index 74ce35e..6e43830 160000
--- a/qcustomplot
+++ b/qcustomplot
@@ -1 +1 @@
-Subproject commit 74ce35e476beb9072c2354e83d515c2c6c4210e2
+Subproject commit 6e438307a9d7958d1b9b14defd5b420f593c0af6
diff --git a/tests/maths/dijkstra.cpp b/tests/maths/dijkstra.cpp
index 251e856..be1aeaa 100644
--- a/tests/maths/dijkstra.cpp
+++ b/tests/maths/dijkstra.cpp
@@ -16,11 +16,13 @@ struct TestEdge {
};
void AddNodes(PathWayGraph& graph, std::size_t number_of_nodes) {
- for (std::size_t i = 0; i < number_of_nodes; ++i)
- graph.AddNode(std::make_shared(Point(0, 0), i));
+ for (std::size_t i = 0; i < number_of_nodes; ++i) {
+ PathWayNode* node = new PathWayNode(Point(0, 0), i, true);
+ graph.AddNode(node);
+ }
}
-void CHECK_GRAPH(std::vector edges, double ans) {
+void CHECK_GRAPH(const std::vector& edges, double ans) {
PathWayGraph graph;
std::size_t number_of_nodes = 0;
diff --git a/tests/maths/optimal_way.cpp b/tests/maths/optimal_way.cpp
index 074390b..a9606ad 100644
--- a/tests/maths/optimal_way.cpp
+++ b/tests/maths/optimal_way.cpp
@@ -16,11 +16,12 @@ void CHECK_OPTIMAL_WAY(
std::vector circles = std::vector(0),
std::vector polys = std::vector(0)) {
OptimalWayCalculator optimal_way(circles, polys);
- optimal_way.GetOptimalWay(point1, point2);
+ optimal_way.FindOptimalWay(point1, point2);
BOOST_TEST(optimal_way.GetOptimalWayLength() == correct_length);
}
BOOST_AUTO_TEST_SUITE(circles, *utf::tolerance(1.0E-5))
+
BOOST_AUTO_TEST_CASE(no_obstacle) {
Point point1(-4, -2);
Point point2(2, 2);
@@ -46,7 +47,7 @@ BOOST_AUTO_TEST_CASE(one_circle) {
CHECK_OPTIMAL_WAY(point1, point2, correct_length, circles);
}
-BOOST_AUTO_TEST_CASE(two_circles) {
+BOOST_AUTO_TEST_CASE(two_circles_1) {
Point point1(-7.98, 1.23);
Point point2(8.16, 2.27);
std::vector circles{{{-6.44, 3.95}, 2.4186773244896},
@@ -55,6 +56,16 @@ BOOST_AUTO_TEST_CASE(two_circles) {
CHECK_OPTIMAL_WAY(point1, point2, correct_length, circles);
}
+BOOST_AUTO_TEST_CASE(two_circles_2) {
+ Point point1(2.8860028860028857, 3.02);
+ Point point2(0.6854256854256855, 3.15);
+ std::vector circles{
+ {{2.2005772005772006, 3.43}, 0.5398077722761565},
+ {{0.9090909090909092, 2.3400000000000003}, 0.6977516532510056}};
+ double correct_length = 2.2359987212926;
+ CHECK_OPTIMAL_WAY(point1, point2, correct_length, circles);
+}
+
BOOST_AUTO_TEST_CASE(three_circles) {
Point point1(-7.38, 4.09);
Point point2(7.44, -1.99);
@@ -76,4 +87,126 @@ BOOST_AUTO_TEST_CASE(many_circles) {
CHECK_OPTIMAL_WAY(point1, point2, correct_length, circles);
}
+BOOST_AUTO_TEST_CASE(many_circles_2) {
+ Point point1(3.10967, 3.27);
+ Point point2(1.29149, 0.84);
+ std::vector circles{
+ {{1.6233766233766234, 2.44}, 0.6374372349316524},
+ {{0.3463203463203463, 3.54}, 0.40295210083288274},
+ {{2.647907647907648, 3.84}, 0.32875169441430113},
+ {{1.9336219336219338, 1.03}, 0.5693287325398032},
+ {{1.5007215007215007, 4.59}, 0.3984371374874696},
+ {{3.5714285714285716, 3.5}, 0.19135454700142618},
+ {{3.5642135642135644, 4.48}, 0.31775186959715124},
+ {{3.2323232323232327, 2.66}, 0.4448578559741175},
+ {{2.676767676767677, 1.96}, 0.2645089838359704}};
+ double correct_length = 3.0726744815740017;
+ CHECK_OPTIMAL_WAY(point1, point2, correct_length, circles);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+BOOST_AUTO_TEST_SUITE(polygons, *utf::tolerance(1.0E-5))
+
+BOOST_AUTO_TEST_CASE(one_poly) {
+ Point point1(-8.62, 1.33);
+ Point point2(3.4752511575043, 6.4339001323606);
+ std::vector circles(0);
+ std::vector vertix{{-4.72, 3.65},
+ {-1.84, 2.51},
+ {0, 3.4729910311284},
+ {-0.9088573459776, 5.828631421059},
+ {-1.9558086303911, 6.5484104290933},
+ {-3.6, 6.19},
+ {-4.78, 4.95}};
+ std::vector polys{{vertix}};
+ double correct_length = 13.5242447275625;
+ CHECK_OPTIMAL_WAY(point1, point2, correct_length, circles, polys);
+}
+
+BOOST_AUTO_TEST_CASE(two_polys) {
+ Point point1(-8.88, 5.55);
+ Point point2(10.84, -3.23);
+ std::vector circles(0);
+ std::vector vertix1{{-5.82, 5.93}, {-6.54, 4.27}, {-4.94, 0.47},
+ {-1.14, 2.11}, {-1.7, 4.55}, {-2.88, 6.17},
+ {-4.54, 6.51}};
+ std::vector vertix2{{3.38, -0.11}, {3.56, 1.85}, {4.8, 2.79},
+ {8.92, 2.55}, {5.66, -1.73}, {3.84, -1.33}};
+ std::vector polys{{vertix1}, {vertix2}};
+ double correct_length = 22.6368146106518;
+ CHECK_OPTIMAL_WAY(point1, point2, correct_length, circles, polys);
+}
+
+BOOST_AUTO_TEST_CASE(two_polys_2) {
+ Point point1(16.0867119995807, 81.82110410565608);
+ Point point2(-26.39655465103465, -5.341297839758592);
+ std::vector circles(0);
+ std::vector vertix1{{-21.9547, 12.0331},
+ {-25.1369, 5.28401},
+ {-13.7517, 4.58098},
+ {-14.3881, 10.9082}};
+ std::vector vertix2{{23.426576955006283, 77.79346124390177},
+ {38.245488935037955, 60.2493893479525},
+ {28.936172434761644, 43.479320623883325},
+ {12.977344148573707, 51.9933555145646},
+ {7.087776566766259, 72.89144115532771}};
+ std::vector polys{{vertix1}, {vertix2}};
+ double correct_length = 98.2718720490988;
+ CHECK_OPTIMAL_WAY(point1, point2, correct_length, circles, polys);
+}
+
+BOOST_AUTO_TEST_CASE(three_polys) {
+ Point point1(-10.8878429725857, -8.4678597369649);
+ Point point2(20.3621535335072, 11.5873162890166);
+ std::vector circles(0);
+ std::vector vertix1{{4.710627269844334, -11.067604777369938},
+ {-10.834786951352983, 0.39249580890520747},
+ {-3.3008319363017242, 8.669235121215035},
+ {6.726756076689037, 8.8284031849133},
+ {7.734820480111389, 3.5228010616377703},
+ {7.8409325225768995, -1.3583528917757175},
+ {7.416484352714857, -4.913106314370323},
+ {6.35536392805975, -9.369812097921768},
+ {5.718691673266686, -10.59010058627514}};
+ std::vector vertix2{{11.0242937965422, -1.1991848280775},
+ {10.2815094992836, 3.3636329979395},
+ {11.7670780938008, 7.1306105054651},
+ {14.8443273253006, 8.669235121215},
+ {16.6482320472143, 5.857265995879},
+ {16.6482320472143, 2.6739047219137},
+ {15, 0},
+ {13.570982815714478, -1.7828010616377599}};
+ std::vector vertix3{{21.6354980430933, -10.0595403739476},
+ {16.6482320472143, -2.7908654650601},
+ {3.8086749088875, -14.7284702424301}};
+ std::vector polys{{vertix1}, {vertix2}, {vertix3}};
+ double correct_length = 43.8106387108272;
+ CHECK_OPTIMAL_WAY(point1, point2, correct_length, circles, polys);
+}
+
+BOOST_AUTO_TEST_CASE(weird_poly) {
+ Point point1(5, -3);
+ Point point2(0.8, 4.29);
+ std::vector circles(0);
+ std::vector vertix1{{-1.66, -1.39}, {3.26, 4.67}};
+ std::vector polys{{vertix1}};
+ double correct_length = 10.3540669068468;
+ CHECK_OPTIMAL_WAY(point1, point2, correct_length, circles, polys);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+BOOST_AUTO_TEST_SUITE(circles_and_polys, *utf::tolerance(1.0E-5))
+BOOST_AUTO_TEST_CASE(one_poly_one_circle) {
+ Point point1(10.88, 3.65);
+ Point point2(2.04, 3.19);
+ std::vector circles{{{4.18, 2.97}, 1.33}};
+ std::vector vertix{{6.96, 1.75}, {6.24, 3.59}, {6.4, 5.69},
+ {7.8, 6.65}, {9.48, 5.31}, {10.52, 2.71},
+ {9.88, 1.81}, {8.02, 0.69}};
+ std::vector polys{{vertix}};
+ double correct_length = 10.9484801817353;
+ CHECK_OPTIMAL_WAY(point1, point2, correct_length, circles, polys);
+}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/maths/tangents_between.cpp b/tests/maths/tangents_between.cpp
index 8d7f180..ef6dabe 100644
--- a/tests/maths/tangents_between.cpp
+++ b/tests/maths/tangents_between.cpp
@@ -9,8 +9,8 @@ namespace tt = boost::test_tools;
namespace utf = boost::unit_test;
using namespace math;
-void CHECK_TANGENTS(std::vector func_tangents,
- std::vector correct_tangents) {
+void CHECK_TANGENTS(const std::vector& func_tangents,
+ const std::vector& correct_tangents) {
std::size_t amount_of_matches = 0;
BOOST_TEST(func_tangents.size() == correct_tangents.size());
for (std::size_t i = 0; i < func_tangents.size(); ++i)