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 🗂 - - - - + + + + Settings ⚙ - - + - - - Help ❔ - - @@ -314,14 +432,13 @@ QPushButton::pressed{background-color: rgb(135, 135, 135);} Add ➕ - - - - + + + + - @@ -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)