diff --git a/.gitignore b/.gitignore
index 9afa465d..c90ab088 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ detector/
*.swp
site/
docs/reference/
+.nicegui/
diff --git a/.syncignore b/.syncignore
index 504aa03f..414f759e 100644
--- a/.syncignore
+++ b/.syncignore
@@ -11,3 +11,4 @@ config.hjson
sync.sh
nicegui
rosys
+.nicegui
\ No newline at end of file
diff --git a/backup/rb27/field_friend.automations.field_provider.json b/backup/rb27/field_friend.automations.field_provider.json
new file mode 100644
index 00000000..53148270
--- /dev/null
+++ b/backup/rb27/field_friend.automations.field_provider.json
@@ -0,0 +1,420 @@
+{
+ "fields": [
+ {
+ "id": "5a8a2850-89d8-4c31-8998-58f95c4bf12b",
+ "name": "Test",
+ "outline_wgs84": [
+ [
+ 53.11086450840848,
+ 13.91407542875983
+ ],
+ [
+ 53.110900665,
+ 13.914103491666667
+ ],
+ [
+ 53.11083908809259,
+ 13.914568552126127
+ ],
+ [
+ 53.11080833166667,
+ 13.914556051666667
+ ]
+ ],
+ "reference_lat": 53.11065129259277,
+ "reference_lon": 13.91371250152588,
+ "visualized": false,
+ "obstacles": [],
+ "rows": [
+ {
+ "id": "a2a8d7a7-1d09-42f3-9a38-d2a35d2f6440",
+ "name": "row_1",
+ "points_wgs84": [
+ [
+ 53.11086900379518,
+ 13.914155492406122
+ ],
+ [
+ 53.11082100387979,
+ 13.914524203377239
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "dbfad64c-0180-4593-b6ff-a9ab87b8024a",
+ "name": "row_2",
+ "points_wgs84": [
+ [
+ 53.11087289058598,
+ 13.91415661849706
+ ],
+ [
+ 53.11082537116117,
+ 13.914524852764124
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "58729d6f-268b-4a37-8d7a-770a4d7f7546",
+ "name": "row_4",
+ "points_wgs84": [
+ [
+ 53.11088111899296,
+ 13.9141582500688
+ ],
+ [
+ 53.11083298032453,
+ 13.91452758242803
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "8d83be1e-40f1-433c-803b-19352b339c77",
+ "name": "row_5",
+ "points_wgs84": [
+ [
+ 53.11088460915779,
+ 13.914162049853772
+ ],
+ [
+ 53.1108366940985,
+ 13.914525808114977
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ }
+ ]
+ },
+ {
+ "id": "f275d15c-b65c-462f-a18b-a8576f888a54",
+ "name": "field_Bohren",
+ "outline_wgs84": [
+ [
+ 53.11088575363881,
+ 13.914051403518485
+ ],
+ [
+ 53.110692736924065,
+ 13.913986660077686
+ ],
+ [
+ 53.11061942665463,
+ 13.914525650421513
+ ],
+ [
+ 53.1108193510101,
+ 13.914618566551765
+ ]
+ ],
+ "reference_lat": 53.110881499529384,
+ "reference_lon": 13.914047767487654,
+ "visualized": false,
+ "obstacles": [],
+ "rows": [
+ {
+ "id": "bc19d73e-6516-4be4-9d5d-9c7f82905791",
+ "name": "row_1",
+ "points_wgs84": [
+ [
+ 53.1108103725628,
+ 13.914533857646589
+ ],
+ [
+ 53.11086585435494,
+ 13.914110043957198
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "cac9f4be-7487-4c72-adb3-93a2f7ba9266",
+ "name": "row_2",
+ "points_wgs84": [
+ [
+ 53.11079158358294,
+ 13.914522718556901
+ ],
+ [
+ 53.11084545526309,
+ 13.914113096249709
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "7e1c954c-1c3d-4751-8b5c-5310db183d02",
+ "name": "row_3",
+ "points_wgs84": [
+ [
+ 53.110771634262846,
+ 13.914515257349777
+ ],
+ [
+ 53.110825714596324,
+ 13.914102179170046
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "72390a9f-2f89-41ae-8285-494e5dc1c9fd",
+ "name": "row_4",
+ "points_wgs84": [
+ [
+ 53.11075179138159,
+ 13.914505245488035
+ ],
+ [
+ 53.110806123173724,
+ 13.914098907293617
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "4228284a-f2b5-446e-a2a2-85281edd0764",
+ "name": "row_5",
+ "points_wgs84": [
+ [
+ 53.11073251919884,
+ 13.914499494997212
+ ],
+ [
+ 53.11078634220459,
+ 13.914090071936823
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ }
+ ]
+ },
+ {
+ "id": "38d0b78e-73ff-481e-a9fd-ad0b4f0bc383",
+ "name": "field_Pendelhacke",
+ "outline_wgs84": [
+ [
+ 53.110885761849865,
+ 13.91405137425968
+ ],
+ [
+ 53.110692719323964,
+ 13.913986645203941
+ ],
+ [
+ 53.11061944584767,
+ 13.914525645292146
+ ],
+ [
+ 53.11081932732413,
+ 13.91461852074301
+ ]
+ ],
+ "reference_lat": 53.11088148947587,
+ "reference_lon": 13.914047757052812,
+ "visualized": false,
+ "obstacles": [],
+ "rows": [
+ {
+ "id": "a1b944a9-2f32-4863-9ed1-5b7d3e3905a9",
+ "name": "row_1",
+ "points_wgs84": [
+ [
+ 53.11080748389169,
+ 13.914528449056974
+ ],
+ [
+ 53.1108616244314,
+ 13.914115170279958
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "06485080-96a3-4387-bd98-ae13c5959378",
+ "name": "row_2",
+ "points_wgs84": [
+ [
+ 53.11078736188202,
+ 13.91452134589732
+ ],
+ [
+ 53.110841041222685,
+ 13.914112616811344
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "7c403dd6-76da-4a44-a793-4fb8814f910e",
+ "name": "row_3",
+ "points_wgs84": [
+ [
+ 53.11076732735035,
+ 13.914514540695514
+ ],
+ [
+ 53.11082206421793,
+ 13.914099414601182
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "66a73378-a6c4-463c-a71a-699779bea36f",
+ "name": "row_4",
+ "points_wgs84": [
+ [
+ 53.11074767905439,
+ 13.914503905811971
+ ],
+ [
+ 53.1108022193147,
+ 13.914094882137139
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "6948c407-cf9b-4638-a7a8-dc7e0dcfb1ba",
+ "name": "row_5",
+ "points_wgs84": [
+ [
+ 53.11072939628047,
+ 13.91449482199291
+ ],
+ [
+ 53.110782272589596,
+ 13.91408976451268
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ }
+ ]
+ },
+ {
+ "id": "e51659c4-d7aa-412a-9f1f-81c2fc1aa0b8",
+ "name": "field_DoppelMechanik",
+ "outline_wgs84": [
+ [
+ 53.11088572490662,
+ 13.914051421541666
+ ],
+ [
+ 53.11069272815888,
+ 13.913986684849567
+ ],
+ [
+ 53.11061941486577,
+ 13.914525678599288
+ ],
+ [
+ 53.11081934782822,
+ 13.914618532493373
+ ]
+ ],
+ "reference_lat": 53.11088150880825,
+ "reference_lon": 13.91404779326869,
+ "visualized": false,
+ "obstacles": [],
+ "rows": [
+ {
+ "id": "afbafd10-dcf6-4c4f-a462-241f7d48c6a1",
+ "name": "row_1",
+ "points_wgs84": [
+ [
+ 53.11079956288332,
+ 13.914524099085297
+ ],
+ [
+ 53.11085311243897,
+ 13.91411221946047
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "870744db-13ce-432a-96dd-a9e23e502836",
+ "name": "row_2",
+ "points_wgs84": [
+ [
+ 53.110780072773125,
+ 13.914515952501517
+ ],
+ [
+ 53.110833534926016,
+ 13.91410706581986
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "620063b9-a870-4000-8be3-e2a6669ae4fd",
+ "name": "row_3",
+ "points_wgs84": [
+ [
+ 53.1107598941182,
+ 13.914508513187641
+ ],
+ [
+ 53.11081359893513,
+ 13.914103616913216
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "bcc78e8f-b5c3-444d-b657-33d7984082c8",
+ "name": "row_4",
+ "points_wgs84": [
+ [
+ 53.11074016554936,
+ 13.914501518993957
+ ],
+ [
+ 53.11079387469022,
+ 13.914094245038237
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "50bd98c7-2f2b-4ec1-9c11-5582f4932f01",
+ "name": "row_5",
+ "points_wgs84": [
+ [
+ 53.11072112793958,
+ 13.914493604773137
+ ],
+ [
+ 53.11077420287833,
+ 13.91408888452689
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/backup/rb27/field_friend.automations.kpi_provider.json b/backup/rb27/field_friend.automations.kpi_provider.json
new file mode 100644
index 00000000..c755f78a
--- /dev/null
+++ b/backup/rb27/field_friend.automations.kpi_provider.json
@@ -0,0 +1,65 @@
+{
+ "current_weeding_kpis": {
+ "distance": 7,
+ "time": 250,
+ "bumps": 0,
+ "low_battery": 0,
+ "can_failure": 0,
+ "automation_stopped": 0,
+ "e_stop_triggered": 0,
+ "soft_e_stop_triggered": 0,
+ "imu_rolling_detected": 0,
+ "gnss_connection_lost": 0,
+ "rows_weeded": 0,
+ "crops_detected": 0,
+ "weeds_detected": 0,
+ "weeds_removed": 0,
+ "punches": 0,
+ "chops": 0,
+ "weeding_completed": 1
+ },
+ "current_mowing_kpis": {
+ "distance": 0,
+ "time": 0,
+ "bumps": 0,
+ "low_battery": 0,
+ "can_failure": 0,
+ "automation_stopped": 0,
+ "e_stop_triggered": 0,
+ "soft_e_stop_triggered": 0,
+ "imu_rolling_detected": 0,
+ "gnss_connection_lost": 0,
+ "mowing_completed": false
+ },
+ "days": [
+ {
+ "date": "2024-04-24",
+ "incidents": {
+ "low_battery": 3
+ }
+ },
+ {
+ "date": "2024-04-26",
+ "incidents": {
+ "low_battery": 9,
+ "punches": 4
+ }
+ },
+ {
+ "date": "2024-04-30",
+ "incidents": {
+ "time": 5641,
+ "distance": 593,
+ "weeds_detected": 1720,
+ "crops_detected": 123,
+ "rows_weeded": 3,
+ "weeding_completed": 13,
+ "punches": 33,
+ "weeds_removed": 55,
+ "chops": 19,
+ "low_battery": 14
+ }
+ }
+ ],
+ "months": []
+}
\ No newline at end of file
diff --git a/backup/rb27/field_friend.automations.mowing.json b/backup/rb27/field_friend.automations.mowing.json
new file mode 100644
index 00000000..4e1a52f1
--- /dev/null
+++ b/backup/rb27/field_friend.automations.mowing.json
@@ -0,0 +1,7 @@
+{
+ "padding": 1.0,
+ "lane_distance": 0.5,
+ "paths": [],
+ "current_path": [],
+ "current_path_segment": null
+}
\ No newline at end of file
diff --git a/backup/rb27/field_friend.automations.path_provider.json b/backup/rb27/field_friend.automations.path_provider.json
new file mode 100644
index 00000000..f05625f8
--- /dev/null
+++ b/backup/rb27/field_friend.automations.path_provider.json
@@ -0,0 +1,3 @@
+{
+ "paths": []
+}
\ No newline at end of file
diff --git a/backup/rb27/field_friend.automations.weeding.json b/backup/rb27/field_friend.automations.weeding.json
new file mode 100644
index 00000000..220a6126
--- /dev/null
+++ b/backup/rb27/field_friend.automations.weeding.json
@@ -0,0 +1,26 @@
+{
+ "use_field_planning": true,
+ "field": null,
+ "start_row_id": null,
+ "end_row_id": null,
+ "minimum_turning_radius": 0.5,
+ "turn_offset": 0.4,
+ "only_monitoring": false,
+ "drill_with_open_tornado": false,
+ "drill_between_crops": false,
+ "with_drilling": false,
+ "with_chopping": false,
+ "chop_if_no_crops": false,
+ "tornado_angle": 110.0,
+ "weed_screw_depth": 0.15,
+ "crop_safety_distance": 0.01,
+ "linear_speed_on_row": 0.08,
+ "angular_speed_on_row": 0.4,
+ "linear_speed_between_rows": 0.3,
+ "angular_speed_between_rows": 0.8,
+ "sorted_weeding_rows": [],
+ "weeding_plan": [],
+ "turn_paths": [],
+ "current_row": null,
+ "current_segment": null
+}
\ No newline at end of file
diff --git a/backup/rb27/field_friend.navigation.gnss.json b/backup/rb27/field_friend.navigation.gnss.json
new file mode 100644
index 00000000..005f4bb1
--- /dev/null
+++ b/backup/rb27/field_friend.navigation.gnss.json
@@ -0,0 +1,13 @@
+{
+ "record": {
+ "timestamp": null,
+ "latitude": 0.0,
+ "longitude": 0.0,
+ "mode": "NNNN",
+ "gps_qual": 0,
+ "altitude": 0.0,
+ "separation": 0.0,
+ "heading": 0.0,
+ "speed_kmh": 0.0
+ }
+}
\ No newline at end of file
diff --git a/backup/rb27/field_friend.vision.calibratable_usb_camera_provider.json b/backup/rb27/field_friend.vision.calibratable_usb_camera_provider.json
new file mode 100644
index 00000000..c5dfb39b
--- /dev/null
+++ b/backup/rb27/field_friend.vision.calibratable_usb_camera_provider.json
@@ -0,0 +1,93 @@
+{
+ "cameras": {
+ "32e4-9230-platform-3610000.xhci-usb-0:2.1:1.0": {
+ "id": "32e4-9230-platform-3610000.xhci-usb-0:2.1:1.0",
+ "name": "32e4-9230-platform-3610000.xhci-usb-0:2.1:1.0",
+ "connect_after_init": true,
+ "streaming": true,
+ "focal_length": 1830,
+ "calibration": {
+ "intrinsics": {
+ "matrix": [
+ [
+ 1433.6721216716353,
+ 0.0,
+ 347.7277694468386
+ ],
+ [
+ 0.0,
+ 1370.2101675446997,
+ 239.1060666171041
+ ],
+ [
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ ],
+ "distortion": [
+ -0.01557127473024067,
+ -56.25674323172758,
+ 0.23577466596211383,
+ 0.011342764817018949,
+ 421.8140030391272
+ ],
+ "rotation": {
+ "R": [
+ [
+ 1.0,
+ 0.0,
+ 0.0
+ ],
+ [
+ 0.0,
+ 1.0,
+ 0.0
+ ],
+ [
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ ]
+ },
+ "size": {
+ "width": 720,
+ "height": 560
+ }
+ },
+ "extrinsics": {
+ "rotation": {
+ "R": [
+ [
+ -0.005087957942356058,
+ 0.9070537937934018,
+ -0.42098399951657745
+ ],
+ [
+ 0.9967070483093687,
+ -0.02946986293924958,
+ -0.075541955420596
+ ],
+ [
+ -0.08092695802019419,
+ -0.419982073835704,
+ -0.9039169680464345
+ ]
+ ]
+ },
+ "translation": [
+ 0.5757624106417404,
+ 0.0740620510871811,
+ 1.1604844656286453
+ ]
+ }
+ },
+ "auto_exposure": true,
+ "exposure": false,
+ "width": 1280,
+ "height": 720,
+ "fps": 9
+ }
+ }
+}
\ No newline at end of file
diff --git a/backup/rb27/field_friend.vision.usb_cam_provider.json b/backup/rb27/field_friend.vision.usb_cam_provider.json
new file mode 100644
index 00000000..d226b1ae
--- /dev/null
+++ b/backup/rb27/field_friend.vision.usb_cam_provider.json
@@ -0,0 +1,93 @@
+{
+ "cameras": {
+ "32e4-9230-platform-3610000.xhci-usb-0:2.1:1.0": {
+ "id": "32e4-9230-platform-3610000.xhci-usb-0:2.1:1.0",
+ "name": "32e4-9230-platform-3610000.xhci-usb-0:2.1:1.0",
+ "connect_after_init": true,
+ "streaming": true,
+ "auto_exposure": true,
+ "exposure": false,
+ "width": 1920,
+ "height": 1080,
+ "fps": 6,
+ "focal_length": 1830,
+ "calibration": {
+ "intrinsics": {
+ "matrix": [
+ [
+ 1433.6721216716353,
+ 0.0,
+ 347.7277694468386
+ ],
+ [
+ 0.0,
+ 1370.2101675446997,
+ 239.1060666171041
+ ],
+ [
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ ],
+ "distortion": [
+ -0.01557127473024067,
+ -56.25674323172758,
+ 0.23577466596211383,
+ 0.011342764817018949,
+ 421.8140030391272
+ ],
+ "rotation": {
+ "R": [
+ [
+ 1.0,
+ 0.0,
+ 0.0
+ ],
+ [
+ 0.0,
+ 1.0,
+ 0.0
+ ],
+ [
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ ]
+ },
+ "size": {
+ "width": 720,
+ "height": 560
+ }
+ },
+ "extrinsics": {
+ "rotation": {
+ "R": [
+ [
+ -0.005087957942356058,
+ 0.9070537937934018,
+ -0.42098399951657745
+ ],
+ [
+ 0.9967070483093687,
+ -0.02946986293924958,
+ -0.075541955420596
+ ],
+ [
+ -0.08092695802019419,
+ -0.419982073835704,
+ -0.9039169680464345
+ ]
+ ]
+ },
+ "translation": [
+ 0.5757624106417404,
+ 0.0740620510871811,
+ 1.1604844656286453
+ ]
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/backup/rb27/rosys.config.json b/backup/rb27/rosys.config.json
new file mode 100644
index 00000000..07b402b0
--- /dev/null
+++ b/backup/rb27/rosys.config.json
@@ -0,0 +1,3 @@
+{
+ "simulation_speed": 1.0
+}
\ No newline at end of file
diff --git a/backup/rb27/rosys.pathplanning.path_planner.json b/backup/rb27/rosys.pathplanning.path_planner.json
new file mode 100644
index 00000000..47655302
--- /dev/null
+++ b/backup/rb27/rosys.pathplanning.path_planner.json
@@ -0,0 +1,29 @@
+{
+ "obstacles": {},
+ "areas": {
+ "38d0b78e-73ff-481e-a9fd-ad0b4f0bc383": {
+ "outline": [
+ {
+ "x": 0.4754654857812373,
+ "y": -0.24222659678731243
+ },
+ {
+ "x": -21.007917850278513,
+ "y": 4.09237901275715
+ },
+ {
+ "x": -29.16229728702451,
+ "y": -32.002028438879016
+ },
+ {
+ "x": -6.917771396089687,
+ "y": -38.22130290308707
+ }
+ ],
+ "id": "38d0b78e-73ff-481e-a9fd-ad0b4f0bc383",
+ "type": null,
+ "color": "green",
+ "closed": true
+ }
+ }
+}
\ No newline at end of file
diff --git a/backup/rb27/rosys.vision.mjpeg_camera.mjpeg_camera_provider.json b/backup/rb27/rosys.vision.mjpeg_camera.mjpeg_camera_provider.json
new file mode 100644
index 00000000..cbd18b8a
--- /dev/null
+++ b/backup/rb27/rosys.vision.mjpeg_camera.mjpeg_camera_provider.json
@@ -0,0 +1,3 @@
+{
+ "cameras": {}
+}
\ No newline at end of file
diff --git a/backup/rb28/field_friend.automations.field_provider.json b/backup/rb28/field_friend.automations.field_provider.json
index 6056075a..9d7ed992 100644
--- a/backup/rb28/field_friend.automations.field_provider.json
+++ b/backup/rb28/field_friend.automations.field_provider.json
@@ -1,33 +1,296 @@
{
"fields": [
{
- "id": "2a621665-b0f0-443c-8252-3bd42c997845",
- "outline": [
+ "id": "5a8a2850-89d8-4c31-8998-58f95c4bf12b",
+ "name": "Test",
+ "outline_wgs84": [
+ [
+ 53.11086450840848,
+ 13.91407542875983
+ ],
+ [
+ 53.110900665,
+ 13.914103491666667
+ ],
+ [
+ 53.11083908809259,
+ 13.914568552126127
+ ],
+ [
+ 53.11080833166667,
+ 13.914556051666667
+ ]
+ ],
+ "reference_lat": 53.11065129259277,
+ "reference_lon": 13.91371250152588,
+ "visualized": false,
+ "obstacles": [],
+ "rows": [
+ {
+ "id": "a2a8d7a7-1d09-42f3-9a38-d2a35d2f6440",
+ "name": "row_1",
+ "points_wgs84": [
+ [
+ 53.11086900379518,
+ 13.914155492406122
+ ],
+ [
+ 53.11082100387979,
+ 13.914524203377239
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "dbfad64c-0180-4593-b6ff-a9ab87b8024a",
+ "name": "row_2",
+ "points_wgs84": [
+ [
+ 53.11087289058598,
+ 13.91415661849706
+ ],
+ [
+ 53.11082537116117,
+ 13.914524852764124
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
{
- "x": 4.14008820351158,
- "y": -16.529322089550206
+ "id": "3b4a2163-f3ff-4598-8272-3210f7d680cc",
+ "name": "row_3",
+ "points_wgs84": [
+ [
+ 53.1108764614488,
+ 13.914158682027072
+ ],
+ [
+ 53.110828752312116,
+ 13.914525133180984
+ ]
+ ],
+ "reverse": false,
+ "crops": []
},
{
- "x": -12.634128191514506,
- "y": -24.040097374894728
+ "id": "58729d6f-268b-4a37-8d7a-770a4d7f7546",
+ "name": "row_4",
+ "points_wgs84": [
+ [
+ 53.11088111899296,
+ 13.9141582500688
+ ],
+ [
+ 53.11083298032453,
+ 13.91452758242803
+ ]
+ ],
+ "reverse": false,
+ "crops": []
},
{
- "x": -18.930759085830115,
- "y": -16.78108433237046
+ "id": "8d83be1e-40f1-433c-803b-19352b339c77",
+ "name": "row_5",
+ "points_wgs84": [
+ [
+ 53.11088460915779,
+ 13.914162049853772
+ ],
+ [
+ 53.1108366940985,
+ 13.914525808114977
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ }
+ ]
+ },
+ {
+ "id": "21ec7d8d-dd94-470b-856b-fbf99348c9a0",
+ "name": "field_2",
+ "outline_wgs84": [
+ [
+ 53.110884221600415,
+ 13.914226577669016
+ ],
+ [
+ 53.110903025069895,
+ 13.914107201931566
+ ],
+ [
+ 53.11086720461946,
+ 13.914098982160908
+ ],
+ [
+ 53.1108632950714,
+ 13.914202533999706
+ ]
+ ],
+ "reference_lat": 53.110884221600415,
+ "reference_lon": 13.914226577669016,
+ "visualized": false,
+ "obstacles": [],
+ "rows": [
+ {
+ "id": "9c07c145-6483-409d-a5d8-e3d1103d295f",
+ "name": "row_1",
+ "points_wgs84": [
+ [
+ 53.11087392461309,
+ 13.91415514363712
+ ],
+ [
+ 53.110876969031665,
+ 13.914130932744571
+ ]
+ ],
+ "reverse": false,
+ "crops": []
},
{
- "x": 0.5057610077650115,
- "y": -10.85220402722948
+ "id": "a8aff49b-891d-4203-bf1d-cfb5fd463ba4",
+ "name": "row_2",
+ "points_wgs84": [
+ [
+ 53.110884664792806,
+ 13.914165015553325
+ ],
+ [
+ 53.110888652810765,
+ 13.914132289955367
+ ]
+ ],
+ "reverse": false,
+ "crops": []
}
+ ]
+ },
+ {
+ "id": "c94823e6-1738-4d8a-995b-72e021fd7951",
+ "name": "field_Tornado",
+ "outline_wgs84": [
+ [
+ 53.110879,
+ 13.91404991093148
+ ],
+ [
+ 53.11068696745771,
+ 13.913990223201246
+ ],
+ [
+ 53.110619,
+ 13.914525293968063
+ ],
+ [
+ 53.11081300514498,
+ 13.914601447086936
+ ]
],
- "reference_lat": 51.983140848333335,
- "reference_lon": 7.434131408333333,
+ "reference_lat": 53.11087948216796,
+ "reference_lon": 13.91404991093148,
"visualized": false,
"obstacles": [],
"rows": [
{
- "id": "c8b2f2b7-4d46-461f-b7d3-bed58bc52b95",
- "points": [],
+ "id": "bb0bf3d5-419d-49f0-b8ef-598f48b25f02",
+ "name": "row_1",
+ "points_wgs84": [
+ [
+ 53.110801817152485,
+ 13.914540262102923
+ ],
+ [
+ 53.110822640312556,
+ 13.914386299598647
+ ],
+ [
+ 53.110857462682276,
+ 13.91411825541417
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "04939b09-604c-470e-bcbc-a04786f36229",
+ "name": "row_2",
+ "points_wgs84": [
+ [
+ 53.110782992884424,
+ 13.914518846076097
+ ],
+ [
+ 53.110818046879785,
+ 13.91425563877989
+ ],
+ [
+ 53.110837330722084,
+ 13.914113336121375
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "2f32bcec-e3d5-49bb-bdda-05aa36e7acd8",
+ "name": "row_3",
+ "points_wgs84": [
+ [
+ 53.110763055542854,
+ 13.914509412317527
+ ],
+ [
+ 53.1107923826172,
+ 13.914293436033752
+ ],
+ [
+ 53.11081891556159,
+ 13.914093666088247
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "d7745165-3fb1-4b0d-8b6d-1f01c0037a80",
+ "name": "row_4",
+ "points_wgs84": [
+ [
+ 53.110742861303926,
+ 13.914510906326248
+ ],
+ [
+ 53.11077224689231,
+ 13.914296073769421
+ ],
+ [
+ 53.110798502365064,
+ 13.914100238657408
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "71a7bbfa-5d58-43c2-97bd-e8c09fbd94e4",
+ "name": "row_5",
+ "points_wgs84": [
+ [
+ 53.11072440969969,
+ 13.914501259190327
+ ],
+ [
+ 53.11075244107501,
+ 13.914289856723581
+ ],
+ [
+ 53.11077917021907,
+ 13.914089400488287
+ ]
+ ],
"reverse": false,
"crops": []
}
diff --git a/backup/rb28/field_friend.automations.followme.json b/backup/rb28/field_friend.automations.followme.json
new file mode 100644
index 00000000..fdd9a507
--- /dev/null
+++ b/backup/rb28/field_friend.automations.followme.json
@@ -0,0 +1,14 @@
+{
+ "stop_distance": 200.0,
+ "max_y_distance": 414.0,
+ "max_matching_distance": 270.0,
+ "max_first_matching_distance": 250.0,
+ "yaw_min": 0.05,
+ "yaw_max": 0.4,
+ "linear_speed": 0.2,
+ "angular_speed": 0.2,
+ "projection_factor": 0.24,
+ "confidence": 0.4,
+ "target_timeout": 6.0,
+ "drive": true
+}
\ No newline at end of file
diff --git a/backup/rb28/field_friend.automations.kpi_provider.json b/backup/rb28/field_friend.automations.kpi_provider.json
new file mode 100644
index 00000000..5bdef5ba
--- /dev/null
+++ b/backup/rb28/field_friend.automations.kpi_provider.json
@@ -0,0 +1,168 @@
+{
+ "current_weeding_kpis": {
+ "distance": 248,
+ "time": 2260,
+ "bumps": 0,
+ "low_battery": 0,
+ "can_failure": 0,
+ "automation_stopped": 0,
+ "e_stop_triggered": 0,
+ "soft_e_stop_triggered": 0,
+ "imu_rolling_detected": 0,
+ "gnss_connection_lost": 0,
+ "rows_weeded": 4,
+ "crops_detected": 118,
+ "weeds_detected": 3605,
+ "weeds_removed": 0,
+ "punches": 0,
+ "chops": 0,
+ "weeding_completed": 1
+ },
+ "current_mowing_kpis": {
+ "distance": 0,
+ "time": 0,
+ "bumps": 0,
+ "low_battery": 0,
+ "can_failure": 0,
+ "automation_stopped": 0,
+ "e_stop_triggered": 0,
+ "soft_e_stop_triggered": 0,
+ "imu_rolling_detected": 0,
+ "gnss_connection_lost": 0,
+ "mowing_completed": false
+ },
+ "days": [
+ {
+ "date": "2024-04-04",
+ "incidents": {
+ "low_battery": 9,
+ "bumps": 1
+ }
+ },
+ {
+ "date": "2024-04-05",
+ "incidents": {
+ "punches": 16,
+ "bumps": 3,
+ "crops_detected": 40,
+ "weeds_detected": 4
+ }
+ },
+ {
+ "date": "2024-04-08",
+ "incidents": {
+ "crops_detected": 50,
+ "punches": 7,
+ "weeds_detected": 195,
+ "weeding_completed": 4,
+ "bumps": 1
+ }
+ },
+ {
+ "date": "2024-04-09",
+ "incidents": {
+ "bumps": 6
+ }
+ },
+ {
+ "date": "2024-04-10",
+ "incidents": {
+ "followme_completed": 4
+ }
+ },
+ {
+ "date": "2024-04-11",
+ "incidents": {
+ "low_battery": 2
+ }
+ },
+ {
+ "date": "2024-04-12",
+ "incidents": {
+ "bumps": 25
+ }
+ },
+ {
+ "date": "2024-04-15",
+ "incidents": {
+ "followme_completed": 3,
+ "bumps": 12
+ }
+ },
+ {
+ "date": "2024-04-17",
+ "incidents": {
+ "followme_completed": 15,
+ "bumps": 7
+ }
+ },
+ {
+ "date": "2024-04-22",
+ "incidents": {
+ "weeds_detected": 150,
+ "crops_detected": 30,
+ "punches": 13,
+ "coin_collecting_completed": 4,
+ "bumps": 8,
+ "followme_completed": 4
+ }
+ },
+ {
+ "date": "2024-04-23",
+ "incidents": {
+ "bumps": 4,
+ "followme_completed": 3,
+ "low_battery": 1
+ }
+ },
+ {
+ "date": "2024-04-24",
+ "incidents": {
+ "followme_completed": 3,
+ "low_battery": 3,
+ "bumps": 1
+ }
+ },
+ {
+ "date": "2024-04-25",
+ "incidents": {
+ "bumps": 4,
+ "followme_completed": 1
+ }
+ },
+ {
+ "date": "2024-04-26",
+ "incidents": {
+ "bumps": 7
+ }
+ },
+ {
+ "date": "2024-04-29",
+ "incidents": {
+ "punches": 21,
+ "bumps": 7,
+ "time": 6996,
+ "distance": 729,
+ "weeds_detected": 10613,
+ "weeding_completed": 16,
+ "crops_detected": 15,
+ "rows_weeded": 5,
+ "coin_collecting_completed": 1
+ }
+ },
+ {
+ "date": "2024-04-30",
+ "incidents": {
+ "time": 12658,
+ "distance": 1391,
+ "weeds_detected": 9827,
+ "crops_detected": 205,
+ "rows_weeded": 26,
+ "weeding_completed": 31,
+ "bumps": 6,
+ "punches": 35
+ }
+ }
+ ],
+ "months": []
+}
\ No newline at end of file
diff --git a/backup/rb28/field_friend.automations.mowing.json b/backup/rb28/field_friend.automations.mowing.json
index ee2420e6..878c86cf 100644
--- a/backup/rb28/field_friend.automations.mowing.json
+++ b/backup/rb28/field_friend.automations.mowing.json
@@ -1,1195 +1,1195 @@
{
- "padding": 1.3399999999999999,
- "lane_distance": 0.6812249086628424,
+ "padding": 1.1400000000000001,
+ "lane_distance": 0.4794285796269571,
"paths": [
[
{
"spline": {
"start": {
- "x": 1.9904484806479574,
- "y": -16.02364468382114
+ "x": -3.3110376044163314,
+ "y": -17.350505281830728
},
"control1": {
- "x": -5.050386505404345,
- "y": -19.176228873513086
+ "x": -3.716498434380511,
+ "y": -15.412266830741077
},
"control2": {
- "x": -5.050386505404345,
- "y": -19.176228873513082
+ "x": -3.7164984343805108,
+ "y": -15.412266830741078
},
"end": {
- "x": -12.091221491456647,
- "y": -22.32881306320503
- },
- "a": 1.9904484806479574,
- "b": -5.050386505404345,
- "c": -5.050386505404345,
- "d": -12.091221491456647,
- "e": -16.02364468382114,
- "f": -19.176228873513086,
- "g": -19.176228873513082,
- "h": -22.32881306320503,
- "m": -14.081669972104605,
- "n": 7.040834986052302,
- "o": -7.040834986052302,
- "p": -6.3051683793839075,
- "q": 3.15258418969195,
- "r": -3.1525841896919466
+ "x": -4.1219592643446905,
+ "y": -13.474028379651427
+ },
+ "a": -3.3110376044163314,
+ "b": -3.716498434380511,
+ "c": -3.7164984343805108,
+ "d": -4.1219592643446905,
+ "e": -17.350505281830728,
+ "f": -15.412266830741077,
+ "g": -15.412266830741078,
+ "h": -13.474028379651427,
+ "m": -0.8109216599283608,
+ "n": 0.4054608299641802,
+ "o": -0.40546082996417976,
+ "p": 3.8764769021793057,
+ "q": -1.9382384510896529,
+ "r": 1.938238451089651
},
"backward": false
},
{
"spline": {
"start": {
- "x": -12.091221491456647,
- "y": -22.32881306320503
+ "x": -4.1219592643446905,
+ "y": -13.474028379651427
},
"control1": {
- "x": -12.177876510763841,
- "y": -22.367613467470502
+ "x": -4.141187934962031,
+ "y": -13.382108899396924
},
"control2": {
- "x": -12.187425779127148,
- "y": -22.36858771862414
+ "x": -4.139539492544631,
+ "y": -13.37078859953336
},
"end": {
- "x": -12.280130986665883,
- "y": -22.348086298246653
- },
- "a": -12.091221491456647,
- "b": -12.177876510763841,
- "c": -12.187425779127148,
- "d": -12.280130986665883,
- "e": -22.32881306320503,
- "f": -22.367613467470502,
- "g": -22.36858771862414,
- "h": -22.348086298246653,
- "m": -0.16026169011931657,
- "n": 0.07710575094388794,
- "o": -0.08665501930719444,
- "p": -0.01635048158070518,
- "q": 0.03782615311183335,
- "r": -0.03880040426547282
+ "x": -4.094894923133487,
+ "y": -13.28817022962653
+ },
+ "a": -4.1219592643446905,
+ "b": -4.141187934962031,
+ "c": -4.139539492544631,
+ "d": -4.094894923133487,
+ "e": -13.474028379651427,
+ "f": -13.382108899396924,
+ "g": -13.37078859953336,
+ "h": -13.28817022962653,
+ "m": 0.022119013959001066,
+ "n": 0.02087711303474027,
+ "o": -0.01922867061734035,
+ "p": 0.15189725043420843,
+ "q": -0.08059918039094072,
+ "r": 0.09191948025450358
},
"backward": false
},
{
"spline": {
"start": {
- "x": -12.280130986665883,
- "y": -22.348086298246653
+ "x": -4.094894923133487,
+ "y": -13.28817022962653
},
"control1": {
- "x": -12.354809913076233,
- "y": -22.331571324553956
+ "x": -4.060578306574829,
+ "y": -13.224664567948308
},
"control2": {
- "x": -12.354693841811876,
- "y": -22.317240040049903
+ "x": -4.043918635265363,
+ "y": -13.231600583556528
},
"end": {
- "x": -12.40480998472941,
- "y": -22.259464105397644
- },
- "a": -12.280130986665883,
- "b": -12.354809913076233,
- "c": -12.354693841811876,
- "d": -12.40480998472941,
- "e": -22.348086298246653,
- "f": -22.331571324553956,
- "g": -22.317240040049903,
- "h": -22.259464105397644,
- "m": -0.1250272118566027,
- "n": 0.07479499767470621,
- "o": -0.07467892641034979,
- "p": 0.045628339336840185,
- "q": -0.002183689188644422,
- "r": 0.016514973692697055
+ "x": -3.976717134225542,
+ "y": -13.205246124821471
+ },
+ "a": -4.094894923133487,
+ "b": -4.060578306574829,
+ "c": -4.043918635265363,
+ "d": -3.976717134225542,
+ "e": -13.28817022962653,
+ "f": -13.224664567948308,
+ "g": -13.231600583556528,
+ "h": -13.205246124821471,
+ "m": 0.06819877497954518,
+ "n": -0.01765694524919148,
+ "o": 0.03431661655865792,
+ "p": 0.10373215162972116,
+ "q": -0.07044167728644268,
+ "r": 0.06350566167822258
},
"backward": false
},
{
"spline": {
"start": {
- "x": -12.40480998472941,
- "y": -22.259464105397644
+ "x": -3.976717134225542,
+ "y": -13.205246124821471
},
"control1": {
- "x": -14.420419836961068,
- "y": -19.935786816452737
+ "x": 2.2764603049283063,
+ "y": -10.752933241430384
},
"control2": {
- "x": -14.42041983696107,
- "y": -19.93578681645274
+ "x": 2.276460304928305,
+ "y": -10.752933241430384
},
"end": {
- "x": -16.43602968919273,
- "y": -17.612109527507833
- },
- "a": -12.40480998472941,
- "b": -14.420419836961068,
- "c": -14.42041983696107,
- "d": -16.43602968919273,
- "e": -22.259464105397644,
- "f": -19.935786816452737,
- "g": -19.93578681645274,
- "h": -17.612109527507833,
- "m": -4.03121970446332,
- "n": 2.0156098522316572,
- "o": -2.015609852231659,
- "p": 4.647354577889818,
- "q": -2.3236772889449107,
- "r": 2.323677288944907
+ "x": 8.529637744082153,
+ "y": -8.300620358039296
+ },
+ "a": -3.976717134225542,
+ "b": 2.2764603049283063,
+ "c": 2.276460304928305,
+ "d": 8.529637744082153,
+ "e": -13.205246124821471,
+ "f": -10.752933241430384,
+ "g": -10.752933241430384,
+ "h": -8.300620358039296,
+ "m": 12.506354878307699,
+ "n": -6.25317743915385,
+ "o": 6.253177439153848,
+ "p": 4.904625766782175,
+ "q": -2.4523128833910874,
+ "r": 2.4523128833910874
},
"backward": false
},
{
"spline": {
"start": {
- "x": -16.43602968919273,
- "y": -17.612109527507833
+ "x": 8.529637744082153,
+ "y": -8.300620358039296
},
"control1": {
- "x": -16.49361810904551,
- "y": -17.5457192470823
+ "x": 8.616788825148694,
+ "y": -8.266442258828048
},
"control2": {
- "x": -16.498206624660558,
- "y": -17.52492779540708
+ "x": 8.628745152649351,
+ "y": -8.266269951951177
},
"end": {
- "x": -16.473910085467818,
- "y": -17.440466125879556
- },
- "a": -16.43602968919273,
- "b": -16.49361810904551,
- "c": -16.498206624660558,
- "d": -16.473910085467818,
- "e": -17.612109527507833,
- "f": -17.5457192470823,
- "g": -17.52492779540708,
- "h": -17.440466125879556,
- "m": -0.024114849429942353,
- "n": 0.052999904237733375,
- "o": -0.05758841985278096,
- "p": 0.10926904660262693,
- "q": -0.045598828750314624,
- "r": 0.06639028042553363
+ "x": 8.716844941786379,
+ "y": -8.297922448705336
+ },
+ "a": 8.529637744082153,
+ "b": 8.616788825148694,
+ "c": 8.628745152649351,
+ "d": 8.716844941786379,
+ "e": -8.300620358039296,
+ "f": -8.266442258828048,
+ "g": -8.266269951951177,
+ "h": -8.297922448705336,
+ "m": 0.15133821520224977,
+ "n": -0.07519475356588323,
+ "o": 0.08715108106654057,
+ "p": 0.0021809887033477082,
+ "q": -0.034005792334376395,
+ "r": 0.034178099211247925
},
"backward": false
},
{
"spline": {
"start": {
- "x": -16.473910085467818,
- "y": -17.440466125879556
+ "x": 8.716844941786379,
+ "y": -8.297922448705336
},
"control1": {
- "x": -16.459375225005935,
- "y": -17.389938823566187
+ "x": 8.783659984830374,
+ "y": -8.321927760792855
},
"control2": {
- "x": -16.426071759980577,
- "y": -17.41801504143529
+ "x": 8.77849499336912,
+ "y": -8.340362260260314
},
"end": {
- "x": -16.375783038651115,
- "y": -17.402675062466564
- },
- "a": -16.473910085467818,
- "b": -16.459375225005935,
- "c": -16.426071759980577,
- "d": -16.375783038651115,
- "e": -17.440466125879556,
- "f": -17.389938823566187,
- "g": -17.41801504143529,
- "h": -17.402675062466564,
- "m": -0.0017833482593694328,
- "n": 0.018768604563476288,
- "o": 0.01453486046188246,
- "p": 0.12201971702029724,
- "q": -0.07860352018247241,
- "r": 0.05052730231336966
+ "x": 8.812981572560112,
+ "y": -8.402420143249453
+ },
+ "a": 8.716844941786379,
+ "b": 8.783659984830374,
+ "c": 8.77849499336912,
+ "d": 8.812981572560112,
+ "e": -8.297922448705336,
+ "f": -8.321927760792855,
+ "g": -8.340362260260314,
+ "h": -8.402420143249453,
+ "m": 0.1116316051574966,
+ "n": -0.07198003450524837,
+ "o": 0.0668150430439951,
+ "p": -0.04919419614174103,
+ "q": 0.00557081262006065,
+ "r": -0.024005312087519215
},
"backward": false
},
{
"spline": {
"start": {
- "x": -16.375783038651115,
- "y": -17.402675062466564
+ "x": 8.812981572560112,
+ "y": -8.402420143249453
},
"control1": {
- "x": -8.321056452272451,
- "y": -14.945676093962286
+ "x": 9.673989682642553,
+ "y": -9.951786354733635
},
"control2": {
- "x": -8.321056452272451,
- "y": -14.945676093962286
+ "x": 9.673989682642551,
+ "y": -9.951786354733635
},
"end": {
- "x": -0.2663298658937867,
- "y": -12.488677125458008
- },
- "a": -16.375783038651115,
- "b": -8.321056452272451,
- "c": -8.321056452272451,
- "d": -0.2663298658937867,
- "e": -17.402675062466564,
- "f": -14.945676093962286,
- "g": -14.945676093962286,
- "h": -12.488677125458008,
- "m": 16.109453172757327,
- "n": -8.054726586378663,
- "o": 8.054726586378663,
- "p": 4.913997937008553,
- "q": -2.456998968504278,
- "r": 2.456998968504278
+ "x": 10.534997792724992,
+ "y": -11.501152566217817
+ },
+ "a": 8.812981572560112,
+ "b": 9.673989682642553,
+ "c": 9.673989682642551,
+ "d": 10.534997792724992,
+ "e": -8.402420143249453,
+ "f": -9.951786354733635,
+ "g": -9.951786354733635,
+ "h": -11.501152566217817,
+ "m": 1.7220162201648854,
+ "n": -0.8610081100824427,
+ "o": 0.8610081100824409,
+ "p": -3.098732422968366,
+ "q": 1.5493662114841822,
+ "r": -1.5493662114841822
},
"backward": false
},
{
"spline": {
"start": {
- "x": -0.2663298658937867,
- "y": -12.488677125458008
+ "x": 10.534997792724992,
+ "y": -11.501152566217817
},
"control1": {
- "x": -0.17566847189296741,
- "y": -12.461021940746974
+ "x": 10.579358670737165,
+ "y": -11.580979044852553
},
"control2": {
- "x": -0.1657873086812938,
- "y": -12.461301514270977
+ "x": 10.58074503418681,
+ "y": -11.596764086769838
},
"end": {
- "x": -0.07683462917804214,
- "y": -12.494038624906537
- },
- "a": -0.2663298658937867,
- "b": -0.17566847189296741,
- "c": -0.1657873086812938,
- "d": -0.07683462917804214,
- "e": -12.488677125458008,
- "f": -12.461021940746974,
- "g": -12.461301514270977,
- "h": -12.494038624906537,
- "m": 0.15985174708072375,
- "n": -0.08078023078914567,
- "o": 0.0906613940008193,
- "p": -0.004522778876523859,
- "q": -0.027934758235035773,
- "r": 0.027655184711033343
+ "x": 10.550977904573669,
+ "y": -11.683101055582897
+ },
+ "a": 10.534997792724992,
+ "b": 10.579358670737165,
+ "c": 10.58074503418681,
+ "d": 10.550977904573669,
+ "e": -11.501152566217817,
+ "f": -11.580979044852553,
+ "g": -11.596764086769838,
+ "h": -11.683101055582897,
+ "m": 0.011821021499743622,
+ "n": -0.04297451456252688,
+ "o": 0.04436087801217248,
+ "p": -0.13459336361322194,
+ "q": 0.0640414367174511,
+ "r": -0.07982647863473602
},
"backward": false
},
{
"spline": {
"start": {
- "x": -0.07683462917804214,
- "y": -12.494038624906537
+ "x": 10.550977904573669,
+ "y": -11.683101055582897
},
"control1": {
- "x": -0.005691927185520265,
- "y": -12.5202211583507
+ "x": 10.530609752900592,
+ "y": -11.742177106872767
},
"control2": {
- "x": -0.008072901222325228,
- "y": -12.534919434322207
+ "x": 10.505467173581247,
+ "y": -11.729463712150888
},
"end": {
- "x": 0.03279927747010902,
- "y": -12.598765139841728
- },
- "a": -0.07683462917804214,
- "b": -0.005691927185520265,
- "c": -0.008072901222325228,
- "d": 0.03279927747010902,
- "e": -12.494038624906537,
- "f": -12.5202211583507,
- "g": -12.534919434322207,
- "h": -12.598765139841728,
- "m": 0.11677682875856604,
- "n": -0.07352367602932683,
- "o": 0.07114270199252187,
- "p": -0.060631687020670455,
- "q": 0.011484257472657688,
- "r": -0.02618253344416388
+ "x": 10.447900146027525,
+ "y": -11.753771710365672
+ },
+ "a": 10.550977904573669,
+ "b": 10.530609752900592,
+ "c": 10.505467173581247,
+ "d": 10.447900146027525,
+ "e": -11.683101055582897,
+ "f": -11.742177106872767,
+ "g": -11.729463712150888,
+ "h": -11.753771710365672,
+ "m": -0.02765002058811028,
+ "n": -0.004774427646268009,
+ "o": -0.02036815167307715,
+ "p": -0.10881083894840948,
+ "q": 0.07178944601174742,
+ "r": -0.05907605128986937
},
"backward": false
},
{
"spline": {
"start": {
- "x": 0.03279927747010902,
- "y": -12.598765139841728
+ "x": 10.447900146027525,
+ "y": -11.753771710365672
},
"control1": {
- "x": 1.0489768792842749,
- "y": -14.186118151022631
+ "x": 3.6810309433337354,
+ "y": -14.61111994404433
},
"control2": {
- "x": 1.0489768792842744,
- "y": -14.186118151022631
+ "x": 3.6810309433337345,
+ "y": -14.611119944044326
},
"end": {
- "x": 2.0651544810984404,
- "y": -15.773471162203535
- },
- "a": 0.03279927747010902,
- "b": 1.0489768792842749,
- "c": 1.0489768792842744,
- "d": 2.0651544810984404,
- "e": -12.598765139841728,
- "f": -14.186118151022631,
- "g": -14.186118151022631,
- "h": -15.773471162203535,
- "m": 2.0323552036283328,
- "n": -1.0161776018141664,
- "o": 1.016177601814166,
- "p": -3.174706022361807,
- "q": 1.5873530111809036,
- "r": -1.5873530111809036
+ "x": -3.085838259360055,
+ "y": -17.468468177722983
+ },
+ "a": 10.447900146027525,
+ "b": 3.6810309433337354,
+ "c": 3.6810309433337345,
+ "d": -3.085838259360055,
+ "e": -11.753771710365672,
+ "f": -14.61111994404433,
+ "g": -14.611119944044326,
+ "h": -17.468468177722983,
+ "m": -13.533738405387577,
+ "n": 6.766869202693789,
+ "o": -6.7668692026937896,
+ "p": -5.714696467357317,
+ "q": 2.8573482336786604,
+ "r": -2.857348233678657
},
"backward": false
},
{
"spline": {
"start": {
- "x": 2.0651544810984404,
- "y": -15.773471162203535
+ "x": -3.085838259360055,
+ "y": -17.468468177722983
},
"control1": {
- "x": 2.0899632936716346,
- "y": -15.812224568371489
+ "x": -3.1305613958215615,
+ "y": -17.487352771343026
},
"control2": {
- "x": 2.065139022555776,
- "y": -15.855260400063573
+ "x": -3.169893880666062,
+ "y": -17.45655125284964
},
"end": {
- "x": 2.0191715439702316,
- "y": -15.853188062398566
- },
- "a": 2.0651544810984404,
- "b": 2.0899632936716346,
- "c": 2.065139022555776,
- "d": 2.0191715439702316,
- "e": -15.773471162203535,
- "f": -15.812224568371489,
- "g": -15.855260400063573,
- "h": -15.853188062398566,
- "m": 0.028489876219366383,
- "n": -0.049633083689052615,
- "o": 0.024808812573194228,
- "p": 0.049390594881224104,
- "q": -0.004282425524129607,
- "r": -0.038753406167954196
+ "x": -3.1622814070037357,
+ "y": -17.408605063680017
+ },
+ "a": -3.085838259360055,
+ "b": -3.1305613958215615,
+ "c": -3.169893880666062,
+ "d": -3.1622814070037357,
+ "e": -17.468468177722983,
+ "f": -17.487352771343026,
+ "g": -17.45655125284964,
+ "h": -17.408605063680017,
+ "m": 0.04155430688982076,
+ "n": 0.005390651617005915,
+ "o": -0.044723136461506385,
+ "p": -0.03254144143718918,
+ "q": 0.04968611211343088,
+ "r": -0.018884593620043688
},
"backward": false
},
{
"spline": {
"start": {
- "x": 2.0191715439702316,
- "y": -15.853188062398566
+ "x": -3.1622814070037357,
+ "y": -17.408605063680017
},
"control1": {
- "x": 1.5013446687688368,
- "y": -15.829843033033173
+ "x": -3.103189753707147,
+ "y": -17.0364238630638
},
"control2": {
- "x": 1.4635125473892967,
- "y": -15.513188272615256
+ "x": -2.8838539886315604,
+ "y": -17.051147868003277
},
"end": {
- "x": 0.9904191949100383,
- "y": -15.725019202644013
- },
- "a": 2.0191715439702316,
- "b": 1.5013446687688368,
- "c": 1.4635125473892967,
- "d": 0.9904191949100383,
- "e": -15.853188062398566,
- "f": -15.829843033033173,
- "g": -15.513188272615256,
- "h": -15.725019202644013,
- "m": -0.9152559849215738,
- "n": 0.47999475382185475,
- "o": -0.5178268752013948,
- "p": -0.8217954214991963,
- "q": 0.2933097310525241,
- "r": 0.023345029365392733
+ "x": -2.961015679406676,
+ "y": -16.682289164624628
+ },
+ "a": -3.1622814070037357,
+ "b": -3.103189753707147,
+ "c": -2.8838539886315604,
+ "d": -2.961015679406676,
+ "e": -17.408605063680017,
+ "f": -17.0364238630638,
+ "g": -17.051147868003277,
+ "h": -16.682289164624628,
+ "m": -0.45674156762970064,
+ "n": 0.16024411177899767,
+ "o": 0.05909165329658883,
+ "p": 0.7704879138738256,
+ "q": -0.38690520555569563,
+ "r": 0.3721812006162182
},
"backward": false
},
{
"spline": {
"start": {
- "x": 0.9904191949100383,
- "y": -15.725019202644013
+ "x": -2.961015679406676,
+ "y": -16.682289164624628
},
"control1": {
- "x": -5.458799399697785,
- "y": -18.612702924461736
+ "x": -3.265372127860259,
+ "y": -15.227363490837753
},
"control2": {
- "x": -5.458799399697786,
- "y": -18.612702924461736
+ "x": -3.2653721278602585,
+ "y": -15.227363490837753
},
"end": {
- "x": -11.90801799430561,
- "y": -21.500386646279456
- },
- "a": 0.9904191949100383,
- "b": -5.458799399697785,
- "c": -5.458799399697786,
- "d": -11.90801799430561,
- "e": -15.725019202644013,
- "f": -18.612702924461736,
- "g": -18.612702924461736,
- "h": -21.500386646279456,
- "m": -12.898437189215644,
- "n": 6.449218594607823,
- "o": -6.4492185946078235,
- "p": -5.775367443635446,
- "q": 2.887683721817723,
- "r": -2.887683721817723
+ "x": -3.5697285763138416,
+ "y": -13.77243781705088
+ },
+ "a": -2.961015679406676,
+ "b": -3.265372127860259,
+ "c": -3.2653721278602585,
+ "d": -3.5697285763138416,
+ "e": -16.682289164624628,
+ "f": -15.227363490837753,
+ "g": -15.227363490837753,
+ "h": -13.77243781705088,
+ "m": -0.6087128969071673,
+ "n": 0.30435644845358345,
+ "o": -0.304356448453583,
+ "p": 2.909851347573749,
+ "q": -1.4549256737868745,
+ "r": 1.4549256737868745
},
"backward": false
},
{
"spline": {
"start": {
- "x": -11.90801799430561,
- "y": -21.500386646279456
+ "x": -3.5697285763138416,
+ "y": -13.77243781705088
},
"control1": {
- "x": -11.994673013612804,
- "y": -21.53918705054493
+ "x": -3.5889572469311823,
+ "y": -13.680518336796377
},
"control2": {
- "x": -12.00422228197611,
- "y": -21.540161301698568
+ "x": -3.587308804513782,
+ "y": -13.669198036932814
},
"end": {
- "x": -12.096927489514846,
- "y": -21.51965988132108
- },
- "a": -11.90801799430561,
- "b": -11.994673013612804,
- "c": -12.00422228197611,
- "d": -12.096927489514846,
- "e": -21.500386646279456,
- "f": -21.53918705054493,
- "g": -21.540161301698568,
- "h": -21.51965988132108,
- "m": -0.16026169011932012,
- "n": 0.07710575094388794,
- "o": -0.08665501930719444,
- "p": -0.016350481580698073,
- "q": 0.03782615311183335,
- "r": -0.03880040426547282
+ "x": -3.5426642351026376,
+ "y": -13.586579667025983
+ },
+ "a": -3.5697285763138416,
+ "b": -3.5889572469311823,
+ "c": -3.587308804513782,
+ "d": -3.5426642351026376,
+ "e": -13.77243781705088,
+ "f": -13.680518336796377,
+ "g": -13.669198036932814,
+ "h": -13.586579667025983,
+ "m": 0.022119013959001954,
+ "n": 0.020877113034741157,
+ "o": -0.019228670617340793,
+ "p": 0.15189725043420843,
+ "q": -0.08059918039094072,
+ "r": 0.09191948025450358
},
"backward": false
},
{
"spline": {
"start": {
- "x": -12.096927489514846,
- "y": -21.51965988132108
+ "x": -3.5426642351026376,
+ "y": -13.586579667025983
},
"control1": {
- "x": -12.171606415925195,
- "y": -21.503144907628382
+ "x": -3.50834761854398,
+ "y": -13.52307400534776
},
"control2": {
- "x": -12.171490344660839,
- "y": -21.48881362312433
+ "x": -3.4916879472345133,
+ "y": -13.53001002095598
},
"end": {
- "x": -12.221606487578372,
- "y": -21.43103768847207
- },
- "a": -12.096927489514846,
- "b": -12.171606415925195,
- "c": -12.171490344660839,
- "d": -12.221606487578372,
- "e": -21.51965988132108,
- "f": -21.503144907628382,
- "g": -21.48881362312433,
- "h": -21.43103768847207,
- "m": -0.12502721185659205,
- "n": 0.07479499767470621,
- "o": -0.07467892641034979,
- "p": 0.04562833933684729,
- "q": -0.002183689188644422,
- "r": 0.016514973692697055
+ "x": -3.424486446194693,
+ "y": -13.503655562220924
+ },
+ "a": -3.5426642351026376,
+ "b": -3.50834761854398,
+ "c": -3.4916879472345133,
+ "d": -3.424486446194693,
+ "e": -13.586579667025983,
+ "f": -13.52307400534776,
+ "g": -13.53001002095598,
+ "h": -13.503655562220924,
+ "m": 0.06819877497954208,
+ "n": -0.017656945249190592,
+ "o": 0.034316616558657476,
+ "p": 0.10373215162971405,
+ "q": -0.07044167728644268,
+ "r": 0.06350566167822258
},
"backward": false
},
{
"spline": {
"start": {
- "x": -12.221606487578372,
- "y": -21.43103768847207
+ "x": -3.424486446194693,
+ "y": -13.503655562220924
},
"control1": {
- "x": -13.727998538515276,
- "y": -19.69440746058662
+ "x": 2.4448925072044294,
+ "y": -11.201857194114352
},
"control2": {
- "x": -13.72799853851528,
- "y": -19.694407460586625
+ "x": 2.444892507204427,
+ "y": -11.201857194114352
},
"end": {
- "x": -15.234390589452184,
- "y": -17.957777232701176
- },
- "a": -12.221606487578372,
- "b": -13.727998538515276,
- "c": -13.72799853851528,
- "d": -15.234390589452184,
- "e": -21.43103768847207,
- "f": -19.69440746058662,
- "g": -19.694407460586625,
- "h": -17.957777232701176,
- "m": -3.0127841018737964,
- "n": 1.5063920509369009,
- "o": -1.5063920509369044,
- "p": 3.4732604557709017,
- "q": -1.7366302278854526,
- "r": 1.736630227885449
+ "x": 8.31427146060355,
+ "y": -8.900058826007779
+ },
+ "a": -3.424486446194693,
+ "b": 2.4448925072044294,
+ "c": 2.444892507204427,
+ "d": 8.31427146060355,
+ "e": -13.503655562220924,
+ "f": -11.201857194114352,
+ "g": -11.201857194114352,
+ "h": -8.900058826007779,
+ "m": 11.73875790679825,
+ "n": -5.869378953399124,
+ "o": 5.8693789533991225,
+ "p": 4.603596736213145,
+ "q": -2.3017983681065726,
+ "r": 2.3017983681065726
},
"backward": false
},
{
"spline": {
"start": {
- "x": -15.234390589452184,
- "y": -17.957777232701176
+ "x": 8.31427146060355,
+ "y": -8.900058826007779
},
"control1": {
- "x": -15.291979009304963,
- "y": -17.891386952275642
+ "x": 8.40142254167009,
+ "y": -8.865880726796531
},
"control2": {
- "x": -15.296567524920015,
- "y": -17.870595500600423
+ "x": 8.413378869170748,
+ "y": -8.86570841991966
},
"end": {
- "x": -15.272270985727273,
- "y": -17.7861338310729
- },
- "a": -15.234390589452184,
- "b": -15.291979009304963,
- "c": -15.296567524920015,
- "d": -15.272270985727273,
- "e": -17.957777232701176,
- "f": -17.891386952275642,
- "g": -17.870595500600423,
- "h": -17.7861338310729,
- "m": -0.02411484942993347,
- "n": 0.052999904237728046,
- "o": -0.05758841985277918,
- "p": 0.10926904660261982,
- "q": -0.045598828750314624,
- "r": 0.06639028042553363
+ "x": 8.501478658307775,
+ "y": -8.897360916673819
+ },
+ "a": 8.31427146060355,
+ "b": 8.40142254167009,
+ "c": 8.413378869170748,
+ "d": 8.501478658307775,
+ "e": -8.900058826007779,
+ "f": -8.865880726796531,
+ "g": -8.86570841991966,
+ "h": -8.897360916673819,
+ "m": 0.1513382152022551,
+ "n": -0.07519475356588323,
+ "o": 0.08715108106654057,
+ "p": 0.0021809887033477082,
+ "q": -0.034005792334376395,
+ "r": 0.034178099211247925
},
"backward": false
},
{
"spline": {
"start": {
- "x": -15.272270985727273,
- "y": -17.7861338310729
+ "x": 8.501478658307775,
+ "y": -8.897360916673819
},
"control1": {
- "x": -15.257736125265389,
- "y": -17.73560652875953
+ "x": 8.56829370135177,
+ "y": -8.921366228761338
},
"control2": {
- "x": -15.22443266024003,
- "y": -17.763682746628632
+ "x": 8.563128709890517,
+ "y": -8.939800728228796
},
"end": {
- "x": -15.174143938910568,
- "y": -17.748342767659906
- },
- "a": -15.272270985727273,
- "b": -15.257736125265389,
- "c": -15.22443266024003,
- "d": -15.174143938910568,
- "e": -17.7861338310729,
- "f": -17.73560652875953,
- "g": -17.763682746628632,
- "h": -17.748342767659906,
- "m": -0.0017833482593676564,
- "n": 0.01876860456347451,
- "o": 0.014534860461884236,
- "p": 0.12201971702030434,
- "q": -0.07860352018247241,
- "r": 0.05052730231336966
+ "x": 8.597615289081508,
+ "y": -9.001858611217935
+ },
+ "a": 8.501478658307775,
+ "b": 8.56829370135177,
+ "c": 8.563128709890517,
+ "d": 8.597615289081508,
+ "e": -8.897360916673819,
+ "f": -8.921366228761338,
+ "g": -8.939800728228796,
+ "h": -9.001858611217935,
+ "m": 0.11163160515749126,
+ "n": -0.07198003450524837,
+ "o": 0.0668150430439951,
+ "p": -0.04919419614174103,
+ "q": 0.00557081262006065,
+ "r": -0.024005312087519215
},
"backward": false
},
{
"spline": {
"start": {
- "x": -15.174143938910568,
- "y": -17.748342767659906
+ "x": 8.597615289081508,
+ "y": -9.001858611217935
},
"control1": {
- "x": -7.86786772485169,
- "y": -15.51964972277024
+ "x": 9.227066450649238,
+ "y": -10.134542903145373
},
"control2": {
- "x": -7.86786772485169,
- "y": -15.51964972277024
+ "x": 9.227066450649236,
+ "y": -10.134542903145373
},
"end": {
- "x": -0.5615915107928121,
- "y": -13.290956677880574
- },
- "a": -15.174143938910568,
- "b": -7.86786772485169,
- "c": -7.86786772485169,
- "d": -0.5615915107928121,
- "e": -17.748342767659906,
- "f": -15.51964972277024,
- "g": -15.51964972277024,
- "h": -13.290956677880574,
- "m": 14.612552428117755,
- "n": -7.306276214058878,
- "o": 7.306276214058878,
- "p": 4.457386089779334,
- "q": -2.228693044889667,
- "r": 2.228693044889667
+ "x": 9.856517612216965,
+ "y": -11.267227195072811
+ },
+ "a": 8.597615289081508,
+ "b": 9.227066450649238,
+ "c": 9.227066450649236,
+ "d": 9.856517612216965,
+ "e": -9.001858611217935,
+ "f": -10.134542903145373,
+ "g": -10.134542903145373,
+ "h": -11.267227195072811,
+ "m": 1.2589023231354606,
+ "n": -0.6294511615677312,
+ "o": 0.6294511615677294,
+ "p": -2.265368583854878,
+ "q": 1.132684291927438,
+ "r": -1.132684291927438
},
"backward": false
},
{
"spline": {
"start": {
- "x": -0.5615915107928121,
- "y": -13.290956677880574
+ "x": 9.856517612216965,
+ "y": -11.267227195072811
},
"control1": {
- "x": -0.4709301167919928,
- "y": -13.263301493169541
+ "x": 9.900878490229138,
+ "y": -11.347053673707547
},
"control2": {
- "x": -0.46104895358031917,
- "y": -13.263581066693543
+ "x": 9.902264853678783,
+ "y": -11.362838715624832
},
"end": {
- "x": -0.3720962740770675,
- "y": -13.296318177329104
- },
- "a": -0.5615915107928121,
- "b": -0.4709301167919928,
- "c": -0.46104895358031917,
- "d": -0.3720962740770675,
- "e": -13.290956677880574,
- "f": -13.263301493169541,
- "g": -13.263581066693543,
- "h": -13.296318177329104,
- "m": 0.1598517470807238,
- "n": -0.08078023078914565,
- "o": 0.0906613940008193,
- "p": -0.004522778876525635,
- "q": -0.027934758235035773,
- "r": 0.027655184711033343
+ "x": 9.872497724065642,
+ "y": -11.449175684437892
+ },
+ "a": 9.856517612216965,
+ "b": 9.900878490229138,
+ "c": 9.902264853678783,
+ "d": 9.872497724065642,
+ "e": -11.267227195072811,
+ "f": -11.347053673707547,
+ "g": -11.362838715624832,
+ "h": -11.449175684437892,
+ "m": 0.011821021499738293,
+ "n": -0.04297451456252688,
+ "o": 0.04436087801217248,
+ "p": -0.13459336361322194,
+ "q": 0.0640414367174511,
+ "r": -0.07982647863473602
},
"backward": false
},
{
"spline": {
"start": {
- "x": -0.3720962740770675,
- "y": -13.296318177329104
+ "x": 9.872497724065642,
+ "y": -11.449175684437892
},
"control1": {
- "x": -0.30095357208454565,
- "y": -13.322500710773268
+ "x": 9.852129572392565,
+ "y": -11.508251735727761
},
"control2": {
- "x": -0.3033345461213507,
- "y": -13.337198986744774
+ "x": 9.82698699307322,
+ "y": -11.495538341005883
},
"end": {
- "x": -0.26246236742891643,
- "y": -13.401044692264295
- },
- "a": -0.3720962740770675,
- "b": -0.30095357208454565,
- "c": -0.3033345461213507,
- "d": -0.26246236742891643,
- "e": -13.296318177329104,
- "f": -13.322500710773268,
- "g": -13.337198986744774,
- "h": -13.401044692264295,
- "m": 0.11677682875856615,
- "n": -0.07352367602932691,
- "o": 0.07114270199252187,
- "p": -0.06063168702066868,
- "q": 0.011484257472657688,
- "r": -0.02618253344416388
+ "x": 9.769419965519498,
+ "y": -11.519846339220667
+ },
+ "a": 9.872497724065642,
+ "b": 9.852129572392565,
+ "c": 9.82698699307322,
+ "d": 9.769419965519498,
+ "e": -11.449175684437892,
+ "f": -11.508251735727761,
+ "g": -11.495538341005883,
+ "h": -11.519846339220667,
+ "m": -0.027650020588108504,
+ "n": -0.004774427646268009,
+ "o": -0.02036815167307715,
+ "p": -0.10881083894840948,
+ "q": 0.07178944601174742,
+ "r": -0.05907605128986937
},
"backward": false
},
{
"spline": {
"start": {
- "x": -0.26246236742891643,
- "y": -13.401044692264295
+ "x": 9.769419965519498,
+ "y": -11.519846339220667
},
"control1": {
- "x": 0.4013314139658026,
- "y": -14.43794518664535
+ "x": 3.51680181558455,
+ "y": -14.160049199868777
},
"control2": {
- "x": 0.4013314139658022,
- "y": -14.43794518664535
+ "x": 3.5168018155845484,
+ "y": -14.160049199868773
},
"end": {
- "x": 1.0651251953605212,
- "y": -15.474845681026409
- },
- "a": -0.26246236742891643,
- "b": 0.4013314139658026,
- "c": 0.4013314139658022,
- "d": 1.0651251953605212,
- "e": -13.401044692264295,
- "f": -14.43794518664535,
- "g": -14.43794518664535,
- "h": -15.474845681026409,
- "m": 1.3275875627894387,
- "n": -0.6637937813947195,
- "o": 0.663793781394719,
- "p": -2.0738009887621143,
- "q": 1.0369004943810562,
- "r": -1.0369004943810562
+ "x": -2.7358163343503996,
+ "y": -16.800252060516883
+ },
+ "a": 9.769419965519498,
+ "b": 3.51680181558455,
+ "c": 3.5168018155845484,
+ "d": -2.7358163343503996,
+ "e": -11.519846339220667,
+ "f": -14.160049199868777,
+ "g": -14.160049199868773,
+ "h": -16.800252060516883,
+ "m": -12.505236299869892,
+ "n": 6.252618149934946,
+ "o": -6.252618149934948,
+ "p": -5.280405721296223,
+ "q": 2.640202860648113,
+ "r": -2.6402028606481096
},
"backward": false
},
{
"spline": {
"start": {
- "x": 1.0651251953605212,
- "y": -15.474845681026409
+ "x": -2.7358163343503996,
+ "y": -16.800252060516883
},
"control1": {
- "x": 1.0899340079337152,
- "y": -15.513599087194363
+ "x": -2.780539470811906,
+ "y": -16.819136654136926
},
"control2": {
- "x": 1.0651097368178575,
- "y": -15.556634918886447
+ "x": -2.819871955656407,
+ "y": -16.78833513564354
},
"end": {
- "x": 1.0191422582323126,
- "y": -15.55456258122144
- },
- "a": 1.0651251953605212,
- "b": 1.0899340079337152,
- "c": 1.0651097368178575,
- "d": 1.0191422582323126,
- "e": -15.474845681026409,
- "f": -15.513599087194363,
- "g": -15.556634918886447,
- "h": -15.55456258122144,
- "m": 0.028489876219364607,
- "n": -0.04963308368905173,
- "o": 0.024808812573194006,
- "p": 0.049390594881224104,
- "q": -0.004282425524129607,
- "r": -0.038753406167954196
+ "x": -2.8122594819940807,
+ "y": -16.740388946473917
+ },
+ "a": -2.7358163343503996,
+ "b": -2.780539470811906,
+ "c": -2.819871955656407,
+ "d": -2.8122594819940807,
+ "e": -16.800252060516883,
+ "f": -16.819136654136926,
+ "g": -16.78833513564354,
+ "h": -16.740388946473917,
+ "m": 0.041554306889821646,
+ "n": 0.005390651617005471,
+ "o": -0.044723136461506385,
+ "p": -0.03254144143718918,
+ "q": 0.04968611211343088,
+ "r": -0.018884593620043688
},
"backward": false
},
{
"spline": {
"start": {
- "x": 1.0191422582323126,
- "y": -15.55456258122144
+ "x": -2.8122594819940807,
+ "y": -16.740388946473917
},
"control1": {
- "x": 0.501315383030917,
- "y": -15.531217551856047
+ "x": -2.7531678286974928,
+ "y": -16.3682077458577
},
"control2": {
- "x": 0.4634832616513772,
- "y": -15.214562791438128
+ "x": -2.5338320636219067,
+ "y": -16.382931750797177
},
"end": {
- "x": -0.009610090827881917,
- "y": -15.426393721466885
- },
- "a": 1.0191422582323126,
- "b": 0.501315383030917,
- "c": 0.4634832616513772,
- "d": -0.009610090827881917,
- "e": -15.55456258122144,
- "f": -15.531217551856047,
- "g": -15.214562791438128,
- "h": -15.426393721466885,
- "m": -0.9152559849215753,
- "n": 0.47999475382185575,
- "o": -0.5178268752013956,
- "p": -0.8217954214992034,
- "q": 0.2933097310525259,
- "r": 0.023345029365392733
+ "x": -2.6109937543970227,
+ "y": -16.014073047418524
+ },
+ "a": -2.8122594819940807,
+ "b": -2.7531678286974928,
+ "c": -2.5338320636219067,
+ "d": -2.6109937543970227,
+ "e": -16.740388946473917,
+ "f": -16.3682077458577,
+ "g": -16.382931750797177,
+ "h": -16.014073047418524,
+ "m": -0.4567415676297002,
+ "n": 0.1602441117789981,
+ "o": 0.05909165329658794,
+ "p": 0.7704879138738256,
+ "q": -0.38690520555569563,
+ "r": 0.3721812006162182
},
"backward": false
},
{
"spline": {
"start": {
- "x": -0.009610090827881917,
- "y": -15.426393721466885
+ "x": -2.6109937543970227,
+ "y": -16.014073047418524
},
"control1": {
- "x": -5.867212293991229,
- "y": -18.049176975410383
+ "x": -2.8142458213400086,
+ "y": -15.042460150934428
},
"control2": {
- "x": -5.86721229399123,
- "y": -18.049176975410383
+ "x": -2.814245821340008,
+ "y": -15.042460150934428
},
"end": {
- "x": -11.724814497154577,
- "y": -20.671960229353882
- },
- "a": -0.009610090827881917,
- "b": -5.867212293991229,
- "c": -5.86721229399123,
- "d": -11.724814497154577,
- "e": -15.426393721466885,
- "f": -18.049176975410383,
- "g": -18.049176975410383,
- "h": -20.671960229353882,
- "m": -11.715204406326691,
- "n": 5.857602203163347,
- "o": -5.8576022031633475,
- "p": -5.245566507886993,
- "q": 2.6227832539434974,
- "r": -2.6227832539434974
+ "x": -3.017497888282994,
+ "y": -14.070847254450332
+ },
+ "a": -2.6109937543970227,
+ "b": -2.8142458213400086,
+ "c": -2.814245821340008,
+ "d": -3.017497888282994,
+ "e": -16.014073047418524,
+ "f": -15.042460150934428,
+ "g": -15.042460150934428,
+ "h": -14.070847254450332,
+ "m": -0.4065041338859734,
+ "n": 0.20325206694298625,
+ "o": -0.2032520669429858,
+ "p": 1.9432257929681924,
+ "q": -0.9716128964840962,
+ "r": 0.9716128964840962
},
"backward": false
},
{
"spline": {
"start": {
- "x": -11.724814497154577,
- "y": -20.671960229353882
+ "x": -3.017497888282994,
+ "y": -14.070847254450332
},
"control1": {
- "x": -11.811469516461772,
- "y": -20.710760633619355
+ "x": -3.0367265589003347,
+ "y": -13.978927774195828
},
"control2": {
- "x": -11.821018784825078,
- "y": -20.711734884772994
+ "x": -3.0350781164829344,
+ "y": -13.967607474332265
},
"end": {
- "x": -11.913723992363813,
- "y": -20.691233464395506
- },
- "a": -11.724814497154577,
- "b": -11.811469516461772,
- "c": -11.821018784825078,
- "d": -11.913723992363813,
- "e": -20.671960229353882,
- "f": -20.710760633619355,
- "g": -20.711734884772994,
- "h": -20.691233464395506,
- "m": -0.16026169011931835,
- "n": 0.07710575094388794,
- "o": -0.08665501930719444,
- "p": -0.01635048158070518,
- "q": 0.03782615311183335,
- "r": -0.03880040426547282
+ "x": -2.99043354707179,
+ "y": -13.884989104425435
+ },
+ "a": -3.017497888282994,
+ "b": -3.0367265589003347,
+ "c": -3.0350781164829344,
+ "d": -2.99043354707179,
+ "e": -14.070847254450332,
+ "f": -13.978927774195828,
+ "g": -13.967607474332265,
+ "h": -13.884989104425435,
+ "m": 0.022119013959002398,
+ "n": 0.020877113034741157,
+ "o": -0.019228670617340793,
+ "p": 0.15189725043419955,
+ "q": -0.08059918039094072,
+ "r": 0.09191948025450358
},
"backward": false
},
{
"spline": {
"start": {
- "x": -11.913723992363813,
- "y": -20.691233464395506
+ "x": -2.99043354707179,
+ "y": -13.884989104425435
},
"control1": {
- "x": -11.988402918774163,
- "y": -20.67471849070281
+ "x": -2.9561169305131325,
+ "y": -13.821483442747212
},
"control2": {
- "x": -11.988286847509807,
- "y": -20.660387206198756
+ "x": -2.9394572592036656,
+ "y": -13.828419458355432
},
"end": {
- "x": -12.03840299042734,
- "y": -20.602611271546497
- },
- "a": -11.913723992363813,
- "b": -11.988402918774163,
- "c": -11.988286847509807,
- "d": -12.03840299042734,
- "e": -20.691233464395506,
- "f": -20.67471849070281,
- "g": -20.660387206198756,
- "h": -20.602611271546497,
- "m": -0.12502721185659382,
- "n": 0.07479499767470621,
- "o": -0.07467892641034979,
- "p": 0.04562833933684729,
- "q": -0.002183689188644422,
- "r": 0.016514973692697055
+ "x": -2.8722557581638455,
+ "y": -13.802064999620375
+ },
+ "a": -2.99043354707179,
+ "b": -2.9561169305131325,
+ "c": -2.9394572592036656,
+ "d": -2.8722557581638455,
+ "e": -13.884989104425435,
+ "f": -13.821483442747212,
+ "g": -13.828419458355432,
+ "h": -13.802064999620375,
+ "m": 0.0681987749795443,
+ "n": -0.017656945249190592,
+ "o": 0.034316616558657476,
+ "p": 0.10373215162971228,
+ "q": -0.07044167728644268,
+ "r": 0.06350566167822258
},
"backward": false
},
{
"spline": {
"start": {
- "x": -12.03840299042734,
- "y": -20.602611271546497
+ "x": -2.8722557581638455,
+ "y": -13.802064999620375
},
"control1": {
- "x": -13.035577240069488,
- "y": -19.453028104720506
+ "x": 2.6133247094805503,
+ "y": -11.650781146798318
},
"control2": {
- "x": -13.035577240069491,
- "y": -19.453028104720506
+ "x": 2.6133247094805485,
+ "y": -11.650781146798318
},
"end": {
- "x": -14.03275148971164,
- "y": -18.303444937894515
- },
- "a": -12.03840299042734,
- "b": -13.035577240069488,
- "c": -13.035577240069491,
- "d": -14.03275148971164,
- "e": -20.602611271546497,
- "f": -19.453028104720506,
- "g": -19.453028104720506,
- "h": -18.303444937894515,
- "m": -1.9943484992842926,
- "n": 0.9971742496421445,
- "o": -0.9971742496421481,
- "p": 2.2991663336519856,
- "q": -1.149583166825991,
- "r": 1.149583166825991
+ "x": 8.098905177124944,
+ "y": -9.49949729397626
+ },
+ "a": -2.8722557581638455,
+ "b": 2.6133247094805503,
+ "c": 2.6133247094805485,
+ "d": 8.098905177124944,
+ "e": -13.802064999620375,
+ "f": -11.650781146798318,
+ "g": -11.650781146798318,
+ "h": -9.49949729397626,
+ "m": 10.971160935288795,
+ "n": -5.4855804676443976,
+ "o": 5.485580467644396,
+ "p": 4.302567705644114,
+ "q": -2.1512838528220577,
+ "r": 2.1512838528220577
},
"backward": false
},
{
"spline": {
"start": {
- "x": -14.03275148971164,
- "y": -18.303444937894515
+ "x": 8.098905177124944,
+ "y": -9.49949729397626
},
"control1": {
- "x": -14.090339909564419,
- "y": -18.23705465746898
+ "x": 8.186056258191485,
+ "y": -9.465319194765012
},
"control2": {
- "x": -14.09492842517947,
- "y": -18.216263205793762
+ "x": 8.198012585692142,
+ "y": -9.46514688788814
},
"end": {
- "x": -14.070631885986728,
- "y": -18.131801536266238
- },
- "a": -14.03275148971164,
- "b": -14.090339909564419,
- "c": -14.09492842517947,
- "d": -14.070631885986728,
- "e": -18.303444937894515,
- "f": -18.23705465746898,
- "g": -18.216263205793762,
- "h": -18.131801536266238,
- "m": -0.024114849429931695,
- "n": 0.052999904237728046,
- "o": -0.05758841985277918,
- "p": 0.10926904660261627,
- "q": -0.045598828750314624,
- "r": 0.06639028042553363
+ "x": 8.28611237482917,
+ "y": -9.4967993846423
+ },
+ "a": 8.098905177124944,
+ "b": 8.186056258191485,
+ "c": 8.198012585692142,
+ "d": 8.28611237482917,
+ "e": -9.49949729397626,
+ "f": -9.465319194765012,
+ "g": -9.46514688788814,
+ "h": -9.4967993846423,
+ "m": 0.15133821520225155,
+ "n": -0.07519475356588323,
+ "o": 0.08715108106654057,
+ "p": 0.002180988703345932,
+ "q": -0.034005792334376395,
+ "r": 0.034178099211247925
},
"backward": false
},
{
"spline": {
"start": {
- "x": -14.070631885986728,
- "y": -18.131801536266238
+ "x": 8.28611237482917,
+ "y": -9.4967993846423
},
"control1": {
- "x": -14.056097025524844,
- "y": -18.08127423395287
+ "x": 8.352927417873165,
+ "y": -9.520804696729819
},
"control2": {
- "x": -14.022793560499485,
- "y": -18.10935045182197
+ "x": 8.347762426411911,
+ "y": -9.539239196197277
},
"end": {
- "x": -13.972504839170023,
- "y": -18.094010472853245
- },
- "a": -14.070631885986728,
- "b": -14.056097025524844,
- "c": -14.022793560499485,
- "d": -13.972504839170023,
- "e": -18.131801536266238,
- "f": -18.08127423395287,
- "g": -18.10935045182197,
- "h": -18.094010472853245,
- "m": -0.0017833482593729855,
- "n": 0.01876860456347451,
- "o": 0.014534860461884236,
- "p": 0.1220197170203079,
- "q": -0.07860352018247241,
- "r": 0.05052730231336966
+ "x": 8.382249005602903,
+ "y": -9.601297079186416
+ },
+ "a": 8.28611237482917,
+ "b": 8.352927417873165,
+ "c": 8.347762426411911,
+ "d": 8.382249005602903,
+ "e": -9.4967993846423,
+ "f": -9.520804696729819,
+ "g": -9.539239196197277,
+ "h": -9.601297079186416,
+ "m": 0.11163160515749126,
+ "n": -0.07198003450524837,
+ "o": 0.0668150430439951,
+ "p": -0.04919419614174281,
+ "q": 0.00557081262006065,
+ "r": -0.024005312087519215
},
"backward": false
},
{
"spline": {
"start": {
- "x": -13.972504839170023,
- "y": -18.094010472853245
+ "x": 8.382249005602903,
+ "y": -9.601297079186416
},
"control1": {
- "x": -7.414678997430931,
- "y": -16.093623351578195
+ "x": 8.78014321865592,
+ "y": -10.317299451557108
},
"control2": {
- "x": -7.41467899743093,
- "y": -16.093623351578195
+ "x": 8.78014321865592,
+ "y": -10.31729945155711
},
"end": {
- "x": -0.8568531556918383,
- "y": -14.093236230303141
- },
- "a": -13.972504839170023,
- "b": -7.414678997430931,
- "c": -7.41467899743093,
- "d": -0.8568531556918383,
- "e": -18.094010472853245,
- "f": -16.093623351578195,
- "g": -16.093623351578195,
- "h": -14.093236230303141,
- "m": 13.115651683478186,
- "n": -6.557825841739091,
- "o": 6.557825841739092,
- "p": 4.000774242550104,
- "q": -2.0003871212750504,
- "r": 2.0003871212750504
+ "x": 9.178037431708939,
+ "y": -11.033301823927802
+ },
+ "a": 8.382249005602903,
+ "b": 8.78014321865592,
+ "c": 8.78014321865592,
+ "d": 9.178037431708939,
+ "e": -9.601297079186416,
+ "f": -10.317299451557108,
+ "g": -10.31729945155711,
+ "h": -11.033301823927802,
+ "m": 0.795788426106034,
+ "n": -0.39789421305301786,
+ "o": 0.39789421305301786,
+ "p": -1.4320047447413806,
+ "q": 0.7160023723706903,
+ "r": -0.7160023723706921
},
"backward": false
},
{
"spline": {
"start": {
- "x": -0.8568531556918383,
- "y": -14.093236230303141
+ "x": 9.178037431708939,
+ "y": -11.033301823927802
},
"control1": {
- "x": -0.766191761691019,
- "y": -14.065581045592108
+ "x": 9.222398309721111,
+ "y": -11.113128302562538
},
"control2": {
- "x": -0.7563105984793453,
- "y": -14.06586061911611
+ "x": 9.223784673170757,
+ "y": -11.128913344479823
},
"end": {
- "x": -0.6673579189760938,
- "y": -14.09859772975167
- },
- "a": -0.8568531556918383,
- "b": -0.766191761691019,
- "c": -0.7563105984793453,
- "d": -0.6673579189760938,
- "e": -14.093236230303141,
- "f": -14.065581045592108,
- "g": -14.06586061911611,
- "h": -14.09859772975167,
- "m": 0.15985174708072336,
- "n": -0.08078023078914565,
- "o": 0.0906613940008193,
- "p": -0.004522778876516753,
- "q": -0.027934758235035773,
- "r": 0.027655184711033343
+ "x": 9.194017543557615,
+ "y": -11.215250313292882
+ },
+ "a": 9.178037431708939,
+ "b": 9.222398309721111,
+ "c": 9.223784673170757,
+ "d": 9.194017543557615,
+ "e": -11.033301823927802,
+ "f": -11.113128302562538,
+ "g": -11.128913344479823,
+ "h": -11.215250313292882,
+ "m": 0.011821021499736517,
+ "n": -0.04297451456252688,
+ "o": 0.04436087801217248,
+ "p": -0.1345933636132326,
+ "q": 0.0640414367174511,
+ "r": -0.07982647863473602
},
"backward": false
},
{
"spline": {
"start": {
- "x": -0.6673579189760938,
- "y": -14.09859772975167
+ "x": 9.194017543557615,
+ "y": -11.215250313292882
},
"control1": {
- "x": -0.5962152169835718,
- "y": -14.124780263195834
+ "x": 9.173649391884538,
+ "y": -11.274326364582752
},
"control2": {
- "x": -0.5985961910203768,
- "y": -14.13947853916734
+ "x": 9.148506812565193,
+ "y": -11.261612969860874
},
"end": {
- "x": -0.5577240123279424,
- "y": -14.203324244686861
- },
- "a": -0.6673579189760938,
- "b": -0.5962152169835718,
- "c": -0.5985961910203768,
- "d": -0.5577240123279424,
- "e": -14.09859772975167,
- "f": -14.124780263195834,
- "g": -14.13947853916734,
- "h": -14.203324244686861,
- "m": 0.11677682875856632,
- "n": -0.07352367602932686,
- "o": 0.07114270199252193,
- "p": -0.06063168702067756,
- "q": 0.011484257472657688,
- "r": -0.02618253344416388
+ "x": 9.090939785011471,
+ "y": -11.285920968075658
+ },
+ "a": 9.194017543557615,
+ "b": 9.173649391884538,
+ "c": 9.148506812565193,
+ "d": 9.090939785011471,
+ "e": -11.215250313292882,
+ "f": -11.274326364582752,
+ "g": -11.261612969860874,
+ "h": -11.285920968075658,
+ "m": -0.027650020588106727,
+ "n": -0.004774427646268009,
+ "o": -0.02036815167307715,
+ "p": -0.10881083894841659,
+ "q": 0.07178944601174742,
+ "r": -0.05907605128986937
},
"backward": false
},
{
"spline": {
"start": {
- "x": -0.5577240123279424,
- "y": -14.203324244686861
+ "x": 9.090939785011471,
+ "y": -11.285920968075658
},
"control1": {
- "x": -0.24631405135267054,
- "y": -14.68977222226807
+ "x": 3.3525726878353623,
+ "y": -13.708978455693217
},
"control2": {
- "x": -0.24631405135267095,
- "y": -14.689772222268072
+ "x": 3.3525726878353628,
+ "y": -13.70897845569322
},
"end": {
- "x": 0.06509590962260095,
- "y": -15.176220199849281
- },
- "a": -0.5577240123279424,
- "b": -0.24631405135267054,
- "c": -0.24631405135267095,
- "d": 0.06509590962260095,
- "e": -14.203324244686861,
- "f": -14.68977222226807,
- "g": -14.689772222268072,
- "h": -15.176220199849281,
- "m": 0.6228199219505446,
- "n": -0.3114099609752723,
- "o": 0.3114099609752719,
- "p": -0.9728959551624214,
- "q": 0.48644797758120717,
- "r": -0.48644797758120895
+ "x": -2.3857944093407464,
+ "y": -16.13203594331078
+ },
+ "a": 9.090939785011471,
+ "b": 3.3525726878353623,
+ "c": 3.3525726878353628,
+ "d": -2.3857944093407464,
+ "e": -11.285920968075658,
+ "f": -13.708978455693217,
+ "g": -13.70897845569322,
+ "h": -16.13203594331078,
+ "m": -11.47673419435222,
+ "n": 5.738367097176109,
+ "o": -5.738367097176109,
+ "p": -4.846114975235114,
+ "q": 2.4230574876175552,
+ "r": -2.423057487617559
},
"backward": false
}
@@ -1198,770 +1198,3080 @@
{
"spline": {
"start": {
- "x": -11.16085925302996,
- "y": -15.916473261222762
+ "x": 10.040576977764365,
+ "y": -11.817216671012797
},
"control1": {
- "x": -13.542611646920335,
- "y": -16.98291991927031
+ "x": 9.872857166860037,
+ "y": -11.015459848946962
},
"control2": {
- "x": -13.542611646920335,
- "y": -16.982919919270312
+ "x": 9.872857166860035,
+ "y": -11.015459848946962
},
"end": {
- "x": -15.92436404081071,
- "y": -18.04936657731786
- },
- "a": -11.16085925302996,
- "b": -13.542611646920335,
- "c": -13.542611646920335,
- "d": -15.92436404081071,
- "e": -15.916473261222762,
- "f": -16.98291991927031,
- "g": -16.982919919270312,
- "h": -18.04936657731786,
- "m": -4.763504787780748,
- "n": 2.381752393890375,
- "o": -2.381752393890375,
- "p": -2.1328933160950854,
- "q": 1.0664466580475427,
- "r": -1.0664466580475462
+ "x": 9.705137355955706,
+ "y": -10.213703026881127
+ },
+ "a": 10.040576977764365,
+ "b": 9.872857166860037,
+ "c": 9.872857166860035,
+ "d": 9.705137355955706,
+ "e": -11.817216671012797,
+ "f": -11.015459848946962,
+ "g": -11.015459848946962,
+ "h": -10.213703026881127,
+ "m": -0.3354396218086517,
+ "n": 0.16771981090432675,
+ "o": -0.16771981090432853,
+ "p": 1.6035136441316702,
+ "q": -0.8017568220658351,
+ "r": 0.8017568220658351
},
"backward": false
},
{
"spline": {
"start": {
- "x": -15.92436404081071,
- "y": -18.04936657731786
+ "x": 9.705137355955706,
+ "y": -10.213703026881127
},
"control1": {
- "x": -15.978768937226278,
- "y": -18.07372675854939
+ "x": 9.684892999619743,
+ "y": -10.116928229001411
},
"control2": {
- "x": -15.989166600633256,
- "y": -18.120743868891662
+ "x": 9.68411244066312,
+ "y": -10.114833677421275
},
"end": {
- "x": -15.950106984749256,
- "y": -18.165773387994314
- },
- "a": -15.92436404081071,
- "b": -15.978768937226278,
- "c": -15.989166600633256,
- "d": -15.950106984749256,
- "e": -18.04936657731786,
- "f": -18.07372675854939,
- "g": -18.120743868891662,
- "h": -18.165773387994314,
- "m": 0.005450046282392762,
- "n": 0.044007233008589,
- "o": -0.05440489641556745,
- "p": 0.02464452035035336,
- "q": -0.022656929110738133,
- "r": -0.024360181231532607
+ "x": 9.636086506776302,
+ "y": -10.028412006861071
+ },
+ "a": 9.705137355955706,
+ "b": 9.684892999619743,
+ "c": 9.68411244066312,
+ "d": 9.636086506776302,
+ "e": -10.213703026881127,
+ "f": -10.116928229001411,
+ "g": -10.114833677421275,
+ "h": -10.028412006861071,
+ "m": -0.06670917230952966,
+ "n": 0.019463797379339454,
+ "o": -0.02024435633596333,
+ "p": 0.17900736527964867,
+ "q": -0.09468024629958016,
+ "r": 0.09677479787971599
},
"backward": false
},
{
"spline": {
"start": {
- "x": -15.950106984749256,
- "y": -18.165773387994314
+ "x": 9.636086506776302,
+ "y": -10.028412006861071
},
"control1": {
- "x": -15.737898567613128,
- "y": -18.410415910889412
+ "x": 9.290748473559788,
+ "y": -9.40698338854662
},
"control2": {
- "x": -15.753619330875859,
- "y": -18.719310154659887
+ "x": 8.774075136077252,
+ "y": -8.104352094647044
},
"end": {
- "x": -15.458040977338284,
- "y": -18.586962834203764
+ "x": 8.919645365222031,
+ "y": -8.800226503741671
+ },
+ "a": 9.636086506776302,
+ "b": 9.290748473559788,
+ "c": 8.774075136077252,
+ "d": 8.919645365222031,
+ "e": -10.028412006861071,
+ "f": -9.40698338854662,
+ "g": -8.104352094647044,
+ "h": -8.800226503741671,
+ "m": 0.8335788708933336,
+ "n": -0.17133530426602128,
+ "o": -0.3453380332165139,
+ "p": -2.6797083785793223,
+ "q": 0.6812026755851228,
+ "r": 0.621428618314452
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 8.919645365222031,
+ "y": -8.800226503741671
+ },
+ "control1": {
+ "x": 9.25508498703069,
+ "y": -10.40374014787334
},
- "a": -15.950106984749256,
- "b": -15.737898567613128,
- "c": -15.753619330875859,
- "d": -15.458040977338284,
- "e": -18.165773387994314,
- "f": -18.410415910889412,
- "g": -18.719310154659887,
- "h": -18.586962834203764,
- "m": 0.539228297199168,
- "n": -0.2279291803988599,
- "o": 0.2122084171361287,
- "p": 0.50549328510197,
- "q": -0.0642517208753759,
- "r": -0.24464252289509858
+ "control2": {
+ "x": 9.25508498703069,
+ "y": -10.40374014787334
+ },
+ "end": {
+ "x": 9.590524608839349,
+ "y": -12.007253792005008
+ },
+ "a": 8.919645365222031,
+ "b": 9.25508498703069,
+ "c": 9.25508498703069,
+ "d": 9.590524608839349,
+ "e": -8.800226503741671,
+ "f": -10.40374014787334,
+ "g": -10.40374014787334,
+ "h": -12.007253792005008,
+ "m": 0.6708792436173177,
+ "n": -0.3354396218086588,
+ "o": 0.3354396218086588,
+ "p": -3.207027288263335,
+ "q": 1.6035136441316684,
+ "r": -1.6035136441316684
},
"backward": false
},
{
"spline": {
"start": {
- "x": -15.458040977338284,
- "y": -18.586962834203764
+ "x": 9.590524608839349,
+ "y": -12.007253792005008
},
"control1": {
- "x": -10.694536189557535,
- "y": -16.454069518108664
+ "x": 9.606332981708317,
+ "y": -12.08282310445007
},
"control2": {
- "x": -10.694536189557535,
- "y": -16.454069518108664
+ "x": 9.59000087466324,
+ "y": -12.114002228886791
},
"end": {
- "x": -5.931031401776787,
- "y": -14.321176202013566
+ "x": 9.518876571100726,
+ "y": -12.144034862453148
+ },
+ "a": 9.590524608839349,
+ "b": 9.606332981708317,
+ "c": 9.59000087466324,
+ "d": 9.518876571100726,
+ "e": -12.007253792005008,
+ "f": -12.08282310445007,
+ "g": -12.114002228886791,
+ "h": -12.144034862453148,
+ "m": -0.022651716603391492,
+ "n": -0.03214047991404456,
+ "o": 0.015808372868967524,
+ "p": -0.04324369713796905,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 9.518876571100726,
+ "y": -12.144034862453148
+ },
+ "control1": {
+ "x": 9.342858495259119,
+ "y": -12.218359475954074
+ },
+ "control2": {
+ "x": 9.179594723161383,
+ "y": -12.384309474909207
},
- "a": -15.458040977338284,
- "b": -10.694536189557535,
- "c": -10.694536189557535,
- "d": -5.931031401776787,
- "e": -18.586962834203764,
- "f": -16.454069518108664,
- "g": -16.454069518108664,
- "h": -14.321176202013566,
- "m": 9.527009575561499,
- "n": -4.763504787780748,
- "o": 4.763504787780748,
- "p": 4.265786632190196,
- "q": -2.1328933160950996,
- "r": 2.1328933160950996
+ "end": {
+ "x": 9.140472239914333,
+ "y": -12.19729091299722
+ },
+ "a": 9.518876571100726,
+ "b": 9.342858495259119,
+ "c": 9.179594723161383,
+ "d": 9.140472239914333,
+ "e": -12.144034862453148,
+ "f": -12.218359475954074,
+ "g": -12.384309474909207,
+ "h": -12.19729091299722,
+ "m": 0.11138698510681522,
+ "n": 0.012754303743871986,
+ "o": -0.17601807584160767,
+ "p": 0.4445939463213282,
+ "q": -0.09162538545420773,
+ "r": -0.07432461350092545
},
"backward": false
},
{
"spline": {
"start": {
- "x": -5.931031401776787,
- "y": -14.321176202013566
+ "x": 9.140472239914333,
+ "y": -12.19729091299722
},
"control1": {
- "x": -5.839940764336174,
- "y": -14.280389718025074
+ "x": 8.751027792687397,
+ "y": -10.335616131131644
},
"control2": {
- "x": -5.839576307662656,
- "y": -14.280253146868457
+ "x": 8.751027792687399,
+ "y": -10.335616131131644
},
"end": {
- "x": -5.7441138298695975,
- "y": -14.251133448396624
+ "x": 8.361583345460463,
+ "y": -8.47394134926607
+ },
+ "a": 9.140472239914333,
+ "b": 8.751027792687397,
+ "c": 8.751027792687399,
+ "d": 8.361583345460463,
+ "e": -12.19729091299722,
+ "f": -10.335616131131644,
+ "g": -10.335616131131644,
+ "h": -8.47394134926607,
+ "m": -0.778888894453873,
+ "n": 0.3894444472269374,
+ "o": -0.3894444472269356,
+ "p": 3.7233495637311496,
+ "q": -1.8616747818655757,
+ "r": 1.8616747818655757
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 8.361583345460463,
+ "y": -8.47394134926607
},
- "a": -5.931031401776787,
- "b": -5.839940764336174,
- "c": -5.839576307662656,
- "d": -5.7441138298695975,
- "e": -14.321176202013566,
- "f": -14.280389718025074,
- "g": -14.280253146868457,
- "h": -14.251133448396624,
- "m": 0.18582420188663384,
- "n": -0.09072618076709471,
- "o": 0.09109063744061263,
- "p": 0.06963304014709415,
- "q": -0.04064991283187602,
- "r": 0.040786483988492606
+ "control1": {
+ "x": 8.34836684143731,
+ "y": -8.410762037249858
+ },
+ "control2": {
+ "x": 8.308101791072355,
+ "y": -8.389003972573823
+ },
+ "end": {
+ "x": 8.248010645611375,
+ "y": -8.412569958286781
+ },
+ "a": 8.361583345460463,
+ "b": 8.34836684143731,
+ "c": 8.308101791072355,
+ "d": 8.248010645611375,
+ "e": -8.47394134926607,
+ "f": -8.410762037249858,
+ "g": -8.389003972573823,
+ "h": -8.412569958286781,
+ "m": 0.007222451245770145,
+ "n": -0.027048546341800872,
+ "o": -0.013216504023153774,
+ "p": -0.0039028030488168497,
+ "q": -0.04142124734017649,
+ "r": 0.06317931201621185
},
"backward": false
},
{
"spline": {
"start": {
- "x": -5.7441138298695975,
- "y": -14.251133448396624
+ "x": 8.248010645611375,
+ "y": -8.412569958286781
},
"control1": {
- "x": -3.22446920155933,
- "y": -13.482545689846898
+ "x": 8.05492832198582,
+ "y": -8.488291185517157
},
"control2": {
- "x": 1.7030503576363891,
- "y": -11.649357270912006
+ "x": 7.866446622056128,
+ "y": -8.448460168768857
},
"end": {
- "x": -0.7012035505236112,
- "y": -12.725879142804372
+ "x": 7.908913333040266,
+ "y": -8.651465258380197
+ },
+ "a": 8.248010645611375,
+ "b": 8.05492832198582,
+ "c": 7.866446622056128,
+ "d": 7.908913333040266,
+ "e": -8.412569958286781,
+ "f": -8.488291185517157,
+ "g": -8.448460168768857,
+ "h": -8.651465258380197,
+ "m": 0.22634778721796422,
+ "n": 0.004600623695862893,
+ "o": -0.19308232362555522,
+ "p": -0.3583883503383163,
+ "q": 0.11555224397867647,
+ "r": -0.07572122723037644
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 7.908913333040266,
+ "y": -8.651465258380197
+ },
+ "control1": {
+ "x": 8.29966660201479,
+ "y": -10.519396646184813
+ },
+ "control2": {
+ "x": 8.299666602014792,
+ "y": -10.519396646184815
},
- "a": -5.7441138298695975,
- "b": -3.22446920155933,
- "c": 1.7030503576363891,
- "d": -0.7012035505236112,
- "e": -14.251133448396624,
- "f": -13.482545689846898,
- "g": -11.649357270912006,
- "h": -12.725879142804372,
- "m": -9.739648398241172,
- "n": 2.407874930885452,
- "o": 2.5196446283102674,
- "p": -3.9743109512124217,
- "q": 1.0646006603851657,
- "r": 0.7685877585497263
+ "end": {
+ "x": 8.690419870989317,
+ "y": -12.38732803398943
+ },
+ "a": 7.908913333040266,
+ "b": 8.29966660201479,
+ "c": 8.299666602014792,
+ "d": 8.690419870989317,
+ "e": -8.651465258380197,
+ "f": -10.519396646184813,
+ "g": -10.519396646184815,
+ "h": -12.38732803398943,
+ "m": 0.7815065379490473,
+ "n": -0.3907532689745228,
+ "o": 0.39075326897452456,
+ "p": -3.7358627756092293,
+ "q": 1.8679313878046138,
+ "r": -1.8679313878046155
},
"backward": false
},
{
"spline": {
"start": {
- "x": -0.7012035505236112,
- "y": -12.725879142804372
+ "x": 8.690419870989317,
+ "y": -12.38732803398943
},
"control1": {
- "x": -7.846460732194735,
- "y": -15.92521911694702
+ "x": 8.706228243858284,
+ "y": -12.462897346434492
},
"control2": {
- "x": -7.846460732194734,
- "y": -15.92521911694702
+ "x": 8.689896136813207,
+ "y": -12.494076470871214
},
"end": {
- "x": -14.991717913865857,
- "y": -19.124559091089665
+ "x": 8.618771833250694,
+ "y": -12.52410910443757
+ },
+ "a": 8.690419870989317,
+ "b": 8.706228243858284,
+ "c": 8.689896136813207,
+ "d": 8.618771833250694,
+ "e": -12.38732803398943,
+ "f": -12.462897346434492,
+ "g": -12.494076470871214,
+ "h": -12.52410910443757,
+ "m": -0.022651716603391492,
+ "n": -0.03214047991404456,
+ "o": 0.015808372868967524,
+ "p": -0.04324369713797793,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 8.618771833250694,
+ "y": -12.52410910443757
+ },
+ "control1": {
+ "x": 8.442753757409086,
+ "y": -12.598433717938496
},
- "a": -0.7012035505236112,
- "b": -7.846460732194735,
- "c": -7.846460732194734,
- "d": -14.991717913865857,
- "e": -12.725879142804372,
- "f": -15.92521911694702,
- "g": -15.92521911694702,
- "h": -19.124559091089665,
- "m": -14.29051436334225,
- "n": 7.145257181671124,
- "o": -7.1452571816711234,
- "p": -6.398679948285293,
- "q": 3.1993399741426476,
- "r": -3.1993399741426476
+ "control2": {
+ "x": 8.27948998531135,
+ "y": -12.76438371689363
+ },
+ "end": {
+ "x": 8.2403675020643,
+ "y": -12.577365154981642
+ },
+ "a": 8.618771833250694,
+ "b": 8.442753757409086,
+ "c": 8.27948998531135,
+ "d": 8.2403675020643,
+ "e": -12.52410910443757,
+ "f": -12.598433717938496,
+ "g": -12.76438371689363,
+ "h": -12.577365154981642,
+ "m": 0.11138698510681522,
+ "n": 0.012754303743871986,
+ "o": -0.17601807584160767,
+ "p": 0.4445939463213264,
+ "q": -0.09162538545420773,
+ "r": -0.07432461350092545
},
"backward": false
},
{
"spline": {
"start": {
- "x": -14.991717913865857,
- "y": -19.124559091089665
+ "x": 8.2403675020643,
+ "y": -12.577365154981642
},
"control1": {
- "x": -15.046122810281425,
- "y": -19.148919272321198
+ "x": 7.848305411342184,
+ "y": -10.703177161237981
},
"control2": {
- "x": -15.056520473688403,
- "y": -19.19593638266347
+ "x": 7.848305411342184,
+ "y": -10.703177161237981
},
"end": {
- "x": -15.017460857804403,
- "y": -19.24096590176612
+ "x": 7.456243320620068,
+ "y": -8.828989167494322
+ },
+ "a": 8.2403675020643,
+ "b": 7.848305411342184,
+ "c": 7.848305411342184,
+ "d": 7.456243320620068,
+ "e": -12.577365154981642,
+ "f": -10.703177161237981,
+ "g": -10.703177161237981,
+ "h": -8.828989167494322,
+ "m": -0.7841241814442323,
+ "n": 0.3920620907221162,
+ "o": -0.3920620907221162,
+ "p": 3.7483759874873215,
+ "q": -1.8741879937436607,
+ "r": 1.8741879937436607
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 7.456243320620068,
+ "y": -8.828989167494322
+ },
+ "control1": {
+ "x": 7.443026816596913,
+ "y": -8.765809855478109
+ },
+ "control2": {
+ "x": 7.402761766231959,
+ "y": -8.744051790802075
},
- "a": -14.991717913865857,
- "b": -15.046122810281425,
- "c": -15.056520473688403,
- "d": -15.017460857804403,
- "e": -19.124559091089665,
- "f": -19.148919272321198,
- "g": -19.19593638266347,
- "h": -19.24096590176612,
- "m": 0.005450046282392762,
- "n": 0.044007233008589,
- "o": -0.05440489641556745,
- "p": 0.024644520350349808,
- "q": -0.022656929110738133,
- "r": -0.024360181231532607
+ "end": {
+ "x": 7.342670620770979,
+ "y": -8.767617776515033
+ },
+ "a": 7.456243320620068,
+ "b": 7.443026816596913,
+ "c": 7.402761766231959,
+ "d": 7.342670620770979,
+ "e": -8.828989167494322,
+ "f": -8.765809855478109,
+ "g": -8.744051790802075,
+ "h": -8.767617776515033,
+ "m": 0.007222451245773698,
+ "n": -0.027048546341799984,
+ "o": -0.013216504023154663,
+ "p": -0.003902803048807968,
+ "q": -0.041421247340180045,
+ "r": 0.06317931201621363
},
"backward": false
},
{
"spline": {
"start": {
- "x": -15.017460857804403,
- "y": -19.24096590176612
+ "x": 7.342670620770979,
+ "y": -8.767617776515033
},
"control1": {
- "x": -14.805252440668278,
- "y": -19.485608424661216
+ "x": 7.149588297145424,
+ "y": -8.84333900374541
},
"control2": {
- "x": -14.82097320393101,
- "y": -19.794502668431686
+ "x": 6.961106597215733,
+ "y": -8.803507986997111
},
"end": {
- "x": -14.525394850393438,
- "y": -19.662155347975567
+ "x": 7.00357330819987,
+ "y": -9.00651307660845
+ },
+ "a": 7.342670620770979,
+ "b": 7.149588297145424,
+ "c": 6.961106597215733,
+ "d": 7.00357330819987,
+ "e": -8.767617776515033,
+ "f": -8.84333900374541,
+ "g": -8.803507986997111,
+ "h": -9.00651307660845,
+ "m": 0.2263477872179651,
+ "n": 0.004600623695862893,
+ "o": -0.19308232362555433,
+ "p": -0.35838835033831096,
+ "q": 0.1155522439786747,
+ "r": -0.07572122723037644
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 7.00357330819987,
+ "y": -9.00651307660845
+ },
+ "control1": {
+ "x": 7.396944220669577,
+ "y": -10.886957676291152
+ },
+ "control2": {
+ "x": 7.396944220669577,
+ "y": -10.886957676291152
},
- "a": -15.017460857804403,
- "b": -14.805252440668278,
- "c": -14.82097320393101,
- "d": -14.525394850393438,
- "e": -19.24096590176612,
- "f": -19.485608424661216,
- "g": -19.794502668431686,
- "h": -19.662155347975567,
- "m": 0.5392282971991538,
- "n": -0.22792918039885635,
- "o": 0.21220841713612515,
- "p": 0.5054932851019593,
- "q": -0.0642517208753759,
- "r": -0.24464252289509503
+ "end": {
+ "x": 7.790315133139284,
+ "y": -12.767402275973854
+ },
+ "a": 7.00357330819987,
+ "b": 7.396944220669577,
+ "c": 7.396944220669577,
+ "d": 7.790315133139284,
+ "e": -9.00651307660845,
+ "f": -10.886957676291152,
+ "g": -10.886957676291152,
+ "h": -12.767402275973854,
+ "m": 0.7867418249394138,
+ "n": -0.3933709124697069,
+ "o": 0.3933709124697069,
+ "p": -3.760889199365405,
+ "q": 1.8804445996827024,
+ "r": -1.8804445996827024
},
"backward": false
},
{
"spline": {
"start": {
- "x": -14.525394850393438,
- "y": -19.662155347975567
+ "x": 7.790315133139284,
+ "y": -12.767402275973854
},
"control1": {
- "x": -7.156892723372116,
- "y": -16.36285585389901
+ "x": 7.8061235060082526,
+ "y": -12.842971588418916
},
"control2": {
- "x": -7.156892723372115,
- "y": -16.36285585389901
+ "x": 7.789791398963174,
+ "y": -12.874150712855638
},
"end": {
- "x": 0.21160940364920622,
- "y": -13.063556359822453
+ "x": 7.718667095400661,
+ "y": -12.904183346421995
+ },
+ "a": 7.790315133139284,
+ "b": 7.8061235060082526,
+ "c": 7.789791398963174,
+ "d": 7.718667095400661,
+ "e": -12.767402275973854,
+ "f": -12.842971588418916,
+ "g": -12.874150712855638,
+ "h": -12.904183346421995,
+ "m": -0.022651716603384386,
+ "n": -0.032140479914047226,
+ "o": 0.015808372868968412,
+ "p": -0.04324369713797083,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 7.718667095400661,
+ "y": -12.904183346421995
+ },
+ "control1": {
+ "x": 7.542649019559054,
+ "y": -12.97850795992292
+ },
+ "control2": {
+ "x": 7.379385247461317,
+ "y": -13.144457958878053
},
- "a": -14.525394850393438,
- "b": -7.156892723372116,
- "c": -7.156892723372115,
- "d": 0.21160940364920622,
- "e": -19.662155347975567,
- "f": -16.36285585389901,
- "g": -16.36285585389901,
- "h": -13.063556359822453,
- "m": 14.73700425404264,
- "n": -7.368502127021321,
- "o": 7.368502127021322,
- "p": 6.59859898815311,
- "q": -3.299299494076557,
- "r": 3.299299494076557
+ "end": {
+ "x": 7.340262764214268,
+ "y": -12.957439396966066
+ },
+ "a": 7.718667095400661,
+ "b": 7.542649019559054,
+ "c": 7.379385247461317,
+ "d": 7.340262764214268,
+ "e": -12.904183346421995,
+ "f": -12.97850795992292,
+ "g": -13.144457958878053,
+ "h": -12.957439396966066,
+ "m": 0.11138698510681788,
+ "n": 0.01275430374387021,
+ "o": -0.17601807584160678,
+ "p": 0.44459394632133353,
+ "q": -0.09162538545420773,
+ "r": -0.07432461350092545
},
"backward": false
},
{
"spline": {
"start": {
- "x": 0.21160940364920622,
- "y": -13.063556359822453
+ "x": 7.340262764214268,
+ "y": -12.957439396966066
},
"control1": {
- "x": 0.2807541078058382,
- "y": -13.032596324321416
+ "x": 6.94558302999697,
+ "y": -11.070738191344322
},
"control2": {
- "x": 0.3159473658054553,
- "y": -13.043105620153257
+ "x": 6.94558302999697,
+ "y": -11.070738191344322
},
"end": {
- "x": 0.3567935939988915,
- "y": -13.106910788858492
+ "x": 6.550903295779673,
+ "y": -9.184036985722576
+ },
+ "a": 7.340262764214268,
+ "b": 6.94558302999697,
+ "c": 6.94558302999697,
+ "d": 6.550903295779673,
+ "e": -12.957439396966066,
+ "f": -11.070738191344322,
+ "g": -11.070738191344322,
+ "h": -9.184036985722576,
+ "m": -0.7893594684345953,
+ "n": 0.39467973421729763,
+ "o": -0.39467973421729763,
+ "p": 3.773402411243488,
+ "q": -1.886701205621744,
+ "r": 1.886701205621744
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 6.550903295779673,
+ "y": -9.184036985722576
+ },
+ "control1": {
+ "x": 6.537686791756518,
+ "y": -9.120857673706363
},
- "a": 0.21160940364920622,
- "b": 0.2807541078058382,
- "c": 0.3159473658054553,
- "d": 0.3567935939988915,
- "e": -13.063556359822453,
- "f": -13.032596324321416,
- "g": -13.043105620153257,
- "h": -13.106910788858492,
- "m": 0.039604416350834004,
- "n": -0.0339514461570149,
- "o": 0.06914470415663199,
- "p": -0.011826541540511215,
- "q": -0.04146933133287867,
- "r": 0.030960035501037098
+ "control2": {
+ "x": 6.497421741391563,
+ "y": -9.099099609030329
+ },
+ "end": {
+ "x": 6.437330595930583,
+ "y": -9.122665594743287
+ },
+ "a": 6.550903295779673,
+ "b": 6.537686791756518,
+ "c": 6.497421741391563,
+ "d": 6.437330595930583,
+ "e": -9.184036985722576,
+ "f": -9.120857673706363,
+ "g": -9.099099609030329,
+ "h": -9.122665594743287,
+ "m": 0.007222451245773698,
+ "n": -0.027048546341799984,
+ "o": -0.013216504023154663,
+ "p": -0.0039028030488115206,
+ "q": -0.041421247340180045,
+ "r": 0.06317931201621363
},
"backward": false
},
{
"spline": {
"start": {
- "x": 0.3567935939988915,
- "y": -13.106910788858492
+ "x": 6.437330595930583,
+ "y": -9.122665594743287
},
"control1": {
- "x": 0.5138141145254159,
- "y": -13.35218975941442
+ "x": 6.244248272305028,
+ "y": -9.198386821973664
},
"control2": {
- "x": 0.848785379649943,
- "y": -13.52465200394487
+ "x": 6.055766572375336,
+ "y": -9.158555805225363
},
"end": {
- "x": 0.5829802387955523,
- "y": -13.64366815561671
+ "x": 6.098233283359473,
+ "y": -9.361560894836703
+ },
+ "a": 6.437330595930583,
+ "b": 6.244248272305028,
+ "c": 6.055766572375336,
+ "d": 6.098233283359473,
+ "e": -9.122665594743287,
+ "f": -9.198386821973664,
+ "g": -9.158555805225363,
+ "h": -9.361560894836703,
+ "m": 0.2263477872179669,
+ "n": 0.004600623695862893,
+ "o": -0.19308232362555522,
+ "p": -0.3583883503383145,
+ "q": 0.11555224397867647,
+ "r": -0.07572122723037644
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 6.098233283359473,
+ "y": -9.361560894836703
+ },
+ "control1": {
+ "x": 6.4942218393243625,
+ "y": -11.25451870639749
+ },
+ "control2": {
+ "x": 6.4942218393243625,
+ "y": -11.25451870639749
},
- "a": 0.3567935939988915,
- "b": 0.5138141145254159,
- "c": 0.848785379649943,
- "d": 0.5829802387955523,
- "e": -13.106910788858492,
- "f": -13.35218975941442,
- "g": -13.52465200394487,
- "h": -13.64366815561671,
- "m": -0.7787271505769207,
- "n": 0.1779507445980027,
- "o": 0.15702052052652443,
- "p": -0.019370633166865048,
- "q": 0.07281672602547751,
- "r": -0.24527897055592796
+ "end": {
+ "x": 6.890210395289252,
+ "y": -13.147476517958278
+ },
+ "a": 6.098233283359473,
+ "b": 6.4942218393243625,
+ "c": 6.4942218393243625,
+ "d": 6.890210395289252,
+ "e": -9.361560894836703,
+ "f": -11.25451870639749,
+ "g": -11.25451870639749,
+ "h": -13.147476517958278,
+ "m": 0.7919771119297785,
+ "n": -0.39598855596488924,
+ "o": 0.39598855596488924,
+ "p": -3.785915623121575,
+ "q": 1.8929578115607875,
+ "r": -1.8929578115607875
},
"backward": false
},
{
"spline": {
"start": {
- "x": 0.5829802387955523,
- "y": -13.64366815561671
+ "x": 6.890210395289252,
+ "y": -13.147476517958278
},
"control1": {
- "x": -6.738045774062729,
- "y": -16.921709880239092
+ "x": 6.90601876815822,
+ "y": -13.22304583040334
},
"control2": {
- "x": -6.73804577406273,
- "y": -16.921709880239092
+ "x": 6.889686661113141,
+ "y": -13.254224954840062
},
"end": {
- "x": -14.059071786921011,
- "y": -20.199751604861472
+ "x": 6.818562357550628,
+ "y": -13.284257588406419
+ },
+ "a": 6.890210395289252,
+ "b": 6.90601876815822,
+ "c": 6.889686661113141,
+ "d": 6.818562357550628,
+ "e": -13.147476517958278,
+ "f": -13.22304583040334,
+ "g": -13.254224954840062,
+ "h": -13.284257588406419,
+ "m": -0.022651716603384386,
+ "n": -0.032140479914047226,
+ "o": 0.015808372868968412,
+ "p": -0.04324369713797793,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 6.818562357550628,
+ "y": -13.284257588406419
},
- "a": 0.5829802387955523,
- "b": -6.738045774062729,
- "c": -6.73804577406273,
- "d": -14.059071786921011,
- "e": -13.64366815561671,
- "f": -16.921709880239092,
- "g": -16.921709880239092,
- "h": -20.199751604861472,
- "m": -14.64205202571656,
- "n": 7.3210260128582805,
- "o": -7.321026012858281,
- "p": -6.5560834492447615,
- "q": 3.2780417246223816,
- "r": -3.2780417246223816
+ "control1": {
+ "x": 6.642544281709022,
+ "y": -13.358582201907344
+ },
+ "control2": {
+ "x": 6.479280509611287,
+ "y": -13.524532200862474
+ },
+ "end": {
+ "x": 6.440158026364237,
+ "y": -13.337513638950488
+ },
+ "a": 6.818562357550628,
+ "b": 6.642544281709022,
+ "c": 6.479280509611287,
+ "d": 6.440158026364237,
+ "e": -13.284257588406419,
+ "f": -13.358582201907344,
+ "g": -13.524532200862474,
+ "h": -13.337513638950488,
+ "m": 0.1113869851068161,
+ "n": 0.01275430374387021,
+ "o": -0.1760180758416059,
+ "p": 0.4445939463213193,
+ "q": -0.09162538545420418,
+ "r": -0.07432461350092545
},
"backward": false
},
{
"spline": {
"start": {
- "x": -14.059071786921011,
- "y": -20.199751604861472
+ "x": 6.440158026364237,
+ "y": -13.337513638950488
},
"control1": {
- "x": -14.113476683336579,
- "y": -20.224111786093005
+ "x": 6.042860648651756,
+ "y": -11.43829922145066
},
"control2": {
- "x": -14.123874346743555,
- "y": -20.271128896435275
+ "x": 6.042860648651756,
+ "y": -11.438299221450658
},
"end": {
- "x": -14.084814730859556,
- "y": -20.316158415537927
+ "x": 5.645563270939276,
+ "y": -9.539084803950828
+ },
+ "a": 6.440158026364237,
+ "b": 6.042860648651756,
+ "c": 6.042860648651756,
+ "d": 5.645563270939276,
+ "e": -13.337513638950488,
+ "f": -11.43829922145066,
+ "g": -11.438299221450658,
+ "h": -9.539084803950828,
+ "m": -0.7945947554249617,
+ "n": 0.39729737771248086,
+ "o": -0.39729737771248086,
+ "p": 3.79842883499966,
+ "q": -1.8992144174998273,
+ "r": 1.899214417499829
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 5.645563270939276,
+ "y": -9.539084803950828
+ },
+ "control1": {
+ "x": 5.632346766916121,
+ "y": -9.475905491934615
+ },
+ "control2": {
+ "x": 5.592081716551166,
+ "y": -9.454147427258581
},
- "a": -14.059071786921011,
- "b": -14.113476683336579,
- "c": -14.123874346743555,
- "d": -14.084814730859556,
- "e": -20.199751604861472,
- "f": -20.224111786093005,
- "g": -20.271128896435275,
- "h": -20.316158415537927,
- "m": 0.005450046282385657,
- "n": 0.044007233008590774,
- "o": -0.05440489641556745,
- "p": 0.024644520350360466,
- "q": -0.022656929110738133,
- "r": -0.024360181231532607
+ "end": {
+ "x": 5.531990571090186,
+ "y": -9.47771341297154
+ },
+ "a": 5.645563270939276,
+ "b": 5.632346766916121,
+ "c": 5.592081716551166,
+ "d": 5.531990571090186,
+ "e": -9.539084803950828,
+ "f": -9.475905491934615,
+ "g": -9.454147427258581,
+ "h": -9.47771341297154,
+ "m": 0.007222451245775474,
+ "n": -0.027048546341799984,
+ "o": -0.013216504023154663,
+ "p": -0.003902803048813297,
+ "q": -0.041421247340180045,
+ "r": 0.06317931201621363
},
"backward": false
},
{
"spline": {
"start": {
- "x": -14.084814730859556,
- "y": -20.316158415537927
+ "x": 5.531990571090186,
+ "y": -9.47771341297154
},
"control1": {
- "x": -13.872606313723427,
- "y": -20.560800938433022
+ "x": 5.338908247464632,
+ "y": -9.553434640201916
},
"control2": {
- "x": -13.888327076986158,
- "y": -20.869695182203493
+ "x": 5.1504265475349404,
+ "y": -9.513603623453617
},
"end": {
- "x": -13.592748723448585,
- "y": -20.737347861747374
+ "x": 5.192893258519078,
+ "y": -9.716608713064955
+ },
+ "a": 5.531990571090186,
+ "b": 5.338908247464632,
+ "c": 5.1504265475349404,
+ "d": 5.192893258519078,
+ "e": -9.47771341297154,
+ "f": -9.553434640201916,
+ "g": -9.513603623453617,
+ "h": -9.716608713064955,
+ "m": 0.22634778721796867,
+ "n": 0.004600623695862893,
+ "o": -0.19308232362555433,
+ "p": -0.3583883503383163,
+ "q": 0.1155522439786747,
+ "r": -0.07572122723037644
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 5.192893258519078,
+ "y": -9.716608713064955
+ },
+ "control1": {
+ "x": 5.5914994579791495,
+ "y": -11.622079736503828
},
- "a": -14.084814730859556,
- "b": -13.872606313723427,
- "c": -13.888327076986158,
- "d": -13.592748723448585,
- "e": -20.316158415537927,
- "f": -20.560800938433022,
- "g": -20.869695182203493,
- "h": -20.737347861747374,
- "m": 0.5392282971991591,
- "n": -0.2279291803988599,
- "o": 0.2122084171361287,
- "p": 0.5054932851019629,
- "q": -0.0642517208753759,
- "r": -0.24464252289509503
+ "control2": {
+ "x": 5.5914994579791495,
+ "y": -11.622079736503828
+ },
+ "end": {
+ "x": 5.990105657439221,
+ "y": -13.5275507599427
+ },
+ "a": 5.192893258519078,
+ "b": 5.5914994579791495,
+ "c": 5.5914994579791495,
+ "d": 5.990105657439221,
+ "e": -9.716608713064955,
+ "f": -11.622079736503828,
+ "g": -11.622079736503828,
+ "h": -13.5275507599427,
+ "m": 0.7972123989201432,
+ "n": -0.3986061994600716,
+ "o": 0.3986061994600716,
+ "p": -3.8109420468777433,
+ "q": 1.9054710234388725,
+ "r": -1.9054710234388725
},
"backward": false
},
{
"spline": {
"start": {
- "x": -13.592748723448585,
- "y": -20.737347861747374
+ "x": 5.990105657439221,
+ "y": -13.5275507599427
},
"control1": {
- "x": -6.319198824753343,
- "y": -17.48056390657917
+ "x": 6.0059140303081895,
+ "y": -13.60312007238776
},
"control2": {
- "x": -6.319198824753344,
- "y": -17.48056390657917
+ "x": 5.98958192326311,
+ "y": -13.634299196824482
},
"end": {
- "x": 0.9543510739418979,
- "y": -14.223779951410968
+ "x": 5.918457619700598,
+ "y": -13.66433183039084
+ },
+ "a": 5.990105657439221,
+ "b": 6.0059140303081895,
+ "c": 5.98958192326311,
+ "d": 5.918457619700598,
+ "e": -13.5275507599427,
+ "f": -13.60312007238776,
+ "g": -13.634299196824482,
+ "h": -13.66433183039084,
+ "m": -0.022651716603386163,
+ "n": -0.032140479914048115,
+ "o": 0.015808372868968412,
+ "p": -0.043243697137976156,
+ "q": 0.044390188008337716,
+ "r": -0.07556931244505982
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 5.918457619700598,
+ "y": -13.66433183039084
+ },
+ "control1": {
+ "x": 5.742439543858991,
+ "y": -13.738656443891765
+ },
+ "control2": {
+ "x": 5.579175771761254,
+ "y": -13.904606442846898
},
- "a": -13.592748723448585,
- "b": -6.319198824753343,
- "c": -6.319198824753344,
- "d": 0.9543510739418979,
- "e": -20.737347861747374,
- "f": -17.48056390657917,
- "g": -17.48056390657917,
- "h": -14.223779951410968,
- "m": 14.547099797390485,
- "n": -7.273549898695243,
- "o": 7.273549898695242,
- "p": 6.5135679103364055,
- "q": -3.2567839551682027,
- "r": 3.2567839551682027
+ "end": {
+ "x": 5.540053288514205,
+ "y": -13.71758788093491
+ },
+ "a": 5.918457619700598,
+ "b": 5.742439543858991,
+ "c": 5.579175771761254,
+ "d": 5.540053288514205,
+ "e": -13.66433183039084,
+ "f": -13.738656443891765,
+ "g": -13.904606442846898,
+ "h": -13.71758788093491,
+ "m": 0.11138698510681433,
+ "n": 0.01275430374387021,
+ "o": -0.17601807584160678,
+ "p": 0.4445939463213264,
+ "q": -0.09162538545420773,
+ "r": -0.07432461350092545
},
"backward": false
},
{
"spline": {
"start": {
- "x": 0.9543510739418979,
- "y": -14.223779951410968
+ "x": 5.540053288514205,
+ "y": -13.71758788093491
},
"control1": {
- "x": 1.0234957780985299,
- "y": -14.192819915909931
+ "x": 5.1401382673065426,
+ "y": -11.805860251556997
},
"control2": {
- "x": 1.058689036098147,
- "y": -14.203329211741773
+ "x": 5.1401382673065426,
+ "y": -11.805860251556997
},
"end": {
- "x": 1.099535264291583,
- "y": -14.267134380447008
+ "x": 4.74022324609888,
+ "y": -9.894132622179082
+ },
+ "a": 5.540053288514205,
+ "b": 5.1401382673065426,
+ "c": 5.1401382673065426,
+ "d": 4.74022324609888,
+ "e": -13.71758788093491,
+ "f": -11.805860251556997,
+ "g": -11.805860251556997,
+ "h": -9.894132622179082,
+ "m": -0.7998300424153246,
+ "n": 0.3999150212076623,
+ "o": -0.3999150212076623,
+ "p": 3.8234552587558284,
+ "q": -1.9117276293779142,
+ "r": 1.9117276293779142
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 4.74022324609888,
+ "y": -9.894132622179082
+ },
+ "control1": {
+ "x": 4.727006742075726,
+ "y": -9.830953310162869
+ },
+ "control2": {
+ "x": 4.686741691710771,
+ "y": -9.809195245486835
},
- "a": 0.9543510739418979,
- "b": 1.0234957780985299,
- "c": 1.058689036098147,
- "d": 1.099535264291583,
- "e": -14.223779951410968,
- "f": -14.192819915909931,
- "g": -14.203329211741773,
- "h": -14.267134380447008,
- "m": 0.03960441635083356,
- "n": -0.03395144615701495,
- "o": 0.06914470415663199,
- "p": -0.01182654154051832,
- "q": -0.04146933133287867,
- "r": 0.030960035501037098
+ "end": {
+ "x": 4.626650546249791,
+ "y": -9.832761231199793
+ },
+ "a": 4.74022324609888,
+ "b": 4.727006742075726,
+ "c": 4.686741691710771,
+ "d": 4.626650546249791,
+ "e": -9.894132622179082,
+ "f": -9.830953310162869,
+ "g": -9.809195245486835,
+ "h": -9.832761231199793,
+ "m": 0.007222451245775474,
+ "n": -0.027048546341799984,
+ "o": -0.013216504023154663,
+ "p": -0.0039028030488097443,
+ "q": -0.041421247340180045,
+ "r": 0.06317931201621363
},
"backward": false
},
{
"spline": {
"start": {
- "x": 1.099535264291583,
- "y": -14.267134380447008
+ "x": 4.626650546249791,
+ "y": -9.832761231199793
},
"control1": {
- "x": 1.2565557848181077,
- "y": -14.512413351002937
+ "x": 4.4335682226242366,
+ "y": -9.90848245843017
},
"control2": {
- "x": 1.5915270499426355,
- "y": -14.684875595533388
+ "x": 4.245086522694545,
+ "y": -9.868651441681871
},
"end": {
- "x": 1.325721909088244,
- "y": -14.803891747205228
+ "x": 4.287553233678683,
+ "y": -10.07165653129321
+ },
+ "a": 4.626650546249791,
+ "b": 4.4335682226242366,
+ "c": 4.245086522694545,
+ "d": 4.287553233678683,
+ "e": -9.832761231199793,
+ "f": -9.90848245843017,
+ "g": -9.868651441681871,
+ "h": -10.07165653129321,
+ "m": 0.2263477872179669,
+ "n": 0.004600623695862893,
+ "o": -0.19308232362555433,
+ "p": -0.35838835033830563,
+ "q": 0.1155522439786747,
+ "r": -0.07572122723037644
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 4.287553233678683,
+ "y": -10.07165653129321
+ },
+ "control1": {
+ "x": 4.688777076633936,
+ "y": -11.989640766610165
+ },
+ "control2": {
+ "x": 4.688777076633936,
+ "y": -11.989640766610165
},
- "a": 1.099535264291583,
- "b": 1.2565557848181077,
- "c": 1.5915270499426355,
- "d": 1.325721909088244,
- "e": -14.267134380447008,
- "f": -14.512413351002937,
- "g": -14.684875595533388,
- "h": -14.803891747205228,
- "m": -0.7787271505769222,
- "n": 0.17795074459800309,
- "o": 0.1570205205265247,
- "p": -0.0193706331668686,
- "q": 0.07281672602547928,
- "r": -0.24527897055592973
+ "end": {
+ "x": 5.090000919589189,
+ "y": -13.907625001927123
+ },
+ "a": 4.287553233678683,
+ "b": 4.688777076633936,
+ "c": 4.688777076633936,
+ "d": 5.090000919589189,
+ "e": -10.07165653129321,
+ "f": -11.989640766610165,
+ "g": -11.989640766610165,
+ "h": -13.907625001927123,
+ "m": 0.8024476859105061,
+ "n": -0.40122384295525304,
+ "o": 0.40122384295525304,
+ "p": -3.8359684706339134,
+ "q": 1.9179842353169558,
+ "r": -1.9179842353169558
},
"backward": false
},
{
"spline": {
"start": {
- "x": 1.325721909088244,
- "y": -14.803891747205228
+ "x": 5.090000919589189,
+ "y": -13.907625001927123
},
"control1": {
- "x": -5.900351875443958,
- "y": -18.039417932919253
+ "x": 5.105809292458157,
+ "y": -13.983194314372184
},
"control2": {
- "x": -5.900351875443956,
- "y": -18.03941793291925
+ "x": 5.089477185413078,
+ "y": -14.014373438808907
},
"end": {
- "x": -13.126425659976158,
- "y": -21.274944118633275
+ "x": 5.018352881850565,
+ "y": -14.044406072375264
+ },
+ "a": 5.090000919589189,
+ "b": 5.105809292458157,
+ "c": 5.089477185413078,
+ "d": 5.018352881850565,
+ "e": -13.907625001927123,
+ "f": -13.983194314372184,
+ "g": -14.014373438808907,
+ "h": -14.044406072375264,
+ "m": -0.022651716603389715,
+ "n": -0.032140479914047226,
+ "o": 0.015808372868968412,
+ "p": -0.04324369713797083,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 5.018352881850565,
+ "y": -14.044406072375264
+ },
+ "control1": {
+ "x": 4.842334806008958,
+ "y": -14.118730685876189
},
- "a": 1.325721909088244,
- "b": -5.900351875443958,
- "c": -5.900351875443956,
- "d": -13.126425659976158,
- "e": -14.803891747205228,
- "f": -18.039417932919253,
- "g": -18.03941793291925,
- "h": -21.274944118633275,
- "m": -14.452147569064406,
- "n": 7.226073784532204,
- "o": -7.226073784532202,
- "p": -6.471052371428058,
- "q": 3.235526185714029,
- "r": -3.2355261857140256
+ "control2": {
+ "x": 4.679071033911222,
+ "y": -14.284680684831322
+ },
+ "end": {
+ "x": 4.6399485506641724,
+ "y": -14.097662122919335
+ },
+ "a": 5.018352881850565,
+ "b": 4.842334806008958,
+ "c": 4.679071033911222,
+ "d": 4.6399485506641724,
+ "e": -14.044406072375264,
+ "f": -14.118730685876189,
+ "g": -14.284680684831322,
+ "h": -14.097662122919335,
+ "m": 0.1113869851068161,
+ "n": 0.01275430374387021,
+ "o": -0.17601807584160678,
+ "p": 0.4445939463213264,
+ "q": -0.09162538545420773,
+ "r": -0.07432461350092545
},
"backward": false
},
{
"spline": {
"start": {
- "x": -13.126425659976158,
- "y": -21.274944118633275
+ "x": 4.6399485506641724,
+ "y": -14.097662122919335
},
"control1": {
- "x": -13.180830556391726,
- "y": -21.299304299864808
+ "x": 4.237415885961329,
+ "y": -12.173421281663334
},
"control2": {
- "x": -13.191228219798703,
- "y": -21.34632141020708
+ "x": 4.237415885961329,
+ "y": -12.173421281663334
},
"end": {
- "x": -13.152168603914703,
- "y": -21.39135092930973
+ "x": 3.834883221258485,
+ "y": -10.249180440407335
+ },
+ "a": 4.6399485506641724,
+ "b": 4.237415885961329,
+ "c": 4.237415885961329,
+ "d": 3.834883221258485,
+ "e": -14.097662122919335,
+ "f": -12.173421281663334,
+ "g": -12.173421281663334,
+ "h": -10.249180440407335,
+ "m": -0.8050653294056875,
+ "n": 0.40253266470284377,
+ "o": -0.40253266470284377,
+ "p": 3.8484816825119985,
+ "q": -1.924240841256001,
+ "r": 1.924240841256001
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 3.834883221258485,
+ "y": -10.249180440407335
+ },
+ "control1": {
+ "x": 3.8216667172353302,
+ "y": -10.186001128391121
+ },
+ "control2": {
+ "x": 3.781401666870376,
+ "y": -10.164243063715087
},
- "a": -13.126425659976158,
- "b": -13.180830556391726,
- "c": -13.191228219798703,
- "d": -13.152168603914703,
- "e": -21.274944118633275,
- "f": -21.299304299864808,
- "g": -21.34632141020708,
- "h": -21.39135092930973,
- "m": 0.005450046282385657,
- "n": 0.044007233008590774,
- "o": -0.05440489641556745,
- "p": 0.02464452035035336,
- "q": -0.022656929110738133,
- "r": -0.024360181231532607
+ "end": {
+ "x": 3.7213105214093956,
+ "y": -10.187809049428045
+ },
+ "a": 3.834883221258485,
+ "b": 3.8216667172353302,
+ "c": 3.781401666870376,
+ "d": 3.7213105214093956,
+ "e": -10.249180440407335,
+ "f": -10.186001128391121,
+ "g": -10.164243063715087,
+ "h": -10.187809049428045,
+ "m": 0.00722245124577281,
+ "n": -0.02704854634179954,
+ "o": -0.013216504023154663,
+ "p": -0.0039028030488115206,
+ "q": -0.041421247340180045,
+ "r": 0.06317931201621363
},
"backward": false
},
{
"spline": {
"start": {
- "x": -13.152168603914703,
- "y": -21.39135092930973
+ "x": 3.7213105214093956,
+ "y": -10.187809049428045
},
"control1": {
- "x": -12.939960186778574,
- "y": -21.635993452204826
+ "x": 3.528228197783841,
+ "y": -10.263530276658422
},
"control2": {
- "x": -12.955680950041305,
- "y": -21.944887695975297
+ "x": 3.3397464978541502,
+ "y": -10.223699259910124
},
"end": {
- "x": -12.660102596503732,
- "y": -21.812540375519177
+ "x": 3.3822132088382872,
+ "y": -10.426704349521462
+ },
+ "a": 3.7213105214093956,
+ "b": 3.528228197783841,
+ "c": 3.3397464978541502,
+ "d": 3.3822132088382872,
+ "e": -10.187809049428045,
+ "f": -10.263530276658422,
+ "g": -10.223699259910124,
+ "h": -10.426704349521462,
+ "m": 0.22634778721796334,
+ "n": 0.0046006236958642255,
+ "o": -0.19308232362555477,
+ "p": -0.35838835033831096,
+ "q": 0.1155522439786747,
+ "r": -0.07572122723037644
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 3.3822132088382872,
+ "y": -10.426704349521462
},
- "a": -13.152168603914703,
- "b": -12.939960186778574,
- "c": -12.955680950041305,
- "d": -12.660102596503732,
- "e": -21.39135092930973,
- "f": -21.635993452204826,
- "g": -21.944887695975297,
- "h": -21.812540375519177,
- "m": 0.5392282971991662,
- "n": -0.2279291803988599,
- "o": 0.2122084171361287,
- "p": 0.50549328510197,
- "q": -0.0642517208753759,
- "r": -0.24464252289509503
+ "control1": {
+ "x": 3.7860546952887217,
+ "y": -12.357201796716504
+ },
+ "control2": {
+ "x": 3.7860546952887217,
+ "y": -12.357201796716504
+ },
+ "end": {
+ "x": 4.189896181739156,
+ "y": -14.287699243911547
+ },
+ "a": 3.3822132088382872,
+ "b": 3.7860546952887217,
+ "c": 3.7860546952887217,
+ "d": 4.189896181739156,
+ "e": -10.426704349521462,
+ "f": -12.357201796716504,
+ "g": -12.357201796716504,
+ "h": -14.287699243911547,
+ "m": 0.807682972900869,
+ "n": -0.4038414864504345,
+ "o": 0.4038414864504345,
+ "p": -3.8609948943900854,
+ "q": 1.9304974471950427,
+ "r": -1.9304974471950427
},
"backward": false
},
{
"spline": {
"start": {
- "x": -12.660102596503732,
- "y": -21.812540375519177
+ "x": 4.189896181739156,
+ "y": -14.287699243911547
},
"control1": {
- "x": -5.481504926134571,
- "y": -18.598271959259332
+ "x": 4.205704554608125,
+ "y": -14.363268556356608
},
"control2": {
- "x": -5.481504926134572,
- "y": -18.598271959259332
+ "x": 4.189372447563046,
+ "y": -14.39444768079333
},
"end": {
- "x": 1.6970927442345891,
- "y": -15.384003542999485
+ "x": 4.118248144000533,
+ "y": -14.424480314359688
+ },
+ "a": 4.189896181739156,
+ "b": 4.205704554608125,
+ "c": 4.189372447563046,
+ "d": 4.118248144000533,
+ "e": -14.287699243911547,
+ "f": -14.363268556356608,
+ "g": -14.39444768079333,
+ "h": -14.424480314359688,
+ "m": -0.022651716603389715,
+ "n": -0.032140479914047226,
+ "o": 0.015808372868968412,
+ "p": -0.04324369713797793,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 4.118248144000533,
+ "y": -14.424480314359688
+ },
+ "control1": {
+ "x": 3.942230068158926,
+ "y": -14.498804927860613
+ },
+ "control2": {
+ "x": 3.7789662960611894,
+ "y": -14.664754926815744
},
- "a": -12.660102596503732,
- "b": -5.481504926134571,
- "c": -5.481504926134572,
- "d": 1.6970927442345891,
- "e": -21.812540375519177,
- "f": -18.598271959259332,
- "g": -18.598271959259332,
- "h": -15.384003542999485,
- "m": 14.35719534073832,
- "n": -7.178597670369162,
- "o": 7.178597670369161,
- "p": 6.42853683251969,
- "q": -3.214268416259845,
- "r": 3.214268416259845
+ "end": {
+ "x": 3.73984381281414,
+ "y": -14.477736364903757
+ },
+ "a": 4.118248144000533,
+ "b": 3.942230068158926,
+ "c": 3.7789662960611894,
+ "d": 3.73984381281414,
+ "e": -14.424480314359688,
+ "f": -14.498804927860613,
+ "g": -14.664754926815744,
+ "h": -14.477736364903757,
+ "m": 0.1113869851068161,
+ "n": 0.01275430374387021,
+ "o": -0.17601807584160678,
+ "p": 0.4445939463213229,
+ "q": -0.09162538545420595,
+ "r": -0.07432461350092545
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 3.73984381281414,
+ "y": -14.477736364903757
+ },
+ "control1": {
+ "x": 3.3346935046161144,
+ "y": -12.540982311769673
+ },
+ "control2": {
+ "x": 3.3346935046161144,
+ "y": -12.540982311769673
+ },
+ "end": {
+ "x": 2.9295431964180887,
+ "y": -10.604228258635588
+ },
+ "a": 3.73984381281414,
+ "b": 3.3346935046161144,
+ "c": 3.3346935046161144,
+ "d": 2.9295431964180887,
+ "e": -14.477736364903757,
+ "f": -12.540982311769673,
+ "g": -12.540982311769673,
+ "h": -10.604228258635588,
+ "m": -0.8103006163960513,
+ "n": 0.40515030819802567,
+ "o": -0.40515030819802567,
+ "p": 3.8735081062681704,
+ "q": -1.9367540531340843,
+ "r": 1.9367540531340843
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 2.9295431964180887,
+ "y": -10.604228258635588
+ },
+ "control1": {
+ "x": 2.916326692394934,
+ "y": -10.541048946619375
+ },
+ "control2": {
+ "x": 2.87606164202998,
+ "y": -10.519290881943341
+ },
+ "end": {
+ "x": 2.8159704965689993,
+ "y": -10.5428568676563
+ },
+ "a": 2.9295431964180887,
+ "b": 2.916326692394934,
+ "c": 2.87606164202998,
+ "d": 2.8159704965689993,
+ "e": -10.604228258635588,
+ "f": -10.541048946619375,
+ "g": -10.519290881943341,
+ "h": -10.5428568676563,
+ "m": 0.00722245124577281,
+ "n": -0.02704854634179954,
+ "o": -0.013216504023154663,
+ "p": -0.003902803048807968,
+ "q": -0.041421247340180045,
+ "r": 0.06317931201621363
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 2.8159704965689993,
+ "y": -10.5428568676563
+ },
+ "control1": {
+ "x": 2.6228881729434446,
+ "y": -10.618578094886676
+ },
+ "control2": {
+ "x": 2.4344064730137536,
+ "y": -10.578747078138377
+ },
+ "end": {
+ "x": 2.476873183997891,
+ "y": -10.781752167749715
+ },
+ "a": 2.8159704965689993,
+ "b": 2.6228881729434446,
+ "c": 2.4344064730137536,
+ "d": 2.476873183997891,
+ "e": -10.5428568676563,
+ "f": -10.618578094886676,
+ "g": -10.578747078138377,
+ "h": -10.781752167749715,
+ "m": 0.2263477872179651,
+ "n": 0.004600623695863781,
+ "o": -0.19308232362555477,
+ "p": -0.35838835033831096,
+ "q": 0.1155522439786747,
+ "r": -0.07572122723037644
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 2.476873183997891,
+ "y": -10.781752167749715
+ },
+ "control1": {
+ "x": 2.8833323139435074,
+ "y": -12.724762826822843
+ },
+ "control2": {
+ "x": 2.8833323139435074,
+ "y": -12.724762826822841
+ },
+ "end": {
+ "x": 3.289791443889124,
+ "y": -14.66777348589597
+ },
+ "a": 2.476873183997891,
+ "b": 2.8833323139435074,
+ "c": 2.8833323139435074,
+ "d": 3.289791443889124,
+ "e": -10.781752167749715,
+ "f": -12.724762826822843,
+ "g": -12.724762826822841,
+ "h": -14.66777348589597,
+ "m": 0.8129182598912328,
+ "n": -0.4064591299456164,
+ "o": 0.4064591299456164,
+ "p": -3.8860213181462626,
+ "q": 1.9430106590731295,
+ "r": -1.9430106590731278
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 3.289791443889124,
+ "y": -14.66777348589597
+ },
+ "control1": {
+ "x": 3.3055998167580922,
+ "y": -14.743342798341029
+ },
+ "control2": {
+ "x": 3.289267709713013,
+ "y": -14.774521922777751
+ },
+ "end": {
+ "x": 3.2181434061505003,
+ "y": -14.804554556344108
+ },
+ "a": 3.289791443889124,
+ "b": 3.3055998167580922,
+ "c": 3.289267709713013,
+ "d": 3.2181434061505003,
+ "e": -14.66777348589597,
+ "f": -14.743342798341029,
+ "g": -14.774521922777751,
+ "h": -14.804554556344108,
+ "m": -0.02265171660338705,
+ "n": -0.03214047991404767,
+ "o": 0.015808372868968412,
+ "p": -0.04324369713796905,
+ "q": 0.044390188008337716,
+ "r": -0.07556931244505982
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 3.2181434061505003,
+ "y": -14.804554556344108
+ },
+ "control1": {
+ "x": 3.042125330308893,
+ "y": -14.878879169845034
+ },
+ "control2": {
+ "x": 2.878861558211156,
+ "y": -15.044829168800167
+ },
+ "end": {
+ "x": 2.8397390749641067,
+ "y": -14.85781060688818
+ },
+ "a": 3.2181434061505003,
+ "b": 3.042125330308893,
+ "c": 2.878861558211156,
+ "d": 2.8397390749641067,
+ "e": -14.804554556344108,
+ "f": -14.878879169845034,
+ "g": -15.044829168800167,
+ "h": -14.85781060688818,
+ "m": 0.111386985106817,
+ "n": 0.01275430374387021,
+ "o": -0.17601807584160722,
+ "p": 0.44459394632133353,
+ "q": -0.09162538545420773,
+ "r": -0.07432461350092545
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 2.8397390749641067,
+ "y": -14.85781060688818
+ },
+ "control1": {
+ "x": 2.4319711232709,
+ "y": -12.908543341876012
+ },
+ "control2": {
+ "x": 2.431971123270899,
+ "y": -12.90854334187601
+ },
+ "end": {
+ "x": 2.0242031715776925,
+ "y": -10.959276076863842
+ },
+ "a": 2.8397390749641067,
+ "b": 2.4319711232709,
+ "c": 2.431971123270899,
+ "d": 2.0242031715776925,
+ "e": -14.85781060688818,
+ "f": -12.908543341876012,
+ "g": -12.90854334187601,
+ "h": -10.959276076863842,
+ "m": -0.8155359033864116,
+ "n": 0.4077679516932058,
+ "o": -0.4077679516932067,
+ "p": 3.898534530024328,
+ "q": -1.9492672650121659,
+ "r": 1.9492672650121676
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 2.0242031715776925,
+ "y": -10.959276076863842
+ },
+ "control1": {
+ "x": 2.010986667554538,
+ "y": -10.89609676484763
+ },
+ "control2": {
+ "x": 1.9707216171895836,
+ "y": -10.874338700171595
+ },
+ "end": {
+ "x": 1.9106304717286033,
+ "y": -10.897904685884553
+ },
+ "a": 2.0242031715776925,
+ "b": 2.010986667554538,
+ "c": 1.9707216171895836,
+ "d": 1.9106304717286033,
+ "e": -10.959276076863842,
+ "f": -10.89609676484763,
+ "g": -10.874338700171595,
+ "h": -10.897904685884553,
+ "m": 0.007222451245773698,
+ "n": -0.02704854634179954,
+ "o": -0.013216504023154663,
+ "p": -0.003902803048818626,
+ "q": -0.04142124734017649,
+ "r": 0.06317931201621185
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 1.9106304717286033,
+ "y": -10.897904685884553
+ },
+ "control1": {
+ "x": 1.7175481481030481,
+ "y": -10.97362591311493
+ },
+ "control2": {
+ "x": 1.5290664481733567,
+ "y": -10.93379489636663
+ },
+ "end": {
+ "x": 1.571533159157494,
+ "y": -11.13679998597797
+ },
+ "a": 1.9106304717286033,
+ "b": 1.7175481481030481,
+ "c": 1.5290664481733567,
+ "d": 1.571533159157494,
+ "e": -10.897904685884553,
+ "f": -10.97362591311493,
+ "g": -10.93379489636663,
+ "h": -11.13679998597797,
+ "m": 0.2263477872179649,
+ "n": 0.004600623695863781,
+ "o": -0.19308232362555522,
+ "p": -0.3583883503383145,
+ "q": 0.11555224397867647,
+ "r": -0.07572122723037644
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 1.571533159157494,
+ "y": -11.13679998597797
+ },
+ "control1": {
+ "x": 1.9806099325982918,
+ "y": -13.09232385692918
+ },
+ "control2": {
+ "x": 1.9806099325982918,
+ "y": -13.09232385692918
+ },
+ "end": {
+ "x": 2.3896867060390896,
+ "y": -15.047847727880393
+ },
+ "a": 1.571533159157494,
+ "b": 1.9806099325982918,
+ "c": 1.9806099325982918,
+ "d": 2.3896867060390896,
+ "e": -11.13679998597797,
+ "f": -13.09232385692918,
+ "g": -13.09232385692918,
+ "h": -15.047847727880393,
+ "m": 0.8181535468815957,
+ "n": -0.40907677344079785,
+ "o": 0.40907677344079785,
+ "p": -3.9110477419024257,
+ "q": 1.955523870951211,
+ "r": -1.955523870951211
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 2.3896867060390896,
+ "y": -15.047847727880393
+ },
+ "control1": {
+ "x": 2.405495078908058,
+ "y": -15.123417040325455
+ },
+ "control2": {
+ "x": 2.3891629718629797,
+ "y": -15.154596164762177
+ },
+ "end": {
+ "x": 2.3180386683004666,
+ "y": -15.184628798328534
+ },
+ "a": 2.3896867060390896,
+ "b": 2.405495078908058,
+ "c": 2.3891629718629797,
+ "d": 2.3180386683004666,
+ "e": -15.047847727880393,
+ "f": -15.123417040325455,
+ "g": -15.154596164762177,
+ "h": -15.184628798328534,
+ "m": -0.02265171660338705,
+ "n": -0.03214047991404678,
+ "o": 0.015808372868968412,
+ "p": -0.04324369713796905,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 2.3180386683004666,
+ "y": -15.184628798328534
+ },
+ "control1": {
+ "x": 2.1420205924588593,
+ "y": -15.25895341182946
+ },
+ "control2": {
+ "x": 1.9787568203611219,
+ "y": -15.424903410784593
+ },
+ "end": {
+ "x": 1.9396343371140725,
+ "y": -15.237884848872605
+ },
+ "a": 2.3180386683004666,
+ "b": 2.1420205924588593,
+ "c": 1.9787568203611219,
+ "d": 1.9396343371140725,
+ "e": -15.184628798328534,
+ "f": -15.25895341182946,
+ "g": -15.424903410784593,
+ "h": -15.237884848872605,
+ "m": 0.11138698510681833,
+ "n": 0.012754303743869766,
+ "o": -0.17601807584160722,
+ "p": 0.4445939463213282,
+ "q": -0.09162538545420773,
+ "r": -0.07432461350092545
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 1.9396343371140725,
+ "y": -15.237884848872605
+ },
+ "control1": {
+ "x": 1.529248741925684,
+ "y": -13.27610437198235
+ },
+ "control2": {
+ "x": 1.529248741925684,
+ "y": -13.27610437198235
+ },
+ "end": {
+ "x": 1.1188631467372954,
+ "y": -11.314323895092096
+ },
+ "a": 1.9396343371140725,
+ "b": 1.529248741925684,
+ "c": 1.529248741925684,
+ "d": 1.1188631467372954,
+ "e": -15.237884848872605,
+ "f": -13.27610437198235,
+ "g": -13.27610437198235,
+ "h": -11.314323895092096,
+ "m": -0.8207711903767771,
+ "n": 0.4103855951883886,
+ "o": -0.4103855951883886,
+ "p": 3.9235609537805107,
+ "q": -1.9617804768902545,
+ "r": 1.9617804768902545
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 1.1188631467372954,
+ "y": -11.314323895092096
+ },
+ "control1": {
+ "x": 1.1056466427141407,
+ "y": -11.251144583075884
+ },
+ "control2": {
+ "x": 1.0653815923491867,
+ "y": -11.22938651839985
+ },
+ "end": {
+ "x": 1.0052904468882065,
+ "y": -11.252952504112807
+ },
+ "a": 1.1188631467372954,
+ "b": 1.1056466427141407,
+ "c": 1.0653815923491867,
+ "d": 1.0052904468882065,
+ "e": -11.314323895092096,
+ "f": -11.251144583075884,
+ "g": -11.22938651839985,
+ "h": -11.252952504112807,
+ "m": 0.007222451245773254,
+ "n": -0.027048546341799318,
+ "o": -0.013216504023154663,
+ "p": -0.0039028030488150733,
+ "q": -0.04142124734017649,
+ "r": 0.06317931201621185
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 1.0052904468882065,
+ "y": -11.252952504112807
+ },
+ "control1": {
+ "x": 0.8122081232626508,
+ "y": -11.328673731343184
+ },
+ "control2": {
+ "x": 0.6237264233329585,
+ "y": -11.288842714594884
+ },
+ "end": {
+ "x": 0.6661931343170959,
+ "y": -11.491847804206223
+ },
+ "a": 1.0052904468882065,
+ "b": 0.8122081232626508,
+ "c": 0.6237264233329585,
+ "d": 0.6661931343170959,
+ "e": -11.252952504112807,
+ "f": -11.328673731343184,
+ "g": -11.288842714594884,
+ "h": -11.491847804206223,
+ "m": 0.22634778721796645,
+ "n": 0.004600623695863337,
+ "o": -0.19308232362555566,
+ "p": -0.3583883503383145,
+ "q": 0.11555224397867647,
+ "r": -0.07572122723037644
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 0.6661931343170959,
+ "y": -11.491847804206223
+ },
+ "control1": {
+ "x": 1.0778875512530761,
+ "y": -13.45988488703552
+ },
+ "control2": {
+ "x": 1.0778875512530761,
+ "y": -13.45988488703552
+ },
+ "end": {
+ "x": 1.4895819681890563,
+ "y": -15.427921969864816
+ },
+ "a": 0.6661931343170959,
+ "b": 1.0778875512530761,
+ "c": 1.0778875512530761,
+ "d": 1.4895819681890563,
+ "e": -11.491847804206223,
+ "f": -13.45988488703552,
+ "g": -13.45988488703552,
+ "h": -15.427921969864816,
+ "m": 0.8233888338719604,
+ "n": -0.4116944169359802,
+ "o": 0.4116944169359802,
+ "p": -3.9360741656585922,
+ "q": 1.9680370828292961,
+ "r": -1.9680370828292961
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 1.4895819681890563,
+ "y": -15.427921969864816
+ },
+ "control1": {
+ "x": 1.5053903410580247,
+ "y": -15.503491282309877
+ },
+ "control2": {
+ "x": 1.4890582340129461,
+ "y": -15.5346704067466
+ },
+ "end": {
+ "x": 1.417933930450433,
+ "y": -15.564703040312956
+ },
+ "a": 1.4895819681890563,
+ "b": 1.5053903410580247,
+ "c": 1.4890582340129461,
+ "d": 1.417933930450433,
+ "e": -15.427921969864816,
+ "f": -15.503491282309877,
+ "g": -15.5346704067466,
+ "h": -15.564703040312956,
+ "m": -0.02265171660338794,
+ "n": -0.032140479914047004,
+ "o": 0.015808372868968412,
+ "p": -0.04324369713797083,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 1.417933930450433,
+ "y": -15.564703040312956
+ },
+ "control1": {
+ "x": 1.241915854608826,
+ "y": -15.639027653813882
+ },
+ "control2": {
+ "x": 1.0786520825110888,
+ "y": -15.804977652769015
+ },
+ "end": {
+ "x": 1.0395295992640392,
+ "y": -15.617959090857028
+ },
+ "a": 1.417933930450433,
+ "b": 1.241915854608826,
+ "c": 1.0786520825110888,
+ "d": 1.0395295992640392,
+ "e": -15.564703040312956,
+ "f": -15.639027653813882,
+ "g": -15.804977652769015,
+ "h": -15.617959090857028,
+ "m": 0.11138698510681766,
+ "n": 0.012754303743869766,
+ "o": -0.176018075841607,
+ "p": 0.44459394632133353,
+ "q": -0.09162538545420773,
+ "r": -0.07432461350092545
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 1.0395295992640392,
+ "y": -15.617959090857028
+ },
+ "control1": {
+ "x": 0.6265263605804682,
+ "y": -13.643665402088688
+ },
+ "control2": {
+ "x": 0.6265263605804684,
+ "y": -13.643665402088688
+ },
+ "end": {
+ "x": 0.21352312189689737,
+ "y": -11.669371713320349
+ },
+ "a": 1.0395295992640392,
+ "b": 0.6265263605804682,
+ "c": 0.6265263605804684,
+ "d": 0.21352312189689737,
+ "e": -15.617959090857028,
+ "f": -13.643665402088688,
+ "g": -13.643665402088688,
+ "h": -11.669371713320349,
+ "m": -0.8260064773671427,
+ "n": 0.41300323868357125,
+ "o": -0.41300323868357103,
+ "p": 3.948587377536679,
+ "q": -1.9742936887683395,
+ "r": 1.9742936887683395
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 0.21352312189689737,
+ "y": -11.669371713320349
+ },
+ "control1": {
+ "x": 0.20030661787374268,
+ "y": -11.606192401304137
+ },
+ "control2": {
+ "x": 0.1600415675087885,
+ "y": -11.584434336628101
+ },
+ "end": {
+ "x": 0.09995042204780843,
+ "y": -11.60800032234106
+ },
+ "a": 0.21352312189689737,
+ "b": 0.20030661787374268,
+ "c": 0.1600415675087885,
+ "d": 0.09995042204780843,
+ "e": -11.669371713320349,
+ "f": -11.606192401304137,
+ "g": -11.584434336628101,
+ "h": -11.60800032234106,
+ "m": 0.007222451245773587,
+ "n": -0.027048546341799484,
+ "o": -0.01321650402315469,
+ "p": -0.0039028030488168497,
+ "q": -0.04142124734017649,
+ "r": 0.06317931201621185
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 0.09995042204780843,
+ "y": -11.60800032234106
+ },
+ "control1": {
+ "x": -0.093131901577747,
+ "y": -11.683721549571437
+ },
+ "control2": {
+ "x": -0.28161360150743864,
+ "y": -11.643890532823137
+ },
+ "end": {
+ "x": -0.23914689052330118,
+ "y": -11.846895622434477
+ },
+ "a": 0.09995042204780843,
+ "b": -0.093131901577747,
+ "c": -0.28161360150743864,
+ "d": -0.23914689052330118,
+ "e": -11.60800032234106,
+ "f": -11.683721549571437,
+ "g": -11.643890532823137,
+ "h": -11.846895622434477,
+ "m": 0.22634778721796525,
+ "n": 0.004600623695863809,
+ "o": -0.19308232362555544,
+ "p": -0.3583883503383163,
+ "q": 0.11555224397867825,
+ "r": -0.07572122723037822
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -0.23914689052330118,
+ "y": -11.846895622434477
+ },
+ "control1": {
+ "x": 0.1751651699078604,
+ "y": -13.827445917141858
+ },
+ "control2": {
+ "x": 0.17516516990786052,
+ "y": -13.827445917141858
+ },
+ "end": {
+ "x": 0.5894772303390221,
+ "y": -15.80799621184924
+ },
+ "a": -0.23914689052330118,
+ "b": 0.1751651699078604,
+ "c": 0.17516516990786052,
+ "d": 0.5894772303390221,
+ "e": -11.846895622434477,
+ "f": -13.827445917141858,
+ "g": -13.827445917141858,
+ "h": -15.80799621184924,
+ "m": 0.8286241208623228,
+ "n": -0.4143120604311615,
+ "o": 0.4143120604311616,
+ "p": -3.9611005894147624,
+ "q": 1.9805502947073812,
+ "r": -1.9805502947073812
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 0.5894772303390221,
+ "y": -15.80799621184924
+ },
+ "control1": {
+ "x": 0.6052856032079905,
+ "y": -15.883565524294301
+ },
+ "control2": {
+ "x": 0.5889534961629119,
+ "y": -15.914744648731023
+ },
+ "end": {
+ "x": 0.5178291926003988,
+ "y": -15.94477728229738
+ },
+ "a": 0.5894772303390221,
+ "b": 0.6052856032079905,
+ "c": 0.5889534961629119,
+ "d": 0.5178291926003988,
+ "e": -15.80799621184924,
+ "f": -15.883565524294301,
+ "g": -15.914744648731023,
+ "h": -15.94477728229738,
+ "m": -0.022651716603387495,
+ "n": -0.032140479914047004,
+ "o": 0.015808372868968412,
+ "p": -0.04324369713797793,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 0.5178291926003988,
+ "y": -15.94477728229738
+ },
+ "control1": {
+ "x": 0.34181111675879183,
+ "y": -16.019101895798308
+ },
+ "control2": {
+ "x": 0.17854734466105446,
+ "y": -16.18505189475344
+ },
+ "end": {
+ "x": 0.139424861414005,
+ "y": -15.998033332841452
+ },
+ "a": 0.5178291926003988,
+ "b": 0.34181111675879183,
+ "c": 0.17854734466105446,
+ "d": 0.139424861414005,
+ "e": -15.94477728229738,
+ "f": -16.019101895798308,
+ "g": -16.18505189475344,
+ "h": -15.998033332841452,
+ "m": 0.11138698510681833,
+ "n": 0.012754303743869655,
+ "o": -0.176018075841607,
+ "p": 0.4445939463213193,
+ "q": -0.09162538545420418,
+ "r": -0.07432461350092723
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 0.139424861414005,
+ "y": -15.998033332841452
+ },
+ "control1": {
+ "x": -0.2761960207647475,
+ "y": -14.011226432195027
+ },
+ "control2": {
+ "x": -0.27619602076474725,
+ "y": -14.011226432195027
+ },
+ "end": {
+ "x": -0.6918169029434997,
+ "y": -12.024419531548602
+ },
+ "a": 0.139424861414005,
+ "b": -0.2761960207647475,
+ "c": -0.27619602076474725,
+ "d": -0.6918169029434997,
+ "e": -15.998033332841452,
+ "f": -14.011226432195027,
+ "g": -14.011226432195027,
+ "h": -12.024419531548602,
+ "m": -0.8312417643575054,
+ "n": 0.4156208821787527,
+ "o": -0.4156208821787525,
+ "p": 3.9736138012928492,
+ "q": -1.9868069006464246,
+ "r": 1.9868069006464246
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -0.6918169029434997,
+ "y": -12.024419531548602
+ },
+ "control1": {
+ "x": -0.7050334069666544,
+ "y": -11.96124021953239
+ },
+ "control2": {
+ "x": -0.7452984573316086,
+ "y": -11.939482154856355
+ },
+ "end": {
+ "x": -0.8053896027925886,
+ "y": -11.963048140569313
+ },
+ "a": -0.6918169029434997,
+ "b": -0.7050334069666544,
+ "c": -0.7452984573316086,
+ "d": -0.8053896027925886,
+ "e": -12.024419531548602,
+ "f": -11.96124021953239,
+ "g": -11.939482154856355,
+ "h": -11.963048140569313,
+ "m": 0.007222451245773698,
+ "n": -0.02704854634179954,
+ "o": -0.013216504023154663,
+ "p": -0.0039028030488168497,
+ "q": -0.04142124734017649,
+ "r": 0.06317931201621185
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -0.8053896027925886,
+ "y": -11.963048140569313
+ },
+ "control1": {
+ "x": -0.9984719264181443,
+ "y": -12.038769367799691
+ },
+ "control2": {
+ "x": -1.1869536263478362,
+ "y": -11.998938351051391
+ },
+ "end": {
+ "x": -1.1444869153636987,
+ "y": -12.201943440662731
+ },
+ "a": -0.8053896027925886,
+ "b": -0.9984719264181443,
+ "c": -1.1869536263478362,
+ "d": -1.1444869153636987,
+ "e": -11.963048140569313,
+ "f": -12.038769367799691,
+ "g": -11.998938351051391,
+ "h": -12.201943440662731,
+ "m": 0.22634778721796556,
+ "n": 0.004600623695863781,
+ "o": -0.19308232362555566,
+ "p": -0.3583883503383234,
+ "q": 0.11555224397867825,
+ "r": -0.07572122723037822
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -1.1444869153636987,
+ "y": -12.201943440662731
+ },
+ "control1": {
+ "x": -0.7275572114373554,
+ "y": -14.195006947248197
+ },
+ "control2": {
+ "x": -0.7275572114373554,
+ "y": -14.195006947248197
+ },
+ "end": {
+ "x": -0.3106275075110121,
+ "y": -16.188070453833664
+ },
+ "a": -1.1444869153636987,
+ "b": -0.7275572114373554,
+ "c": -0.7275572114373554,
+ "d": -0.3106275075110121,
+ "e": -12.201943440662731,
+ "f": -14.195006947248197,
+ "g": -14.195006947248197,
+ "h": -16.188070453833664,
+ "m": 0.8338594078526866,
+ "n": -0.4169297039263433,
+ "o": 0.4169297039263433,
+ "p": -3.9861270131709325,
+ "q": 1.9930635065854663,
+ "r": -1.9930635065854663
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -0.3106275075110121,
+ "y": -16.188070453833664
+ },
+ "control1": {
+ "x": -0.29481913464204373,
+ "y": -16.263639766278725
+ },
+ "control2": {
+ "x": -0.3111512416871223,
+ "y": -16.294818890715447
+ },
+ "end": {
+ "x": -0.38227554524963536,
+ "y": -16.324851524281804
+ },
+ "a": -0.3106275075110121,
+ "b": -0.29481913464204373,
+ "c": -0.3111512416871223,
+ "d": -0.38227554524963536,
+ "e": -16.188070453833664,
+ "f": -16.263639766278725,
+ "g": -16.294818890715447,
+ "h": -16.324851524281804,
+ "m": -0.022651716603387495,
+ "n": -0.03214047991404695,
+ "o": 0.015808372868968357,
+ "p": -0.04324369713797083,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -0.38227554524963536,
+ "y": -16.324851524281804
+ },
+ "control1": {
+ "x": -0.5582936210912419,
+ "y": -16.399176137782728
+ },
+ "control2": {
+ "x": -0.7215573931889789,
+ "y": -16.565126136737863
+ },
+ "end": {
+ "x": -0.7606798764360283,
+ "y": -16.378107574825876
+ },
+ "a": -0.38227554524963536,
+ "b": -0.5582936210912419,
+ "c": -0.7215573931889789,
+ "d": -0.7606798764360283,
+ "e": -16.324851524281804,
+ "f": -16.399176137782728,
+ "g": -16.565126136737863,
+ "h": -16.378107574825876,
+ "m": 0.1113869851068181,
+ "n": 0.012754303743869544,
+ "o": -0.17601807584160656,
+ "p": 0.44459394632134064,
+ "q": -0.09162538545421128,
+ "r": -0.07432461350092368
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -0.7606798764360283,
+ "y": -16.378107574825876
+ },
+ "control1": {
+ "x": -1.1789184021099626,
+ "y": -14.378787462301366
+ },
+ "control2": {
+ "x": -1.1789184021099626,
+ "y": -14.378787462301366
+ },
+ "end": {
+ "x": -1.5971569277838968,
+ "y": -12.379467349776856
+ },
+ "a": -0.7606798764360283,
+ "b": -1.1789184021099626,
+ "c": -1.1789184021099626,
+ "d": -1.5971569277838968,
+ "e": -16.378107574825876,
+ "f": -14.378787462301366,
+ "g": -14.378787462301366,
+ "h": -12.379467349776856,
+ "m": -0.8364770513478685,
+ "n": 0.41823852567393427,
+ "o": -0.41823852567393427,
+ "p": 3.9986402250490194,
+ "q": -1.9993201125245097,
+ "r": 1.9993201125245097
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -1.5971569277838968,
+ "y": -12.379467349776856
+ },
+ "control1": {
+ "x": -1.6103734318070515,
+ "y": -12.316288037760645
+ },
+ "control2": {
+ "x": -1.6506384821720057,
+ "y": -12.29452997308461
+ },
+ "end": {
+ "x": -1.710729627632986,
+ "y": -12.318095958797567
+ },
+ "a": -1.5971569277838968,
+ "b": -1.6103734318070515,
+ "c": -1.6506384821720057,
+ "d": -1.710729627632986,
+ "e": -12.379467349776856,
+ "f": -12.316288037760645,
+ "g": -12.29452997308461,
+ "h": -12.318095958797567,
+ "m": 0.00722245124577281,
+ "n": -0.02704854634179954,
+ "o": -0.013216504023154663,
+ "p": -0.0039028030488168497,
+ "q": -0.04142124734017649,
+ "r": 0.06317931201621185
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -1.710729627632986,
+ "y": -12.318095958797567
+ },
+ "control1": {
+ "x": -1.903811951258541,
+ "y": -12.393817186027944
+ },
+ "control2": {
+ "x": -2.0922936511882324,
+ "y": -12.353986169279645
+ },
+ "end": {
+ "x": -2.049826940204095,
+ "y": -12.556991258890983
+ },
+ "a": -1.710729627632986,
+ "b": -1.903811951258541,
+ "c": -2.0922936511882324,
+ "d": -2.049826940204095,
+ "e": -12.318095958797567,
+ "f": -12.393817186027944,
+ "g": -12.353986169279645,
+ "h": -12.556991258890983,
+ "m": 0.22634778721796578,
+ "n": 0.004600623695863559,
+ "o": -0.193082323625555,
+ "p": -0.35838835033830563,
+ "q": 0.1155522439786747,
+ "r": -0.07572122723037644
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -2.049826940204095,
+ "y": -12.556991258890983
+ },
+ "control1": {
+ "x": -1.63027959278257,
+ "y": -14.562567977354536
+ },
+ "control2": {
+ "x": -1.63027959278257,
+ "y": -14.562567977354536
+ },
+ "end": {
+ "x": -1.210732245361045,
+ "y": -16.568144695818088
+ },
+ "a": -2.049826940204095,
+ "b": -1.63027959278257,
+ "c": -1.63027959278257,
+ "d": -1.210732245361045,
+ "e": -12.556991258890983,
+ "f": -14.562567977354536,
+ "g": -14.562567977354536,
+ "h": -16.568144695818088,
+ "m": 0.83909469484305,
+ "n": -0.419547347421525,
+ "o": 0.419547347421525,
+ "p": -4.0111534369271045,
+ "q": 2.005576718463553,
+ "r": -2.005576718463553
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -1.210732245361045,
+ "y": -16.568144695818088
+ },
+ "control1": {
+ "x": -1.1949238724920765,
+ "y": -16.64371400826315
+ },
+ "control2": {
+ "x": -1.2112559795371551,
+ "y": -16.67489313269987
+ },
+ "end": {
+ "x": -1.2823802830996682,
+ "y": -16.70492576626623
+ },
+ "a": -1.210732245361045,
+ "b": -1.1949238724920765,
+ "c": -1.2112559795371551,
+ "d": -1.2823802830996682,
+ "e": -16.568144695818088,
+ "f": -16.64371400826315,
+ "g": -16.67489313269987,
+ "h": -16.70492576626623,
+ "m": -0.02265171660338705,
+ "n": -0.032140479914047004,
+ "o": 0.015808372868968412,
+ "p": -0.04324369713797793,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -1.2823802830996682,
+ "y": -16.70492576626623
+ },
+ "control1": {
+ "x": -1.458398358941275,
+ "y": -16.779250379767156
+ },
+ "control2": {
+ "x": -1.6216621310390122,
+ "y": -16.945200378722287
+ },
+ "end": {
+ "x": -1.6607846142860616,
+ "y": -16.7581818168103
+ },
+ "a": -1.2823802830996682,
+ "b": -1.458398358941275,
+ "c": -1.6216621310390122,
+ "d": -1.6607846142860616,
+ "e": -16.70492576626623,
+ "f": -16.779250379767156,
+ "g": -16.945200378722287,
+ "h": -16.7581818168103,
+ "m": 0.1113869851068181,
+ "n": 0.012754303743869544,
+ "o": -0.17601807584160678,
+ "p": 0.4445939463213193,
+ "q": -0.09162538545420418,
+ "r": -0.07432461350092723
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -1.6607846142860616,
+ "y": -16.7581818168103
+ },
+ "control1": {
+ "x": -2.0816407834551773,
+ "y": -14.746348492407705
+ },
+ "control2": {
+ "x": -2.0816407834551773,
+ "y": -14.746348492407705
+ },
+ "end": {
+ "x": -2.502496952624293,
+ "y": -12.73451516800511
+ },
+ "a": -1.6607846142860616,
+ "b": -2.0816407834551773,
+ "c": -2.0816407834551773,
+ "d": -2.502496952624293,
+ "e": -16.7581818168103,
+ "f": -14.746348492407705,
+ "g": -14.746348492407705,
+ "h": -12.73451516800511,
+ "m": -0.8417123383382314,
+ "n": 0.4208561691691157,
+ "o": -0.4208561691691157,
+ "p": 4.0236666488051895,
+ "q": -2.0118333244025948,
+ "r": 2.0118333244025948
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -2.502496952624293,
+ "y": -12.73451516800511
+ },
+ "control1": {
+ "x": -2.5157134566474477,
+ "y": -12.671335855988897
+ },
+ "control2": {
+ "x": -2.5559785070124015,
+ "y": -12.649577791312861
+ },
+ "end": {
+ "x": -2.6160696524733824,
+ "y": -12.67314377702582
+ },
+ "a": -2.502496952624293,
+ "b": -2.5157134566474477,
+ "c": -2.5559785070124015,
+ "d": -2.6160696524733824,
+ "e": -12.73451516800511,
+ "f": -12.671335855988897,
+ "g": -12.649577791312861,
+ "h": -12.67314377702582,
+ "m": 0.0072224512457719214,
+ "n": -0.027048546341799096,
+ "o": -0.013216504023154663,
+ "p": -0.0039028030488168497,
+ "q": -0.04142124734017827,
+ "r": 0.06317931201621363
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -2.6160696524733824,
+ "y": -12.67314377702582
+ },
+ "control1": {
+ "x": -2.8091519760989376,
+ "y": -12.748865004256196
+ },
+ "control2": {
+ "x": -2.997633676028629,
+ "y": -12.709033987507897
+ },
+ "end": {
+ "x": -2.9551669650444916,
+ "y": -12.912039077119235
+ },
+ "a": -2.6160696524733824,
+ "b": -2.8091519760989376,
+ "c": -2.997633676028629,
+ "d": -2.9551669650444916,
+ "e": -12.67314377702582,
+ "f": -12.748865004256196,
+ "g": -12.709033987507897,
+ "h": -12.912039077119235,
+ "m": 0.226347787217966,
+ "n": 0.004600623695863781,
+ "o": -0.19308232362555522,
+ "p": -0.3583883503383074,
+ "q": 0.1155522439786747,
+ "r": -0.07572122723037644
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -2.9551669650444916,
+ "y": -12.912039077119235
+ },
+ "control1": {
+ "x": -2.5330019741277847,
+ "y": -14.930129007460874
+ },
+ "control2": {
+ "x": -2.5330019741277847,
+ "y": -14.930129007460874
+ },
+ "end": {
+ "x": -2.110836983211078,
+ "y": -16.948218937802512
+ },
+ "a": -2.9551669650444916,
+ "b": -2.5330019741277847,
+ "c": -2.5330019741277847,
+ "d": -2.110836983211078,
+ "e": -12.912039077119235,
+ "f": -14.930129007460874,
+ "g": -14.930129007460874,
+ "h": -16.948218937802512,
+ "m": 0.8443299818334138,
+ "n": -0.4221649909167069,
+ "o": 0.4221649909167069,
+ "p": -4.036179860683276,
+ "q": 2.018089930341638,
+ "r": -2.018089930341638
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -2.110836983211078,
+ "y": -16.948218937802512
+ },
+ "control1": {
+ "x": -2.0950286103421094,
+ "y": -17.023788250247573
+ },
+ "control2": {
+ "x": -2.111360717387188,
+ "y": -17.054967374684296
+ },
+ "end": {
+ "x": -2.182485020949701,
+ "y": -17.085000008250653
+ },
+ "a": -2.110836983211078,
+ "b": -2.0950286103421094,
+ "c": -2.111360717387188,
+ "d": -2.182485020949701,
+ "e": -16.948218937802512,
+ "f": -17.023788250247573,
+ "g": -17.054967374684296,
+ "h": -17.085000008250653,
+ "m": -0.02265171660338705,
+ "n": -0.03214047991404678,
+ "o": 0.015808372868968412,
+ "p": -0.04324369713797083,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -2.182485020949701,
+ "y": -17.085000008250653
+ },
+ "control1": {
+ "x": -2.358503096791308,
+ "y": -17.15932462175158
+ },
+ "control2": {
+ "x": -2.5217668688890456,
+ "y": -17.32527462070671
+ },
+ "end": {
+ "x": -2.560889352136095,
+ "y": -17.138256058794724
+ },
+ "a": -2.182485020949701,
+ "b": -2.358503096791308,
+ "c": -2.5217668688890456,
+ "d": -2.560889352136095,
+ "e": -17.085000008250653,
+ "f": -17.15932462175158,
+ "g": -17.32527462070671,
+ "h": -17.138256058794724,
+ "m": 0.11138698510681833,
+ "n": 0.012754303743869766,
+ "o": -0.17601807584160722,
+ "p": 0.4445939463213264,
+ "q": -0.09162538545420418,
+ "r": -0.07432461350092723
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -2.560889352136095,
+ "y": -17.138256058794724
+ },
+ "control1": {
+ "x": -2.9843631648003925,
+ "y": -15.113909522514044
+ },
+ "control2": {
+ "x": -2.9843631648003925,
+ "y": -15.113909522514044
+ },
+ "end": {
+ "x": -3.40783697746469,
+ "y": -13.089562986233364
+ },
+ "a": -2.560889352136095,
+ "b": -2.9843631648003925,
+ "c": -2.9843631648003925,
+ "d": -3.40783697746469,
+ "e": -17.138256058794724,
+ "f": -15.113909522514044,
+ "g": -15.113909522514044,
+ "h": -13.089562986233364,
+ "m": -0.8469476253285952,
+ "n": 0.4234738126642976,
+ "o": -0.4234738126642976,
+ "p": 4.048693072561363,
+ "q": -2.02434653628068,
+ "r": 2.02434653628068
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -3.40783697746469,
+ "y": -13.089562986233364
+ },
+ "control1": {
+ "x": -3.421053481487845,
+ "y": -13.02638367421715
+ },
+ "control2": {
+ "x": -3.4613185318527986,
+ "y": -13.004625609541115
+ },
+ "end": {
+ "x": -3.5214096773137795,
+ "y": -13.028191595254073
+ },
+ "a": -3.40783697746469,
+ "b": -3.421053481487845,
+ "c": -3.4613185318527986,
+ "d": -3.5214096773137795,
+ "e": -13.089562986233364,
+ "f": -13.02638367421715,
+ "g": -13.004625609541115,
+ "h": -13.028191595254073,
+ "m": 0.00722245124577281,
+ "n": -0.027048546341799096,
+ "o": -0.013216504023154663,
+ "p": -0.003902803048813297,
+ "q": -0.04142124734017827,
+ "r": 0.06317931201621363
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -3.5214096773137795,
+ "y": -13.028191595254073
+ },
+ "control1": {
+ "x": -3.7144920009393347,
+ "y": -13.10391282248445
+ },
+ "control2": {
+ "x": -3.9029737008690257,
+ "y": -13.064081805736151
+ },
+ "end": {
+ "x": -3.8605069898848883,
+ "y": -13.26708689534749
+ },
+ "a": -3.5214096773137795,
+ "b": -3.7144920009393347,
+ "c": -3.9029737008690257,
+ "d": -3.8605069898848883,
+ "e": -13.028191595254073,
+ "f": -13.10391282248445,
+ "g": -13.064081805736151,
+ "h": -13.26708689534749,
+ "m": 0.22634778721796334,
+ "n": 0.0046006236958642255,
+ "o": -0.19308232362555522,
+ "p": -0.3583883503383145,
+ "q": 0.1155522439786747,
+ "r": -0.07572122723037644
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -3.8605069898848883,
+ "y": -13.26708689534749
+ },
+ "control1": {
+ "x": -3.4357243554729995,
+ "y": -15.297690037567211
+ },
+ "control2": {
+ "x": -3.435724355473,
+ "y": -15.297690037567211
+ },
+ "end": {
+ "x": -3.010941721061111,
+ "y": -17.328293179786932
+ },
+ "a": -3.8605069898848883,
+ "b": -3.4357243554729995,
+ "c": -3.435724355473,
+ "d": -3.010941721061111,
+ "e": -13.26708689534749,
+ "f": -15.297690037567211,
+ "g": -15.297690037567211,
+ "h": -17.328293179786932,
+ "m": 0.8495652688237789,
+ "n": -0.42478263441188924,
+ "o": 0.4247826344118888,
+ "p": -4.061206284439443,
+ "q": 2.0306031422197215,
+ "r": -2.0306031422197215
},
"backward": false
}
@@ -1971,1190 +4281,3080 @@
{
"spline": {
"start": {
- "x": 1.9904484806479574,
- "y": -16.02364468382114
+ "x": 10.040576977764365,
+ "y": -11.817216671012797
+ },
+ "control1": {
+ "x": 9.872857166860037,
+ "y": -11.015459848946962
+ },
+ "control2": {
+ "x": 9.872857166860035,
+ "y": -11.015459848946962
+ },
+ "end": {
+ "x": 9.705137355955706,
+ "y": -10.213703026881127
+ },
+ "a": 10.040576977764365,
+ "b": 9.872857166860037,
+ "c": 9.872857166860035,
+ "d": 9.705137355955706,
+ "e": -11.817216671012797,
+ "f": -11.015459848946962,
+ "g": -11.015459848946962,
+ "h": -10.213703026881127,
+ "m": -0.3354396218086517,
+ "n": 0.16771981090432675,
+ "o": -0.16771981090432853,
+ "p": 1.6035136441316702,
+ "q": -0.8017568220658351,
+ "r": 0.8017568220658351
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 9.705137355955706,
+ "y": -10.213703026881127
+ },
+ "control1": {
+ "x": 9.684892999619743,
+ "y": -10.116928229001411
+ },
+ "control2": {
+ "x": 9.68411244066312,
+ "y": -10.114833677421275
+ },
+ "end": {
+ "x": 9.636086506776302,
+ "y": -10.028412006861071
+ },
+ "a": 9.705137355955706,
+ "b": 9.684892999619743,
+ "c": 9.68411244066312,
+ "d": 9.636086506776302,
+ "e": -10.213703026881127,
+ "f": -10.116928229001411,
+ "g": -10.114833677421275,
+ "h": -10.028412006861071,
+ "m": -0.06670917230952966,
+ "n": 0.019463797379339454,
+ "o": -0.02024435633596333,
+ "p": 0.17900736527964867,
+ "q": -0.09468024629958016,
+ "r": 0.09677479787971599
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 9.636086506776302,
+ "y": -10.028412006861071
+ },
+ "control1": {
+ "x": 9.290748473559788,
+ "y": -9.40698338854662
+ },
+ "control2": {
+ "x": 8.774075136077252,
+ "y": -8.104352094647044
+ },
+ "end": {
+ "x": 8.919645365222031,
+ "y": -8.800226503741671
+ },
+ "a": 9.636086506776302,
+ "b": 9.290748473559788,
+ "c": 8.774075136077252,
+ "d": 8.919645365222031,
+ "e": -10.028412006861071,
+ "f": -9.40698338854662,
+ "g": -8.104352094647044,
+ "h": -8.800226503741671,
+ "m": 0.8335788708933336,
+ "n": -0.17133530426602128,
+ "o": -0.3453380332165139,
+ "p": -2.6797083785793223,
+ "q": 0.6812026755851228,
+ "r": 0.621428618314452
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 8.919645365222031,
+ "y": -8.800226503741671
+ },
+ "control1": {
+ "x": 9.25508498703069,
+ "y": -10.40374014787334
+ },
+ "control2": {
+ "x": 9.25508498703069,
+ "y": -10.40374014787334
+ },
+ "end": {
+ "x": 9.590524608839349,
+ "y": -12.007253792005008
+ },
+ "a": 8.919645365222031,
+ "b": 9.25508498703069,
+ "c": 9.25508498703069,
+ "d": 9.590524608839349,
+ "e": -8.800226503741671,
+ "f": -10.40374014787334,
+ "g": -10.40374014787334,
+ "h": -12.007253792005008,
+ "m": 0.6708792436173177,
+ "n": -0.3354396218086588,
+ "o": 0.3354396218086588,
+ "p": -3.207027288263335,
+ "q": 1.6035136441316684,
+ "r": -1.6035136441316684
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 9.590524608839349,
+ "y": -12.007253792005008
+ },
+ "control1": {
+ "x": 9.606332981708317,
+ "y": -12.08282310445007
+ },
+ "control2": {
+ "x": 9.59000087466324,
+ "y": -12.114002228886791
+ },
+ "end": {
+ "x": 9.518876571100726,
+ "y": -12.144034862453148
+ },
+ "a": 9.590524608839349,
+ "b": 9.606332981708317,
+ "c": 9.59000087466324,
+ "d": 9.518876571100726,
+ "e": -12.007253792005008,
+ "f": -12.08282310445007,
+ "g": -12.114002228886791,
+ "h": -12.144034862453148,
+ "m": -0.022651716603391492,
+ "n": -0.03214047991404456,
+ "o": 0.015808372868967524,
+ "p": -0.04324369713796905,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 9.518876571100726,
+ "y": -12.144034862453148
+ },
+ "control1": {
+ "x": 9.342858495259119,
+ "y": -12.218359475954074
+ },
+ "control2": {
+ "x": 9.179594723161383,
+ "y": -12.384309474909207
+ },
+ "end": {
+ "x": 9.140472239914333,
+ "y": -12.19729091299722
+ },
+ "a": 9.518876571100726,
+ "b": 9.342858495259119,
+ "c": 9.179594723161383,
+ "d": 9.140472239914333,
+ "e": -12.144034862453148,
+ "f": -12.218359475954074,
+ "g": -12.384309474909207,
+ "h": -12.19729091299722,
+ "m": 0.11138698510681522,
+ "n": 0.012754303743871986,
+ "o": -0.17601807584160767,
+ "p": 0.4445939463213282,
+ "q": -0.09162538545420773,
+ "r": -0.07432461350092545
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 9.140472239914333,
+ "y": -12.19729091299722
+ },
+ "control1": {
+ "x": 8.751027792687397,
+ "y": -10.335616131131644
+ },
+ "control2": {
+ "x": 8.751027792687399,
+ "y": -10.335616131131644
+ },
+ "end": {
+ "x": 8.361583345460463,
+ "y": -8.47394134926607
+ },
+ "a": 9.140472239914333,
+ "b": 8.751027792687397,
+ "c": 8.751027792687399,
+ "d": 8.361583345460463,
+ "e": -12.19729091299722,
+ "f": -10.335616131131644,
+ "g": -10.335616131131644,
+ "h": -8.47394134926607,
+ "m": -0.778888894453873,
+ "n": 0.3894444472269374,
+ "o": -0.3894444472269356,
+ "p": 3.7233495637311496,
+ "q": -1.8616747818655757,
+ "r": 1.8616747818655757
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 8.361583345460463,
+ "y": -8.47394134926607
+ },
+ "control1": {
+ "x": 8.34836684143731,
+ "y": -8.410762037249858
+ },
+ "control2": {
+ "x": 8.308101791072355,
+ "y": -8.389003972573823
+ },
+ "end": {
+ "x": 8.248010645611375,
+ "y": -8.412569958286781
+ },
+ "a": 8.361583345460463,
+ "b": 8.34836684143731,
+ "c": 8.308101791072355,
+ "d": 8.248010645611375,
+ "e": -8.47394134926607,
+ "f": -8.410762037249858,
+ "g": -8.389003972573823,
+ "h": -8.412569958286781,
+ "m": 0.007222451245770145,
+ "n": -0.027048546341800872,
+ "o": -0.013216504023153774,
+ "p": -0.0039028030488168497,
+ "q": -0.04142124734017649,
+ "r": 0.06317931201621185
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 8.248010645611375,
+ "y": -8.412569958286781
+ },
+ "control1": {
+ "x": 8.05492832198582,
+ "y": -8.488291185517157
+ },
+ "control2": {
+ "x": 7.866446622056128,
+ "y": -8.448460168768857
+ },
+ "end": {
+ "x": 7.908913333040266,
+ "y": -8.651465258380197
+ },
+ "a": 8.248010645611375,
+ "b": 8.05492832198582,
+ "c": 7.866446622056128,
+ "d": 7.908913333040266,
+ "e": -8.412569958286781,
+ "f": -8.488291185517157,
+ "g": -8.448460168768857,
+ "h": -8.651465258380197,
+ "m": 0.22634778721796422,
+ "n": 0.004600623695862893,
+ "o": -0.19308232362555522,
+ "p": -0.3583883503383163,
+ "q": 0.11555224397867647,
+ "r": -0.07572122723037644
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 7.908913333040266,
+ "y": -8.651465258380197
+ },
+ "control1": {
+ "x": 8.29966660201479,
+ "y": -10.519396646184813
+ },
+ "control2": {
+ "x": 8.299666602014792,
+ "y": -10.519396646184815
+ },
+ "end": {
+ "x": 8.690419870989317,
+ "y": -12.38732803398943
+ },
+ "a": 7.908913333040266,
+ "b": 8.29966660201479,
+ "c": 8.299666602014792,
+ "d": 8.690419870989317,
+ "e": -8.651465258380197,
+ "f": -10.519396646184813,
+ "g": -10.519396646184815,
+ "h": -12.38732803398943,
+ "m": 0.7815065379490473,
+ "n": -0.3907532689745228,
+ "o": 0.39075326897452456,
+ "p": -3.7358627756092293,
+ "q": 1.8679313878046138,
+ "r": -1.8679313878046155
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 8.690419870989317,
+ "y": -12.38732803398943
+ },
+ "control1": {
+ "x": 8.706228243858284,
+ "y": -12.462897346434492
+ },
+ "control2": {
+ "x": 8.689896136813207,
+ "y": -12.494076470871214
+ },
+ "end": {
+ "x": 8.618771833250694,
+ "y": -12.52410910443757
+ },
+ "a": 8.690419870989317,
+ "b": 8.706228243858284,
+ "c": 8.689896136813207,
+ "d": 8.618771833250694,
+ "e": -12.38732803398943,
+ "f": -12.462897346434492,
+ "g": -12.494076470871214,
+ "h": -12.52410910443757,
+ "m": -0.022651716603391492,
+ "n": -0.03214047991404456,
+ "o": 0.015808372868967524,
+ "p": -0.04324369713797793,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 8.618771833250694,
+ "y": -12.52410910443757
+ },
+ "control1": {
+ "x": 8.442753757409086,
+ "y": -12.598433717938496
+ },
+ "control2": {
+ "x": 8.27948998531135,
+ "y": -12.76438371689363
+ },
+ "end": {
+ "x": 8.2403675020643,
+ "y": -12.577365154981642
+ },
+ "a": 8.618771833250694,
+ "b": 8.442753757409086,
+ "c": 8.27948998531135,
+ "d": 8.2403675020643,
+ "e": -12.52410910443757,
+ "f": -12.598433717938496,
+ "g": -12.76438371689363,
+ "h": -12.577365154981642,
+ "m": 0.11138698510681522,
+ "n": 0.012754303743871986,
+ "o": -0.17601807584160767,
+ "p": 0.4445939463213264,
+ "q": -0.09162538545420773,
+ "r": -0.07432461350092545
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 8.2403675020643,
+ "y": -12.577365154981642
+ },
+ "control1": {
+ "x": 7.848305411342184,
+ "y": -10.703177161237981
+ },
+ "control2": {
+ "x": 7.848305411342184,
+ "y": -10.703177161237981
+ },
+ "end": {
+ "x": 7.456243320620068,
+ "y": -8.828989167494322
+ },
+ "a": 8.2403675020643,
+ "b": 7.848305411342184,
+ "c": 7.848305411342184,
+ "d": 7.456243320620068,
+ "e": -12.577365154981642,
+ "f": -10.703177161237981,
+ "g": -10.703177161237981,
+ "h": -8.828989167494322,
+ "m": -0.7841241814442323,
+ "n": 0.3920620907221162,
+ "o": -0.3920620907221162,
+ "p": 3.7483759874873215,
+ "q": -1.8741879937436607,
+ "r": 1.8741879937436607
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 7.456243320620068,
+ "y": -8.828989167494322
+ },
+ "control1": {
+ "x": 7.443026816596913,
+ "y": -8.765809855478109
+ },
+ "control2": {
+ "x": 7.402761766231959,
+ "y": -8.744051790802075
+ },
+ "end": {
+ "x": 7.342670620770979,
+ "y": -8.767617776515033
+ },
+ "a": 7.456243320620068,
+ "b": 7.443026816596913,
+ "c": 7.402761766231959,
+ "d": 7.342670620770979,
+ "e": -8.828989167494322,
+ "f": -8.765809855478109,
+ "g": -8.744051790802075,
+ "h": -8.767617776515033,
+ "m": 0.007222451245773698,
+ "n": -0.027048546341799984,
+ "o": -0.013216504023154663,
+ "p": -0.003902803048807968,
+ "q": -0.041421247340180045,
+ "r": 0.06317931201621363
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 7.342670620770979,
+ "y": -8.767617776515033
+ },
+ "control1": {
+ "x": 7.149588297145424,
+ "y": -8.84333900374541
+ },
+ "control2": {
+ "x": 6.961106597215733,
+ "y": -8.803507986997111
+ },
+ "end": {
+ "x": 7.00357330819987,
+ "y": -9.00651307660845
+ },
+ "a": 7.342670620770979,
+ "b": 7.149588297145424,
+ "c": 6.961106597215733,
+ "d": 7.00357330819987,
+ "e": -8.767617776515033,
+ "f": -8.84333900374541,
+ "g": -8.803507986997111,
+ "h": -9.00651307660845,
+ "m": 0.2263477872179651,
+ "n": 0.004600623695862893,
+ "o": -0.19308232362555433,
+ "p": -0.35838835033831096,
+ "q": 0.1155522439786747,
+ "r": -0.07572122723037644
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 7.00357330819987,
+ "y": -9.00651307660845
+ },
+ "control1": {
+ "x": 7.396944220669577,
+ "y": -10.886957676291152
+ },
+ "control2": {
+ "x": 7.396944220669577,
+ "y": -10.886957676291152
+ },
+ "end": {
+ "x": 7.790315133139284,
+ "y": -12.767402275973854
+ },
+ "a": 7.00357330819987,
+ "b": 7.396944220669577,
+ "c": 7.396944220669577,
+ "d": 7.790315133139284,
+ "e": -9.00651307660845,
+ "f": -10.886957676291152,
+ "g": -10.886957676291152,
+ "h": -12.767402275973854,
+ "m": 0.7867418249394138,
+ "n": -0.3933709124697069,
+ "o": 0.3933709124697069,
+ "p": -3.760889199365405,
+ "q": 1.8804445996827024,
+ "r": -1.8804445996827024
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 7.790315133139284,
+ "y": -12.767402275973854
+ },
+ "control1": {
+ "x": 7.8061235060082526,
+ "y": -12.842971588418916
+ },
+ "control2": {
+ "x": 7.789791398963174,
+ "y": -12.874150712855638
+ },
+ "end": {
+ "x": 7.718667095400661,
+ "y": -12.904183346421995
+ },
+ "a": 7.790315133139284,
+ "b": 7.8061235060082526,
+ "c": 7.789791398963174,
+ "d": 7.718667095400661,
+ "e": -12.767402275973854,
+ "f": -12.842971588418916,
+ "g": -12.874150712855638,
+ "h": -12.904183346421995,
+ "m": -0.022651716603384386,
+ "n": -0.032140479914047226,
+ "o": 0.015808372868968412,
+ "p": -0.04324369713797083,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 7.718667095400661,
+ "y": -12.904183346421995
+ },
+ "control1": {
+ "x": 7.542649019559054,
+ "y": -12.97850795992292
+ },
+ "control2": {
+ "x": 7.379385247461317,
+ "y": -13.144457958878053
+ },
+ "end": {
+ "x": 7.340262764214268,
+ "y": -12.957439396966066
+ },
+ "a": 7.718667095400661,
+ "b": 7.542649019559054,
+ "c": 7.379385247461317,
+ "d": 7.340262764214268,
+ "e": -12.904183346421995,
+ "f": -12.97850795992292,
+ "g": -13.144457958878053,
+ "h": -12.957439396966066,
+ "m": 0.11138698510681788,
+ "n": 0.01275430374387021,
+ "o": -0.17601807584160678,
+ "p": 0.44459394632133353,
+ "q": -0.09162538545420773,
+ "r": -0.07432461350092545
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 7.340262764214268,
+ "y": -12.957439396966066
+ },
+ "control1": {
+ "x": 6.94558302999697,
+ "y": -11.070738191344322
+ },
+ "control2": {
+ "x": 6.94558302999697,
+ "y": -11.070738191344322
+ },
+ "end": {
+ "x": 6.550903295779673,
+ "y": -9.184036985722576
+ },
+ "a": 7.340262764214268,
+ "b": 6.94558302999697,
+ "c": 6.94558302999697,
+ "d": 6.550903295779673,
+ "e": -12.957439396966066,
+ "f": -11.070738191344322,
+ "g": -11.070738191344322,
+ "h": -9.184036985722576,
+ "m": -0.7893594684345953,
+ "n": 0.39467973421729763,
+ "o": -0.39467973421729763,
+ "p": 3.773402411243488,
+ "q": -1.886701205621744,
+ "r": 1.886701205621744
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 6.550903295779673,
+ "y": -9.184036985722576
+ },
+ "control1": {
+ "x": 6.537686791756518,
+ "y": -9.120857673706363
+ },
+ "control2": {
+ "x": 6.497421741391563,
+ "y": -9.099099609030329
+ },
+ "end": {
+ "x": 6.437330595930583,
+ "y": -9.122665594743287
+ },
+ "a": 6.550903295779673,
+ "b": 6.537686791756518,
+ "c": 6.497421741391563,
+ "d": 6.437330595930583,
+ "e": -9.184036985722576,
+ "f": -9.120857673706363,
+ "g": -9.099099609030329,
+ "h": -9.122665594743287,
+ "m": 0.007222451245773698,
+ "n": -0.027048546341799984,
+ "o": -0.013216504023154663,
+ "p": -0.0039028030488115206,
+ "q": -0.041421247340180045,
+ "r": 0.06317931201621363
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 6.437330595930583,
+ "y": -9.122665594743287
+ },
+ "control1": {
+ "x": 6.244248272305028,
+ "y": -9.198386821973664
+ },
+ "control2": {
+ "x": 6.055766572375336,
+ "y": -9.158555805225363
+ },
+ "end": {
+ "x": 6.098233283359473,
+ "y": -9.361560894836703
+ },
+ "a": 6.437330595930583,
+ "b": 6.244248272305028,
+ "c": 6.055766572375336,
+ "d": 6.098233283359473,
+ "e": -9.122665594743287,
+ "f": -9.198386821973664,
+ "g": -9.158555805225363,
+ "h": -9.361560894836703,
+ "m": 0.2263477872179669,
+ "n": 0.004600623695862893,
+ "o": -0.19308232362555522,
+ "p": -0.3583883503383145,
+ "q": 0.11555224397867647,
+ "r": -0.07572122723037644
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 6.098233283359473,
+ "y": -9.361560894836703
+ },
+ "control1": {
+ "x": 6.4942218393243625,
+ "y": -11.25451870639749
+ },
+ "control2": {
+ "x": 6.4942218393243625,
+ "y": -11.25451870639749
+ },
+ "end": {
+ "x": 6.890210395289252,
+ "y": -13.147476517958278
+ },
+ "a": 6.098233283359473,
+ "b": 6.4942218393243625,
+ "c": 6.4942218393243625,
+ "d": 6.890210395289252,
+ "e": -9.361560894836703,
+ "f": -11.25451870639749,
+ "g": -11.25451870639749,
+ "h": -13.147476517958278,
+ "m": 0.7919771119297785,
+ "n": -0.39598855596488924,
+ "o": 0.39598855596488924,
+ "p": -3.785915623121575,
+ "q": 1.8929578115607875,
+ "r": -1.8929578115607875
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 6.890210395289252,
+ "y": -13.147476517958278
+ },
+ "control1": {
+ "x": 6.90601876815822,
+ "y": -13.22304583040334
+ },
+ "control2": {
+ "x": 6.889686661113141,
+ "y": -13.254224954840062
+ },
+ "end": {
+ "x": 6.818562357550628,
+ "y": -13.284257588406419
+ },
+ "a": 6.890210395289252,
+ "b": 6.90601876815822,
+ "c": 6.889686661113141,
+ "d": 6.818562357550628,
+ "e": -13.147476517958278,
+ "f": -13.22304583040334,
+ "g": -13.254224954840062,
+ "h": -13.284257588406419,
+ "m": -0.022651716603384386,
+ "n": -0.032140479914047226,
+ "o": 0.015808372868968412,
+ "p": -0.04324369713797793,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 6.818562357550628,
+ "y": -13.284257588406419
+ },
+ "control1": {
+ "x": 6.642544281709022,
+ "y": -13.358582201907344
+ },
+ "control2": {
+ "x": 6.479280509611287,
+ "y": -13.524532200862474
+ },
+ "end": {
+ "x": 6.440158026364237,
+ "y": -13.337513638950488
+ },
+ "a": 6.818562357550628,
+ "b": 6.642544281709022,
+ "c": 6.479280509611287,
+ "d": 6.440158026364237,
+ "e": -13.284257588406419,
+ "f": -13.358582201907344,
+ "g": -13.524532200862474,
+ "h": -13.337513638950488,
+ "m": 0.1113869851068161,
+ "n": 0.01275430374387021,
+ "o": -0.1760180758416059,
+ "p": 0.4445939463213193,
+ "q": -0.09162538545420418,
+ "r": -0.07432461350092545
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 6.440158026364237,
+ "y": -13.337513638950488
+ },
+ "control1": {
+ "x": 6.042860648651756,
+ "y": -11.43829922145066
+ },
+ "control2": {
+ "x": 6.042860648651756,
+ "y": -11.438299221450658
+ },
+ "end": {
+ "x": 5.645563270939276,
+ "y": -9.539084803950828
+ },
+ "a": 6.440158026364237,
+ "b": 6.042860648651756,
+ "c": 6.042860648651756,
+ "d": 5.645563270939276,
+ "e": -13.337513638950488,
+ "f": -11.43829922145066,
+ "g": -11.438299221450658,
+ "h": -9.539084803950828,
+ "m": -0.7945947554249617,
+ "n": 0.39729737771248086,
+ "o": -0.39729737771248086,
+ "p": 3.79842883499966,
+ "q": -1.8992144174998273,
+ "r": 1.899214417499829
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 5.645563270939276,
+ "y": -9.539084803950828
+ },
+ "control1": {
+ "x": 5.632346766916121,
+ "y": -9.475905491934615
+ },
+ "control2": {
+ "x": 5.592081716551166,
+ "y": -9.454147427258581
+ },
+ "end": {
+ "x": 5.531990571090186,
+ "y": -9.47771341297154
+ },
+ "a": 5.645563270939276,
+ "b": 5.632346766916121,
+ "c": 5.592081716551166,
+ "d": 5.531990571090186,
+ "e": -9.539084803950828,
+ "f": -9.475905491934615,
+ "g": -9.454147427258581,
+ "h": -9.47771341297154,
+ "m": 0.007222451245775474,
+ "n": -0.027048546341799984,
+ "o": -0.013216504023154663,
+ "p": -0.003902803048813297,
+ "q": -0.041421247340180045,
+ "r": 0.06317931201621363
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 5.531990571090186,
+ "y": -9.47771341297154
+ },
+ "control1": {
+ "x": 5.338908247464632,
+ "y": -9.553434640201916
+ },
+ "control2": {
+ "x": 5.1504265475349404,
+ "y": -9.513603623453617
+ },
+ "end": {
+ "x": 5.192893258519078,
+ "y": -9.716608713064955
+ },
+ "a": 5.531990571090186,
+ "b": 5.338908247464632,
+ "c": 5.1504265475349404,
+ "d": 5.192893258519078,
+ "e": -9.47771341297154,
+ "f": -9.553434640201916,
+ "g": -9.513603623453617,
+ "h": -9.716608713064955,
+ "m": 0.22634778721796867,
+ "n": 0.004600623695862893,
+ "o": -0.19308232362555433,
+ "p": -0.3583883503383163,
+ "q": 0.1155522439786747,
+ "r": -0.07572122723037644
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 5.192893258519078,
+ "y": -9.716608713064955
+ },
+ "control1": {
+ "x": 5.5914994579791495,
+ "y": -11.622079736503828
+ },
+ "control2": {
+ "x": 5.5914994579791495,
+ "y": -11.622079736503828
+ },
+ "end": {
+ "x": 5.990105657439221,
+ "y": -13.5275507599427
+ },
+ "a": 5.192893258519078,
+ "b": 5.5914994579791495,
+ "c": 5.5914994579791495,
+ "d": 5.990105657439221,
+ "e": -9.716608713064955,
+ "f": -11.622079736503828,
+ "g": -11.622079736503828,
+ "h": -13.5275507599427,
+ "m": 0.7972123989201432,
+ "n": -0.3986061994600716,
+ "o": 0.3986061994600716,
+ "p": -3.8109420468777433,
+ "q": 1.9054710234388725,
+ "r": -1.9054710234388725
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 5.990105657439221,
+ "y": -13.5275507599427
+ },
+ "control1": {
+ "x": 6.0059140303081895,
+ "y": -13.60312007238776
+ },
+ "control2": {
+ "x": 5.98958192326311,
+ "y": -13.634299196824482
+ },
+ "end": {
+ "x": 5.918457619700598,
+ "y": -13.66433183039084
+ },
+ "a": 5.990105657439221,
+ "b": 6.0059140303081895,
+ "c": 5.98958192326311,
+ "d": 5.918457619700598,
+ "e": -13.5275507599427,
+ "f": -13.60312007238776,
+ "g": -13.634299196824482,
+ "h": -13.66433183039084,
+ "m": -0.022651716603386163,
+ "n": -0.032140479914048115,
+ "o": 0.015808372868968412,
+ "p": -0.043243697137976156,
+ "q": 0.044390188008337716,
+ "r": -0.07556931244505982
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 5.918457619700598,
+ "y": -13.66433183039084
+ },
+ "control1": {
+ "x": 5.742439543858991,
+ "y": -13.738656443891765
+ },
+ "control2": {
+ "x": 5.579175771761254,
+ "y": -13.904606442846898
+ },
+ "end": {
+ "x": 5.540053288514205,
+ "y": -13.71758788093491
+ },
+ "a": 5.918457619700598,
+ "b": 5.742439543858991,
+ "c": 5.579175771761254,
+ "d": 5.540053288514205,
+ "e": -13.66433183039084,
+ "f": -13.738656443891765,
+ "g": -13.904606442846898,
+ "h": -13.71758788093491,
+ "m": 0.11138698510681433,
+ "n": 0.01275430374387021,
+ "o": -0.17601807584160678,
+ "p": 0.4445939463213264,
+ "q": -0.09162538545420773,
+ "r": -0.07432461350092545
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 5.540053288514205,
+ "y": -13.71758788093491
+ },
+ "control1": {
+ "x": 5.1401382673065426,
+ "y": -11.805860251556997
+ },
+ "control2": {
+ "x": 5.1401382673065426,
+ "y": -11.805860251556997
+ },
+ "end": {
+ "x": 4.74022324609888,
+ "y": -9.894132622179082
+ },
+ "a": 5.540053288514205,
+ "b": 5.1401382673065426,
+ "c": 5.1401382673065426,
+ "d": 4.74022324609888,
+ "e": -13.71758788093491,
+ "f": -11.805860251556997,
+ "g": -11.805860251556997,
+ "h": -9.894132622179082,
+ "m": -0.7998300424153246,
+ "n": 0.3999150212076623,
+ "o": -0.3999150212076623,
+ "p": 3.8234552587558284,
+ "q": -1.9117276293779142,
+ "r": 1.9117276293779142
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 4.74022324609888,
+ "y": -9.894132622179082
+ },
+ "control1": {
+ "x": 4.727006742075726,
+ "y": -9.830953310162869
+ },
+ "control2": {
+ "x": 4.686741691710771,
+ "y": -9.809195245486835
+ },
+ "end": {
+ "x": 4.626650546249791,
+ "y": -9.832761231199793
+ },
+ "a": 4.74022324609888,
+ "b": 4.727006742075726,
+ "c": 4.686741691710771,
+ "d": 4.626650546249791,
+ "e": -9.894132622179082,
+ "f": -9.830953310162869,
+ "g": -9.809195245486835,
+ "h": -9.832761231199793,
+ "m": 0.007222451245775474,
+ "n": -0.027048546341799984,
+ "o": -0.013216504023154663,
+ "p": -0.0039028030488097443,
+ "q": -0.041421247340180045,
+ "r": 0.06317931201621363
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 4.626650546249791,
+ "y": -9.832761231199793
},
"control1": {
- "x": -5.050386505404345,
- "y": -19.176228873513086
+ "x": 4.4335682226242366,
+ "y": -9.90848245843017
},
"control2": {
- "x": -5.050386505404345,
- "y": -19.176228873513082
+ "x": 4.245086522694545,
+ "y": -9.868651441681871
},
"end": {
- "x": -12.091221491456647,
- "y": -22.32881306320503
- },
- "a": 1.9904484806479574,
- "b": -5.050386505404345,
- "c": -5.050386505404345,
- "d": -12.091221491456647,
- "e": -16.02364468382114,
- "f": -19.176228873513086,
- "g": -19.176228873513082,
- "h": -22.32881306320503,
- "m": -14.081669972104605,
- "n": 7.040834986052302,
- "o": -7.040834986052302,
- "p": -6.3051683793839075,
- "q": 3.15258418969195,
- "r": -3.1525841896919466
+ "x": 4.287553233678683,
+ "y": -10.07165653129321
+ },
+ "a": 4.626650546249791,
+ "b": 4.4335682226242366,
+ "c": 4.245086522694545,
+ "d": 4.287553233678683,
+ "e": -9.832761231199793,
+ "f": -9.90848245843017,
+ "g": -9.868651441681871,
+ "h": -10.07165653129321,
+ "m": 0.2263477872179669,
+ "n": 0.004600623695862893,
+ "o": -0.19308232362555433,
+ "p": -0.35838835033830563,
+ "q": 0.1155522439786747,
+ "r": -0.07572122723037644
},
"backward": false
},
{
"spline": {
"start": {
- "x": -12.091221491456647,
- "y": -22.32881306320503
+ "x": 4.287553233678683,
+ "y": -10.07165653129321
},
"control1": {
- "x": -12.177876510763841,
- "y": -22.367613467470502
+ "x": 4.688777076633936,
+ "y": -11.989640766610165
},
"control2": {
- "x": -12.187425779127148,
- "y": -22.36858771862414
+ "x": 4.688777076633936,
+ "y": -11.989640766610165
},
"end": {
- "x": -12.280130986665883,
- "y": -22.348086298246653
- },
- "a": -12.091221491456647,
- "b": -12.177876510763841,
- "c": -12.187425779127148,
- "d": -12.280130986665883,
- "e": -22.32881306320503,
- "f": -22.367613467470502,
- "g": -22.36858771862414,
- "h": -22.348086298246653,
- "m": -0.16026169011931657,
- "n": 0.07710575094388794,
- "o": -0.08665501930719444,
- "p": -0.01635048158070518,
- "q": 0.03782615311183335,
- "r": -0.03880040426547282
+ "x": 5.090000919589189,
+ "y": -13.907625001927123
+ },
+ "a": 4.287553233678683,
+ "b": 4.688777076633936,
+ "c": 4.688777076633936,
+ "d": 5.090000919589189,
+ "e": -10.07165653129321,
+ "f": -11.989640766610165,
+ "g": -11.989640766610165,
+ "h": -13.907625001927123,
+ "m": 0.8024476859105061,
+ "n": -0.40122384295525304,
+ "o": 0.40122384295525304,
+ "p": -3.8359684706339134,
+ "q": 1.9179842353169558,
+ "r": -1.9179842353169558
},
"backward": false
},
{
"spline": {
"start": {
- "x": -12.280130986665883,
- "y": -22.348086298246653
+ "x": 5.090000919589189,
+ "y": -13.907625001927123
},
"control1": {
- "x": -12.354809913076233,
- "y": -22.331571324553956
+ "x": 5.105809292458157,
+ "y": -13.983194314372184
},
"control2": {
- "x": -12.354693841811876,
- "y": -22.317240040049903
+ "x": 5.089477185413078,
+ "y": -14.014373438808907
},
"end": {
- "x": -12.40480998472941,
- "y": -22.259464105397644
- },
- "a": -12.280130986665883,
- "b": -12.354809913076233,
- "c": -12.354693841811876,
- "d": -12.40480998472941,
- "e": -22.348086298246653,
- "f": -22.331571324553956,
- "g": -22.317240040049903,
- "h": -22.259464105397644,
- "m": -0.1250272118566027,
- "n": 0.07479499767470621,
- "o": -0.07467892641034979,
- "p": 0.045628339336840185,
- "q": -0.002183689188644422,
- "r": 0.016514973692697055
+ "x": 5.018352881850565,
+ "y": -14.044406072375264
+ },
+ "a": 5.090000919589189,
+ "b": 5.105809292458157,
+ "c": 5.089477185413078,
+ "d": 5.018352881850565,
+ "e": -13.907625001927123,
+ "f": -13.983194314372184,
+ "g": -14.014373438808907,
+ "h": -14.044406072375264,
+ "m": -0.022651716603389715,
+ "n": -0.032140479914047226,
+ "o": 0.015808372868968412,
+ "p": -0.04324369713797083,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
},
"backward": false
},
{
"spline": {
"start": {
- "x": -12.40480998472941,
- "y": -22.259464105397644
+ "x": 5.018352881850565,
+ "y": -14.044406072375264
},
"control1": {
- "x": -14.420419836961068,
- "y": -19.935786816452737
+ "x": 4.842334806008958,
+ "y": -14.118730685876189
},
"control2": {
- "x": -14.42041983696107,
- "y": -19.93578681645274
+ "x": 4.679071033911222,
+ "y": -14.284680684831322
},
"end": {
- "x": -16.43602968919273,
- "y": -17.612109527507833
- },
- "a": -12.40480998472941,
- "b": -14.420419836961068,
- "c": -14.42041983696107,
- "d": -16.43602968919273,
- "e": -22.259464105397644,
- "f": -19.935786816452737,
- "g": -19.93578681645274,
- "h": -17.612109527507833,
- "m": -4.03121970446332,
- "n": 2.0156098522316572,
- "o": -2.015609852231659,
- "p": 4.647354577889818,
- "q": -2.3236772889449107,
- "r": 2.323677288944907
+ "x": 4.6399485506641724,
+ "y": -14.097662122919335
+ },
+ "a": 5.018352881850565,
+ "b": 4.842334806008958,
+ "c": 4.679071033911222,
+ "d": 4.6399485506641724,
+ "e": -14.044406072375264,
+ "f": -14.118730685876189,
+ "g": -14.284680684831322,
+ "h": -14.097662122919335,
+ "m": 0.1113869851068161,
+ "n": 0.01275430374387021,
+ "o": -0.17601807584160678,
+ "p": 0.4445939463213264,
+ "q": -0.09162538545420773,
+ "r": -0.07432461350092545
},
"backward": false
},
{
"spline": {
"start": {
- "x": -16.43602968919273,
- "y": -17.612109527507833
+ "x": 4.6399485506641724,
+ "y": -14.097662122919335
},
"control1": {
- "x": -16.49361810904551,
- "y": -17.5457192470823
+ "x": 4.237415885961329,
+ "y": -12.173421281663334
},
"control2": {
- "x": -16.498206624660558,
- "y": -17.52492779540708
+ "x": 4.237415885961329,
+ "y": -12.173421281663334
},
"end": {
- "x": -16.473910085467818,
- "y": -17.440466125879556
- },
- "a": -16.43602968919273,
- "b": -16.49361810904551,
- "c": -16.498206624660558,
- "d": -16.473910085467818,
- "e": -17.612109527507833,
- "f": -17.5457192470823,
- "g": -17.52492779540708,
- "h": -17.440466125879556,
- "m": -0.024114849429942353,
- "n": 0.052999904237733375,
- "o": -0.05758841985278096,
- "p": 0.10926904660262693,
- "q": -0.045598828750314624,
- "r": 0.06639028042553363
+ "x": 3.834883221258485,
+ "y": -10.249180440407335
+ },
+ "a": 4.6399485506641724,
+ "b": 4.237415885961329,
+ "c": 4.237415885961329,
+ "d": 3.834883221258485,
+ "e": -14.097662122919335,
+ "f": -12.173421281663334,
+ "g": -12.173421281663334,
+ "h": -10.249180440407335,
+ "m": -0.8050653294056875,
+ "n": 0.40253266470284377,
+ "o": -0.40253266470284377,
+ "p": 3.8484816825119985,
+ "q": -1.924240841256001,
+ "r": 1.924240841256001
},
"backward": false
},
{
"spline": {
"start": {
- "x": -16.473910085467818,
- "y": -17.440466125879556
+ "x": 3.834883221258485,
+ "y": -10.249180440407335
},
"control1": {
- "x": -16.459375225005935,
- "y": -17.389938823566187
+ "x": 3.8216667172353302,
+ "y": -10.186001128391121
},
"control2": {
- "x": -16.426071759980577,
- "y": -17.41801504143529
+ "x": 3.781401666870376,
+ "y": -10.164243063715087
},
"end": {
- "x": -16.375783038651115,
- "y": -17.402675062466564
- },
- "a": -16.473910085467818,
- "b": -16.459375225005935,
- "c": -16.426071759980577,
- "d": -16.375783038651115,
- "e": -17.440466125879556,
- "f": -17.389938823566187,
- "g": -17.41801504143529,
- "h": -17.402675062466564,
- "m": -0.0017833482593694328,
- "n": 0.018768604563476288,
- "o": 0.01453486046188246,
- "p": 0.12201971702029724,
- "q": -0.07860352018247241,
- "r": 0.05052730231336966
+ "x": 3.7213105214093956,
+ "y": -10.187809049428045
+ },
+ "a": 3.834883221258485,
+ "b": 3.8216667172353302,
+ "c": 3.781401666870376,
+ "d": 3.7213105214093956,
+ "e": -10.249180440407335,
+ "f": -10.186001128391121,
+ "g": -10.164243063715087,
+ "h": -10.187809049428045,
+ "m": 0.00722245124577281,
+ "n": -0.02704854634179954,
+ "o": -0.013216504023154663,
+ "p": -0.0039028030488115206,
+ "q": -0.041421247340180045,
+ "r": 0.06317931201621363
},
"backward": false
},
{
"spline": {
"start": {
- "x": -16.375783038651115,
- "y": -17.402675062466564
+ "x": 3.7213105214093956,
+ "y": -10.187809049428045
},
"control1": {
- "x": -8.321056452272451,
- "y": -14.945676093962286
+ "x": 3.528228197783841,
+ "y": -10.263530276658422
},
"control2": {
- "x": -8.321056452272451,
- "y": -14.945676093962286
+ "x": 3.3397464978541502,
+ "y": -10.223699259910124
},
"end": {
- "x": -0.2663298658937867,
- "y": -12.488677125458008
- },
- "a": -16.375783038651115,
- "b": -8.321056452272451,
- "c": -8.321056452272451,
- "d": -0.2663298658937867,
- "e": -17.402675062466564,
- "f": -14.945676093962286,
- "g": -14.945676093962286,
- "h": -12.488677125458008,
- "m": 16.109453172757327,
- "n": -8.054726586378663,
- "o": 8.054726586378663,
- "p": 4.913997937008553,
- "q": -2.456998968504278,
- "r": 2.456998968504278
+ "x": 3.3822132088382872,
+ "y": -10.426704349521462
+ },
+ "a": 3.7213105214093956,
+ "b": 3.528228197783841,
+ "c": 3.3397464978541502,
+ "d": 3.3822132088382872,
+ "e": -10.187809049428045,
+ "f": -10.263530276658422,
+ "g": -10.223699259910124,
+ "h": -10.426704349521462,
+ "m": 0.22634778721796334,
+ "n": 0.0046006236958642255,
+ "o": -0.19308232362555477,
+ "p": -0.35838835033831096,
+ "q": 0.1155522439786747,
+ "r": -0.07572122723037644
},
"backward": false
},
{
"spline": {
"start": {
- "x": -0.2663298658937867,
- "y": -12.488677125458008
+ "x": 3.3822132088382872,
+ "y": -10.426704349521462
},
"control1": {
- "x": -0.17566847189296741,
- "y": -12.461021940746974
+ "x": 3.7860546952887217,
+ "y": -12.357201796716504
},
"control2": {
- "x": -0.1657873086812938,
- "y": -12.461301514270977
+ "x": 3.7860546952887217,
+ "y": -12.357201796716504
},
"end": {
- "x": -0.07683462917804214,
- "y": -12.494038624906537
- },
- "a": -0.2663298658937867,
- "b": -0.17566847189296741,
- "c": -0.1657873086812938,
- "d": -0.07683462917804214,
- "e": -12.488677125458008,
- "f": -12.461021940746974,
- "g": -12.461301514270977,
- "h": -12.494038624906537,
- "m": 0.15985174708072375,
- "n": -0.08078023078914567,
- "o": 0.0906613940008193,
- "p": -0.004522778876523859,
- "q": -0.027934758235035773,
- "r": 0.027655184711033343
+ "x": 4.189896181739156,
+ "y": -14.287699243911547
+ },
+ "a": 3.3822132088382872,
+ "b": 3.7860546952887217,
+ "c": 3.7860546952887217,
+ "d": 4.189896181739156,
+ "e": -10.426704349521462,
+ "f": -12.357201796716504,
+ "g": -12.357201796716504,
+ "h": -14.287699243911547,
+ "m": 0.807682972900869,
+ "n": -0.4038414864504345,
+ "o": 0.4038414864504345,
+ "p": -3.8609948943900854,
+ "q": 1.9304974471950427,
+ "r": -1.9304974471950427
},
"backward": false
},
{
"spline": {
"start": {
- "x": -0.07683462917804214,
- "y": -12.494038624906537
+ "x": 4.189896181739156,
+ "y": -14.287699243911547
},
"control1": {
- "x": -0.005691927185520265,
- "y": -12.5202211583507
+ "x": 4.205704554608125,
+ "y": -14.363268556356608
},
"control2": {
- "x": -0.008072901222325228,
- "y": -12.534919434322207
+ "x": 4.189372447563046,
+ "y": -14.39444768079333
},
"end": {
- "x": 0.03279927747010902,
- "y": -12.598765139841728
- },
- "a": -0.07683462917804214,
- "b": -0.005691927185520265,
- "c": -0.008072901222325228,
- "d": 0.03279927747010902,
- "e": -12.494038624906537,
- "f": -12.5202211583507,
- "g": -12.534919434322207,
- "h": -12.598765139841728,
- "m": 0.11677682875856604,
- "n": -0.07352367602932683,
- "o": 0.07114270199252187,
- "p": -0.060631687020670455,
- "q": 0.011484257472657688,
- "r": -0.02618253344416388
+ "x": 4.118248144000533,
+ "y": -14.424480314359688
+ },
+ "a": 4.189896181739156,
+ "b": 4.205704554608125,
+ "c": 4.189372447563046,
+ "d": 4.118248144000533,
+ "e": -14.287699243911547,
+ "f": -14.363268556356608,
+ "g": -14.39444768079333,
+ "h": -14.424480314359688,
+ "m": -0.022651716603389715,
+ "n": -0.032140479914047226,
+ "o": 0.015808372868968412,
+ "p": -0.04324369713797793,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
},
"backward": false
},
{
"spline": {
"start": {
- "x": 0.03279927747010902,
- "y": -12.598765139841728
+ "x": 4.118248144000533,
+ "y": -14.424480314359688
},
"control1": {
- "x": 1.0489768792842749,
- "y": -14.186118151022631
+ "x": 3.942230068158926,
+ "y": -14.498804927860613
},
"control2": {
- "x": 1.0489768792842744,
- "y": -14.186118151022631
+ "x": 3.7789662960611894,
+ "y": -14.664754926815744
},
"end": {
- "x": 2.0651544810984404,
- "y": -15.773471162203535
- },
- "a": 0.03279927747010902,
- "b": 1.0489768792842749,
- "c": 1.0489768792842744,
- "d": 2.0651544810984404,
- "e": -12.598765139841728,
- "f": -14.186118151022631,
- "g": -14.186118151022631,
- "h": -15.773471162203535,
- "m": 2.0323552036283328,
- "n": -1.0161776018141664,
- "o": 1.016177601814166,
- "p": -3.174706022361807,
- "q": 1.5873530111809036,
- "r": -1.5873530111809036
+ "x": 3.73984381281414,
+ "y": -14.477736364903757
+ },
+ "a": 4.118248144000533,
+ "b": 3.942230068158926,
+ "c": 3.7789662960611894,
+ "d": 3.73984381281414,
+ "e": -14.424480314359688,
+ "f": -14.498804927860613,
+ "g": -14.664754926815744,
+ "h": -14.477736364903757,
+ "m": 0.1113869851068161,
+ "n": 0.01275430374387021,
+ "o": -0.17601807584160678,
+ "p": 0.4445939463213229,
+ "q": -0.09162538545420595,
+ "r": -0.07432461350092545
},
"backward": false
},
{
"spline": {
"start": {
- "x": 2.0651544810984404,
- "y": -15.773471162203535
+ "x": 3.73984381281414,
+ "y": -14.477736364903757
},
"control1": {
- "x": 2.0899632936716346,
- "y": -15.812224568371489
+ "x": 3.3346935046161144,
+ "y": -12.540982311769673
},
"control2": {
- "x": 2.065139022555776,
- "y": -15.855260400063573
+ "x": 3.3346935046161144,
+ "y": -12.540982311769673
},
"end": {
- "x": 2.0191715439702316,
- "y": -15.853188062398566
- },
- "a": 2.0651544810984404,
- "b": 2.0899632936716346,
- "c": 2.065139022555776,
- "d": 2.0191715439702316,
- "e": -15.773471162203535,
- "f": -15.812224568371489,
- "g": -15.855260400063573,
- "h": -15.853188062398566,
- "m": 0.028489876219366383,
- "n": -0.049633083689052615,
- "o": 0.024808812573194228,
- "p": 0.049390594881224104,
- "q": -0.004282425524129607,
- "r": -0.038753406167954196
+ "x": 2.9295431964180887,
+ "y": -10.604228258635588
+ },
+ "a": 3.73984381281414,
+ "b": 3.3346935046161144,
+ "c": 3.3346935046161144,
+ "d": 2.9295431964180887,
+ "e": -14.477736364903757,
+ "f": -12.540982311769673,
+ "g": -12.540982311769673,
+ "h": -10.604228258635588,
+ "m": -0.8103006163960513,
+ "n": 0.40515030819802567,
+ "o": -0.40515030819802567,
+ "p": 3.8735081062681704,
+ "q": -1.9367540531340843,
+ "r": 1.9367540531340843
},
"backward": false
},
{
"spline": {
"start": {
- "x": 2.0191715439702316,
- "y": -15.853188062398566
+ "x": 2.9295431964180887,
+ "y": -10.604228258635588
},
"control1": {
- "x": 1.5013446687688368,
- "y": -15.829843033033173
+ "x": 2.916326692394934,
+ "y": -10.541048946619375
},
"control2": {
- "x": 1.4635125473892967,
- "y": -15.513188272615256
+ "x": 2.87606164202998,
+ "y": -10.519290881943341
},
"end": {
- "x": 0.9904191949100383,
- "y": -15.725019202644013
- },
- "a": 2.0191715439702316,
- "b": 1.5013446687688368,
- "c": 1.4635125473892967,
- "d": 0.9904191949100383,
- "e": -15.853188062398566,
- "f": -15.829843033033173,
- "g": -15.513188272615256,
- "h": -15.725019202644013,
- "m": -0.9152559849215738,
- "n": 0.47999475382185475,
- "o": -0.5178268752013948,
- "p": -0.8217954214991963,
- "q": 0.2933097310525241,
- "r": 0.023345029365392733
+ "x": 2.8159704965689993,
+ "y": -10.5428568676563
+ },
+ "a": 2.9295431964180887,
+ "b": 2.916326692394934,
+ "c": 2.87606164202998,
+ "d": 2.8159704965689993,
+ "e": -10.604228258635588,
+ "f": -10.541048946619375,
+ "g": -10.519290881943341,
+ "h": -10.5428568676563,
+ "m": 0.00722245124577281,
+ "n": -0.02704854634179954,
+ "o": -0.013216504023154663,
+ "p": -0.003902803048807968,
+ "q": -0.041421247340180045,
+ "r": 0.06317931201621363
},
"backward": false
},
{
"spline": {
"start": {
- "x": 0.9904191949100383,
- "y": -15.725019202644013
+ "x": 2.8159704965689993,
+ "y": -10.5428568676563
},
"control1": {
- "x": -5.458799399697785,
- "y": -18.612702924461736
+ "x": 2.6228881729434446,
+ "y": -10.618578094886676
},
"control2": {
- "x": -5.458799399697786,
- "y": -18.612702924461736
+ "x": 2.4344064730137536,
+ "y": -10.578747078138377
},
"end": {
- "x": -11.90801799430561,
- "y": -21.500386646279456
+ "x": 2.476873183997891,
+ "y": -10.781752167749715
+ },
+ "a": 2.8159704965689993,
+ "b": 2.6228881729434446,
+ "c": 2.4344064730137536,
+ "d": 2.476873183997891,
+ "e": -10.5428568676563,
+ "f": -10.618578094886676,
+ "g": -10.578747078138377,
+ "h": -10.781752167749715,
+ "m": 0.2263477872179651,
+ "n": 0.004600623695863781,
+ "o": -0.19308232362555477,
+ "p": -0.35838835033831096,
+ "q": 0.1155522439786747,
+ "r": -0.07572122723037644
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 2.476873183997891,
+ "y": -10.781752167749715
+ },
+ "control1": {
+ "x": 2.8833323139435074,
+ "y": -12.724762826822843
+ },
+ "control2": {
+ "x": 2.8833323139435074,
+ "y": -12.724762826822841
},
- "a": 0.9904191949100383,
- "b": -5.458799399697785,
- "c": -5.458799399697786,
- "d": -11.90801799430561,
- "e": -15.725019202644013,
- "f": -18.612702924461736,
- "g": -18.612702924461736,
- "h": -21.500386646279456,
- "m": -12.898437189215644,
- "n": 6.449218594607823,
- "o": -6.4492185946078235,
- "p": -5.775367443635446,
- "q": 2.887683721817723,
- "r": -2.887683721817723
+ "end": {
+ "x": 3.289791443889124,
+ "y": -14.66777348589597
+ },
+ "a": 2.476873183997891,
+ "b": 2.8833323139435074,
+ "c": 2.8833323139435074,
+ "d": 3.289791443889124,
+ "e": -10.781752167749715,
+ "f": -12.724762826822843,
+ "g": -12.724762826822841,
+ "h": -14.66777348589597,
+ "m": 0.8129182598912328,
+ "n": -0.4064591299456164,
+ "o": 0.4064591299456164,
+ "p": -3.8860213181462626,
+ "q": 1.9430106590731295,
+ "r": -1.9430106590731278
},
"backward": false
},
{
"spline": {
"start": {
- "x": -11.90801799430561,
- "y": -21.500386646279456
+ "x": 3.289791443889124,
+ "y": -14.66777348589597
},
"control1": {
- "x": -11.994673013612804,
- "y": -21.53918705054493
+ "x": 3.3055998167580922,
+ "y": -14.743342798341029
},
"control2": {
- "x": -12.00422228197611,
- "y": -21.540161301698568
+ "x": 3.289267709713013,
+ "y": -14.774521922777751
},
"end": {
- "x": -12.096927489514846,
- "y": -21.51965988132108
+ "x": 3.2181434061505003,
+ "y": -14.804554556344108
+ },
+ "a": 3.289791443889124,
+ "b": 3.3055998167580922,
+ "c": 3.289267709713013,
+ "d": 3.2181434061505003,
+ "e": -14.66777348589597,
+ "f": -14.743342798341029,
+ "g": -14.774521922777751,
+ "h": -14.804554556344108,
+ "m": -0.02265171660338705,
+ "n": -0.03214047991404767,
+ "o": 0.015808372868968412,
+ "p": -0.04324369713796905,
+ "q": 0.044390188008337716,
+ "r": -0.07556931244505982
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 3.2181434061505003,
+ "y": -14.804554556344108
+ },
+ "control1": {
+ "x": 3.042125330308893,
+ "y": -14.878879169845034
},
- "a": -11.90801799430561,
- "b": -11.994673013612804,
- "c": -12.00422228197611,
- "d": -12.096927489514846,
- "e": -21.500386646279456,
- "f": -21.53918705054493,
- "g": -21.540161301698568,
- "h": -21.51965988132108,
- "m": -0.16026169011932012,
- "n": 0.07710575094388794,
- "o": -0.08665501930719444,
- "p": -0.016350481580698073,
- "q": 0.03782615311183335,
- "r": -0.03880040426547282
+ "control2": {
+ "x": 2.878861558211156,
+ "y": -15.044829168800167
+ },
+ "end": {
+ "x": 2.8397390749641067,
+ "y": -14.85781060688818
+ },
+ "a": 3.2181434061505003,
+ "b": 3.042125330308893,
+ "c": 2.878861558211156,
+ "d": 2.8397390749641067,
+ "e": -14.804554556344108,
+ "f": -14.878879169845034,
+ "g": -15.044829168800167,
+ "h": -14.85781060688818,
+ "m": 0.111386985106817,
+ "n": 0.01275430374387021,
+ "o": -0.17601807584160722,
+ "p": 0.44459394632133353,
+ "q": -0.09162538545420773,
+ "r": -0.07432461350092545
},
"backward": false
},
{
"spline": {
"start": {
- "x": -12.096927489514846,
- "y": -21.51965988132108
+ "x": 2.8397390749641067,
+ "y": -14.85781060688818
},
"control1": {
- "x": -12.171606415925195,
- "y": -21.503144907628382
+ "x": 2.4319711232709,
+ "y": -12.908543341876012
},
"control2": {
- "x": -12.171490344660839,
- "y": -21.48881362312433
+ "x": 2.431971123270899,
+ "y": -12.90854334187601
},
"end": {
- "x": -12.221606487578372,
- "y": -21.43103768847207
+ "x": 2.0242031715776925,
+ "y": -10.959276076863842
+ },
+ "a": 2.8397390749641067,
+ "b": 2.4319711232709,
+ "c": 2.431971123270899,
+ "d": 2.0242031715776925,
+ "e": -14.85781060688818,
+ "f": -12.908543341876012,
+ "g": -12.90854334187601,
+ "h": -10.959276076863842,
+ "m": -0.8155359033864116,
+ "n": 0.4077679516932058,
+ "o": -0.4077679516932067,
+ "p": 3.898534530024328,
+ "q": -1.9492672650121659,
+ "r": 1.9492672650121676
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 2.0242031715776925,
+ "y": -10.959276076863842
+ },
+ "control1": {
+ "x": 2.010986667554538,
+ "y": -10.89609676484763
+ },
+ "control2": {
+ "x": 1.9707216171895836,
+ "y": -10.874338700171595
},
- "a": -12.096927489514846,
- "b": -12.171606415925195,
- "c": -12.171490344660839,
- "d": -12.221606487578372,
- "e": -21.51965988132108,
- "f": -21.503144907628382,
- "g": -21.48881362312433,
- "h": -21.43103768847207,
- "m": -0.12502721185659205,
- "n": 0.07479499767470621,
- "o": -0.07467892641034979,
- "p": 0.04562833933684729,
- "q": -0.002183689188644422,
- "r": 0.016514973692697055
+ "end": {
+ "x": 1.9106304717286033,
+ "y": -10.897904685884553
+ },
+ "a": 2.0242031715776925,
+ "b": 2.010986667554538,
+ "c": 1.9707216171895836,
+ "d": 1.9106304717286033,
+ "e": -10.959276076863842,
+ "f": -10.89609676484763,
+ "g": -10.874338700171595,
+ "h": -10.897904685884553,
+ "m": 0.007222451245773698,
+ "n": -0.02704854634179954,
+ "o": -0.013216504023154663,
+ "p": -0.003902803048818626,
+ "q": -0.04142124734017649,
+ "r": 0.06317931201621185
},
"backward": false
},
{
"spline": {
"start": {
- "x": -12.221606487578372,
- "y": -21.43103768847207
+ "x": 1.9106304717286033,
+ "y": -10.897904685884553
},
"control1": {
- "x": -13.727998538515276,
- "y": -19.69440746058662
+ "x": 1.7175481481030481,
+ "y": -10.97362591311493
},
"control2": {
- "x": -13.72799853851528,
- "y": -19.694407460586625
+ "x": 1.5290664481733567,
+ "y": -10.93379489636663
},
"end": {
- "x": -15.234390589452184,
- "y": -17.957777232701176
+ "x": 1.571533159157494,
+ "y": -11.13679998597797
+ },
+ "a": 1.9106304717286033,
+ "b": 1.7175481481030481,
+ "c": 1.5290664481733567,
+ "d": 1.571533159157494,
+ "e": -10.897904685884553,
+ "f": -10.97362591311493,
+ "g": -10.93379489636663,
+ "h": -11.13679998597797,
+ "m": 0.2263477872179649,
+ "n": 0.004600623695863781,
+ "o": -0.19308232362555522,
+ "p": -0.3583883503383145,
+ "q": 0.11555224397867647,
+ "r": -0.07572122723037644
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 1.571533159157494,
+ "y": -11.13679998597797
+ },
+ "control1": {
+ "x": 1.9806099325982918,
+ "y": -13.09232385692918
+ },
+ "control2": {
+ "x": 1.9806099325982918,
+ "y": -13.09232385692918
},
- "a": -12.221606487578372,
- "b": -13.727998538515276,
- "c": -13.72799853851528,
- "d": -15.234390589452184,
- "e": -21.43103768847207,
- "f": -19.69440746058662,
- "g": -19.694407460586625,
- "h": -17.957777232701176,
- "m": -3.0127841018737964,
- "n": 1.5063920509369009,
- "o": -1.5063920509369044,
- "p": 3.4732604557709017,
- "q": -1.7366302278854526,
- "r": 1.736630227885449
+ "end": {
+ "x": 2.3896867060390896,
+ "y": -15.047847727880393
+ },
+ "a": 1.571533159157494,
+ "b": 1.9806099325982918,
+ "c": 1.9806099325982918,
+ "d": 2.3896867060390896,
+ "e": -11.13679998597797,
+ "f": -13.09232385692918,
+ "g": -13.09232385692918,
+ "h": -15.047847727880393,
+ "m": 0.8181535468815957,
+ "n": -0.40907677344079785,
+ "o": 0.40907677344079785,
+ "p": -3.9110477419024257,
+ "q": 1.955523870951211,
+ "r": -1.955523870951211
},
"backward": false
},
{
"spline": {
"start": {
- "x": -15.234390589452184,
- "y": -17.957777232701176
+ "x": 2.3896867060390896,
+ "y": -15.047847727880393
},
"control1": {
- "x": -15.291979009304963,
- "y": -17.891386952275642
+ "x": 2.405495078908058,
+ "y": -15.123417040325455
},
"control2": {
- "x": -15.296567524920015,
- "y": -17.870595500600423
+ "x": 2.3891629718629797,
+ "y": -15.154596164762177
},
"end": {
- "x": -15.272270985727273,
- "y": -17.7861338310729
+ "x": 2.3180386683004666,
+ "y": -15.184628798328534
+ },
+ "a": 2.3896867060390896,
+ "b": 2.405495078908058,
+ "c": 2.3891629718629797,
+ "d": 2.3180386683004666,
+ "e": -15.047847727880393,
+ "f": -15.123417040325455,
+ "g": -15.154596164762177,
+ "h": -15.184628798328534,
+ "m": -0.02265171660338705,
+ "n": -0.03214047991404678,
+ "o": 0.015808372868968412,
+ "p": -0.04324369713796905,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 2.3180386683004666,
+ "y": -15.184628798328534
+ },
+ "control1": {
+ "x": 2.1420205924588593,
+ "y": -15.25895341182946
+ },
+ "control2": {
+ "x": 1.9787568203611219,
+ "y": -15.424903410784593
},
- "a": -15.234390589452184,
- "b": -15.291979009304963,
- "c": -15.296567524920015,
- "d": -15.272270985727273,
- "e": -17.957777232701176,
- "f": -17.891386952275642,
- "g": -17.870595500600423,
- "h": -17.7861338310729,
- "m": -0.02411484942993347,
- "n": 0.052999904237728046,
- "o": -0.05758841985277918,
- "p": 0.10926904660261982,
- "q": -0.045598828750314624,
- "r": 0.06639028042553363
+ "end": {
+ "x": 1.9396343371140725,
+ "y": -15.237884848872605
+ },
+ "a": 2.3180386683004666,
+ "b": 2.1420205924588593,
+ "c": 1.9787568203611219,
+ "d": 1.9396343371140725,
+ "e": -15.184628798328534,
+ "f": -15.25895341182946,
+ "g": -15.424903410784593,
+ "h": -15.237884848872605,
+ "m": 0.11138698510681833,
+ "n": 0.012754303743869766,
+ "o": -0.17601807584160722,
+ "p": 0.4445939463213282,
+ "q": -0.09162538545420773,
+ "r": -0.07432461350092545
},
"backward": false
},
{
"spline": {
"start": {
- "x": -15.272270985727273,
- "y": -17.7861338310729
+ "x": 1.9396343371140725,
+ "y": -15.237884848872605
},
"control1": {
- "x": -15.257736125265389,
- "y": -17.73560652875953
+ "x": 1.529248741925684,
+ "y": -13.27610437198235
},
"control2": {
- "x": -15.22443266024003,
- "y": -17.763682746628632
+ "x": 1.529248741925684,
+ "y": -13.27610437198235
},
"end": {
- "x": -15.174143938910568,
- "y": -17.748342767659906
+ "x": 1.1188631467372954,
+ "y": -11.314323895092096
+ },
+ "a": 1.9396343371140725,
+ "b": 1.529248741925684,
+ "c": 1.529248741925684,
+ "d": 1.1188631467372954,
+ "e": -15.237884848872605,
+ "f": -13.27610437198235,
+ "g": -13.27610437198235,
+ "h": -11.314323895092096,
+ "m": -0.8207711903767771,
+ "n": 0.4103855951883886,
+ "o": -0.4103855951883886,
+ "p": 3.9235609537805107,
+ "q": -1.9617804768902545,
+ "r": 1.9617804768902545
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 1.1188631467372954,
+ "y": -11.314323895092096
+ },
+ "control1": {
+ "x": 1.1056466427141407,
+ "y": -11.251144583075884
},
- "a": -15.272270985727273,
- "b": -15.257736125265389,
- "c": -15.22443266024003,
- "d": -15.174143938910568,
- "e": -17.7861338310729,
- "f": -17.73560652875953,
- "g": -17.763682746628632,
- "h": -17.748342767659906,
- "m": -0.0017833482593676564,
- "n": 0.01876860456347451,
- "o": 0.014534860461884236,
- "p": 0.12201971702030434,
- "q": -0.07860352018247241,
- "r": 0.05052730231336966
+ "control2": {
+ "x": 1.0653815923491867,
+ "y": -11.22938651839985
+ },
+ "end": {
+ "x": 1.0052904468882065,
+ "y": -11.252952504112807
+ },
+ "a": 1.1188631467372954,
+ "b": 1.1056466427141407,
+ "c": 1.0653815923491867,
+ "d": 1.0052904468882065,
+ "e": -11.314323895092096,
+ "f": -11.251144583075884,
+ "g": -11.22938651839985,
+ "h": -11.252952504112807,
+ "m": 0.007222451245773254,
+ "n": -0.027048546341799318,
+ "o": -0.013216504023154663,
+ "p": -0.0039028030488150733,
+ "q": -0.04142124734017649,
+ "r": 0.06317931201621185
},
"backward": false
},
{
"spline": {
"start": {
- "x": -15.174143938910568,
- "y": -17.748342767659906
+ "x": 1.0052904468882065,
+ "y": -11.252952504112807
},
"control1": {
- "x": -7.86786772485169,
- "y": -15.51964972277024
+ "x": 0.8122081232626508,
+ "y": -11.328673731343184
},
"control2": {
- "x": -7.86786772485169,
- "y": -15.51964972277024
+ "x": 0.6237264233329585,
+ "y": -11.288842714594884
},
"end": {
- "x": -0.5615915107928121,
- "y": -13.290956677880574
+ "x": 0.6661931343170959,
+ "y": -11.491847804206223
+ },
+ "a": 1.0052904468882065,
+ "b": 0.8122081232626508,
+ "c": 0.6237264233329585,
+ "d": 0.6661931343170959,
+ "e": -11.252952504112807,
+ "f": -11.328673731343184,
+ "g": -11.288842714594884,
+ "h": -11.491847804206223,
+ "m": 0.22634778721796645,
+ "n": 0.004600623695863337,
+ "o": -0.19308232362555566,
+ "p": -0.3583883503383145,
+ "q": 0.11555224397867647,
+ "r": -0.07572122723037644
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 0.6661931343170959,
+ "y": -11.491847804206223
+ },
+ "control1": {
+ "x": 1.0778875512530761,
+ "y": -13.45988488703552
+ },
+ "control2": {
+ "x": 1.0778875512530761,
+ "y": -13.45988488703552
},
- "a": -15.174143938910568,
- "b": -7.86786772485169,
- "c": -7.86786772485169,
- "d": -0.5615915107928121,
- "e": -17.748342767659906,
- "f": -15.51964972277024,
- "g": -15.51964972277024,
- "h": -13.290956677880574,
- "m": 14.612552428117755,
- "n": -7.306276214058878,
- "o": 7.306276214058878,
- "p": 4.457386089779334,
- "q": -2.228693044889667,
- "r": 2.228693044889667
+ "end": {
+ "x": 1.4895819681890563,
+ "y": -15.427921969864816
+ },
+ "a": 0.6661931343170959,
+ "b": 1.0778875512530761,
+ "c": 1.0778875512530761,
+ "d": 1.4895819681890563,
+ "e": -11.491847804206223,
+ "f": -13.45988488703552,
+ "g": -13.45988488703552,
+ "h": -15.427921969864816,
+ "m": 0.8233888338719604,
+ "n": -0.4116944169359802,
+ "o": 0.4116944169359802,
+ "p": -3.9360741656585922,
+ "q": 1.9680370828292961,
+ "r": -1.9680370828292961
},
"backward": false
},
{
"spline": {
"start": {
- "x": -0.5615915107928121,
- "y": -13.290956677880574
+ "x": 1.4895819681890563,
+ "y": -15.427921969864816
},
"control1": {
- "x": -0.4709301167919928,
- "y": -13.263301493169541
+ "x": 1.5053903410580247,
+ "y": -15.503491282309877
},
"control2": {
- "x": -0.46104895358031917,
- "y": -13.263581066693543
+ "x": 1.4890582340129461,
+ "y": -15.5346704067466
},
"end": {
- "x": -0.3720962740770675,
- "y": -13.296318177329104
+ "x": 1.417933930450433,
+ "y": -15.564703040312956
+ },
+ "a": 1.4895819681890563,
+ "b": 1.5053903410580247,
+ "c": 1.4890582340129461,
+ "d": 1.417933930450433,
+ "e": -15.427921969864816,
+ "f": -15.503491282309877,
+ "g": -15.5346704067466,
+ "h": -15.564703040312956,
+ "m": -0.02265171660338794,
+ "n": -0.032140479914047004,
+ "o": 0.015808372868968412,
+ "p": -0.04324369713797083,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 1.417933930450433,
+ "y": -15.564703040312956
},
- "a": -0.5615915107928121,
- "b": -0.4709301167919928,
- "c": -0.46104895358031917,
- "d": -0.3720962740770675,
- "e": -13.290956677880574,
- "f": -13.263301493169541,
- "g": -13.263581066693543,
- "h": -13.296318177329104,
- "m": 0.1598517470807238,
- "n": -0.08078023078914565,
- "o": 0.0906613940008193,
- "p": -0.004522778876525635,
- "q": -0.027934758235035773,
- "r": 0.027655184711033343
+ "control1": {
+ "x": 1.241915854608826,
+ "y": -15.639027653813882
+ },
+ "control2": {
+ "x": 1.0786520825110888,
+ "y": -15.804977652769015
+ },
+ "end": {
+ "x": 1.0395295992640392,
+ "y": -15.617959090857028
+ },
+ "a": 1.417933930450433,
+ "b": 1.241915854608826,
+ "c": 1.0786520825110888,
+ "d": 1.0395295992640392,
+ "e": -15.564703040312956,
+ "f": -15.639027653813882,
+ "g": -15.804977652769015,
+ "h": -15.617959090857028,
+ "m": 0.11138698510681766,
+ "n": 0.012754303743869766,
+ "o": -0.176018075841607,
+ "p": 0.44459394632133353,
+ "q": -0.09162538545420773,
+ "r": -0.07432461350092545
},
"backward": false
},
{
"spline": {
"start": {
- "x": -0.3720962740770675,
- "y": -13.296318177329104
+ "x": 1.0395295992640392,
+ "y": -15.617959090857028
},
"control1": {
- "x": -0.30095357208454565,
- "y": -13.322500710773268
+ "x": 0.6265263605804682,
+ "y": -13.643665402088688
},
"control2": {
- "x": -0.3033345461213507,
- "y": -13.337198986744774
+ "x": 0.6265263605804684,
+ "y": -13.643665402088688
},
"end": {
- "x": -0.26246236742891643,
- "y": -13.401044692264295
+ "x": 0.21352312189689737,
+ "y": -11.669371713320349
+ },
+ "a": 1.0395295992640392,
+ "b": 0.6265263605804682,
+ "c": 0.6265263605804684,
+ "d": 0.21352312189689737,
+ "e": -15.617959090857028,
+ "f": -13.643665402088688,
+ "g": -13.643665402088688,
+ "h": -11.669371713320349,
+ "m": -0.8260064773671427,
+ "n": 0.41300323868357125,
+ "o": -0.41300323868357103,
+ "p": 3.948587377536679,
+ "q": -1.9742936887683395,
+ "r": 1.9742936887683395
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 0.21352312189689737,
+ "y": -11.669371713320349
+ },
+ "control1": {
+ "x": 0.20030661787374268,
+ "y": -11.606192401304137
+ },
+ "control2": {
+ "x": 0.1600415675087885,
+ "y": -11.584434336628101
},
- "a": -0.3720962740770675,
- "b": -0.30095357208454565,
- "c": -0.3033345461213507,
- "d": -0.26246236742891643,
- "e": -13.296318177329104,
- "f": -13.322500710773268,
- "g": -13.337198986744774,
- "h": -13.401044692264295,
- "m": 0.11677682875856615,
- "n": -0.07352367602932691,
- "o": 0.07114270199252187,
- "p": -0.06063168702066868,
- "q": 0.011484257472657688,
- "r": -0.02618253344416388
+ "end": {
+ "x": 0.09995042204780843,
+ "y": -11.60800032234106
+ },
+ "a": 0.21352312189689737,
+ "b": 0.20030661787374268,
+ "c": 0.1600415675087885,
+ "d": 0.09995042204780843,
+ "e": -11.669371713320349,
+ "f": -11.606192401304137,
+ "g": -11.584434336628101,
+ "h": -11.60800032234106,
+ "m": 0.007222451245773587,
+ "n": -0.027048546341799484,
+ "o": -0.01321650402315469,
+ "p": -0.0039028030488168497,
+ "q": -0.04142124734017649,
+ "r": 0.06317931201621185
},
"backward": false
},
{
"spline": {
"start": {
- "x": -0.26246236742891643,
- "y": -13.401044692264295
+ "x": 0.09995042204780843,
+ "y": -11.60800032234106
},
"control1": {
- "x": 0.4013314139658026,
- "y": -14.43794518664535
+ "x": -0.093131901577747,
+ "y": -11.683721549571437
},
"control2": {
- "x": 0.4013314139658022,
- "y": -14.43794518664535
+ "x": -0.28161360150743864,
+ "y": -11.643890532823137
},
"end": {
- "x": 1.0651251953605212,
- "y": -15.474845681026409
+ "x": -0.23914689052330118,
+ "y": -11.846895622434477
+ },
+ "a": 0.09995042204780843,
+ "b": -0.093131901577747,
+ "c": -0.28161360150743864,
+ "d": -0.23914689052330118,
+ "e": -11.60800032234106,
+ "f": -11.683721549571437,
+ "g": -11.643890532823137,
+ "h": -11.846895622434477,
+ "m": 0.22634778721796525,
+ "n": 0.004600623695863809,
+ "o": -0.19308232362555544,
+ "p": -0.3583883503383163,
+ "q": 0.11555224397867825,
+ "r": -0.07572122723037822
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -0.23914689052330118,
+ "y": -11.846895622434477
+ },
+ "control1": {
+ "x": 0.1751651699078604,
+ "y": -13.827445917141858
},
- "a": -0.26246236742891643,
- "b": 0.4013314139658026,
- "c": 0.4013314139658022,
- "d": 1.0651251953605212,
- "e": -13.401044692264295,
- "f": -14.43794518664535,
- "g": -14.43794518664535,
- "h": -15.474845681026409,
- "m": 1.3275875627894387,
- "n": -0.6637937813947195,
- "o": 0.663793781394719,
- "p": -2.0738009887621143,
- "q": 1.0369004943810562,
- "r": -1.0369004943810562
+ "control2": {
+ "x": 0.17516516990786052,
+ "y": -13.827445917141858
+ },
+ "end": {
+ "x": 0.5894772303390221,
+ "y": -15.80799621184924
+ },
+ "a": -0.23914689052330118,
+ "b": 0.1751651699078604,
+ "c": 0.17516516990786052,
+ "d": 0.5894772303390221,
+ "e": -11.846895622434477,
+ "f": -13.827445917141858,
+ "g": -13.827445917141858,
+ "h": -15.80799621184924,
+ "m": 0.8286241208623228,
+ "n": -0.4143120604311615,
+ "o": 0.4143120604311616,
+ "p": -3.9611005894147624,
+ "q": 1.9805502947073812,
+ "r": -1.9805502947073812
},
"backward": false
},
{
"spline": {
"start": {
- "x": 1.0651251953605212,
- "y": -15.474845681026409
+ "x": 0.5894772303390221,
+ "y": -15.80799621184924
},
"control1": {
- "x": 1.0899340079337152,
- "y": -15.513599087194363
+ "x": 0.6052856032079905,
+ "y": -15.883565524294301
},
"control2": {
- "x": 1.0651097368178575,
- "y": -15.556634918886447
+ "x": 0.5889534961629119,
+ "y": -15.914744648731023
},
"end": {
- "x": 1.0191422582323126,
- "y": -15.55456258122144
+ "x": 0.5178291926003988,
+ "y": -15.94477728229738
+ },
+ "a": 0.5894772303390221,
+ "b": 0.6052856032079905,
+ "c": 0.5889534961629119,
+ "d": 0.5178291926003988,
+ "e": -15.80799621184924,
+ "f": -15.883565524294301,
+ "g": -15.914744648731023,
+ "h": -15.94477728229738,
+ "m": -0.022651716603387495,
+ "n": -0.032140479914047004,
+ "o": 0.015808372868968412,
+ "p": -0.04324369713797793,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": 0.5178291926003988,
+ "y": -15.94477728229738
+ },
+ "control1": {
+ "x": 0.34181111675879183,
+ "y": -16.019101895798308
+ },
+ "control2": {
+ "x": 0.17854734466105446,
+ "y": -16.18505189475344
},
- "a": 1.0651251953605212,
- "b": 1.0899340079337152,
- "c": 1.0651097368178575,
- "d": 1.0191422582323126,
- "e": -15.474845681026409,
- "f": -15.513599087194363,
- "g": -15.556634918886447,
- "h": -15.55456258122144,
- "m": 0.028489876219364607,
- "n": -0.04963308368905173,
- "o": 0.024808812573194006,
- "p": 0.049390594881224104,
- "q": -0.004282425524129607,
- "r": -0.038753406167954196
+ "end": {
+ "x": 0.139424861414005,
+ "y": -15.998033332841452
+ },
+ "a": 0.5178291926003988,
+ "b": 0.34181111675879183,
+ "c": 0.17854734466105446,
+ "d": 0.139424861414005,
+ "e": -15.94477728229738,
+ "f": -16.019101895798308,
+ "g": -16.18505189475344,
+ "h": -15.998033332841452,
+ "m": 0.11138698510681833,
+ "n": 0.012754303743869655,
+ "o": -0.176018075841607,
+ "p": 0.4445939463213193,
+ "q": -0.09162538545420418,
+ "r": -0.07432461350092723
},
"backward": false
},
{
"spline": {
"start": {
- "x": 1.0191422582323126,
- "y": -15.55456258122144
+ "x": 0.139424861414005,
+ "y": -15.998033332841452
},
"control1": {
- "x": 0.501315383030917,
- "y": -15.531217551856047
+ "x": -0.2761960207647475,
+ "y": -14.011226432195027
},
"control2": {
- "x": 0.4634832616513772,
- "y": -15.214562791438128
+ "x": -0.27619602076474725,
+ "y": -14.011226432195027
},
"end": {
- "x": -0.009610090827881917,
- "y": -15.426393721466885
+ "x": -0.6918169029434997,
+ "y": -12.024419531548602
+ },
+ "a": 0.139424861414005,
+ "b": -0.2761960207647475,
+ "c": -0.27619602076474725,
+ "d": -0.6918169029434997,
+ "e": -15.998033332841452,
+ "f": -14.011226432195027,
+ "g": -14.011226432195027,
+ "h": -12.024419531548602,
+ "m": -0.8312417643575054,
+ "n": 0.4156208821787527,
+ "o": -0.4156208821787525,
+ "p": 3.9736138012928492,
+ "q": -1.9868069006464246,
+ "r": 1.9868069006464246
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -0.6918169029434997,
+ "y": -12.024419531548602
+ },
+ "control1": {
+ "x": -0.7050334069666544,
+ "y": -11.96124021953239
+ },
+ "control2": {
+ "x": -0.7452984573316086,
+ "y": -11.939482154856355
},
- "a": 1.0191422582323126,
- "b": 0.501315383030917,
- "c": 0.4634832616513772,
- "d": -0.009610090827881917,
- "e": -15.55456258122144,
- "f": -15.531217551856047,
- "g": -15.214562791438128,
- "h": -15.426393721466885,
- "m": -0.9152559849215753,
- "n": 0.47999475382185575,
- "o": -0.5178268752013956,
- "p": -0.8217954214992034,
- "q": 0.2933097310525259,
- "r": 0.023345029365392733
+ "end": {
+ "x": -0.8053896027925886,
+ "y": -11.963048140569313
+ },
+ "a": -0.6918169029434997,
+ "b": -0.7050334069666544,
+ "c": -0.7452984573316086,
+ "d": -0.8053896027925886,
+ "e": -12.024419531548602,
+ "f": -11.96124021953239,
+ "g": -11.939482154856355,
+ "h": -11.963048140569313,
+ "m": 0.007222451245773698,
+ "n": -0.02704854634179954,
+ "o": -0.013216504023154663,
+ "p": -0.0039028030488168497,
+ "q": -0.04142124734017649,
+ "r": 0.06317931201621185
},
"backward": false
},
{
"spline": {
"start": {
- "x": -0.009610090827881917,
- "y": -15.426393721466885
+ "x": -0.8053896027925886,
+ "y": -11.963048140569313
},
"control1": {
- "x": -5.867212293991229,
- "y": -18.049176975410383
+ "x": -0.9984719264181443,
+ "y": -12.038769367799691
},
"control2": {
- "x": -5.86721229399123,
- "y": -18.049176975410383
+ "x": -1.1869536263478362,
+ "y": -11.998938351051391
},
"end": {
- "x": -11.724814497154577,
- "y": -20.671960229353882
+ "x": -1.1444869153636987,
+ "y": -12.201943440662731
+ },
+ "a": -0.8053896027925886,
+ "b": -0.9984719264181443,
+ "c": -1.1869536263478362,
+ "d": -1.1444869153636987,
+ "e": -11.963048140569313,
+ "f": -12.038769367799691,
+ "g": -11.998938351051391,
+ "h": -12.201943440662731,
+ "m": 0.22634778721796556,
+ "n": 0.004600623695863781,
+ "o": -0.19308232362555566,
+ "p": -0.3583883503383234,
+ "q": 0.11555224397867825,
+ "r": -0.07572122723037822
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -1.1444869153636987,
+ "y": -12.201943440662731
+ },
+ "control1": {
+ "x": -0.7275572114373554,
+ "y": -14.195006947248197
+ },
+ "control2": {
+ "x": -0.7275572114373554,
+ "y": -14.195006947248197
},
- "a": -0.009610090827881917,
- "b": -5.867212293991229,
- "c": -5.86721229399123,
- "d": -11.724814497154577,
- "e": -15.426393721466885,
- "f": -18.049176975410383,
- "g": -18.049176975410383,
- "h": -20.671960229353882,
- "m": -11.715204406326691,
- "n": 5.857602203163347,
- "o": -5.8576022031633475,
- "p": -5.245566507886993,
- "q": 2.6227832539434974,
- "r": -2.6227832539434974
+ "end": {
+ "x": -0.3106275075110121,
+ "y": -16.188070453833664
+ },
+ "a": -1.1444869153636987,
+ "b": -0.7275572114373554,
+ "c": -0.7275572114373554,
+ "d": -0.3106275075110121,
+ "e": -12.201943440662731,
+ "f": -14.195006947248197,
+ "g": -14.195006947248197,
+ "h": -16.188070453833664,
+ "m": 0.8338594078526866,
+ "n": -0.4169297039263433,
+ "o": 0.4169297039263433,
+ "p": -3.9861270131709325,
+ "q": 1.9930635065854663,
+ "r": -1.9930635065854663
},
"backward": false
},
{
"spline": {
"start": {
- "x": -11.724814497154577,
- "y": -20.671960229353882
+ "x": -0.3106275075110121,
+ "y": -16.188070453833664
},
"control1": {
- "x": -11.811469516461772,
- "y": -20.710760633619355
+ "x": -0.29481913464204373,
+ "y": -16.263639766278725
},
"control2": {
- "x": -11.821018784825078,
- "y": -20.711734884772994
+ "x": -0.3111512416871223,
+ "y": -16.294818890715447
},
"end": {
- "x": -11.913723992363813,
- "y": -20.691233464395506
+ "x": -0.38227554524963536,
+ "y": -16.324851524281804
+ },
+ "a": -0.3106275075110121,
+ "b": -0.29481913464204373,
+ "c": -0.3111512416871223,
+ "d": -0.38227554524963536,
+ "e": -16.188070453833664,
+ "f": -16.263639766278725,
+ "g": -16.294818890715447,
+ "h": -16.324851524281804,
+ "m": -0.022651716603387495,
+ "n": -0.03214047991404695,
+ "o": 0.015808372868968357,
+ "p": -0.04324369713797083,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -0.38227554524963536,
+ "y": -16.324851524281804
+ },
+ "control1": {
+ "x": -0.5582936210912419,
+ "y": -16.399176137782728
},
- "a": -11.724814497154577,
- "b": -11.811469516461772,
- "c": -11.821018784825078,
- "d": -11.913723992363813,
- "e": -20.671960229353882,
- "f": -20.710760633619355,
- "g": -20.711734884772994,
- "h": -20.691233464395506,
- "m": -0.16026169011931835,
- "n": 0.07710575094388794,
- "o": -0.08665501930719444,
- "p": -0.01635048158070518,
- "q": 0.03782615311183335,
- "r": -0.03880040426547282
+ "control2": {
+ "x": -0.7215573931889789,
+ "y": -16.565126136737863
+ },
+ "end": {
+ "x": -0.7606798764360283,
+ "y": -16.378107574825876
+ },
+ "a": -0.38227554524963536,
+ "b": -0.5582936210912419,
+ "c": -0.7215573931889789,
+ "d": -0.7606798764360283,
+ "e": -16.324851524281804,
+ "f": -16.399176137782728,
+ "g": -16.565126136737863,
+ "h": -16.378107574825876,
+ "m": 0.1113869851068181,
+ "n": 0.012754303743869544,
+ "o": -0.17601807584160656,
+ "p": 0.44459394632134064,
+ "q": -0.09162538545421128,
+ "r": -0.07432461350092368
},
"backward": false
},
{
"spline": {
"start": {
- "x": -11.913723992363813,
- "y": -20.691233464395506
+ "x": -0.7606798764360283,
+ "y": -16.378107574825876
},
"control1": {
- "x": -11.988402918774163,
- "y": -20.67471849070281
+ "x": -1.1789184021099626,
+ "y": -14.378787462301366
},
"control2": {
- "x": -11.988286847509807,
- "y": -20.660387206198756
+ "x": -1.1789184021099626,
+ "y": -14.378787462301366
},
"end": {
- "x": -12.03840299042734,
- "y": -20.602611271546497
+ "x": -1.5971569277838968,
+ "y": -12.379467349776856
+ },
+ "a": -0.7606798764360283,
+ "b": -1.1789184021099626,
+ "c": -1.1789184021099626,
+ "d": -1.5971569277838968,
+ "e": -16.378107574825876,
+ "f": -14.378787462301366,
+ "g": -14.378787462301366,
+ "h": -12.379467349776856,
+ "m": -0.8364770513478685,
+ "n": 0.41823852567393427,
+ "o": -0.41823852567393427,
+ "p": 3.9986402250490194,
+ "q": -1.9993201125245097,
+ "r": 1.9993201125245097
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -1.5971569277838968,
+ "y": -12.379467349776856
+ },
+ "control1": {
+ "x": -1.6103734318070515,
+ "y": -12.316288037760645
+ },
+ "control2": {
+ "x": -1.6506384821720057,
+ "y": -12.29452997308461
},
- "a": -11.913723992363813,
- "b": -11.988402918774163,
- "c": -11.988286847509807,
- "d": -12.03840299042734,
- "e": -20.691233464395506,
- "f": -20.67471849070281,
- "g": -20.660387206198756,
- "h": -20.602611271546497,
- "m": -0.12502721185659382,
- "n": 0.07479499767470621,
- "o": -0.07467892641034979,
- "p": 0.04562833933684729,
- "q": -0.002183689188644422,
- "r": 0.016514973692697055
+ "end": {
+ "x": -1.710729627632986,
+ "y": -12.318095958797567
+ },
+ "a": -1.5971569277838968,
+ "b": -1.6103734318070515,
+ "c": -1.6506384821720057,
+ "d": -1.710729627632986,
+ "e": -12.379467349776856,
+ "f": -12.316288037760645,
+ "g": -12.29452997308461,
+ "h": -12.318095958797567,
+ "m": 0.00722245124577281,
+ "n": -0.02704854634179954,
+ "o": -0.013216504023154663,
+ "p": -0.0039028030488168497,
+ "q": -0.04142124734017649,
+ "r": 0.06317931201621185
},
"backward": false
},
{
"spline": {
"start": {
- "x": -12.03840299042734,
- "y": -20.602611271546497
+ "x": -1.710729627632986,
+ "y": -12.318095958797567
},
"control1": {
- "x": -13.035577240069488,
- "y": -19.453028104720506
+ "x": -1.903811951258541,
+ "y": -12.393817186027944
},
"control2": {
- "x": -13.035577240069491,
- "y": -19.453028104720506
+ "x": -2.0922936511882324,
+ "y": -12.353986169279645
},
"end": {
- "x": -14.03275148971164,
- "y": -18.303444937894515
+ "x": -2.049826940204095,
+ "y": -12.556991258890983
+ },
+ "a": -1.710729627632986,
+ "b": -1.903811951258541,
+ "c": -2.0922936511882324,
+ "d": -2.049826940204095,
+ "e": -12.318095958797567,
+ "f": -12.393817186027944,
+ "g": -12.353986169279645,
+ "h": -12.556991258890983,
+ "m": 0.22634778721796578,
+ "n": 0.004600623695863559,
+ "o": -0.193082323625555,
+ "p": -0.35838835033830563,
+ "q": 0.1155522439786747,
+ "r": -0.07572122723037644
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -2.049826940204095,
+ "y": -12.556991258890983
},
- "a": -12.03840299042734,
- "b": -13.035577240069488,
- "c": -13.035577240069491,
- "d": -14.03275148971164,
- "e": -20.602611271546497,
- "f": -19.453028104720506,
- "g": -19.453028104720506,
- "h": -18.303444937894515,
- "m": -1.9943484992842926,
- "n": 0.9971742496421445,
- "o": -0.9971742496421481,
- "p": 2.2991663336519856,
- "q": -1.149583166825991,
- "r": 1.149583166825991
+ "control1": {
+ "x": -1.63027959278257,
+ "y": -14.562567977354536
+ },
+ "control2": {
+ "x": -1.63027959278257,
+ "y": -14.562567977354536
+ },
+ "end": {
+ "x": -1.210732245361045,
+ "y": -16.568144695818088
+ },
+ "a": -2.049826940204095,
+ "b": -1.63027959278257,
+ "c": -1.63027959278257,
+ "d": -1.210732245361045,
+ "e": -12.556991258890983,
+ "f": -14.562567977354536,
+ "g": -14.562567977354536,
+ "h": -16.568144695818088,
+ "m": 0.83909469484305,
+ "n": -0.419547347421525,
+ "o": 0.419547347421525,
+ "p": -4.0111534369271045,
+ "q": 2.005576718463553,
+ "r": -2.005576718463553
},
"backward": false
},
{
"spline": {
"start": {
- "x": -14.03275148971164,
- "y": -18.303444937894515
+ "x": -1.210732245361045,
+ "y": -16.568144695818088
},
"control1": {
- "x": -14.090339909564419,
- "y": -18.23705465746898
+ "x": -1.1949238724920765,
+ "y": -16.64371400826315
},
"control2": {
- "x": -14.09492842517947,
- "y": -18.216263205793762
+ "x": -1.2112559795371551,
+ "y": -16.67489313269987
},
"end": {
- "x": -14.070631885986728,
- "y": -18.131801536266238
+ "x": -1.2823802830996682,
+ "y": -16.70492576626623
+ },
+ "a": -1.210732245361045,
+ "b": -1.1949238724920765,
+ "c": -1.2112559795371551,
+ "d": -1.2823802830996682,
+ "e": -16.568144695818088,
+ "f": -16.64371400826315,
+ "g": -16.67489313269987,
+ "h": -16.70492576626623,
+ "m": -0.02265171660338705,
+ "n": -0.032140479914047004,
+ "o": 0.015808372868968412,
+ "p": -0.04324369713797793,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -1.2823802830996682,
+ "y": -16.70492576626623
+ },
+ "control1": {
+ "x": -1.458398358941275,
+ "y": -16.779250379767156
+ },
+ "control2": {
+ "x": -1.6216621310390122,
+ "y": -16.945200378722287
},
- "a": -14.03275148971164,
- "b": -14.090339909564419,
- "c": -14.09492842517947,
- "d": -14.070631885986728,
- "e": -18.303444937894515,
- "f": -18.23705465746898,
- "g": -18.216263205793762,
- "h": -18.131801536266238,
- "m": -0.024114849429931695,
- "n": 0.052999904237728046,
- "o": -0.05758841985277918,
- "p": 0.10926904660261627,
- "q": -0.045598828750314624,
- "r": 0.06639028042553363
+ "end": {
+ "x": -1.6607846142860616,
+ "y": -16.7581818168103
+ },
+ "a": -1.2823802830996682,
+ "b": -1.458398358941275,
+ "c": -1.6216621310390122,
+ "d": -1.6607846142860616,
+ "e": -16.70492576626623,
+ "f": -16.779250379767156,
+ "g": -16.945200378722287,
+ "h": -16.7581818168103,
+ "m": 0.1113869851068181,
+ "n": 0.012754303743869544,
+ "o": -0.17601807584160678,
+ "p": 0.4445939463213193,
+ "q": -0.09162538545420418,
+ "r": -0.07432461350092723
},
"backward": false
},
{
"spline": {
"start": {
- "x": -14.070631885986728,
- "y": -18.131801536266238
+ "x": -1.6607846142860616,
+ "y": -16.7581818168103
},
"control1": {
- "x": -14.056097025524844,
- "y": -18.08127423395287
+ "x": -2.0816407834551773,
+ "y": -14.746348492407705
},
"control2": {
- "x": -14.022793560499485,
- "y": -18.10935045182197
+ "x": -2.0816407834551773,
+ "y": -14.746348492407705
},
"end": {
- "x": -13.972504839170023,
- "y": -18.094010472853245
+ "x": -2.502496952624293,
+ "y": -12.73451516800511
+ },
+ "a": -1.6607846142860616,
+ "b": -2.0816407834551773,
+ "c": -2.0816407834551773,
+ "d": -2.502496952624293,
+ "e": -16.7581818168103,
+ "f": -14.746348492407705,
+ "g": -14.746348492407705,
+ "h": -12.73451516800511,
+ "m": -0.8417123383382314,
+ "n": 0.4208561691691157,
+ "o": -0.4208561691691157,
+ "p": 4.0236666488051895,
+ "q": -2.0118333244025948,
+ "r": 2.0118333244025948
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -2.502496952624293,
+ "y": -12.73451516800511
+ },
+ "control1": {
+ "x": -2.5157134566474477,
+ "y": -12.671335855988897
},
- "a": -14.070631885986728,
- "b": -14.056097025524844,
- "c": -14.022793560499485,
- "d": -13.972504839170023,
- "e": -18.131801536266238,
- "f": -18.08127423395287,
- "g": -18.10935045182197,
- "h": -18.094010472853245,
- "m": -0.0017833482593729855,
- "n": 0.01876860456347451,
- "o": 0.014534860461884236,
- "p": 0.1220197170203079,
- "q": -0.07860352018247241,
- "r": 0.05052730231336966
+ "control2": {
+ "x": -2.5559785070124015,
+ "y": -12.649577791312861
+ },
+ "end": {
+ "x": -2.6160696524733824,
+ "y": -12.67314377702582
+ },
+ "a": -2.502496952624293,
+ "b": -2.5157134566474477,
+ "c": -2.5559785070124015,
+ "d": -2.6160696524733824,
+ "e": -12.73451516800511,
+ "f": -12.671335855988897,
+ "g": -12.649577791312861,
+ "h": -12.67314377702582,
+ "m": 0.0072224512457719214,
+ "n": -0.027048546341799096,
+ "o": -0.013216504023154663,
+ "p": -0.0039028030488168497,
+ "q": -0.04142124734017827,
+ "r": 0.06317931201621363
},
"backward": false
},
{
"spline": {
"start": {
- "x": -13.972504839170023,
- "y": -18.094010472853245
+ "x": -2.6160696524733824,
+ "y": -12.67314377702582
},
"control1": {
- "x": -7.414678997430931,
- "y": -16.093623351578195
+ "x": -2.8091519760989376,
+ "y": -12.748865004256196
},
"control2": {
- "x": -7.41467899743093,
- "y": -16.093623351578195
+ "x": -2.997633676028629,
+ "y": -12.709033987507897
},
"end": {
- "x": -0.8568531556918383,
- "y": -14.093236230303141
+ "x": -2.9551669650444916,
+ "y": -12.912039077119235
+ },
+ "a": -2.6160696524733824,
+ "b": -2.8091519760989376,
+ "c": -2.997633676028629,
+ "d": -2.9551669650444916,
+ "e": -12.67314377702582,
+ "f": -12.748865004256196,
+ "g": -12.709033987507897,
+ "h": -12.912039077119235,
+ "m": 0.226347787217966,
+ "n": 0.004600623695863781,
+ "o": -0.19308232362555522,
+ "p": -0.3583883503383074,
+ "q": 0.1155522439786747,
+ "r": -0.07572122723037644
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -2.9551669650444916,
+ "y": -12.912039077119235
+ },
+ "control1": {
+ "x": -2.5330019741277847,
+ "y": -14.930129007460874
+ },
+ "control2": {
+ "x": -2.5330019741277847,
+ "y": -14.930129007460874
},
- "a": -13.972504839170023,
- "b": -7.414678997430931,
- "c": -7.41467899743093,
- "d": -0.8568531556918383,
- "e": -18.094010472853245,
- "f": -16.093623351578195,
- "g": -16.093623351578195,
- "h": -14.093236230303141,
- "m": 13.115651683478186,
- "n": -6.557825841739091,
- "o": 6.557825841739092,
- "p": 4.000774242550104,
- "q": -2.0003871212750504,
- "r": 2.0003871212750504
+ "end": {
+ "x": -2.110836983211078,
+ "y": -16.948218937802512
+ },
+ "a": -2.9551669650444916,
+ "b": -2.5330019741277847,
+ "c": -2.5330019741277847,
+ "d": -2.110836983211078,
+ "e": -12.912039077119235,
+ "f": -14.930129007460874,
+ "g": -14.930129007460874,
+ "h": -16.948218937802512,
+ "m": 0.8443299818334138,
+ "n": -0.4221649909167069,
+ "o": 0.4221649909167069,
+ "p": -4.036179860683276,
+ "q": 2.018089930341638,
+ "r": -2.018089930341638
},
"backward": false
},
{
"spline": {
"start": {
- "x": -0.8568531556918383,
- "y": -14.093236230303141
+ "x": -2.110836983211078,
+ "y": -16.948218937802512
},
"control1": {
- "x": -0.766191761691019,
- "y": -14.065581045592108
+ "x": -2.0950286103421094,
+ "y": -17.023788250247573
},
"control2": {
- "x": -0.7563105984793453,
- "y": -14.06586061911611
+ "x": -2.111360717387188,
+ "y": -17.054967374684296
},
"end": {
- "x": -0.6673579189760938,
- "y": -14.09859772975167
+ "x": -2.182485020949701,
+ "y": -17.085000008250653
+ },
+ "a": -2.110836983211078,
+ "b": -2.0950286103421094,
+ "c": -2.111360717387188,
+ "d": -2.182485020949701,
+ "e": -16.948218937802512,
+ "f": -17.023788250247573,
+ "g": -17.054967374684296,
+ "h": -17.085000008250653,
+ "m": -0.02265171660338705,
+ "n": -0.03214047991404678,
+ "o": 0.015808372868968412,
+ "p": -0.04324369713797083,
+ "q": 0.04439018800833949,
+ "r": -0.0755693124450616
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -2.182485020949701,
+ "y": -17.085000008250653
+ },
+ "control1": {
+ "x": -2.358503096791308,
+ "y": -17.15932462175158
+ },
+ "control2": {
+ "x": -2.5217668688890456,
+ "y": -17.32527462070671
},
- "a": -0.8568531556918383,
- "b": -0.766191761691019,
- "c": -0.7563105984793453,
- "d": -0.6673579189760938,
- "e": -14.093236230303141,
- "f": -14.065581045592108,
- "g": -14.06586061911611,
- "h": -14.09859772975167,
- "m": 0.15985174708072336,
- "n": -0.08078023078914565,
- "o": 0.0906613940008193,
- "p": -0.004522778876516753,
- "q": -0.027934758235035773,
- "r": 0.027655184711033343
+ "end": {
+ "x": -2.560889352136095,
+ "y": -17.138256058794724
+ },
+ "a": -2.182485020949701,
+ "b": -2.358503096791308,
+ "c": -2.5217668688890456,
+ "d": -2.560889352136095,
+ "e": -17.085000008250653,
+ "f": -17.15932462175158,
+ "g": -17.32527462070671,
+ "h": -17.138256058794724,
+ "m": 0.11138698510681833,
+ "n": 0.012754303743869766,
+ "o": -0.17601807584160722,
+ "p": 0.4445939463213264,
+ "q": -0.09162538545420418,
+ "r": -0.07432461350092723
},
"backward": false
},
{
"spline": {
"start": {
- "x": -0.6673579189760938,
- "y": -14.09859772975167
+ "x": -2.560889352136095,
+ "y": -17.138256058794724
},
"control1": {
- "x": -0.5962152169835718,
- "y": -14.124780263195834
+ "x": -2.9843631648003925,
+ "y": -15.113909522514044
},
"control2": {
- "x": -0.5985961910203768,
- "y": -14.13947853916734
+ "x": -2.9843631648003925,
+ "y": -15.113909522514044
},
"end": {
- "x": -0.5577240123279424,
- "y": -14.203324244686861
+ "x": -3.40783697746469,
+ "y": -13.089562986233364
+ },
+ "a": -2.560889352136095,
+ "b": -2.9843631648003925,
+ "c": -2.9843631648003925,
+ "d": -3.40783697746469,
+ "e": -17.138256058794724,
+ "f": -15.113909522514044,
+ "g": -15.113909522514044,
+ "h": -13.089562986233364,
+ "m": -0.8469476253285952,
+ "n": 0.4234738126642976,
+ "o": -0.4234738126642976,
+ "p": 4.048693072561363,
+ "q": -2.02434653628068,
+ "r": 2.02434653628068
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -3.40783697746469,
+ "y": -13.089562986233364
+ },
+ "control1": {
+ "x": -3.421053481487845,
+ "y": -13.02638367421715
+ },
+ "control2": {
+ "x": -3.4613185318527986,
+ "y": -13.004625609541115
},
- "a": -0.6673579189760938,
- "b": -0.5962152169835718,
- "c": -0.5985961910203768,
- "d": -0.5577240123279424,
- "e": -14.09859772975167,
- "f": -14.124780263195834,
- "g": -14.13947853916734,
- "h": -14.203324244686861,
- "m": 0.11677682875856632,
- "n": -0.07352367602932686,
- "o": 0.07114270199252193,
- "p": -0.06063168702067756,
- "q": 0.011484257472657688,
- "r": -0.02618253344416388
+ "end": {
+ "x": -3.5214096773137795,
+ "y": -13.028191595254073
+ },
+ "a": -3.40783697746469,
+ "b": -3.421053481487845,
+ "c": -3.4613185318527986,
+ "d": -3.5214096773137795,
+ "e": -13.089562986233364,
+ "f": -13.02638367421715,
+ "g": -13.004625609541115,
+ "h": -13.028191595254073,
+ "m": 0.00722245124577281,
+ "n": -0.027048546341799096,
+ "o": -0.013216504023154663,
+ "p": -0.003902803048813297,
+ "q": -0.04142124734017827,
+ "r": 0.06317931201621363
},
"backward": false
},
{
"spline": {
"start": {
- "x": -0.5577240123279424,
- "y": -14.203324244686861
+ "x": -3.5214096773137795,
+ "y": -13.028191595254073
},
"control1": {
- "x": -0.24631405135267054,
- "y": -14.68977222226807
+ "x": -3.7144920009393347,
+ "y": -13.10391282248445
},
"control2": {
- "x": -0.24631405135267095,
- "y": -14.689772222268072
+ "x": -3.9029737008690257,
+ "y": -13.064081805736151
},
"end": {
- "x": 0.06509590962260095,
- "y": -15.176220199849281
+ "x": -3.8605069898848883,
+ "y": -13.26708689534749
+ },
+ "a": -3.5214096773137795,
+ "b": -3.7144920009393347,
+ "c": -3.9029737008690257,
+ "d": -3.8605069898848883,
+ "e": -13.028191595254073,
+ "f": -13.10391282248445,
+ "g": -13.064081805736151,
+ "h": -13.26708689534749,
+ "m": 0.22634778721796334,
+ "n": 0.0046006236958642255,
+ "o": -0.19308232362555522,
+ "p": -0.3583883503383145,
+ "q": 0.1155522439786747,
+ "r": -0.07572122723037644
+ },
+ "backward": false
+ },
+ {
+ "spline": {
+ "start": {
+ "x": -3.8605069898848883,
+ "y": -13.26708689534749
+ },
+ "control1": {
+ "x": -3.4357243554729995,
+ "y": -15.297690037567211
},
- "a": -0.5577240123279424,
- "b": -0.24631405135267054,
- "c": -0.24631405135267095,
- "d": 0.06509590962260095,
- "e": -14.203324244686861,
- "f": -14.68977222226807,
- "g": -14.689772222268072,
- "h": -15.176220199849281,
- "m": 0.6228199219505446,
- "n": -0.3114099609752723,
- "o": 0.3114099609752719,
- "p": -0.9728959551624214,
- "q": 0.48644797758120717,
- "r": -0.48644797758120895
+ "control2": {
+ "x": -3.435724355473,
+ "y": -15.297690037567211
+ },
+ "end": {
+ "x": -3.010941721061111,
+ "y": -17.328293179786932
+ },
+ "a": -3.8605069898848883,
+ "b": -3.4357243554729995,
+ "c": -3.435724355473,
+ "d": -3.010941721061111,
+ "e": -13.26708689534749,
+ "f": -15.297690037567211,
+ "g": -15.297690037567211,
+ "h": -17.328293179786932,
+ "m": 0.8495652688237789,
+ "n": -0.42478263441188924,
+ "o": 0.4247826344118888,
+ "p": -4.061206284439443,
+ "q": 2.0306031422197215,
+ "r": -2.0306031422197215
},
"backward": false
}
@@ -3162,35 +7362,35 @@
"current_path_segment": {
"spline": {
"start": {
- "x": -13.972504839170023,
- "y": -18.094010472853245
+ "x": 0.6661931343170959,
+ "y": -11.491847804206223
},
"control1": {
- "x": -7.414678997430931,
- "y": -16.093623351578195
+ "x": 1.0778875512530761,
+ "y": -13.45988488703552
},
"control2": {
- "x": -7.41467899743093,
- "y": -16.093623351578195
+ "x": 1.0778875512530761,
+ "y": -13.45988488703552
},
"end": {
- "x": -0.8568531556918383,
- "y": -14.093236230303141
- },
- "a": -13.972504839170023,
- "b": -7.414678997430931,
- "c": -7.41467899743093,
- "d": -0.8568531556918383,
- "e": -18.094010472853245,
- "f": -16.093623351578195,
- "g": -16.093623351578195,
- "h": -14.093236230303141,
- "m": 13.115651683478186,
- "n": -6.557825841739091,
- "o": 6.557825841739092,
- "p": 4.000774242550104,
- "q": -2.0003871212750504,
- "r": 2.0003871212750504
+ "x": 1.4895819681890563,
+ "y": -15.427921969864816
+ },
+ "a": 0.6661931343170959,
+ "b": 1.0778875512530761,
+ "c": 1.0778875512530761,
+ "d": 1.4895819681890563,
+ "e": -11.491847804206223,
+ "f": -13.45988488703552,
+ "g": -13.45988488703552,
+ "h": -15.427921969864816,
+ "m": 0.8233888338719604,
+ "n": -0.4116944169359802,
+ "o": 0.4116944169359802,
+ "p": -3.9360741656585922,
+ "q": 1.9680370828292961,
+ "r": -1.9680370828292961
},
"backward": false
}
diff --git a/backup/rb28/field_friend.automations.weeding.json b/backup/rb28/field_friend.automations.weeding.json
new file mode 100644
index 00000000..efd3fd67
--- /dev/null
+++ b/backup/rb28/field_friend.automations.weeding.json
@@ -0,0 +1,26 @@
+{
+ "use_field_planning": true,
+ "field": null,
+ "start_row_id": null,
+ "end_row_id": null,
+ "minimum_turning_radius": 0.5,
+ "turn_offset": 0.4,
+ "only_monitoring": false,
+ "drill_with_open_tornado": false,
+ "drill_between_crops": false,
+ "only_drilling": false,
+ "only_chopping": false,
+ "chop_if_no_crops": false,
+ "tornado_angle": 110.0,
+ "weed_screw_depth": 0.15,
+ "crop_safety_distance": 0.01,
+ "linear_speed_on_row": 0.5,
+ "angular_speed_on_row": 0.5,
+ "linear_speed_between_rows": 0.5,
+ "angular_speed_between_rows": 0.5,
+ "sorted_weeding_rows": [],
+ "weeding_plan": [],
+ "turn_paths": [],
+ "current_row": null,
+ "current_segment": null
+}
\ No newline at end of file
diff --git a/backup/rb28/field_friend.navigation.gnss.json b/backup/rb28/field_friend.navigation.gnss.json
index dc185d46..407aec13 100644
--- a/backup/rb28/field_friend.navigation.gnss.json
+++ b/backup/rb28/field_friend.navigation.gnss.json
@@ -1,4 +1,13 @@
{
- "reference_lat": 51.983140848333335,
- "reference_lon": 7.434131408333333
+ "record": {
+ "timestamp": 1714383598.7,
+ "latitude": 53.110880316666666,
+ "longitude": 13.91411435,
+ "mode": "DAAA",
+ "gps_qual": 2,
+ "altitude": 62.1027,
+ "separation": "43.2568",
+ "heading": 0.0,
+ "speed_kmh": 0.0
+ }
}
\ No newline at end of file
diff --git a/backup/rb28/field_friend.vision.calibratable_usb_camera_provider.json b/backup/rb28/field_friend.vision.calibratable_usb_camera_provider.json
new file mode 100644
index 00000000..ecfc8049
--- /dev/null
+++ b/backup/rb28/field_friend.vision.calibratable_usb_camera_provider.json
@@ -0,0 +1,93 @@
+{
+ "cameras": {
+ "32e4-9230-platform-3610000.xhci-usb-0:2.1:1.0": {
+ "id": "32e4-9230-platform-3610000.xhci-usb-0:2.1:1.0",
+ "name": "32e4-9230-platform-3610000.xhci-usb-0:2.1:1.0",
+ "connect_after_init": true,
+ "streaming": true,
+ "focal_length": 1830,
+ "calibration": {
+ "intrinsics": {
+ "matrix": [
+ [
+ 730.672004356113,
+ 0.0,
+ 349.4579020458706
+ ],
+ [
+ 0.0,
+ 747.3229371982118,
+ 306.47658848091226
+ ],
+ [
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ ],
+ "distortion": [
+ -0.4433783071214983,
+ 0.26015075527427883,
+ 0.0038723050893755035,
+ -0.000710634350529248,
+ -0.09040485124205291
+ ],
+ "rotation": {
+ "R": [
+ [
+ 1.0,
+ 0.0,
+ 0.0
+ ],
+ [
+ 0.0,
+ 1.0,
+ 0.0
+ ],
+ [
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ ]
+ },
+ "size": {
+ "width": 780,
+ "height": 720
+ }
+ },
+ "extrinsics": {
+ "rotation": {
+ "R": [
+ [
+ -0.01439018074828502,
+ 0.7644481697651403,
+ -0.6445245677557675
+ ],
+ [
+ 0.9997806209878847,
+ 0.0011890510028333967,
+ -0.02091162487212134
+ ],
+ [
+ -0.015219480776667554,
+ -0.6446840946544593,
+ -0.7642975765396935
+ ]
+ ]
+ },
+ "translation": [
+ 0.3088200185097347,
+ 0.004181817389632946,
+ 0.31627615299671547
+ ]
+ }
+ },
+ "auto_exposure": true,
+ "exposure": false,
+ "width": 1280,
+ "height": 720,
+ "fps": 9
+ }
+ }
+}
\ No newline at end of file
diff --git a/backup/rb28/field_friend.vision.usb_cam_provider.json b/backup/rb28/field_friend.vision.usb_cam_provider.json
index be58249f..d61eb546 100644
--- a/backup/rb28/field_friend.vision.usb_cam_provider.json
+++ b/backup/rb28/field_friend.vision.usb_cam_provider.json
@@ -1,21 +1,88 @@
{
"cameras": {
- "HD_USB_Camera_HD_USB_Camera": {
- "id": "HD_USB_Camera_HD_USB_Camera",
- "name": "HD_USB_Camera_HD_USB_Camera",
- "connect_after_init": true,
- "streaming": true,
- "auto_exposure": true,
- "exposure": false,
- "width": 1920,
- "height": 1080,
- "fps": 6
- },
"32e4-9230-platform-3610000.xhci-usb-0:2.1:1.0": {
"id": "32e4-9230-platform-3610000.xhci-usb-0:2.1:1.0",
"name": "32e4-9230-platform-3610000.xhci-usb-0:2.1:1.0",
"connect_after_init": true,
"streaming": true,
+ "focal_length": 1830,
+ "calibration": {
+ "intrinsics": {
+ "matrix": [
+ [
+ 1079.9508535164803,
+ 0.0,
+ 544.1826309856062
+ ],
+ [
+ 0.0,
+ 1101.1475820425799,
+ 403.07410538903343
+ ],
+ [
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ ],
+ "distortion": [
+ -0.3826870922741008,
+ 0.01375680101360712,
+ 0.004251100356821879,
+ 0.0006867591075843546,
+ 0.18994139003680205
+ ],
+ "rotation": {
+ "R": [
+ [
+ 1.0,
+ 0.0,
+ 0.0
+ ],
+ [
+ 0.0,
+ 1.0,
+ 0.0
+ ],
+ [
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ ]
+ },
+ "size": {
+ "width": 1220,
+ "height": 980
+ }
+ },
+ "extrinsics": {
+ "rotation": {
+ "R": [
+ [
+ -0.018691479240049325,
+ 0.7690563101689658,
+ -0.6389076775193069
+ ],
+ [
+ 0.999686091558257,
+ 0.0037122194868790537,
+ -0.024777767685117014
+ ],
+ [
+ -0.016683733059336223,
+ -0.6391702521361403,
+ -0.7688842837745014
+ ]
+ ]
+ },
+ "translation": [
+ 0.3047716549397838,
+ 0.00466836222855056,
+ 0.3154736356456431
+ ]
+ }
+ },
"auto_exposure": true,
"exposure": false,
"width": 1920,
diff --git a/backup/rb28/rosys.pathplanning.path_planner.json b/backup/rb28/rosys.pathplanning.path_planner.json
index 30fe16ff..f33b9a7f 100644
--- a/backup/rb28/rosys.pathplanning.path_planner.json
+++ b/backup/rb28/rosys.pathplanning.path_planner.json
@@ -1,26 +1,132 @@
{
- "obstacles": {},
+ "obstacles": {
+ "row_bb0bf3d5-419d-49f0-b8ef-598f48b25f02": {
+ "id": "row_bb0bf3d5-419d-49f0-b8ef-598f48b25f02",
+ "outline": [
+ {
+ "x": -8.653099477350798,
+ "y": -32.84647608886028
+ },
+ {
+ "x": -8.653099477350798,
+ "y": -4.566697692851725
+ },
+ {
+ "x": -2.440510181265307,
+ "y": -4.566697692851725
+ },
+ {
+ "x": -2.440510181265307,
+ "y": -32.84647608886028
+ }
+ ]
+ },
+ "row_d7745165-3fb1-4b0d-8b6d-1f01c0037a80": {
+ "id": "row_d7745165-3fb1-4b0d-8b6d-1f01c0037a80",
+ "outline": [
+ {
+ "x": -15.15901450292266,
+ "y": -30.805428384420015
+ },
+ {
+ "x": -15.15901450292266,
+ "y": -3.2768422375404103
+ },
+ {
+ "x": -9.085392068597827,
+ "y": -3.2768422375404103
+ },
+ {
+ "x": -9.085392068597827,
+ "y": -30.805428384420015
+ }
+ ]
+ },
+ "row_04939b09-604c-470e-bcbc-a04786f36229": {
+ "id": "row_04939b09-604c-470e-bcbc-a04786f36229",
+ "outline": [
+ {
+ "x": -10.748030900121053,
+ "y": -31.412360766999765
+ },
+ {
+ "x": -10.748030900121053,
+ "y": -4.237278556612357
+ },
+ {
+ "x": -4.680963230413845,
+ "y": -4.237278556612357
+ },
+ {
+ "x": -4.680963230413845,
+ "y": -31.412360766999765
+ }
+ ]
+ },
+ "row_71a7bbfa-5d58-43c2-97bd-e8c09fbd94e4": {
+ "id": "row_71a7bbfa-5d58-43c2-97bd-e8c09fbd94e4",
+ "outline": [
+ {
+ "x": -17.267664661646293,
+ "y": -30.234691581604128
+ },
+ {
+ "x": -17.267664661646293,
+ "y": -2.634428410873591
+ },
+ {
+ "x": -11.153551086545443,
+ "y": -2.634428410873591
+ },
+ {
+ "x": -11.153551086545443,
+ "y": -30.234691581604128
+ }
+ ]
+ },
+ "row_2f32bcec-e3d5-49bb-bdda-05aa36e7acd8": {
+ "id": "row_2f32bcec-e3d5-49bb-bdda-05aa36e7acd8",
+ "outline": [
+ {
+ "x": -12.928431467082303,
+ "y": -31.136975020580866
+ },
+ {
+ "x": -12.928431467082303,
+ "y": -3.6884484944825418
+ },
+ {
+ "x": -6.791170875092781,
+ "y": -3.6884484944825418
+ },
+ {
+ "x": -6.791170875092781,
+ "y": -31.136975020580866
+ }
+ ]
+ }
+ },
"areas": {
- "2a621665-b0f0-443c-8252-3bd42c997845": {
+ "c94823e6-1738-4d8a-995b-72e021fd7951": {
"outline": [
{
- "x": 4.14008820351158,
- "y": -16.529322089550206
+ "x": -0.05365968084014772,
+ "y": -6.5714156384155456e-18
},
{
- "x": -12.634128191514506,
- "y": -24.040097374894728
+ "x": -21.424643655025765,
+ "y": 3.99701288123126
},
{
- "x": -18.930759085830115,
- "y": -16.78108433237046
+ "x": -28.98852605656782,
+ "y": -31.834266595067685
},
{
- "x": 0.5057610077650115,
- "y": -10.85220402722948
+ "x": -7.397976402045103,
+ "y": -36.933732669956406
}
],
- "id": "2a621665-b0f0-443c-8252-3bd42c997845",
+ "id": "c94823e6-1738-4d8a-995b-72e021fd7951",
"type": null,
"color": "green",
"closed": true
diff --git a/backup/rb28/rosys.vision.mjpeg_camera.mjpeg_camera_provider.json b/backup/rb28/rosys.vision.mjpeg_camera.mjpeg_camera_provider.json
new file mode 100644
index 00000000..ee111faa
--- /dev/null
+++ b/backup/rb28/rosys.vision.mjpeg_camera.mjpeg_camera_provider.json
@@ -0,0 +1,36 @@
+{
+ "cameras": {
+ "b8:a4:4f:8f:8b:77-1": {
+ "id": "b8:a4:4f:8f:8b:77-1",
+ "name": "b8:a4:4f:8f:8b:77-1",
+ "connect_after_init": false,
+ "streaming": true,
+ "username": "root",
+ "password": "zauberzg!"
+ },
+ "b8:a4:4f:8f:8b:77-2": {
+ "id": "b8:a4:4f:8f:8b:77-2",
+ "name": "b8:a4:4f:8f:8b:77-2",
+ "connect_after_init": false,
+ "streaming": true,
+ "username": "root",
+ "password": "zauberzg!"
+ },
+ "b8:a4:4f:8f:8b:77-3": {
+ "id": "b8:a4:4f:8f:8b:77-3",
+ "name": "b8:a4:4f:8f:8b:77-3",
+ "connect_after_init": false,
+ "streaming": true,
+ "username": "root",
+ "password": "zauberzg!"
+ },
+ "b8:a4:4f:8f:8b:77-4": {
+ "id": "b8:a4:4f:8f:8b:77-4",
+ "name": "b8:a4:4f:8f:8b:77-4",
+ "connect_after_init": false,
+ "streaming": true,
+ "username": "root",
+ "password": "zauberzg!"
+ }
+ }
+}
\ No newline at end of file
diff --git a/backup/zauberzeug@192.168.42.2/field_friend.automations.field_provider.json b/backup/zauberzeug@192.168.42.2/field_friend.automations.field_provider.json
new file mode 100644
index 00000000..1f837775
--- /dev/null
+++ b/backup/zauberzeug@192.168.42.2/field_friend.automations.field_provider.json
@@ -0,0 +1,420 @@
+{
+ "fields": [
+ {
+ "id": "5a8a2850-89d8-4c31-8998-58f95c4bf12b",
+ "name": "Test",
+ "outline_wgs84": [
+ [
+ 53.11086450840848,
+ 13.91407542875983
+ ],
+ [
+ 53.110900665,
+ 13.914103491666667
+ ],
+ [
+ 53.11083908809259,
+ 13.914568552126127
+ ],
+ [
+ 53.11080833166667,
+ 13.914556051666667
+ ]
+ ],
+ "reference_lat": 53.11065129259277,
+ "reference_lon": 13.91371250152588,
+ "visualized": false,
+ "obstacles": [],
+ "rows": [
+ {
+ "id": "a2a8d7a7-1d09-42f3-9a38-d2a35d2f6440",
+ "name": "row_1",
+ "points_wgs84": [
+ [
+ 53.11086900379518,
+ 13.914155492406122
+ ],
+ [
+ 53.11082100387979,
+ 13.914524203377239
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "dbfad64c-0180-4593-b6ff-a9ab87b8024a",
+ "name": "row_2",
+ "points_wgs84": [
+ [
+ 53.11087289058598,
+ 13.91415661849706
+ ],
+ [
+ 53.11082537116117,
+ 13.914524852764124
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "58729d6f-268b-4a37-8d7a-770a4d7f7546",
+ "name": "row_4",
+ "points_wgs84": [
+ [
+ 53.11088111899296,
+ 13.9141582500688
+ ],
+ [
+ 53.11083298032453,
+ 13.91452758242803
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "8d83be1e-40f1-433c-803b-19352b339c77",
+ "name": "row_5",
+ "points_wgs84": [
+ [
+ 53.11088460915779,
+ 13.914162049853772
+ ],
+ [
+ 53.1108366940985,
+ 13.914525808114977
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ }
+ ]
+ },
+ {
+ "id": "f275d15c-b65c-462f-a18b-a8576f888a54",
+ "name": "field_Bohren",
+ "outline_wgs84": [
+ [
+ 53.110881503838094,
+ 13.914047766613132
+ ],
+ [
+ 53.110692736924065,
+ 13.913986660077686
+ ],
+ [
+ 53.11061942665463,
+ 13.914525650421513
+ ],
+ [
+ 53.110815801795376,
+ 13.91460211337871
+ ]
+ ],
+ "reference_lat": 53.110881499529384,
+ "reference_lon": 13.914047767487654,
+ "visualized": false,
+ "obstacles": [],
+ "rows": [
+ {
+ "id": "bc19d73e-6516-4be4-9d5d-9c7f82905791",
+ "name": "row_1",
+ "points_wgs84": [
+ [
+ 53.1108103725628,
+ 13.914533857646589
+ ],
+ [
+ 53.11086579154916,
+ 13.914106568851572
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "cac9f4be-7487-4c72-adb3-93a2f7ba9266",
+ "name": "row_2",
+ "points_wgs84": [
+ [
+ 53.11079158358294,
+ 13.914522718556901
+ ],
+ [
+ 53.11084545526309,
+ 13.914113096249709
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "7e1c954c-1c3d-4751-8b5c-5310db183d02",
+ "name": "row_3",
+ "points_wgs84": [
+ [
+ 53.110771634262846,
+ 13.914515257349777
+ ],
+ [
+ 53.110825714596324,
+ 13.914102179170046
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "72390a9f-2f89-41ae-8285-494e5dc1c9fd",
+ "name": "row_4",
+ "points_wgs84": [
+ [
+ 53.11075179138159,
+ 13.914505245488035
+ ],
+ [
+ 53.110806123173724,
+ 13.914098907293617
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "4228284a-f2b5-446e-a2a2-85281edd0764",
+ "name": "row_5",
+ "points_wgs84": [
+ [
+ 53.11073251919884,
+ 13.914499494997212
+ ],
+ [
+ 53.11078634220459,
+ 13.914090071936823
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ }
+ ]
+ },
+ {
+ "id": "38d0b78e-73ff-481e-a9fd-ad0b4f0bc383",
+ "name": "field_Pendelhacke",
+ "outline_wgs84": [
+ [
+ 53.1108814932072,
+ 13.91404776075555
+ ],
+ [
+ 53.110692719323964,
+ 13.913986645203941
+ ],
+ [
+ 53.11061944584767,
+ 13.914525645292146
+ ],
+ [
+ 53.110814933096734,
+ 13.914602167478126
+ ]
+ ],
+ "reference_lat": 53.11088148947587,
+ "reference_lon": 13.914047757052812,
+ "visualized": false,
+ "obstacles": [],
+ "rows": [
+ {
+ "id": "a1b944a9-2f32-4863-9ed1-5b7d3e3905a9",
+ "name": "row_1",
+ "points_wgs84": [
+ [
+ 53.11080748389169,
+ 13.914528449056974
+ ],
+ [
+ 53.1108616244314,
+ 13.914115170279958
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "06485080-96a3-4387-bd98-ae13c5959378",
+ "name": "row_2",
+ "points_wgs84": [
+ [
+ 53.11078736188202,
+ 13.91452134589732
+ ],
+ [
+ 53.110841041222685,
+ 13.914112616811344
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "7c403dd6-76da-4a44-a793-4fb8814f910e",
+ "name": "row_3",
+ "points_wgs84": [
+ [
+ 53.11076732735035,
+ 13.914514540695514
+ ],
+ [
+ 53.11082206421793,
+ 13.914099414601182
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "66a73378-a6c4-463c-a71a-699779bea36f",
+ "name": "row_4",
+ "points_wgs84": [
+ [
+ 53.11074767905439,
+ 13.914503905811971
+ ],
+ [
+ 53.1108022193147,
+ 13.914094882137139
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "6948c407-cf9b-4638-a7a8-dc7e0dcfb1ba",
+ "name": "row_5",
+ "points_wgs84": [
+ [
+ 53.11072939628047,
+ 13.91449482199291
+ ],
+ [
+ 53.110782272589596,
+ 13.91408976451268
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ }
+ ]
+ },
+ {
+ "id": "e51659c4-d7aa-412a-9f1f-81c2fc1aa0b8",
+ "name": "field_DoppelMechanik",
+ "outline_wgs84": [
+ [
+ 53.11088151081755,
+ 13.914047789228333
+ ],
+ [
+ 53.11069272815888,
+ 13.913986684849567
+ ],
+ [
+ 53.11061941486577,
+ 13.914525678599288
+ ],
+ [
+ 53.11081493838742,
+ 13.914602171888317
+ ]
+ ],
+ "reference_lat": 53.11088150880825,
+ "reference_lon": 13.91404779326869,
+ "visualized": false,
+ "obstacles": [],
+ "rows": [
+ {
+ "id": "afbafd10-dcf6-4c4f-a462-241f7d48c6a1",
+ "name": "row_1",
+ "points_wgs84": [
+ [
+ 53.11079956288332,
+ 13.914524099085297
+ ],
+ [
+ 53.11085311243897,
+ 13.91411221946047
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "870744db-13ce-432a-96dd-a9e23e502836",
+ "name": "row_2",
+ "points_wgs84": [
+ [
+ 53.110780072773125,
+ 13.914515952501517
+ ],
+ [
+ 53.110833534926016,
+ 13.91410706581986
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "620063b9-a870-4000-8be3-e2a6669ae4fd",
+ "name": "row_3",
+ "points_wgs84": [
+ [
+ 53.1107598941182,
+ 13.914508513187641
+ ],
+ [
+ 53.11081359893513,
+ 13.914103616913216
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "bcc78e8f-b5c3-444d-b657-33d7984082c8",
+ "name": "row_4",
+ "points_wgs84": [
+ [
+ 53.11074016554936,
+ 13.914501518993957
+ ],
+ [
+ 53.11079387469022,
+ 13.914094245038237
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ },
+ {
+ "id": "50bd98c7-2f2b-4ec1-9c11-5582f4932f01",
+ "name": "row_5",
+ "points_wgs84": [
+ [
+ 53.11072112793958,
+ 13.914493604773137
+ ],
+ [
+ 53.11077420287833,
+ 13.91408888452689
+ ]
+ ],
+ "reverse": false,
+ "crops": []
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/backup/zauberzeug@192.168.42.2/field_friend.automations.kpi_provider.json b/backup/zauberzeug@192.168.42.2/field_friend.automations.kpi_provider.json
new file mode 100644
index 00000000..ba018716
--- /dev/null
+++ b/backup/zauberzeug@192.168.42.2/field_friend.automations.kpi_provider.json
@@ -0,0 +1,64 @@
+{
+ "current_weeding_kpis": {
+ "distance": 0,
+ "time": 0,
+ "bumps": 0,
+ "low_battery": 0,
+ "can_failure": 0,
+ "automation_stopped": 0,
+ "e_stop_triggered": 0,
+ "soft_e_stop_triggered": 0,
+ "imu_rolling_detected": 0,
+ "gnss_connection_lost": 0,
+ "rows_weeded": 0,
+ "crops_detected": 0,
+ "weeds_detected": 0,
+ "weeds_removed": 0,
+ "punches": 0,
+ "chops": 0,
+ "weeding_completed": false
+ },
+ "current_mowing_kpis": {
+ "distance": 0,
+ "time": 0,
+ "bumps": 0,
+ "low_battery": 0,
+ "can_failure": 0,
+ "automation_stopped": 0,
+ "e_stop_triggered": 0,
+ "soft_e_stop_triggered": 0,
+ "imu_rolling_detected": 0,
+ "gnss_connection_lost": 0,
+ "mowing_completed": false
+ },
+ "days": [
+ {
+ "date": "2024-04-24",
+ "incidents": {
+ "low_battery": 3
+ }
+ },
+ {
+ "date": "2024-04-26",
+ "incidents": {
+ "low_battery": 9,
+ "punches": 4
+ }
+ },
+ {
+ "date": "2024-04-30",
+ "incidents": {
+ "time": 3817,
+ "distance": 277,
+ "weeds_detected": 1497,
+ "crops_detected": 94,
+ "rows_weeded": 3,
+ "weeding_completed": 4,
+ "punches": 33,
+ "weeds_removed": 55,
+ "chops": 19
+ }
+ }
+ ],
+ "months": []
+}
\ No newline at end of file
diff --git a/backup/zauberzeug@192.168.42.2/field_friend.automations.mowing.json b/backup/zauberzeug@192.168.42.2/field_friend.automations.mowing.json
new file mode 100644
index 00000000..4e1a52f1
--- /dev/null
+++ b/backup/zauberzeug@192.168.42.2/field_friend.automations.mowing.json
@@ -0,0 +1,7 @@
+{
+ "padding": 1.0,
+ "lane_distance": 0.5,
+ "paths": [],
+ "current_path": [],
+ "current_path_segment": null
+}
\ No newline at end of file
diff --git a/backup/zauberzeug@192.168.42.2/field_friend.automations.path_provider.json b/backup/zauberzeug@192.168.42.2/field_friend.automations.path_provider.json
new file mode 100644
index 00000000..f05625f8
--- /dev/null
+++ b/backup/zauberzeug@192.168.42.2/field_friend.automations.path_provider.json
@@ -0,0 +1,3 @@
+{
+ "paths": []
+}
\ No newline at end of file
diff --git a/backup/zauberzeug@192.168.42.2/field_friend.automations.weeding.json b/backup/zauberzeug@192.168.42.2/field_friend.automations.weeding.json
new file mode 100644
index 00000000..b5668515
--- /dev/null
+++ b/backup/zauberzeug@192.168.42.2/field_friend.automations.weeding.json
@@ -0,0 +1,25 @@
+{
+ "use_field_planning": true,
+ "field": null,
+ "start_row_id": null,
+ "end_row_id": null,
+ "minimum_turning_radius": 0.5,
+ "only_monitoring": false,
+ "drill_with_open_tornado": false,
+ "drill_between_crops": false,
+ "only_drilling": false,
+ "only_chopping": false,
+ "chop_if_no_crops": false,
+ "tornado_angle": 110.0,
+ "weed_screw_depth": 0.15,
+ "crop_safety_distance": 0.01,
+ "linear_speed_on_row": 0.08,
+ "angular_speed_on_row": 0.4,
+ "linear_speed_between_rows": 0.3,
+ "angular_speed_between_rows": 0.8,
+ "sorted_weeding_rows": [],
+ "weeding_plan": [],
+ "turn_paths": [],
+ "current_row": null,
+ "current_segment": null
+}
\ No newline at end of file
diff --git a/backup/zauberzeug@192.168.42.2/field_friend.navigation.gnss.json b/backup/zauberzeug@192.168.42.2/field_friend.navigation.gnss.json
new file mode 100644
index 00000000..005f4bb1
--- /dev/null
+++ b/backup/zauberzeug@192.168.42.2/field_friend.navigation.gnss.json
@@ -0,0 +1,13 @@
+{
+ "record": {
+ "timestamp": null,
+ "latitude": 0.0,
+ "longitude": 0.0,
+ "mode": "NNNN",
+ "gps_qual": 0,
+ "altitude": 0.0,
+ "separation": 0.0,
+ "heading": 0.0,
+ "speed_kmh": 0.0
+ }
+}
\ No newline at end of file
diff --git a/backup/zauberzeug@192.168.42.2/field_friend.vision.calibratable_usb_camera_provider.json b/backup/zauberzeug@192.168.42.2/field_friend.vision.calibratable_usb_camera_provider.json
new file mode 100644
index 00000000..c5dfb39b
--- /dev/null
+++ b/backup/zauberzeug@192.168.42.2/field_friend.vision.calibratable_usb_camera_provider.json
@@ -0,0 +1,93 @@
+{
+ "cameras": {
+ "32e4-9230-platform-3610000.xhci-usb-0:2.1:1.0": {
+ "id": "32e4-9230-platform-3610000.xhci-usb-0:2.1:1.0",
+ "name": "32e4-9230-platform-3610000.xhci-usb-0:2.1:1.0",
+ "connect_after_init": true,
+ "streaming": true,
+ "focal_length": 1830,
+ "calibration": {
+ "intrinsics": {
+ "matrix": [
+ [
+ 1433.6721216716353,
+ 0.0,
+ 347.7277694468386
+ ],
+ [
+ 0.0,
+ 1370.2101675446997,
+ 239.1060666171041
+ ],
+ [
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ ],
+ "distortion": [
+ -0.01557127473024067,
+ -56.25674323172758,
+ 0.23577466596211383,
+ 0.011342764817018949,
+ 421.8140030391272
+ ],
+ "rotation": {
+ "R": [
+ [
+ 1.0,
+ 0.0,
+ 0.0
+ ],
+ [
+ 0.0,
+ 1.0,
+ 0.0
+ ],
+ [
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ ]
+ },
+ "size": {
+ "width": 720,
+ "height": 560
+ }
+ },
+ "extrinsics": {
+ "rotation": {
+ "R": [
+ [
+ -0.005087957942356058,
+ 0.9070537937934018,
+ -0.42098399951657745
+ ],
+ [
+ 0.9967070483093687,
+ -0.02946986293924958,
+ -0.075541955420596
+ ],
+ [
+ -0.08092695802019419,
+ -0.419982073835704,
+ -0.9039169680464345
+ ]
+ ]
+ },
+ "translation": [
+ 0.5757624106417404,
+ 0.0740620510871811,
+ 1.1604844656286453
+ ]
+ }
+ },
+ "auto_exposure": true,
+ "exposure": false,
+ "width": 1280,
+ "height": 720,
+ "fps": 9
+ }
+ }
+}
\ No newline at end of file
diff --git a/backup/zauberzeug@192.168.42.2/field_friend.vision.usb_cam_provider.json b/backup/zauberzeug@192.168.42.2/field_friend.vision.usb_cam_provider.json
new file mode 100644
index 00000000..d226b1ae
--- /dev/null
+++ b/backup/zauberzeug@192.168.42.2/field_friend.vision.usb_cam_provider.json
@@ -0,0 +1,93 @@
+{
+ "cameras": {
+ "32e4-9230-platform-3610000.xhci-usb-0:2.1:1.0": {
+ "id": "32e4-9230-platform-3610000.xhci-usb-0:2.1:1.0",
+ "name": "32e4-9230-platform-3610000.xhci-usb-0:2.1:1.0",
+ "connect_after_init": true,
+ "streaming": true,
+ "auto_exposure": true,
+ "exposure": false,
+ "width": 1920,
+ "height": 1080,
+ "fps": 6,
+ "focal_length": 1830,
+ "calibration": {
+ "intrinsics": {
+ "matrix": [
+ [
+ 1433.6721216716353,
+ 0.0,
+ 347.7277694468386
+ ],
+ [
+ 0.0,
+ 1370.2101675446997,
+ 239.1060666171041
+ ],
+ [
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ ],
+ "distortion": [
+ -0.01557127473024067,
+ -56.25674323172758,
+ 0.23577466596211383,
+ 0.011342764817018949,
+ 421.8140030391272
+ ],
+ "rotation": {
+ "R": [
+ [
+ 1.0,
+ 0.0,
+ 0.0
+ ],
+ [
+ 0.0,
+ 1.0,
+ 0.0
+ ],
+ [
+ 0.0,
+ 0.0,
+ 1.0
+ ]
+ ]
+ },
+ "size": {
+ "width": 720,
+ "height": 560
+ }
+ },
+ "extrinsics": {
+ "rotation": {
+ "R": [
+ [
+ -0.005087957942356058,
+ 0.9070537937934018,
+ -0.42098399951657745
+ ],
+ [
+ 0.9967070483093687,
+ -0.02946986293924958,
+ -0.075541955420596
+ ],
+ [
+ -0.08092695802019419,
+ -0.419982073835704,
+ -0.9039169680464345
+ ]
+ ]
+ },
+ "translation": [
+ 0.5757624106417404,
+ 0.0740620510871811,
+ 1.1604844656286453
+ ]
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/backup/zauberzeug@192.168.42.2/rosys.config.json b/backup/zauberzeug@192.168.42.2/rosys.config.json
new file mode 100644
index 00000000..07b402b0
--- /dev/null
+++ b/backup/zauberzeug@192.168.42.2/rosys.config.json
@@ -0,0 +1,3 @@
+{
+ "simulation_speed": 1.0
+}
\ No newline at end of file
diff --git a/backup/zauberzeug@192.168.42.2/rosys.pathplanning.path_planner.json b/backup/zauberzeug@192.168.42.2/rosys.pathplanning.path_planner.json
new file mode 100644
index 00000000..e3fa2fa6
--- /dev/null
+++ b/backup/zauberzeug@192.168.42.2/rosys.pathplanning.path_planner.json
@@ -0,0 +1,114 @@
+{
+ "obstacles": {
+ "row_a2a8d7a7-1d09-42f3-9a38-d2a35d2f6440": {
+ "id": "row_a2a8d7a7-1d09-42f3-9a38-d2a35d2f6440",
+ "outline": [
+ {
+ "x": 24.218813097300302,
+ "y": -29.674937338763694
+ },
+ {
+ "x": 24.218813097300302,
+ "y": -54.34577219385262
+ },
+ {
+ "x": 18.89719766451805,
+ "y": -54.34577219385262
+ },
+ {
+ "x": 18.89719766451805,
+ "y": -29.674937338763694
+ }
+ ]
+ },
+ "row_dbfad64c-0180-4593-b6ff-a9ab87b8024a": {
+ "id": "row_dbfad64c-0180-4593-b6ff-a9ab87b8024a",
+ "outline": [
+ {
+ "x": 24.65136812285677,
+ "y": -29.750343471273254
+ },
+ {
+ "x": 24.65136812285677,
+ "y": -54.38925300182701
+ },
+ {
+ "x": 19.38322571902191,
+ "y": -54.38925300182701
+ },
+ {
+ "x": 19.38322571902191,
+ "y": -29.750343471273254
+ }
+ ]
+ },
+ "row_58729d6f-268b-4a37-8d7a-770a4d7f7546": {
+ "id": "row_58729d6f-268b-4a37-8d7a-770a4d7f7546",
+ "outline": [
+ {
+ "x": 25.567094688912526,
+ "y": -29.859596160954577
+ },
+ {
+ "x": 25.567094688912526,
+ "y": -54.5720358214478
+ },
+ {
+ "x": 20.23003907146769,
+ "y": -54.5720358214478
+ },
+ {
+ "x": 20.23003907146769,
+ "y": -29.859596160954577
+ }
+ ]
+ },
+ "row_8d83be1e-40f1-433c-803b-19352b339c77": {
+ "id": "row_8d83be1e-40f1-433c-803b-19352b339c77",
+ "outline": [
+ {
+ "x": 25.95551098654778,
+ "y": -30.11404670703774
+ },
+ {
+ "x": 25.95551098654778,
+ "y": -54.45321394411896
+ },
+ {
+ "x": 20.64333751816373,
+ "y": -54.45321394411896
+ },
+ {
+ "x": 20.64333751816373,
+ "y": -30.11404670703774
+ }
+ ]
+ }
+ },
+ "areas": {
+ "5a8a2850-89d8-4c31-8998-58f95c4bf12b": {
+ "outline": [
+ {
+ "x": 23.72849874434108,
+ "y": -24.303468212230452
+ },
+ {
+ "x": 27.752316275418853,
+ "y": -26.182682363470505
+ },
+ {
+ "x": 20.899794534151816,
+ "y": -57.32557073067396
+ },
+ {
+ "x": 17.476952549804945,
+ "y": -56.48851568957107
+ }
+ ],
+ "id": "5a8a2850-89d8-4c31-8998-58f95c4bf12b",
+ "type": null,
+ "color": "green",
+ "closed": true
+ }
+ }
+}
\ No newline at end of file
diff --git a/backup/zauberzeug@192.168.42.2/rosys.vision.mjpeg_camera.mjpeg_camera_provider.json b/backup/zauberzeug@192.168.42.2/rosys.vision.mjpeg_camera.mjpeg_camera_provider.json
new file mode 100644
index 00000000..cbd18b8a
--- /dev/null
+++ b/backup/zauberzeug@192.168.42.2/rosys.vision.mjpeg_camera.mjpeg_camera_provider.json
@@ -0,0 +1,3 @@
+{
+ "cameras": {}
+}
\ No newline at end of file
diff --git a/config/ff12_config_rb32/hardware.py b/config/ff12_config_rb32/hardware.py
index a44039cc..a628ce60 100644
--- a/config/ff12_config_rb32/hardware.py
+++ b/config/ff12_config_rb32/hardware.py
@@ -36,8 +36,8 @@
'end_bottom_pin': 5,
'ref_motor_pin': 35,
'ref_gear_pin': 18,
- 'ref_t_pin': 4,
- 'ref_b_pin': 33,
+ 'ref_knife_stop_pin': 4,
+ 'ref_knife_ground_pin': 33,
'motors_on_expander': False,
'end_stops_on_expander': True,
'is_z_reversed': True,
@@ -45,6 +45,8 @@
'speed_limit': 1.5,
'turn_speed_limit': 1.5,
'current_limit': 30,
+ 'z_reference_speed': 0.0075,
+ 'turn_reference_speed': 0.25,
},
'flashlight': {
'version': 'flashlight_pwm_v2',
diff --git a/config/u3_config_rb27/hardware.py b/config/u3_config_rb27/hardware.py
index 54fe25ee..183c540b 100644
--- a/config/u3_config_rb27/hardware.py
+++ b/config/u3_config_rb27/hardware.py
@@ -11,6 +11,8 @@
'y_axis': {
'version': 'chain_axis',
'name': 'chain_axis',
+ 'min_position': -0.10,
+ 'max_position': 0.10,
'motor_on_expander': False,
'end_stops_on_expander': True,
'step_pin': 13,
diff --git a/config/u3_config_rb27/params.py b/config/u3_config_rb27/params.py
index 14384c98..2e4f0d4c 100644
--- a/config/u3_config_rb27/params.py
+++ b/config/u3_config_rb27/params.py
@@ -8,4 +8,5 @@
'drill_radius': 0.025,
'chop_radius': 0.07,
'tool': 'dual_mechanism',
+ 'antenna_offset': 0.195,
}
diff --git a/config/u4_config_rb28/camera.py b/config/u4_config_rb28/camera.py
index 3e799d61..923bedee 100644
--- a/config/u4_config_rb28/camera.py
+++ b/config/u4_config_rb28/camera.py
@@ -5,9 +5,9 @@
'auto_exposure': True,
},
'crop': {
- 'left': 350,
- 'right': 350,
- 'up': 50,
- 'down': 50,
+ 'left': 250,
+ 'right': 250,
+ 'up': 0,
+ 'down': 0,
},
}
diff --git a/config/u4_config_rb28/hardware.py b/config/u4_config_rb28/hardware.py
index c7249826..bd17102e 100644
--- a/config/u4_config_rb28/hardware.py
+++ b/config/u4_config_rb28/hardware.py
@@ -13,7 +13,7 @@
'version': 'y_axis_stepper',
'name': 'y_axis',
'max_speed': 60_000,
- 'reference_speed': 20_000,
+ 'reference_speed': 10_000,
'min_position': -0.068,
'max_position': 0.068,
'axis_offset': 0.075,
@@ -38,8 +38,8 @@
'end_bottom_pin': 5,
'ref_motor_pin': 33,
'ref_gear_pin': 4,
- 'ref_t_pin': 35,
- 'ref_b_pin': 18,
+ 'ref_knife_stop_pin': 35,
+ 'ref_knife_ground_pin': 18,
'motors_on_expander': False,
'end_stops_on_expander': True,
'is_z_reversed': True,
@@ -47,6 +47,8 @@
'speed_limit': 1.5,
'turn_speed_limit': 1.3,
'current_limit': 30,
+ 'z_reference_speed': 0.0075,
+ 'turn_reference_speed': 0.25,
},
'flashlight': {
'version': 'flashlight_pwm',
diff --git a/config/u4_config_rb28/params.py b/config/u4_config_rb28/params.py
index afcc88e5..3f53a21f 100644
--- a/config/u4_config_rb28/params.py
+++ b/config/u4_config_rb28/params.py
@@ -5,5 +5,6 @@
'wheel_distance': 0.47,
'work_x': -0.0105,
'drill_radius': 0.025,
+ 'antenna_offset': 0.205,
'tool': 'tornado',
}
diff --git a/config/u5_config_rb33/params.py b/config/u5_config_rb33/params.py
index 0b95e8a6..9f3c75a8 100644
--- a/config/u5_config_rb33/params.py
+++ b/config/u5_config_rb33/params.py
@@ -5,5 +5,6 @@
'wheel_distance': 0.47,
'work_x': 0.093,
'drill_radius': 0.025,
+ 'antenna_offset': 0.205,
'tool': 'weed_screw',
}
diff --git a/config/u6_config_rb34/params.py b/config/u6_config_rb34/params.py
index 0b95e8a6..9f3c75a8 100644
--- a/config/u6_config_rb34/params.py
+++ b/config/u6_config_rb34/params.py
@@ -5,5 +5,6 @@
'wheel_distance': 0.47,
'work_x': 0.093,
'drill_radius': 0.025,
+ 'antenna_offset': 0.205,
'tool': 'weed_screw',
}
diff --git a/docker-compose.jetson.orin.yml b/docker-compose.jetson.orin.yml
index 11bcc738..4c72d07f 100644
--- a/docker-compose.jetson.orin.yml
+++ b/docker-compose.jetson.orin.yml
@@ -5,20 +5,6 @@ services:
restart: always
privileged: true
- # detector:
- # image: "zauberzeug/yolov5-detector:nlv0.8.8-35.4.1"
- # restart: always
- # ports:
- # - "8004:80"
- # hostname: ${ROBOT_ID}
- # environment:
- # - HOST=learning-loop.ai
- # - ORGANIZATION=zuckerruebe
- # - PROJECT=uckerbots
- # - NVIDIA_VISIBLE_DEVICES=all
- # volumes:
- # - ~/data_plants:/data
-
detector:
image: "zauberzeug/yolov5-detector:nlv0.8.8-35.4.1"
restart: always
@@ -27,13 +13,27 @@ services:
hostname: ${ROBOT_ID}
environment:
- HOST=learning-loop.ai
- - ORGANIZATION=zauberzeug
- - PROJECT=coins
+ - ORGANIZATION=zuckerruebe
+ - PROJECT=uckerbots
- NVIDIA_VISIBLE_DEVICES=all
volumes:
- - ~/data_coins:/data
+ - ~/data_plants:/data
+
+ # detector:
+ # image: "zauberzeug/yolov5-detector:nlv0.8.8-35.4.1"
+ # restart: always
+ # ports:
+ # - "8004:80"
+ # hostname: ${ROBOT_ID}
+ # environment:
+ # - HOST=learning-loop.ai
+ # - ORGANIZATION=zauberzeug
+ # - PROJECT=coins
+ # - NVIDIA_VISIBLE_DEVICES=all
+ # volumes:
+ # - ~/data_coins:/data
- circle_sight:
+ monitoring:
image: "zauberzeug/yolov5-detector:nlv0.8.8-35.4.1"
restart: always
ports:
diff --git a/docker-compose.yml b/docker-compose.yml
index b4f5accf..1e417596 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -25,12 +25,12 @@ services:
max-size: "200m"
max-file: "100"
init: true
- healthcheck:
- test: ["CMD", "curl", "-f", "http://localhost/"]
- interval: 60s
- timeout: 20s
- retries: 3
- start_period: 60s
+ # healthcheck:
+ # test: curl --fail http://localhost/status || exit 1
+ # interval: 20s
+ # retries: 3
+ # start_period: 1h
+ # timeout: 10s
autoheal: # restart containers which turn unhealthy
image: willfarrell/autoheal
diff --git a/field_friend/automations/automation_watcher.py b/field_friend/automations/automation_watcher.py
index 17f14604..8f7aa55a 100644
--- a/field_friend/automations/automation_watcher.py
+++ b/field_friend/automations/automation_watcher.py
@@ -53,8 +53,9 @@ def pause(self, reason: str) -> None:
if self.path_recorder.state == 'recording':
return
else:
- self.log.info(f'pausing automation because {reason}')
- self.automator.pause(because=f'{reason})')
+ if self.automator.is_running:
+ self.log.info(f'pausing automation because {reason}')
+ self.automator.pause(because=f'{reason})')
return
if reason.startswith('GNSS') and not self.gnss_watch_active:
return
diff --git a/field_friend/automations/coin_collecting.py b/field_friend/automations/coin_collecting.py
index 10e72a63..6a39fac5 100644
--- a/field_friend/automations/coin_collecting.py
+++ b/field_friend/automations/coin_collecting.py
@@ -40,12 +40,12 @@ async def start(self) -> None:
rosys.notify('Y-Axis is in alarm, aborting', 'negative')
self.log.error('Y-Axis is in alarm, aborting')
return
- if self.system.field_friend.z_axis.ref_t:
- rosys.notify('Tornado is in top ref', 'negative')
- self.log.error('Tornado is in top ref')
+ if self.system.field_friend.z_axis.ref_knife_stop:
+ rosys.notify('Tornado is in knife stop ref', 'negative')
+ self.log.error('Tornado is in knife stop ref')
return
if not await self.system.puncher.try_home():
- rosys.notify('Puncher homing failed, aborting', 'error')
+ rosys.notify('Puncher homing failed, aborting', 'negative')
self.log.error('Puncher homing failed, aborting')
self.work_x = self.system.field_friend.WORK_X
self.system.odometer.reset()
diff --git a/field_friend/automations/field_provider.py b/field_friend/automations/field_provider.py
index 96b88e13..d50f5fb0 100644
--- a/field_friend/automations/field_provider.py
+++ b/field_friend/automations/field_provider.py
@@ -84,6 +84,19 @@ def outline(self) -> list[Point]:
else:
return []
+ def area(self) -> float:
+ if len(self.outline) > 0:
+ polygon = Polygon([(p.x, p.y) for p in self.outline])
+ return polygon.area
+ else:
+ return 0.0
+
+ def worked_area(self, worked_rows: int) -> float:
+ worked_area = 0.0
+ if self.area() > 0:
+ worked_area = worked_rows * self.area() / len(self.rows)
+ return worked_area
+
class Active_object(TypedDict):
object_type: Literal["Obstacles", "Rows", "Outline"]
diff --git a/field_friend/automations/kpi_provider.py b/field_friend/automations/kpi_provider.py
index 716c2fe5..83b0849f 100644
--- a/field_friend/automations/kpi_provider.py
+++ b/field_friend/automations/kpi_provider.py
@@ -26,7 +26,9 @@ class Weeding_KPIs(KPIs):
rows_weeded: int = 0
crops_detected: int = 0
weeds_detected: int = 0
+ weeds_removed: int = 0
punches: int = 0
+ chops: int = 0
weeding_completed: bool = False
diff --git a/field_friend/automations/plant_locator.py b/field_friend/automations/plant_locator.py
index 855c7153..4439b97d 100644
--- a/field_friend/automations/plant_locator.py
+++ b/field_friend/automations/plant_locator.py
@@ -5,8 +5,8 @@
from .plant_provider import Plant, PlantProvider
-WEED_CATEGORY_NAME = ['coin', 'weed']
-CROP_CATEGORY_NAME = ['coin_with_hole', 'crop']
+WEED_CATEGORY_NAME = ['coin', 'weed', 'big_weed', 'thistle', 'orache', 'weedy_area', ]
+CROP_CATEGORY_NAME = ['coin_with_hole', 'crop', 'sugar_beet', 'onion', 'garlic', ]
MINIMUM_WEED_CONFIDENCE = 0.8
MINIMUM_CROP_CONFIDENCE = 0.75
@@ -42,8 +42,10 @@ async def _detect_plants(self) -> None:
t = rosys.time()
camera = next((camera for camera in self.camera_provider.cameras.values() if camera.is_connected), None)
if not camera:
- raise DetectorError()
+ self.log.error('no connected camera found')
+ return
if camera.calibration is None:
+ self.log.error('no calibration found')
raise DetectorError()
new_image = camera.latest_captured_image
if new_image is None or new_image.detections:
@@ -67,7 +69,7 @@ async def _detect_plants(self) -> None:
continue
world_point = self.odometer.prediction.transform(floor_point.projection())
weed = Plant(position=world_point, type=d.category_name, detection_time=rosys.time())
- self.plant_provider.add_weed(weed)
+ await self.plant_provider.add_weed(weed)
elif d.category_name in self.crop_category_names and d.confidence >= self.minimum_crop_confidence:
# self.log.info('crop found')
image_point = rosys.geometry.Point(x=d.cx, y=d.cy)
@@ -78,7 +80,7 @@ async def _detect_plants(self) -> None:
world_point = self.odometer.prediction.transform(floor_point.projection())
crop = Plant(position=world_point, type=d.category_name,
detection_time=rosys.time(), confidence=d.confidence)
- self.plant_provider.add_crop(crop)
+ await self.plant_provider.add_crop(crop)
elif d.category_name not in self.crop_category_names and d.category_name not in self.weed_category_names:
self.log.info(f'{d.category_name} not in categories')
# else:
diff --git a/field_friend/automations/plant_provider.py b/field_friend/automations/plant_provider.py
index 629d57b7..bde9b08b 100644
--- a/field_friend/automations/plant_provider.py
+++ b/field_friend/automations/plant_provider.py
@@ -20,6 +20,17 @@ def __post_init__(self) -> None:
self.id = str(uuid.uuid4())
+def check_if_plant_exists(plant: Plant, plants: list[Plant], distance: float) -> bool:
+ for p in plants:
+ if p.position.distance(plant.position) < distance and p.type == plant.type:
+ if p.confidence > plant.confidence:
+ return True
+ p.position = plant.position
+ p.confidence = plant.confidence
+ return True
+ return False
+
+
class PlantProvider:
def __init__(self) -> None:
@@ -38,20 +49,16 @@ def __init__(self) -> None:
rosys.on_repeat(self.prune, 10.0)
- def prune(self, max_age: float = 10 * 60.0) -> None:
- self.weeds[:] = [weed for weed in self.weeds if weed.detection_time > rosys.time() - max_age]
- self.crops[:] = [crop for crop in self.crops if crop.detection_time > rosys.time() - max_age]
+ def prune(self) -> None:
+ weeds_max_age = 10.0
+ crops_max_age = 60.0 * 60.0
+ self.weeds[:] = [weed for weed in self.weeds if weed.detection_time > rosys.time() - weeds_max_age]
+ self.crops[:] = [crop for crop in self.crops if crop.detection_time > rosys.time() - crops_max_age]
self.PLANTS_CHANGED.emit()
- def add_weed(self, weed: Plant) -> None:
- for w in self.weeds:
- if w.position.distance(weed.position) < 0.02 and w.type == weed.type:
- if w.confidence > weed.confidence:
- return
- w.position = weed.position
- w.confidence = weed.confidence
- self.PLANTS_CHANGED.emit()
- return
+ async def add_weed(self, weed: Plant) -> None:
+ if await rosys.run.cpu_bound(check_if_plant_exists, weed, self.weeds, 0.05):
+ return
self.weeds.append(weed)
self.PLANTS_CHANGED.emit()
self.ADDED_NEW_WEED.emit()
@@ -64,15 +71,9 @@ def clear_weeds(self) -> None:
self.weeds.clear()
self.PLANTS_CHANGED.emit()
- def add_crop(self, crop: Plant) -> None:
- for c in self.crops:
- if c.position.distance(crop.position) < 0.05 and c.type == crop.type:
- if c.confidence > crop.confidence:
- return
- c.position = crop.position
- c.confidence = crop.confidence
- self.PLANTS_CHANGED.emit()
- return
+ async def add_crop(self, crop: Plant) -> None:
+ if await rosys.run.cpu_bound(check_if_plant_exists, crop, self.crops, 0.05):
+ return
self.crops.append(crop)
self.PLANTS_CHANGED.emit()
self.ADDED_NEW_CROP.emit()
diff --git a/field_friend/automations/puncher.py b/field_friend/automations/puncher.py
index 5e7fc3d8..dad19f62 100644
--- a/field_friend/automations/puncher.py
+++ b/field_friend/automations/puncher.py
@@ -44,10 +44,7 @@ async def drive_to_punch(self, local_target_x: float) -> None:
rosys.notify('no y or z axis', 'negative')
return
self.log.info(f'Driving to punch at {local_target_x}...')
- if self.field_friend.tool in ['tornado', 'weed_screw', 'none']:
- work_x = self.field_friend.WORK_X
- elif self.field_friend.tool in ['dual_mechanism']:
- work_x = self.field_friend.WORK_X_DRILL
+ work_x = self.field_friend.WORK_X
if local_target_x < work_x:
self.log.info(f'Target: {local_target_x} is behind')
axis_distance = local_target_x - work_x
@@ -55,7 +52,7 @@ async def drive_to_punch(self, local_target_x: float) -> None:
world_target = self.driver.prediction.transform(local_target)
await self.driver.drive_to(world_target, backward=axis_distance < 0)
- async def punch(self, y: float, *, depth: float = 0.01, angle: float = 180) -> None:
+ async def punch(self, y: float, *, depth: float = 0.01, angle: float = 180, turns: float = 2.0) -> None:
self.log.info(f'Punching at {y} with depth {depth}...')
if self.field_friend.y_axis is None or self.field_friend.z_axis is None:
rosys.notify('no y or z axis', 'negative')
@@ -72,7 +69,7 @@ async def punch(self, y: float, *, depth: float = 0.01, angle: float = 180) -> N
raise PuncherException('homing failed')
await rosys.sleep(0.5)
if isinstance(self.field_friend.y_axis, ChainAxis):
- if not self.field_friend.y_axis.MIN_POSITION+self.field_friend.y_axis.WORK_OFFSET <= y <= self.field_friend.y_axis.MAX_POSITION-self.field_friend.y_axis.WORK_OFFSET:
+ if not self.field_friend.y_axis.min_position <= y <= self.field_friend.y_axis.max_position:
rosys.notify('y position out of range', type='negative')
raise PuncherException('y position out of range')
elif isinstance(self.field_friend.y_axis, YAxis):
@@ -82,7 +79,7 @@ async def punch(self, y: float, *, depth: float = 0.01, angle: float = 180) -> N
if isinstance(self.field_friend.z_axis, Tornado):
await self.field_friend.y_axis.move_to(y)
- await self.tornado_drill(angle=angle)
+ await self.tornado_drill(angle=angle, turns=turns)
elif isinstance(self.field_friend.z_axis, ZAxis):
await self.field_friend.y_axis.move_to(y)
await self.field_friend.z_axis.move_to(-depth)
@@ -108,14 +105,18 @@ async def clear_view(self) -> None:
await self.field_friend.y_axis.move_to(y, speed=self.field_friend.y_axis.max_speed)
await self.field_friend.y_axis.stop()
- async def drive_and_punch(self, x: float, y: float, depth: float = 0.05, angle: float = 180) -> None:
+ async def drive_and_punch(self, x: float, y: float, depth: float = 0.05, angle: float = 180, turns: float = 2.0, backwards_allowed: bool = True) -> None:
if self.field_friend.y_axis is None or self.field_friend.z_axis is None:
rosys.notify('no y or z axis', 'negative')
return
try:
+ work_x = self.field_friend.WORK_X
+ if x < work_x and not backwards_allowed:
+ self.log.warning(f'target x: {x} is behind')
+ return
await self.drive_to_punch(x)
- await self.punch(y, depth=depth, angle=angle)
- await self.clear_view()
+ await self.punch(y, depth=depth, angle=angle, turns=turns)
+ # await self.clear_view()
except Exception as e:
raise PuncherException('drive and punch failed') from e
@@ -127,8 +128,9 @@ async def chop(self) -> None:
else:
await self.field_friend.y_axis.move_dw_to_r_ref()
await self.field_friend.y_axis.stop()
+ self.kpi_provider.increment_weeding_kpi('chops')
- async def tornado_drill(self, angle: float = 180) -> None:
+ async def tornado_drill(self, angle: float = 180, turns: float = 2) -> None:
self.log.info(f'Drilling with tornado at {angle}...')
if not isinstance(self.field_friend.z_axis, Tornado):
raise PuncherException('tornado drill is only available for tornado axis')
@@ -142,17 +144,15 @@ async def tornado_drill(self, angle: float = 180) -> None:
await rosys.sleep(0.5)
await self.field_friend.z_axis.move_down_until_reference()
- current_angle = self.field_friend.z_axis.position_turn
- await self.field_friend.z_axis.turn_by(current_angle-angle)
- await rosys.sleep(3)
- current_angle = self.field_friend.z_axis.position_turn
- await self.field_friend.z_axis.turn_by(current_angle+700)
- await rosys.sleep(3)
+ await self.field_friend.z_axis.turn_knifes_to(angle)
+ await rosys.sleep(2)
+ await self.field_friend.z_axis.turn_by(turns)
+ await rosys.sleep(2)
await self.field_friend.z_axis.return_to_reference()
await rosys.sleep(0.5)
- if not await self.field_friend.z_axis.try_reference_turn():
- raise PuncherException('tornado reference failed')
+ await self.field_friend.z_axis.turn_knifes_to(0)
+ await rosys.sleep(0.5)
except Exception as e:
raise PuncherException(f'tornado drill failed because of: {e}') from e
finally:
diff --git a/field_friend/automations/weeding.py b/field_friend/automations/weeding.py
index 1815b467..329f6cbf 100644
--- a/field_friend/automations/weeding.py
+++ b/field_friend/automations/weeding.py
@@ -1,4 +1,6 @@
import logging
+from copy import deepcopy
+from random import randint
from typing import TYPE_CHECKING, Any, Optional
import numpy as np
@@ -21,6 +23,9 @@ class WorkflowException(Exception):
class Weeding(rosys.persistence.PersistentModule):
+ WORKING_DISTANCE = 0.06
+ DRIVE_DISTANCE = 0.04
+
def __init__(self, system: 'System') -> None:
super().__init__()
self.PATH_PLANNED = rosys.event.Event()
@@ -30,7 +35,7 @@ def __init__(self, system: 'System') -> None:
self.system = system
self.kpi_provider = system.kpi_provider
- # default settings
+ # general settings
self.continue_canceled_weeding: bool = False
self.use_monitor_workflow: bool = False
@@ -40,9 +45,17 @@ def __init__(self, system: 'System') -> None:
self.start_row_id: Optional[str] = None
self.end_row_id: Optional[str] = None
self.minimum_turning_radius: float = 0.5
+ self.turn_offset: float = 0.4
# workflow settings
self.only_monitoring: bool = False
+ # tornado
+ self.drill_with_open_tornado: bool = False
+ self.drill_between_crops: bool = False
+ # dual mechanism
+ self.with_drilling: bool = False
+ self.with_chopping: bool = False
+ self.chop_if_no_crops: bool = False
# tool settings
self.tornado_angle: float = 110.0
@@ -50,7 +63,15 @@ def __init__(self, system: 'System') -> None:
self.crop_safety_distance: float = 0.01
# driver settings
-
+ self.linear_speed_on_row: float = 0.08
+ self.angular_speed_on_row: float = 0.4
+ self.linear_speed_between_rows: float = 0.3
+ self.angular_speed_between_rows: float = 0.8
+
+ self.state: str = 'idle'
+ self.start_time: Optional[float] = None
+ self.last_pose: Optional[Pose] = None
+ self.drived_distance: float = 0.0
self.sorted_weeding_rows: list = []
self.weeding_plan: Optional[list[list[PathSegment]]] = None
self.turn_paths: list[list[PathSegment]] = []
@@ -60,28 +81,76 @@ def __init__(self, system: 'System') -> None:
self.crops_to_handle: dict[str, Point] = {}
self.weeds_to_handle: dict[str, Point] = {}
+ rosys.on_repeat(self._update_time_and_distance, 0.1)
+
+ def _update_time_and_distance(self):
+ if self.state == 'idle':
+ return
+ if self.start_time is None:
+ self.start_time = rosys.time()
+ if self.last_pose is None:
+ self.last_pose = self.system.odometer.prediction
+ self.drived_distance = 0.0
+ self.drived_distance += self.system.odometer.prediction.distance(self.last_pose)
+ if self.drived_distance > 1:
+ self.kpi_provider.increment_weeding_kpi('distance')
+ self.drived_distance -= 1
+ self.last_pose = self.system.odometer.prediction
+ passed_time = rosys.time() - self.start_time
+ if passed_time > 1:
+ self.kpi_provider.increment_weeding_kpi('time')
+ self.start_time = rosys.time()
+
def backup(self) -> dict:
- return {
+ dict = {
'use_field_planning': self.use_field_planning,
+ 'field': rosys.persistence.to_dict(self.field) if self.field else None,
'start_row_id': self.start_row_id,
'end_row_id': self.end_row_id,
- 'tornado_angle': self.tornado_angle,
'minimum_turning_radius': self.minimum_turning_radius,
+ 'turn_offset': self.turn_offset,
'only_monitoring': self.only_monitoring,
+ 'drill_with_open_tornado': self.drill_with_open_tornado,
+ 'drill_between_crops': self.drill_between_crops,
+ 'with_drilling': self.with_drilling,
+ 'with_chopping': self.with_chopping,
+ 'chop_if_no_crops': self.chop_if_no_crops,
+ 'tornado_angle': self.tornado_angle,
+ 'weed_screw_depth': self.weed_screw_depth,
+ 'crop_safety_distance': self.crop_safety_distance,
+ 'linear_speed_on_row': self.linear_speed_on_row,
+ 'angular_speed_on_row': self.angular_speed_on_row,
+ 'linear_speed_between_rows': self.linear_speed_between_rows,
+ 'angular_speed_between_rows': self.angular_speed_between_rows,
'sorted_weeding_rows': self.sorted_weeding_rows,
'weeding_plan': [[rosys.persistence.to_dict(segment) for segment in row] for row in self.weeding_plan] if self.weeding_plan else [],
'turn_paths': [rosys.persistence.to_dict(segment) for segment in self.turn_paths],
'current_row': rosys.persistence.to_dict(self.current_row) if self.current_row else None,
'current_segment': rosys.persistence.to_dict(self.current_segment) if self.current_segment else None,
}
+ self.log.info(f'backing up: {dict}')
+ return dict
def restore(self, data: dict[str, Any]) -> None:
- self.use_field_planning = data.get('use_field_planning', False)
- self.start_row_id = data.get('start_row_id')
- self.end_row_id = data.get('end_row_id')
- self.tornado_angle = data.get('tornado_angle', 110.0)
- self.minimum_turning_radius = data.get('minimum_turning_radius', 0.5)
- self.only_monitoring = data.get('only_monitoring', False)
+ self.use_field_planning = data.get('use_field_planning', self.use_field_planning)
+ self.field = rosys.persistence.from_dict(Field, data['field']) if data['field'] else None
+ self.start_row_id = data.get('start_row_id', self.start_row_id)
+ self.end_row_id = data.get('end_row_id', self.end_row_id)
+ self.minimum_turning_radius = data.get('minimum_turning_radius', self.minimum_turning_radius)
+ self.turn_offset = data.get('turn_offset', self.turn_offset)
+ self.only_monitoring = data.get('only_monitoring', self.only_monitoring)
+ self.drill_with_open_tornado = data.get('drill_with_open_tornado', self.drill_with_open_tornado)
+ self.drill_between_crops = data.get('drill_between_crops', self.drill_between_crops)
+ self.with_drilling = data.get('with_drilling', self.with_drilling)
+ self.with_chopping = data.get('with_chopping', self.with_chopping)
+ self.chop_if_no_crops = data.get('chop_if_no_crops', self.chop_if_no_crops)
+ self.tornado_angle = data.get('tornado_angle', self.tornado_angle)
+ self.weed_screw_depth = data.get('weed_screw_depth', self.weed_screw_depth)
+ self.crop_safety_distance = data.get('crop_safety_distance', self.crop_safety_distance)
+ self.linear_speed_on_row = data.get('linear_speed_on_row', self.linear_speed_on_row)
+ self.angular_speed_on_row = data.get('angular_speed_on_row', self.angular_speed_on_row)
+ self.linear_speed_between_rows = data.get('linear_speed_between_rows', self.linear_speed_between_rows)
+ self.angular_speed_between_rows = data.get('angular_speed_between_rows', self.angular_speed_between_rows)
self.sorted_weeding_rows = data.get('sorted_weeding_rows', [])
self.weeding_plan = [
[rosys.persistence.from_dict(PathSegment, segment_data)
@@ -95,6 +164,7 @@ def restore(self, data: dict[str, Any]) -> None:
'current_segment']) if data['current_segment'] else None
def invalidate(self) -> None:
+ self.log.info('backing up...')
self.request_backup()
async def start(self):
@@ -105,6 +175,7 @@ async def start(self):
if self.use_field_planning and not await self._field_planning():
rosys.notify('Field planning failed', 'negative')
return
+ self.invalidate()
await self._weeding()
async def _check_hardware_ready(self) -> bool:
@@ -160,10 +231,6 @@ async def _field_planning(self) -> bool:
self.turn_paths = await self._generate_turn_paths()
if not self.turn_paths:
self.log.error('No turn paths available')
- return False
- self.log.info(f'Turn paths: {self.turn_paths}')
- self.log.info(
- f'Planned path: {[path_segment for path in self.weeding_plan for path_segment in path] + self.turn_paths}')
paths = [path_segment for path in self.weeding_plan for path_segment in path]
turn_paths = [path_segment for path in self.turn_paths for path_segment in path]
self.PATH_PLANNED.emit(paths + turn_paths)
@@ -201,23 +268,25 @@ def _make_plan(self) -> Optional[list[list[rosys.driving.PathSegment]]]:
distance_to_last_row = min([point.distance(robot_position) for point in rows[-1].points(reference)])
if distance_to_first_row > distance_to_last_row:
rows = list(reversed(rows))
- minimum_row_distance = 1 # 1 = no row needs to be skippen when turning
+ minimum_row_distance = 1 # 1 = no row needs to be skipped when turning
if len(rows) > 1:
rows_distance = rows[0].points(reference)[0].distance(rows[1].points(reference)[0])
+ self.log.info(f'Rows distance: {rows_distance}')
+ self.log.info(f'Minimum turning radius: {self.minimum_turning_radius}')
if self.minimum_turning_radius * 2 > rows_distance:
+ self.log.info('Rows distance is smaller than minimum turning radius * 2')
minimum_row_distance = int(
np.ceil(self.minimum_turning_radius * 2 / rows_distance))
- self.log.info(f'Minimum turning distance: {minimum_row_distance}')
+ self.log.info(f'Minimum row distance: {minimum_row_distance} need to skip {minimum_row_distance - 1} rows')
if minimum_row_distance > 1:
sequence = find_sequence(len(rows), minimum_distance=minimum_row_distance)
if not sequence:
self.log.warning('No sequence found')
- return None
- # sequence = list(range(len(rows)))
+ sequence = list(range(len(rows)))
else:
sequence = list(range(len(rows)))
- self.log.info(f'Sequence: {sequence}')
+ self.log.info(f'Row sequence: {sequence}')
paths = []
switch_first_row = False
@@ -264,10 +333,9 @@ async def _generate_turn_paths(self) -> list[list[PathSegment]]:
row_points = row.points([self.field.reference_lat, self.field.reference_lon])
# create a small polygon around the row to avoid the robot driving through the row
row_polygon = [
- Point(x=row_points[0].x - 0.01, y=row_points[0].y - 0.01),
- Point(x=row_points[0].x - 0.01, y=row_points[-1].y + 0.01),
- Point(x=row_points[-1].x + 0.01, y=row_points[-1].y + 0.01),
- Point(x=row_points[-1].x + 0.01, y=row_points[0].y - 0.01),
+ Point(x=row_points[0].x, y=row_points[0].y),
+ Point(x=row_points[0].x - 0.01, y=row_points[0].y + 0.01),
+ Point(x=row_points[-1].x, y=row_points[-1].y),
]
self.system.path_planner.obstacles[f'row_{row.id}'] = rosys.pathplanning.Obstacle(
id=f'row_{row.id}', outline=row_polygon)
@@ -275,17 +343,23 @@ async def _generate_turn_paths(self) -> list[list[PathSegment]]:
area = rosys.pathplanning.Area(id=f'{self.field.id}', outline=self.field.outline)
self.system.path_planner.areas = {area.id: area}
for i in range(len(self.weeding_plan) - 1):
- # remove this rows from obstacles to allow starting in it an insert it afterwards again
+ # remove the current and the rows from obstacles to allow starting in it an insert it afterwards again
start_row = self.sorted_weeding_rows[i]
end_row = self.sorted_weeding_rows[i + 1]
temp_removed_start_row = self.system.path_planner.obstacles.pop(f'row_{start_row.id}')
temp_removed_end_row = self.system.path_planner.obstacles.pop(f'row_{end_row.id}')
- start_pose = Pose(x=self.weeding_plan[i][-1].spline.end.x,
- y=self.weeding_plan[i][-1].spline.end.y,
- yaw=self.weeding_plan[i][-1].spline.start.direction(self.weeding_plan[i][-1].spline.end))
- end_pose = Pose(x=self.weeding_plan[i + 1][0].spline.start.x,
- y=self.weeding_plan[i + 1][0].spline.start.y,
- yaw=self.weeding_plan[i + 1][0].spline.start.direction(self.weeding_plan[i + 1][0].spline.end))
+ start_point = Point(x=self.weeding_plan[i][-1].spline.end.x,
+ y=self.weeding_plan[i][-1].spline.end.y
+ )
+ yaw = self.weeding_plan[i][-1].spline.start.direction(self.weeding_plan[i][-1].spline.end)
+ offset_start_point = start_point.polar(self.turn_offset, yaw)
+
+ start_pose = Pose(x=offset_start_point.x, y=offset_start_point.y, yaw=yaw)
+ end_point = Point(x=self.weeding_plan[i + 1][0].spline.start.x,
+ y=self.weeding_plan[i + 1][0].spline.start.y)
+ end_yaw = self.weeding_plan[i + 1][0].spline.start.direction(self.weeding_plan[i + 1][0].spline.end)
+ offset_end_point = end_point.polar(0.5, yaw)
+ end_pose = Pose(x=offset_end_point.x, y=offset_end_point.y, yaw=end_yaw)
self.log.info(f'Searching path from row {i} to row {i + 1}...')
turn_path = await self.system.path_planner.search(start=start_pose, goal=end_pose, timeout=120)
if turn_path:
@@ -295,12 +369,17 @@ async def _generate_turn_paths(self) -> list[list[PathSegment]]:
return []
self.system.path_planner.obstacles[f'row_{start_row.id}'] = temp_removed_start_row
self.system.path_planner.obstacles[f'row_{end_row.id}'] = temp_removed_end_row
+ # # clear all row obstacles
+ # for row in self.field.rows:
+ # self.system.path_planner.obstacles.pop(f'row_{row.id}')
return turn_paths
async def _weeding(self):
self.log.info('Starting driving...')
await rosys.sleep(0.5)
+ self.state = 'running'
try:
+
if self.weeding_plan:
await self._weed_with_plan()
self.log.info('Weeding with plan completed')
@@ -309,7 +388,7 @@ async def _weeding(self):
self.log.info('Planless weeding completed')
except WorkflowException as e:
- self.kpi_provider.increment('automation_stopped')
+ self.kpi_provider.increment_weeding_kpi('automation_stopped')
self.log.error(f'WorkflowException: {e}')
finally:
self.kpi_provider.increment_weeding_kpi('weeding_completed')
@@ -341,7 +420,8 @@ async def _weed_with_plan(self):
if self.continue_canceled_weeding and self.current_row != self.sorted_weeding_rows[i]:
continue
self.system.driver.parameters.can_drive_backwards = False
- self.system.driver.parameters.minimum_turning_radius = 0.02
+ self.system.driver.parameters.linear_speed_limit = self.linear_speed_on_row
+ self.system.driver.parameters.angular_speed_limit = self.angular_speed_on_row
self.current_row = self.sorted_weeding_rows[i]
self.system.plant_locator.pause()
self.system.plant_provider.clear()
@@ -359,6 +439,7 @@ async def _weed_with_plan(self):
self.current_segment = segment
self.invalidate()
if not self.system.is_real:
+ self.system.detector.simulated_objects = []
self._create_simulated_plants()
self.log.info(f'Driving row {i + 1}/{len(self.weeding_plan)} and segment {j + 1}/{len(path)}...')
self.row_segment_completed = False
@@ -374,15 +455,20 @@ async def _weed_with_plan(self):
await self._handle_plants()
self.crops_to_handle = {}
self.weeds_to_handle = {}
+ if self.system.odometer.prediction.relative_point(self.current_segment.spline.end).x < 0.01:
+ self.row_segment_completed = True
await self.system.field_friend.flashlight.turn_off()
self.system.plant_locator.pause()
if i < len(self.weeding_plan) - 1:
self.system.driver.parameters.can_drive_backwards = True
+ self.system.driver.parameters.linear_speed_limit = self.linear_speed_between_rows
+ self.system.driver.parameters.angular_speed_limit = self.angular_speed_between_rows
self.log.info('Driving to next row...')
turn_path = self.turn_paths[i]
await self.system.driver.drive_path(turn_path)
await rosys.sleep(1)
+ self.kpi_provider.increment_weeding_kpi('rows_weeded')
self.system.automation_watcher.stop_field_watch()
self.system.automation_watcher.gnss_watch_active = False
@@ -406,12 +492,12 @@ async def _weed_planless(self):
await rosys.sleep(2)
self.system.plant_locator.resume()
await rosys.sleep(0.5)
- await self._get_upcoming_crops()
+ await self._get_upcoming_plants()
while self.crops_to_handle or self.weeds_to_handle:
await self._handle_plants()
already_explored_count = 0
await rosys.sleep(0.2)
- await self._get_upcoming_crops()
+ await self._get_upcoming_plants()
if not self.crops_to_handle and already_explored_count != 5:
self.log.info('No crops found, advancing a bit to ensure there are really no more crops')
target = self.system.odometer.prediction.transform(Point(x=0.10, y=0))
@@ -429,15 +515,21 @@ async def _drive_segment(self):
async def _check_for_plants(self):
self.log.info('Checking for plants...')
while True:
- await self._get_upcoming_crops()
- if self.system.field_friend.tool == 'tornado':
+ await self._get_upcoming_plants()
+ if self.system.field_friend.tool in ['tornado', 'none'] or self.use_monitor_workflow:
if self.crops_to_handle:
return
- if self.weeds_to_handle or self.crops_to_handle:
- return
+ elif self.system.field_friend.tool == 'dual_mechanism':
+ if (self.with_drilling and not self.only_monitoring) and (self.crops_to_handle or self.weeds_to_handle):
+ return
+ elif self.crops_to_handle:
+ return
+ else:
+ if self.weeds_to_handle or self.crops_to_handle:
+ return
await rosys.sleep(0.2)
- async def _get_upcoming_crops(self):
+ async def _get_upcoming_plants(self):
relative_crop_positions = {
c.id: self.system.odometer.prediction.relative_point(c.position)
for c in self.system.plant_provider.crops if c.position.distance(self.system.odometer.prediction.point) < 0.5
@@ -447,13 +539,12 @@ async def _get_upcoming_crops(self):
# Correctly filter to get upcoming crops based on their x position
upcoming_crop_positions = {
c: pos for c, pos in relative_crop_positions.items()
- if self.system.field_friend.WORK_X + self.system.field_friend.DRILL_RADIUS < pos.x <= self.system.odometer.prediction.relative_point(self.current_segment.spline.end).x
+ if self.system.field_friend.WORK_X+self.system.field_friend.DRILL_RADIUS < pos.x <= self.system.odometer.prediction.relative_point(self.current_segment.spline.end).x
}
- self.log.info(f'Upcoming crops in segment: {upcoming_crop_positions}')
else:
upcoming_crop_positions = {
c: pos for c, pos in relative_crop_positions.items()
- if self.system.field_friend.WORK_X + self.system.field_friend.DRILL_RADIUS < pos.x
+ if self.system.field_friend.WORK_X < pos.x < 0.4
}
# Sort the upcoming_crop_positions dictionary by the .x attribute of its values
@@ -469,12 +560,12 @@ async def _get_upcoming_crops(self):
# Filter to get upcoming weeds based on their .x position
upcoming_weed_positions = {
w: pos for w, pos in relative_weed_positions.items()
- if self.system.field_friend.WORK_X + self.system.field_friend.DRILL_RADIUS < pos.x <= self.current_segment.spline.end.x
+ if self.system.field_friend.WORK_X+self.system.field_friend.DRILL_RADIUS < pos.x <= self.system.odometer.prediction.relative_point(self.current_segment.spline.end).x
}
else:
upcoming_weed_positions = {
w: pos for w, pos in relative_weed_positions.items()
- if self.system.field_friend.WORK_X + self.system.field_friend.DRILL_RADIUS < pos.x
+ if self.system.field_friend.WORK_X < pos.x < 0.4
}
# Sort the upcoming_weed_positions dictionary by the .x attribute of its values
@@ -489,55 +580,52 @@ async def _handle_plants(self) -> None:
if self.system.field_friend.tool == 'tornado' and self.crops_to_handle and not self.use_monitor_workflow:
await self._tornado_workflow()
- elif self.system.field_friend.tool == 'weed_screw':
+ elif self.system.field_friend.tool == 'weed_screw' and not self.use_monitor_workflow:
await self._weed_screw_workflow()
+ elif self.system.field_friend.tool == 'dual_mechanism' and not self.use_monitor_workflow and self.crops_to_handle:
+ await self._dual_mechanism_workflow()
+ elif self.system.field_friend.tool == 'dual_mechanism' and not self.use_monitor_workflow and self.with_drilling and not self.only_monitoring:
+ await self._dual_mechanism_workflow()
elif self.system.field_friend.tool == 'none' or self.use_monitor_workflow:
await self._monitor_workflow()
- # ToDo: implement workflow of other tools
-
async def _tornado_workflow(self) -> None:
self.log.info('Starting Tornado Workflow..')
try:
closest_crop_position = list(self.crops_to_handle.values())[0]
self.log.info(f'Closest crop position: {closest_crop_position}')
# fist check if the closest crop is in the working area
- if closest_crop_position.x < self.system.field_friend.WORK_X + 0.05:
+ if closest_crop_position.x < self.system.field_friend.WORK_X + self.WORKING_DISTANCE:
self.log.info(f'target next crop at {closest_crop_position}')
# do not steer while advancing on a crop
- drive_distance = closest_crop_position.x - self.system.field_friend.WORK_X
- target = self.system.odometer.prediction.transform(Point(x=drive_distance, y=0))
- await self.system.driver.drive_to(target)
+
if not self.only_monitoring and self.system.field_friend.can_reach(closest_crop_position):
- await self.system.puncher.punch(closest_crop_position.y, angle=self.tornado_angle)
- if len(self.crops_to_handle) > 1:
+ await self.system.puncher.drive_and_punch(closest_crop_position.x, closest_crop_position.y, angle=self.tornado_angle)
+ if self.drill_with_open_tornado:
+ await self.system.puncher.punch(closest_crop_position.y, angle=0)
+ else:
+ drive_distance = closest_crop_position.x - self.system.field_friend.WORK_X
+ target = self.system.odometer.prediction.transform(Point(x=drive_distance, y=0))
+ await self.system.driver.drive_to(target)
+
+ if len(self.crops_to_handle) > 1 and self.drill_between_crops:
self.log.info('checking for second closest crop')
second_closest_crop_position = list(self.crops_to_handle.values())[1]
distance_to_next_crop = closest_crop_position.distance(second_closest_crop_position)
if distance_to_next_crop > 0.15:
# get the target of half the distance between the two crops
+ target = closest_crop_position.x + distance_to_next_crop / 2
self.log.info(f'driving to position between two crops: {target}')
- target = self.system.odometer.prediction.transform(
- Point(x=closest_crop_position.x + distance_to_next_crop / 2 - self.system.field_friend.WORK_X, y=0))
- await self.system.driver.drive_to(target)
- self.log.info(f'target between two crops at {target}')
if not self.only_monitoring:
- # punch in the middle position with closed knives
- await self.system.puncher.punch(0, angle=180)
+ # punch in the middle position with closed knifes
+ await self.system.puncher.drive_and_punch(target, 0, angle=180)
+ else:
+ drive_distance = target - self.system.field_friend.WORK_X
+ target = self.system.odometer.prediction.transform(Point(x=drive_distance, y=0))
+ await self.system.driver.drive_to(target)
- self.system.plant_locator.resume()
else:
- self.log.info('follow line of crops')
- farthest_crop = list(self.crops_to_handle.values())[-1]
- self.log.info(f'Farthest crop: {farthest_crop}')
- upcoming_world_position = self.system.odometer.prediction.transform(farthest_crop)
- yaw = self.system.odometer.prediction.point.direction(upcoming_world_position)
- # only apply minimal yaw corrections to avoid oversteering
- yaw = eliminate_2pi(self.system.odometer.prediction.yaw) * 0.85 + eliminate_2pi(yaw) * 0.15
- target = self.system.odometer.prediction.point.polar(0.04, yaw)
-
- self.log.info(f'current world position: {self.system.odometer.prediction} target next crop at {target}')
- await self.system.driver.drive_to(target)
+ await self._follow_line_of_crops()
await rosys.sleep(0.2)
self.log.info('workflow completed')
except Exception as e:
@@ -549,26 +637,18 @@ async def _monitor_workflow(self) -> None:
closest_crop_position = list(self.crops_to_handle.values())[0]
self.log.info(f'Closest crop position: {closest_crop_position}')
# fist check if the closest crop is in the working area
- if closest_crop_position.x < 0.05:
+ if closest_crop_position.x < self.WORKING_DISTANCE:
self.log.info(f'target next crop at {closest_crop_position}')
# do not steer while advancing on a crop
- target = self.system.odometer.prediction.transform(Point(x=closest_crop_position.x, y=0))
+ drive_distance = closest_crop_position.x - self.system.field_friend.WORK_X
+ target = self.system.odometer.prediction.transform(Point(x=drive_distance, y=0))
await self.system.driver.drive_to(target)
self.system.plant_locator.resume()
else:
if self.crops_to_handle:
- self.log.info('follow line of crops')
- farthest_crop = list(self.crops_to_handle.values())[-1]
- self.log.info(f'Farthest crop: {farthest_crop}')
- upcoming_world_position = self.system.odometer.prediction.transform(farthest_crop)
- yaw = self.system.odometer.prediction.point.direction(upcoming_world_position)
- # only apply minimal yaw corrections to avoid oversteering
- yaw = eliminate_2pi(self.system.odometer.prediction.yaw) * 0.85 + eliminate_2pi(yaw) * 0.15
- target = self.system.odometer.prediction.point.polar(0.04, yaw)
+ await self._follow_line_of_crops()
else:
- target = self.system.odometer.prediction.point.polar(0.04, self.system.odometer.prediction.yaw)
- self.log.info(f'current world position: {self.system.odometer.prediction} target next crop at {target}')
- await self.system.driver.drive_to(target)
+ await self._driving_a_bit_forward()
await rosys.sleep(0.2)
self.log.info('workflow completed')
except Exception as e:
@@ -577,80 +657,173 @@ async def _monitor_workflow(self) -> None:
async def _weed_screw_workflow(self) -> None:
self.log.info('Starting Weed Screw Workflow...')
try:
+ starting_position = deepcopy(self.system.odometer.prediction)
self._keep_crops_safe()
- next_weed_position = None
- if self.weeds_to_handle:
- next_weed_position = list(self.weeds_to_handle.values())[0]
- self.log.info(f'Next weed position: {next_weed_position}')
-
- if next_weed_position and next_weed_position.x < self.system.field_friend.WORK_X + 0.06:
- self.log.info(f'target next weed at {next_weed_position}')
- drive_distance = next_weed_position.x - self.system.field_friend.WORK_X
- target = self.system.odometer.prediction.transform(
- Point(x=drive_distance, y=0))
- await self.system.driver.drive_to(target)
- corrected_positions = [Point(x=position.x - drive_distance, y=position.y)
- for _, position in self.weeds_to_handle.items()]
- for index, position in enumerate(corrected_positions):
- if self.system.field_friend.can_reach(position):
- if not self.only_monitoring:
- await self.system.puncher.punch(position.y, depth=self.weed_screw_depth)
- corrected_positions.pop(index)
- self.log.info(f'Punched weed at {position}')
- for index, other_position in enumerate(corrected_positions):
- if position.distance(other_position) < self.system.field_friend.DRILL_RADIUS:
- corrected_positions.pop(index)
- self.log.info(f'Punched weed at {other_position}')
- else:
- self.log.info(f'Could not reach weed at {position}')
- self.system.plant_locator.resume()
+ weeds_in_range = {weed_id: position for weed_id, position in self.weeds_to_handle.items(
+ ) if position.x < self.system.field_friend.WORK_X + self.WORKING_DISTANCE and self.system.field_friend.can_reach(position)}
+ if weeds_in_range:
+ self.log.info(f'Weeds in range {len(weeds_in_range)}')
+ while weeds_in_range:
+ next_weed_position = list(weeds_in_range.values())[0]
+ weed_world_position = starting_position.transform(next_weed_position)
+ corrected_relative_weed_position = self.system.odometer.prediction.relative_point(
+ weed_world_position)
+ self.log.info(f'Targeting weed at {next_weed_position}')
+ if not self.only_monitoring:
+ await self.system.puncher.drive_and_punch(
+ corrected_relative_weed_position.x, corrected_relative_weed_position.y, depth=self.weed_screw_depth, backwards_allowed=False)
+ punched_weeds = [weed_id for weed_id, position in weeds_in_range.items(
+ ) if position.distance(next_weed_position) <= self.system.field_friend.DRILL_RADIUS]
+ for weed_id in punched_weeds:
+ self.system.plant_provider.remove_weed(weed_id)
+ if weed_id in weeds_in_range:
+ del weeds_in_range[weed_id]
+ self.kpi_provider.increment_weeding_kpi('weeds_removed')
+
elif self.crops_to_handle:
- self.log.info('follow line of crops')
- farthest_crop = list(self.crops_to_handle.values())[-1]
- self.log.info(f'Farthest crop: {farthest_crop}')
- upcoming_world_position = self.system.odometer.prediction.transform(farthest_crop)
- yaw = self.system.odometer.prediction.point.direction(upcoming_world_position)
- # only apply minimal yaw corrections to avoid oversteering
- yaw = eliminate_2pi(self.system.odometer.prediction.yaw) * 0.85 + eliminate_2pi(yaw) * 0.15
- target = self.system.odometer.prediction.point.polar(0.04, yaw)
-
- self.log.info(f'current world position: {self.system.odometer.prediction} target next crop at {target}')
- await self.system.driver.drive_to(target)
+ await self._follow_line_of_crops()
else:
- self.log.info('No crops and no weeeds in range, driving forward a bit...')
- target = self.system.odometer.prediction.point.polar(0.04, self.system.odometer.prediction.yaw)
- self.log.info(f'current world position: {self.system.odometer.prediction} target: {target}')
- await self.system.driver.drive_to(target)
-
+ await self._driving_a_bit_forward()
await rosys.sleep(0.2)
- self.log.info('workflow completed')
+ self.log.info('Workflow completed')
except Exception as e:
raise WorkflowException(f'Error while Weed Screw Workflow: {e}') from e
+ async def _dual_mechanism_workflow(self) -> None:
+ self.log.info('Starting dual mechanism workflow...')
+ try:
+ moved = False
+ starting_position = deepcopy(self.system.odometer.prediction)
+ if self.crops_to_handle:
+ next_crop_position = list(self.crops_to_handle.values())[0]
+ # first: check if weeds near crop
+ if self.with_chopping:
+ self.log.info(f'Drilling allowed: only chopping is {self.with_chopping}')
+ self._keep_crops_safe()
+ weeds_in_range = {weed_id: position for weed_id, position in self.weeds_to_handle.items() if next_crop_position.x - self.system.field_friend.DRILL_RADIUS*2
+ < position.x < next_crop_position.x + self.system.field_friend.DRILL_RADIUS*2 and self.system.field_friend.can_reach(position)}
+ self.log.info(f'weed_position in range: {weeds_in_range.items()}')
+ if weeds_in_range:
+ self.log.info(f' {len(weeds_in_range)} Weeds in range for drilling')
+ while weeds_in_range:
+ next_weed_position = list(weeds_in_range.values())[0]
+ self.log.info(f'Next weed position: {next_weed_position}')
+ weed_world_position = starting_position.transform(next_weed_position)
+ corrected_relative_weed_position = self.system.odometer.prediction.relative_point(
+ weed_world_position)
+ self.log.info(f'corrected relative weed position: {corrected_relative_weed_position}')
+ moved = True
+ if not self.only_monitoring:
+ await self.system.puncher.drive_and_punch(
+ corrected_relative_weed_position.x, next_weed_position.y, depth=self.weed_screw_depth, backwards_allowed=False)
+ punched_weeds = [weed_id for weed_id, position in weeds_in_range.items(
+ ) if position.distance(next_weed_position) < self.system.field_friend.DRILL_RADIUS]
+ for weed_id in punched_weeds:
+ self.system.plant_provider.remove_weed(weed_id)
+ if weed_id in weeds_in_range:
+ del weeds_in_range[weed_id]
+ self.kpi_provider.increment_weeding_kpi('weeds_removed')
+ await self.system.puncher.clear_view()
+ # second: check if weed before crop to chop
+ if self.with_drilling:
+ self.log.info(f'Chopping allowed: only drilling is {self.with_drilling}')
+ weeds_in_range = {weed_id: position for weed_id, position in self.weeds_to_handle.items() if position.x < next_crop_position.x - (
+ self.system.field_friend.DRILL_RADIUS) and self.system.field_friend.can_reach(position, second_tool=True)}
+ if weeds_in_range:
+ self.log.info('Weeds in range for chopping before crop')
+ crop_world_position = starting_position.transform(next_crop_position)
+ corrected_relative_crop_position = self.system.odometer.prediction.relative_point(
+ crop_world_position)
+ target_position = corrected_relative_crop_position.x - \
+ self.system.field_friend.DRILL_RADIUS - self.system.field_friend.CHOP_RADIUS
+ axis_distance = target_position - self.system.field_friend.WORK_X_CHOP
+ if axis_distance >= 0:
+ local_target = Point(x=axis_distance, y=0)
+ world_target = self.system.driver.prediction.transform(local_target)
+ moved = True
+ await self.system.driver.drive_to(world_target)
+ if not self.only_monitoring:
+ await self.system.puncher.chop()
+ choped_weeds = [weed_id for weed_id, position in self.weeds_to_handle.items(
+ ) if target_position - self.system.field_friend.CHOP_RADIUS < self.system.odometer.prediction.relative_point(starting_position.transform(position)).x < target_position + self.system.field_friend.CHOP_RADIUS]
+ for weed_id in choped_weeds:
+ self.system.plant_provider.remove_weed(weed_id)
+ self.kpi_provider.increment_weeding_kpi('weeds_removed')
+ else:
+ self.log.warning(f'Weed position {next_weed_position} is behind field friend')
+ if not moved:
+ await self._follow_line_of_crops()
+ moved = True
+ elif self.weeds_to_handle and self.chop_if_no_crops:
+ self.log.info('No crops in range, checking for weeds...')
+ weeds_in_range = {weed_id: position for weed_id, position in self.weeds_to_handle.items() if self.system.field_friend.WORK_X_CHOP <
+ position.x < self.system.field_friend.WORK_X + self.WORKING_DISTANCE and self.system.field_friend.can_reach(position)}
+ if weeds_in_range:
+ next_weed_position = list(weeds_in_range.values())[0]
+ axis_distance = next_weed_position.x - self.system.field_friend.WORK_X_CHOP + self.system.field_friend.CHOP_RADIUS
+ if axis_distance >= 0:
+ local_target = Point(x=axis_distance, y=0)
+ self.log.info(f'Next weed position: {next_weed_position}')
+ self.log.info(f'Axis distance: {axis_distance}')
+ world_target = self.system.driver.prediction.transform(local_target)
+ moved = True
+ await self.system.driver.drive_to(world_target)
+ if not self.only_monitoring:
+ await self.system.puncher.chop()
+ choped_weeds = [weed_id for weed_id, position in self.weeds_to_handle.items(
+ ) if axis_distance - self.system.field_friend.CHOP_RADIUS < self.system.odometer.prediction.relative_point(starting_position.transform(position)).x < axis_distance + self.system.field_friend.CHOP_RADIUS]
+ for weed_id in choped_weeds:
+ self.system.plant_provider.remove_weed(weed_id)
+ self.kpi_provider.increment_weeding_kpi('weeds_removed')
+ else:
+ self.log.warning(f'Weed position {next_weed_position} is behind field friend')
+ if not moved:
+ await self._driving_a_bit_forward()
+ await rosys.sleep(0.2)
+ self.log.info('Workflow completed')
+ except Exception as e:
+ raise WorkflowException(f'Error while double mechanism Workflow: {e}') from e
+
+ async def _follow_line_of_crops(self):
+ self.log.info('Following line of crops...')
+ farthest_crop = list(self.crops_to_handle.values())[-1]
+ self.log.info(f'Farthest crop: {farthest_crop}')
+ upcoming_world_position = self.system.odometer.prediction.transform(farthest_crop)
+ yaw = self.system.odometer.prediction.point.direction(upcoming_world_position)
+ # only apply minimal yaw corrections to avoid oversteering
+ yaw = eliminate_2pi(self.system.odometer.prediction.yaw) * 0.85 + eliminate_2pi(yaw) * 0.15
+ target = self.system.odometer.prediction.point.polar(self.DRIVE_DISTANCE, yaw)
+ self.log.info(f'Current world position: {self.system.odometer.prediction} Target next crop at {target}')
+ await self.system.driver.drive_to(target)
+
+ async def _driving_a_bit_forward(self):
+ self.log.info('No crops and no weeds in range, driving forward a bit...')
+ target = self.system.odometer.prediction.point.polar(self.DRIVE_DISTANCE, self.system.odometer.prediction.yaw)
+ self.log.info(f'Current world position: {self.system.odometer.prediction} Target: {target}')
+ await self.system.driver.drive_to(target)
+
def _keep_crops_safe(self) -> None:
self.log.info('Keeping crops safe...')
for crop, crop_position in self.crops_to_handle.items():
- if crop_position.x > 0.06:
- continue
for weed, weed_position in self.weeds_to_handle.items():
- if weed_position.x > 0.06:
- continue
offset = self.system.field_friend.DRILL_RADIUS + \
self.crop_safety_distance - crop_position.distance(weed_position)
if offset > 0:
safe_weed_position = weed_position.polar(offset, crop_position.direction(weed_position))
self.weeds_to_handle[weed] = safe_weed_position
- self.log.info(f'Moved weed {weed} to {safe_weed_position} to safe {crop} at {crop_position}')
+ self.log.info(
+ f'Moved weed {weed} from {weed_position} to {safe_weed_position} by {offset} to safe {crop} at {crop_position}')
def _safe_crop_to_row(self, crop_id: str) -> None:
if self.current_row is None:
return
+ self.log.info(f'Saving crop {crop_id} to row {self.current_row.name}...')
crop = next((c for c in self.system.plant_provider.crops if c.id == crop_id), None)
if crop is None:
self.log.error(f'Error in crop saving: Crop with id {crop_id} not found')
return
for c in self.current_row.crops:
- if c.position.distance(crop.position) < 0.08 and c.type == crop.type:
+ if c.position.distance(crop.position) < 0.05 and c.type == crop.type:
if c.confidence >= crop.confidence:
return
self.log.info('Updating crop with higher confidence')
@@ -663,60 +836,43 @@ def _safe_crop_to_row(self, crop_id: str) -> None:
def _create_simulated_plants(self):
self.log.info('Creating simulated plants...')
if self.current_segment:
- first_point = self.current_segment.spline.start
- last_point = self.current_segment.spline.end
+ self.log.info('Creating simulated plants for current segment')
distance = self.current_segment.spline.start.distance(self.current_segment.spline.end)
for i in range(1, int(distance/0.20)):
self.system.plant_provider.add_crop(Plant(
id=str(i),
type='beet',
- position=first_point.polar(0.20*i, first_point.direction(last_point)),
+ position=self.system.odometer.prediction.point.polar(
+ 0.20*i, self.system.odometer.prediction.yaw).polar(randint(-2, 2)*0.01, self.system.odometer.prediction.yaw+np.pi/2),
detection_time=rosys.time(),
confidence=0.9,
))
- for j in range(0, 2):
+ for j in range(1, 7):
self.system.plant_provider.add_weed(Plant(
- id=str(i),
+ id=f'{i}_{j}',
type='weed',
- position=first_point.polar(0.20*i, first_point.direction(last_point)
- ).polar(0.04*j, first_point.direction(last_point) + 1.57),
- detection_time=rosys.time(),
- confidence=0.9,
- ))
- for j in range(0, 2):
- self.system.plant_provider.add_weed(Plant(
- id=str(i),
- type='weed',
- position=first_point.polar(0.20*i, first_point.direction(last_point)
- ).polar(0.02*j, first_point.direction(last_point) - 1.57),
+ position=self.system.odometer.prediction.point.polar(
+ 0.20*i+randint(-5, 5)*0.01, self.system.odometer.prediction.yaw).polar(randint(-15, 15)*0.01, self.system.odometer.prediction.yaw + np.pi/2),
detection_time=rosys.time(),
confidence=0.9,
))
else:
+ self.log.info('Creating simulated plants for whole row')
for i in range(0, 30):
self.system.plant_provider.add_crop(Plant(
id=str(i),
type='beet',
- position=self.system.odometer.prediction.point.polar(0.20*i, self.system.odometer.prediction.yaw),
+ position=self.system.odometer.prediction.point.polar(
+ 0.20*i, self.system.odometer.prediction.yaw).polar(randint(-2, 2)*0.01, self.system.odometer.prediction.yaw+np.pi/2),
detection_time=rosys.time(),
confidence=0.9,
))
- for i in range(0, 30):
- for j in range(0, 2):
- self.system.plant_provider.add_weed(Plant(
- id=str(i),
- type='weed',
- position=self.system.odometer.prediction.point.polar(
- 0.20*i, self.system.odometer.prediction.yaw).polar(0.02*j, self.system.odometer.prediction.yaw + 1.57),
- detection_time=rosys.time(),
- confidence=0.9,
- ))
- for j in range(0, 2):
+ for j in range(1, 7):
self.system.plant_provider.add_weed(Plant(
- id=str(i),
+ id=f'{i}_{j}',
type='weed',
position=self.system.odometer.prediction.point.polar(
- 0.20*i, self.system.odometer.prediction.yaw).polar(0.02*j, self.system.odometer.prediction.yaw - 1.57),
+ 0.20*i+randint(-5, 5)*0.01, self.system.odometer.prediction.yaw).polar(randint(-15, 15)*0.01, self.system.odometer.prediction.yaw + np.pi/2),
detection_time=rosys.time(),
confidence=0.9,
))
diff --git a/field_friend/hardware/chain_axis.py b/field_friend/hardware/chain_axis.py
index 91c94c07..8907189a 100644
--- a/field_friend/hardware/chain_axis.py
+++ b/field_friend/hardware/chain_axis.py
@@ -16,13 +16,14 @@ class ChainAxis(rosys.hardware.Module, abc.ABC):
MAX_POSITION = 0.14235
CHAIN_RADIUS = 0.05185
RADIUS_STEPS = 8400 # TODO: make configurable (U2=16000, U3 = 8400)
- WORK_OFFSET = 0.04
REF_OFFSET = 1500
POSITION_OFFSET = CHAIN_RADIUS / RADIUS_STEPS * REF_OFFSET
TOP_DOWN_FACTOR = 1.0589
- def __init__(self, **kwargs) -> None:
+ def __init__(self, min_position: float = -0.10, max_position: float = 0.10, **kwargs) -> None:
super().__init__(**kwargs)
+ self.min_position = min_position
+ self.max_position = max_position
self.steps: int = 0
self.alarm: bool = False
@@ -76,7 +77,7 @@ async def move_to(self, position: float, speed: int | None = None) -> None:
speed = self.DEFAULT_SPEED
if not self.is_referenced:
raise RuntimeError('yaxis is not referenced, reference first')
- if not self.MIN_POSITION + self.WORK_OFFSET <= position <= self.MAX_POSITION - self.WORK_OFFSET:
+ if not self.min_position <= position <= self.max_position:
raise RuntimeError(f'target yaxis {position} is out of range')
if not self.ref_t:
raise RuntimeError('yaxis is not at top reference, move to top reference first')
@@ -128,6 +129,8 @@ class ChainAxisHardware(ChainAxis, rosys.hardware.ModuleHardware):
def __init__(self, robot_brain: rosys.hardware.RobotBrain, *,
name: str = 'chain_axis',
expander: Optional[rosys.hardware.ExpanderHardware],
+ min_position: float = -0.10,
+ max_position: float = 0.10,
step_pin: int = 5,
dir_pin: int = 4,
alarm_pin: int = 13,
@@ -196,7 +199,8 @@ def __init__(self, robot_brain: rosys.hardware.RobotBrain, *,
f'{name}_alarm.level',
f'{name}_ref_t.level',
]
- super().__init__(robot_brain=robot_brain, lizard_code=lizard_code, core_message_fields=core_message_fields)
+ super().__init__(min_position=min_position, max_position=max_position, robot_brain=robot_brain,
+ lizard_code=lizard_code, core_message_fields=core_message_fields)
async def stop(self) -> None:
await super().stop()
diff --git a/field_friend/hardware/field_friend.py b/field_friend/hardware/field_friend.py
index 57eaf511..002cb39b 100644
--- a/field_friend/hardware/field_friend.py
+++ b/field_friend/hardware/field_friend.py
@@ -21,7 +21,6 @@ class FieldFriend(rosys.hardware.Robot):
DRILL_RADIUS = 0.025
CHOP_RADIUS = 0.07
WORK_X_CHOP = 0.04
- WORK_X_DRILL = 0.175
def __init__(
self, *,
@@ -61,21 +60,12 @@ def can_reach(self, local_point: rosys.geometry.Point, second_tool: bool = False
The point is given in local coordinates, i.e. the origin is the center of the tool.
"""
- if self.tool in ['weed_screw'] and isinstance(self.y_axis, YAxis):
- return self.WORK_X - self.DRILL_RADIUS <= local_point.x <= self.WORK_X + self.DRILL_RADIUS \
- and self.y_axis.min_position <= local_point.y <= self.y_axis.max_position
- elif self.tool in ['tornado'] and isinstance(self.y_axis, YAxis):
+ if self.tool in ['weed_screw', 'tornado'] and isinstance(self.y_axis, YAxis):
return self.y_axis.min_position <= local_point.y <= self.y_axis.max_position
- elif self.tool in ['double_mechanism'] and isinstance(self.y_axis, ChainAxis):
- if not second_tool:
- work_x = self.WORK_X_CHOP
- tool_radius = self.CHOP_RADIUS
- return work_x - tool_radius <= local_point.x <= work_x + tool_radius \
- and self.y_axis.MIN_POSITION <= local_point.y <= self.y_axis.MAX_POSITION
+ elif self.tool in ['dual_mechanism'] and isinstance(self.y_axis, ChainAxis):
+ if second_tool:
+ return self.y_axis.MIN_POSITION <= local_point.y <= self.y_axis.MAX_POSITION
else:
- work_x = self.WORK_X_DRILL
- tool_radius = self.DRILL_RADIUS
- return work_x - tool_radius <= local_point.x <= work_x + tool_radius \
- and self.y_axis.MIN_POSITION+self.y_axis.WORK_OFFSET <= local_point.y <= self.y_axis.MAX_POSITION-self.y_axis.WORK_OFFSET
+ return self.y_axis.min_position <= local_point.y <= self.y_axis.max_position
else:
raise NotImplementedError(f'Tool {self.tool} is not implemented for reachability check')
diff --git a/field_friend/hardware/field_friend_hardware.py b/field_friend/hardware/field_friend_hardware.py
index 353f5d03..b5bfe267 100644
--- a/field_friend/hardware/field_friend_hardware.py
+++ b/field_friend/hardware/field_friend_hardware.py
@@ -37,13 +37,14 @@ def __init__(self) -> None:
self.WHEEL_DIAMETER: float = self.THOOTH_COUNT * self.PITCH / np.pi
self.M_PER_TICK: float = self.WHEEL_DIAMETER * np.pi / self.MOTOR_GEAR_RATIO
self.WHEEL_DISTANCE: float = config_params['wheel_distance']
+ self.ANTENNA_OFFSET: float = config_params['antenna_offset']
tool: str = config_params['tool']
if tool in ['tornado', 'weed_screw', 'none']:
self.WORK_X: float = config_params['work_x']
self.DRILL_RADIUS: float = config_params['drill_radius']
elif tool in ['dual_mechanism']:
self.WORK_X_CHOP: float = config_params['work_x_chop']
- self.WORK_X_DRILL: float = config_params['work_x_drill']
+ self.WORK_X: float = config_params['work_x_drill']
self.DRILL_RADIUS = config_params['drill_radius']
self.CHOP_RADIUS: float = config_params['chop_radius']
else:
@@ -97,6 +98,8 @@ def __init__(self) -> None:
y_axis = ChainAxisHardware(robot_brain,
expander=expander,
name=config_hardware['y_axis']['name'],
+ min_position=config_hardware['y_axis']['min_position'],
+ max_position=config_hardware['y_axis']['max_position'],
step_pin=config_hardware['y_axis']['step_pin'],
dir_pin=config_hardware['y_axis']['dir_pin'],
alarm_pin=config_hardware['y_axis']['alarm_pin'],
@@ -179,8 +182,8 @@ def __init__(self) -> None:
end_bottom_pin=config_hardware['z_axis']['end_bottom_pin'],
ref_motor_pin=config_hardware['z_axis']['ref_motor_pin'],
ref_gear_pin=config_hardware['z_axis']['ref_gear_pin'],
- ref_t_pin=config_hardware['z_axis']['ref_t_pin'],
- ref_b_pin=config_hardware['z_axis']['ref_b_pin'],
+ ref_knife_stop_pin=config_hardware['z_axis']['ref_knife_stop_pin'],
+ ref_knife_ground_pin=config_hardware['z_axis']['ref_knife_ground_pin'],
motors_on_expander=config_hardware['z_axis']['motors_on_expander'],
end_stops_on_expander=config_hardware['z_axis']['end_stops_on_expander'],
is_z_reversed=config_hardware['z_axis']['is_z_reversed'],
@@ -188,6 +191,8 @@ def __init__(self) -> None:
speed_limit=config_hardware['z_axis']['speed_limit'],
turn_speed_limit=config_hardware['z_axis']['turn_speed_limit'],
current_limit=config_hardware['z_axis']['current_limit'],
+ z_reference_speed=config_hardware['z_axis']['z_reference_speed'],
+ turn_reference_speed=config_hardware['z_axis']['turn_reference_speed'],
)
elif config_hardware['z_axis']['version'] == 'z_axis_canopen':
z_axis = ZAxisCanOpenHardware(robot_brain,
diff --git a/field_friend/hardware/field_friend_simulation.py b/field_friend/hardware/field_friend_simulation.py
index 189fe90e..61c32ffa 100644
--- a/field_friend/hardware/field_friend_simulation.py
+++ b/field_friend/hardware/field_friend_simulation.py
@@ -30,9 +30,9 @@ def __init__(self, robot_id) -> None:
if tool in ['tornado', 'weed_screw', 'none']:
self.WORK_X = config_params['work_x']
self.DRILL_RADIUS = config_params['drill_radius']
- elif tool in ['double_mechanism']:
+ elif tool in ['dual_mechanism']:
self.WORK_X_CHOP = config_params['work_x_chop']
- self.WORK_X_DRILL = config_params['work_x_drill']
+ self.WORK_X = config_params['work_x_drill']
self.DRILL_RADIUS = config_params['drill_radius']
self.CHOP_RADIUS = config_params['chop_radius']
else:
@@ -44,13 +44,9 @@ def __init__(self, robot_id) -> None:
y_axis = ChainAxisSimulation()
elif config_hardware['y_axis']['version'] in ['y_axis_stepper', 'y_axis_canopen']:
y_axis = YAxisSimulation(
- max_speed=config_hardware['y_axis']['max_speed'],
- reference_speed=config_hardware['y_axis']['reference_speed'],
min_position=config_hardware['y_axis']['min_position'],
max_position=config_hardware['y_axis']['max_position'],
axis_offset=config_hardware['y_axis']['axis_offset'],
- steps_per_m=config_hardware['y_axis']['steps_per_m'],
- reversed_direction=config_hardware['y_axis']['reversed_direction']
)
elif config_hardware['y_axis']['version'] == 'none':
y_axis = None
@@ -60,13 +56,9 @@ def __init__(self, robot_id) -> None:
z_axis: ZAxisSimulation | TornadoSimulation | None
if config_hardware['z_axis']['version'] in ['z_axis_stepper', 'z_axis_canopen']:
z_axis = ZAxisSimulation(
- max_speed=config_hardware['z_axis']['max_speed'],
- reference_speed=config_hardware['z_axis']['reference_speed'],
min_position=config_hardware['z_axis']['min_position'],
max_position=config_hardware['z_axis']['max_position'],
axis_offset=config_hardware['z_axis']['axis_offset'],
- steps_per_m=config_hardware['z_axis']['steps_per_m'],
- reversed_direction=config_hardware['z_axis']['reversed_direction']
)
elif config_hardware['z_axis']['version'] == 'tornado':
diff --git a/field_friend/hardware/safety.py b/field_friend/hardware/safety.py
index f1fdd69f..4ae67015 100644
--- a/field_friend/hardware/safety.py
+++ b/field_friend/hardware/safety.py
@@ -84,9 +84,9 @@ def __init__(self, robot_brain: rosys.hardware.RobotBrain, *,
lizard_code += f'when {y_axis.name}_ref_t.level == 1 then {wheels.name}.speed(0, 0); end\n'
if isinstance(z_axis, TornadoHardware):
if isinstance(y_axis, YAxisStepperHardware):
- lizard_code += f'when {z_axis.name}_ref_knive_ground.level == 1 then {wheels.name}.speed(0, 0); {y_axis.name}.stop(); end\n'
+ lizard_code += f'when {z_axis.name}_ref_knife_ground.level == 1 then {wheels.name}.speed(0, 0); {y_axis.name}.stop(); end\n'
elif isinstance(y_axis, YAxisCanOpenHardware):
- lizard_code += f'when {z_axis.name}_ref_knive_ground.level == 1 then {wheels.name}.speed(0, 0); {y_axis.name}_motor.set_ctrl_enable(false); end\n'
+ lizard_code += f'when {z_axis.name}_ref_knife_ground.level == 1 then {wheels.name}.speed(0, 0); {y_axis.name}_motor.set_ctrl_enable(false); end\n'
# implement watchdog for rosys modules
lizard_code += f'when core.last_message_age > 1000 then {wheels.name}.speed(0, 0); end\n'
diff --git a/field_friend/hardware/tornado.py b/field_friend/hardware/tornado.py
index 2411db28..3e9a7faf 100644
--- a/field_friend/hardware/tornado.py
+++ b/field_friend/hardware/tornado.py
@@ -14,6 +14,8 @@ def __init__(self, min_position, **kwargs) -> None:
self.position_z: float = 0.0
self.position_turn: float = 0.0
+ self.last_angle: float = 0.0
+
self.is_referenced: bool = False
self.z_is_referenced: bool = False
self.turn_is_referenced: bool = False
@@ -23,8 +25,8 @@ def __init__(self, min_position, **kwargs) -> None:
self.ref_motor: bool = False
self.ref_gear: bool = False
- self.ref_t: bool = False
- self.ref_b: bool = False
+ self.ref_knife_stop: bool = False
+ self.ref_knife_ground: bool = False
rosys.on_shutdown(self.stop)
@@ -53,7 +55,12 @@ async def move_down_until_reference(self) -> None:
raise RuntimeError('zaxis is not referenced, reference first')
@abc.abstractmethod
- async def turn_by(self, angle: float) -> None:
+ async def turn_by(self, turns: float) -> None:
+ if not self.turn_is_referenced:
+ raise RuntimeError('zaxis is not referenced, reference first')
+
+ @abc.abstractmethod
+ async def turn_knifes_to(self, angle: float) -> None:
if not self.turn_is_referenced:
raise RuntimeError('zaxis is not referenced, reference first')
@@ -99,8 +106,8 @@ def __init__(self, robot_brain: rosys.hardware.RobotBrain, *,
end_bottom_pin: int = 5,
ref_motor_pin: int = 33,
ref_gear_pin: int = 4,
- ref_t_pin: int = 35,
- ref_b_pin: int = 18,
+ ref_knife_stop_pin: int = 35,
+ ref_knife_ground_pin: int = 18,
motors_on_expander: bool = False,
end_stops_on_expander: bool = True,
is_z_reversed: bool = False,
@@ -108,12 +115,16 @@ def __init__(self, robot_brain: rosys.hardware.RobotBrain, *,
speed_limit: int = 1,
turn_speed_limit: int = 1,
current_limit: int = 20,
+ z_reference_speed: float = 0.0075,
+ turn_reference_speed: float = 0.25,
) -> None:
self.name = name
self.expander = expander
self.speed_limit = speed_limit
self.turn_speed_limit = turn_speed_limit
self.current_limit = current_limit
+ self.z_reference_speed = z_reference_speed
+ self.turn_reference_speed = turn_reference_speed
lizard_code = remove_indentation(f'''
{name}_z = {expander.name + "." if motors_on_expander and expander else ""}ODriveMotor({can.name}, {z_can_address})
@@ -128,8 +139,8 @@ def __init__(self, robot_brain: rosys.hardware.RobotBrain, *,
{name}_end_bottom = {expander.name + "." if end_stops_on_expander and expander else ""}Input({end_bottom_pin})
{name}_ref_motor = {expander.name + "." if end_stops_on_expander and expander else ""}Input({ref_motor_pin})
{name}_ref_gear = {expander.name + "." if end_stops_on_expander and expander else ""}Input({ref_gear_pin})
- {name}_ref_t = {expander.name + "." if end_stops_on_expander and expander else ""}Input({ref_t_pin})
- {name}_ref_b = {expander.name + "." if end_stops_on_expander and expander else ""}Input({ref_b_pin})
+ {name}_ref_knife_stop = {expander.name + "." if end_stops_on_expander and expander else ""}Input({ref_knife_stop_pin})
+ {name}_ref_knife_ground = {expander.name + "." if end_stops_on_expander and expander else ""}Input({ref_knife_ground_pin})
bool {name}_is_referencing = false;
bool {name}_end_top_enabled = true;
bool {name}_end_bottom_enabled = true;
@@ -150,14 +161,14 @@ def __init__(self, robot_brain: rosys.hardware.RobotBrain, *,
when {name}_ref_gear_enabled and {name}_is_referencing and {name}_ref_gear.level == 1 then
{name}_turn.speed(0);
end
- bool {name}_knive_ground_enabled = false;
- bool {name}_knive_stop_enabled = false;
- when {name}_knive_ground_enabled and {name}_ref_b.level == 1 then
+ bool {name}_knife_ground_enabled = false;
+ bool {name}_knife_stop_enabled = false;
+ when {name}_knife_ground_enabled and {name}_ref_knife_ground.level == 1 then
{name}_z.off();
end
- when {name}_knive_stop_enabled and {name}_ref_t.level == 1 then
+ when {name}_knife_stop_enabled and {name}_ref_knife_stop.level == 1 then
en3.off();
- {name}_knive_stop_enabled = false;
+ {name}_knife_stop_enabled = false;
end
''')
core_message_fields = [
@@ -165,8 +176,8 @@ def __init__(self, robot_brain: rosys.hardware.RobotBrain, *,
f'{name}_end_bottom.level',
f'{name}_ref_motor.level',
f'{name}_ref_gear.level',
- f'{name}_ref_t.level',
- f'{name}_ref_b.level',
+ f'{name}_ref_knife_stop.level',
+ f'{name}_ref_knife_ground.level',
f'{name}_z.position:3',
f'{name}_turn.position:3',
]
@@ -191,6 +202,8 @@ async def move_to(self, position: float) -> None:
except RuntimeError as e:
raise Exception(e) from e
await self.robot_brain.send(f'{self.name}_z.position({position});')
+ while not position - 0.005 < self.position_z < position + 0.005:
+ await rosys.sleep(0.1)
async def move_down_until_reference(self) -> None:
try:
@@ -200,24 +213,24 @@ async def move_down_until_reference(self) -> None:
try:
self.log.info('moving z axis down')
await self.robot_brain.send(
- f'{self.name}_knive_stop_enabled = true;'
- f'{self.name}_knive_ground_enabled = true;'
+ f'{self.name}_knife_stop_enabled = true;'
+ f'{self.name}_knife_ground_enabled = true;'
)
await rosys.sleep(0.5)
await self.robot_brain.send(
f'{self.name}_z.position({self.min_position});'
)
await rosys.sleep(0.5)
- while self.ref_b and not self.ref_t:
+ while self.ref_knife_ground and not self.ref_knife_stop:
if self.min_position - 0.005 <= self.position_z <= self.min_position + 0.005:
self.log.info('minimum position reached')
break
self.log.info('moving z axis down')
await rosys.sleep(0.1)
- if self.ref_t:
- raise Exception('Error while moving z axis down: Ref Top is active')
- if not self.ref_b:
- self.log.info('bottom ref reached')
+ if self.ref_knife_stop:
+ raise Exception('Error while moving z axis down: Ref knifes stop triggered')
+ if not self.ref_knife_ground:
+ self.log.info('Ref ground triggered: Bottom ground reached')
except Exception as e:
self.log.error(f'error while moving z axis down: {e}')
self.is_referenced = False
@@ -229,16 +242,35 @@ async def move_down_until_reference(self) -> None:
self.log.info('finalizing moving z axis down')
await self.robot_brain.send(
f'{self.name}_z.speed(0);'
- f'{self.name}_knive_stop_enabled = false;'
- f'{self.name}_knive_ground_enabled = false;'
+ f'{self.name}_knife_stop_enabled = false;'
+ f'{self.name}_knife_ground_enabled = false;'
)
- async def turn_by(self, angle: float) -> None:
+ async def turn_by(self, turns: float) -> None:
try:
- await super().turn_by(angle)
+ await super().turn_by(turns)
+ except RuntimeError as e:
+ raise Exception(e)
+ target = self.position_turn/360 + turns
+ await self.robot_brain.send(f'{self.name}_turn.position({target});')
+ while not target - 0.01 < self.position_turn/360 < target + 0.01:
+ await rosys.sleep(0.1)
+
+ async def turn_knifes_to(self, angle: float) -> None:
+ try:
+ await super().turn_knifes_to(angle)
except RuntimeError as e:
raise Exception(e) from e
- await self.robot_brain.send(f'{self.name}_turn.position({angle/360});')
+ target_angle = angle - self.last_angle
+ if target_angle == 0:
+ return
+ elif target_angle < 0:
+ target_angle = 360 - self.last_angle + angle
+ target = (self.position_turn - target_angle)/360
+ await self.robot_brain.send(f'{self.name}_turn.position({target});')
+ while not target - 0.01 < self.position_turn/360 < target + 0.01:
+ await rosys.sleep(0.1)
+ self.last_angle = angle
async def try_reference_z(self) -> bool:
if not await super().try_reference_z():
@@ -264,11 +296,11 @@ async def try_reference_z(self) -> bool:
await self.robot_brain.send(f'{self.name}_end_top_enabled = true;')
await rosys.sleep(1)
await self.robot_brain.send(
- f'{self.name}_z.speed({0.005*self.speed_limit});'
+ f'{self.name}_z.speed({self.z_reference_speed});'
)
await rosys.sleep(0.2)
while not self.end_top:
- await self.robot_brain.send(f'{self.name}_z.speed({0.005*self.speed_limit});')
+ await self.robot_brain.send(f'{self.name}_z.speed({self.z_reference_speed});')
await rosys.sleep(0.2)
await self.robot_brain.send(f'{self.name}_z.speed(0);')
@@ -276,10 +308,10 @@ async def try_reference_z(self) -> bool:
self.log.info('moving out of end top')
await self.robot_brain.send(f'{self.name}_end_top_enabled = false;')
await rosys.sleep(1)
- await self.robot_brain.send(f'{self.name}_z.speed({-0.005*self.speed_limit});')
+ await self.robot_brain.send(f'{self.name}_z.speed({-self.z_reference_speed});')
await rosys.sleep(0.2)
while self.end_top:
- await self.robot_brain.send(f'{self.name}_z.speed({-0.005*self.speed_limit});')
+ await self.robot_brain.send(f'{self.name}_z.speed({-self.z_reference_speed});')
await rosys.sleep(0.2)
await self.robot_brain.send(f'{self.name}_z.speed(0);')
@@ -287,10 +319,10 @@ async def try_reference_z(self) -> bool:
self.log.info('moving slowly to end top')
await self.robot_brain.send(f'{self.name}_end_top_enabled = true;')
await rosys.sleep(1)
- await self.robot_brain.send(f'{self.name}_z.speed({0.001*self.speed_limit});')
+ await self.robot_brain.send(f'{self.name}_z.speed({self.z_reference_speed/5});')
await rosys.sleep(0.2)
while not self.end_top:
- await self.robot_brain.send(f'{self.name}_z.speed({0.001*self.speed_limit});')
+ await self.robot_brain.send(f'{self.name}_z.speed({self.z_reference_speed/5});')
await rosys.sleep(0.2)
await self.robot_brain.send(f'{self.name}_z.speed(0);')
@@ -298,10 +330,10 @@ async def try_reference_z(self) -> bool:
self.log.info('moving slowly out of end top')
await self.robot_brain.send(f'{self.name}_end_top_enabled = false;')
await rosys.sleep(1)
- await self.robot_brain.send(f'{self.name}_z.speed({-0.001*self.speed_limit});')
+ await self.robot_brain.send(f'{self.name}_z.speed({-self.z_reference_speed/5});')
await rosys.sleep(0.2)
while self.end_top:
- await self.robot_brain.send(f'{self.name}_z.speed({-0.001*self.speed_limit});')
+ await self.robot_brain.send(f'{self.name}_z.speed({-self.z_reference_speed/5});')
await rosys.sleep(0.2)
await self.robot_brain.send(f'{self.name}_z.speed(0);')
@@ -342,21 +374,21 @@ async def try_reference_turn(self) -> bool:
await self.robot_brain.send(f'{self.name}_ref_gear_enabled = true;')
await rosys.sleep(1)
await self.robot_brain.send(
- f'{self.name}_turn.speed({0.18*self.speed_limit});'
+ f'{self.name}_turn.speed({self.turn_reference_speed});'
)
await rosys.sleep(0.2)
while not self.ref_gear:
- await self.robot_brain.send(f'{self.name}_turn.speed({0.18*self.speed_limit});')
+ await self.robot_brain.send(f'{self.name}_turn.speed({self.turn_reference_speed});')
await rosys.sleep(0.1)
await self.robot_brain.send(f'{self.name}_turn.speed(0);')
# drive to motor ref
await self.robot_brain.send(f'{self.name}_ref_motor_enabled = true;')
await rosys.sleep(1)
- await self.robot_brain.send(f'{self.name}_turn.move({-0.18*self.speed_limit});')
+ await self.robot_brain.send(f'{self.name}_turn.move({-self.turn_reference_speed});')
await rosys.sleep(0.2)
while not self.ref_motor:
- await self.robot_brain.send(f'{self.name}_turn.speed({-0.18*self.speed_limit});')
+ await self.robot_brain.send(f'{self.name}_turn.speed({-self.turn_reference_speed});')
await rosys.sleep(0.1)
await self.robot_brain.send(f'{self.name}_turn.speed(0);')
@@ -370,6 +402,7 @@ async def try_reference_turn(self) -> bool:
await rosys.sleep(0.2)
self.log.info('referencing tornado turn position done')
self.turn_is_referenced = True
+ self.last_angle = 0
return True
except Exception as e:
self.log.error(f'error while referencing tornado turn position: {e}')
@@ -388,8 +421,8 @@ def handle_core_output(self, time: float, words: list[str]) -> None:
self.is_referenced = False
self.ref_motor = int(words.pop(0)) == 0
self.ref_gear = int(words.pop(0)) == 1
- self.ref_t = int(words.pop(0)) == 1
- self.ref_b = int(words.pop(0)) == 0
+ self.ref_knife_stop = int(words.pop(0)) == 1
+ self.ref_knife_ground = int(words.pop(0)) == 0
self.position_z = float(words.pop(0))
self.position_turn = float(words.pop(0)) * 360
@@ -436,6 +469,20 @@ async def turn_by(self, angle: float) -> None:
raise Exception(e) from e
self.position_turn = angle
+ async def turn_knifes_to(self, angle: float) -> None:
+ try:
+ await super().turn_knifes_to(angle)
+ except RuntimeError as e:
+ raise Exception(e) from e
+ self.position_turn = angle
+
+ async def turn_knifes_to(self, angle: float) -> None:
+ try:
+ await super().turn_knifes_to(angle)
+ except RuntimeError as e:
+ raise Exception(e)
+ self.position_turn = angle
+
async def try_reference_z(self) -> bool:
if not await super().try_reference_z():
return False
diff --git a/field_friend/interface/components/automation_controls.py b/field_friend/interface/components/automation_controls.py
index ed36d5db..5c56f237 100644
--- a/field_friend/interface/components/automation_controls.py
+++ b/field_friend/interface/components/automation_controls.py
@@ -21,7 +21,8 @@ def __init__(
async def start() -> None:
if callable(can_start):
# Directly handle both synchronous and asynchronous can_start
- proceed = can_start() if not isinstance(can_start(), Awaitable) else await can_start()
+ result = can_start()
+ proceed = result if not isinstance(result, Awaitable) else await result
if not proceed:
return
diff --git a/field_friend/interface/components/field_friend_object.py b/field_friend/interface/components/field_friend_object.py
index c1e607e3..0aa0e080 100644
--- a/field_friend/interface/components/field_friend_object.py
+++ b/field_friend/interface/components/field_friend_object.py
@@ -4,7 +4,7 @@
from rosys.geometry import Prism
from rosys.vision import CameraProjector, CameraProvider, camera_objects
-from ...hardware import ChainAxis, FieldFriend, YAxis
+from ...hardware import ChainAxis, FieldFriend, Tornado, YAxis, ZAxis
class field_friend_object(robot_object):
@@ -34,22 +34,28 @@ def __init__(self, odometer: Odometer, camera_provider: CameraProvider,
def update(self) -> None:
super().update()
- if isinstance(self.robot.y_axis, YAxis):
+ if isinstance(self.robot.y_axis, YAxis) and isinstance(self.robot.z_axis, ZAxis):
self.tool.move(x=self.robot.WORK_X, y=self.robot.y_axis.position, z=self.robot.z_axis.position)
if self.robot.y_axis.position > self.robot.y_axis.max_position or self.robot.y_axis.position < self.robot.y_axis.min_position:
self.tool.material('red')
else:
self.tool.material('#4488ff')
+ elif isinstance(self.robot.y_axis, YAxis) and isinstance(self.robot.z_axis, Tornado):
+ self.tool.move(x=self.robot.WORK_X, y=self.robot.y_axis.position, z=self.robot.z_axis.position_z)
+ if self.robot.y_axis.position > self.robot.y_axis.max_position or self.robot.y_axis.position < self.robot.y_axis.min_position:
+ self.tool.material('red')
+ else:
+ self.tool.material('#4488ff')
elif isinstance(self.robot.y_axis, ChainAxis):
if self.robot.y_axis.MIN_POSITION <= self.robot.y_axis.position <= self.robot.y_axis.MAX_POSITION:
self.tool.move(x=self.robot.WORK_X_CHOP, y=self.robot.y_axis.position)
- self.second_tool.move(x=self.robot.WORK_X_DRILL, y=self.robot.y_axis.position,
+ self.second_tool.move(x=self.robot.WORK_X, y=self.robot.y_axis.position,
z=self.robot.z_axis.position)
elif self.robot.y_axis.position > self.robot.y_axis.MAX_POSITION:
difference = self.robot.y_axis.position - self.robot.y_axis.MAX_POSITION
self.tool.move(x=self.robot.WORK_X_CHOP, y=self.robot.y_axis.MAX_POSITION - difference)
- self.second_tool.move(x=self.robot.WORK_X_DRILL, y=self.robot.y_axis.MAX_POSITION - difference)
+ self.second_tool.move(x=self.robot.WORK_X, y=self.robot.y_axis.MAX_POSITION - difference)
else:
difference = self.robot.y_axis.MIN_POSITION - self.robot.y_axis.position
self.tool.move(x=self.robot.WORK_X_CHOP, y=self.robot.y_axis.MIN_POSITION + difference)
- self.second_tool.move(x=self.robot.WORK_X_DRILL, y=self.robot.y_axis.MIN_POSITION + difference)
+ self.second_tool.move(x=self.robot.WORK_X, y=self.robot.y_axis.MIN_POSITION + difference)
diff --git a/field_friend/interface/components/field_planner.py b/field_friend/interface/components/field_planner.py
index aacffda7..e80a623c 100644
--- a/field_friend/interface/components/field_planner.py
+++ b/field_friend/interface/components/field_planner.py
@@ -1,4 +1,3 @@
-import json
import logging
import uuid
from typing import Literal, Optional
@@ -6,8 +5,6 @@
import rosys
from nicegui import events, ui
-from field_friend.navigation.point_transformation import cartesian_to_wgs84, wgs84_to_cartesian
-
from ...automations import Field, FieldObstacle, FieldProvider, Row
from ...navigation import Gnss
from .geodata_picker import geodata_picker
@@ -343,7 +340,7 @@ def remove_point(self, field: Field, point: Optional[list] = None) -> None:
def add_field(self) -> None:
new_id = str(uuid.uuid4())
- field = Field(id=f'{new_id}', name=f'{new_id}', outline_wgs84=[])
+ field = Field(id=f'{new_id}', name=f'field_{len(self.field_provider.fields)+1}', outline_wgs84=[])
self.field_provider.add_field(field)
def delete_field(self, field: Field) -> None:
@@ -353,7 +350,7 @@ def clear_fields(self) -> None:
self.field_provider.clear_fields()
def add_obstacle(self, field: Field) -> None:
- obstacle = FieldObstacle(id=f'{str(uuid.uuid4())}', name=f'{str(uuid.uuid4())}', points_wgs84=[])
+ obstacle = FieldObstacle(id=f'{str(uuid.uuid4())}', name=f'obstacle_{len(field.obstacles)+1}', points_wgs84=[])
self.field_provider.add_obstacle(field, obstacle)
async def add_obstacle_point(self, field: Field, obstacle: FieldObstacle, point: Optional[list] = None, new_point: Optional[list] = None) -> None:
@@ -399,7 +396,7 @@ def remove_obstacle_point(self, obstacle: FieldObstacle, point: Optional[list] =
self.field_provider.invalidate()
def add_row(self, field: Field) -> None:
- row = Row(id=f'{str(uuid.uuid4())}', name=f'{str(uuid.uuid4())}', points_wgs84=[])
+ row = Row(id=f'{str(uuid.uuid4())}', name=f'row_{len(field.rows)+1}', points_wgs84=[])
self.field_provider.add_row(field, row)
def add_row_point(self, field: Field, row: Row, point: Optional[list] = None, new_point: Optional[list] = None) -> None:
diff --git a/field_friend/interface/components/geodata_picker.py b/field_friend/interface/components/geodata_picker.py
index d783c063..cf15375b 100644
--- a/field_friend/interface/components/geodata_picker.py
+++ b/field_friend/interface/components/geodata_picker.py
@@ -97,7 +97,7 @@ async def restore_from_file(self, e: events.UploadEventArguments) -> None:
coordinates.pop()
reference_point = coordinates[0]
new_id = str(uuid.uuid4())
- field = Field(id=f'{new_id}', name=f'{new_id}', outline_wgs84=coordinates,
+ field = Field(id=f'{new_id}', name=f'field_{len(self.field_provider.fields)+1}', outline_wgs84=coordinates,
reference_lat=reference_point[0], reference_lon=reference_point[1])
self.field_provider.add_field(field)
return
diff --git a/field_friend/interface/components/hardware_control.py b/field_friend/interface/components/hardware_control.py
index 29d2f04f..957b9dba 100644
--- a/field_friend/interface/components/hardware_control.py
+++ b/field_friend/interface/components/hardware_control.py
@@ -124,10 +124,10 @@ async def toggle_flashlight(e: ValueChangeEventArguments) -> None:
with ui.row():
if isinstance(field_friend.y_axis, ChainAxis):
ui.button(on_click=lambda: automator.start(
- puncher.punch(field_friend.y_axis.MAX_POSITION-field_friend.y_axis.WORK_OFFSET, depth=depth.value)))
+ puncher.punch(field_friend.y_axis.min_position, depth=depth.value)))
ui.button(on_click=lambda: automator.start(puncher.punch(0, depth=depth.value)))
ui.button(on_click=lambda: automator.start(
- puncher.punch(field_friend.y_axis.MIN_POSITION+field_friend.y_axis.WORK_OFFSET, depth=depth.value)))
+ puncher.punch(field_friend.y_axis.max_position, depth=depth.value)))
elif isinstance(field_friend.y_axis, YAxis) and isinstance(field_friend.z_axis, Tornado):
ui.button(on_click=lambda: automator.start(
puncher.punch(field_friend.y_axis.min_position, angle=angle.value)))
diff --git a/field_friend/interface/components/key_controls.py b/field_friend/interface/components/key_controls.py
index 6b622b88..aec8db35 100644
--- a/field_friend/interface/components/key_controls.py
+++ b/field_friend/interface/components/key_controls.py
@@ -27,9 +27,6 @@ def handle_keys(self, e: KeyEventArguments) -> None:
if e.key == '!':
self.automator.start(self.puncher.try_home())
- if e.action.keydown and e.key == 'r' and e.modifiers.shift:
- self.system.restart()
-
if e.action.keydown and e.key == 's':
if self.automator.is_running:
self.automator.stop(because='stop button was pressed')
diff --git a/field_friend/interface/components/leaflet_map.py b/field_friend/interface/components/leaflet_map.py
index f904e1ec..45b990ba 100644
--- a/field_friend/interface/components/leaflet_map.py
+++ b/field_friend/interface/components/leaflet_map.py
@@ -1,13 +1,14 @@
import logging
-from typing import TYPE_CHECKING, Dict, List, Union
import uuid
-import rosys.geometry
-from nicegui import events, ui, elements
+from typing import TYPE_CHECKING
+
import rosys
+import rosys.geometry
+from nicegui import app, events, ui
+from nicegui.elements.leaflet_layers import TileLayer
+
from .key_controls import KeyControls
-from ...automations import Field
-import numpy as np
if TYPE_CHECKING:
from field_friend.system import System
@@ -32,28 +33,22 @@ def __init__(self, system: 'System', draw_tools: bool) -> None:
},
'edit': False,
}
- self.center_point = [51.983159, 7.434212]
+ center_point = [51.983159, 7.434212]
if self.field_provider.active_field is None:
- self.center_point = [51.983159, 7.434212]
+ center_point = [51.983159, 7.434212]
else:
if len(self.field_provider.active_field.outline_wgs84) > 0:
- self.center_point = self.field_provider.active_field.outline_wgs84[0]
+ center_point = self.field_provider.active_field.outline_wgs84[0]
+ self.m: ui.leaflet
if draw_tools:
- self.m = ui.leaflet(center=(self.center_point[0], self.center_point[1]),
+ self.m = ui.leaflet(center=(center_point[0], center_point[1]),
zoom=13, draw_control=self.draw_control)
else:
- self.m = ui.leaflet(center=(self.center_point[0], self.center_point[1]),
+ self.m = ui.leaflet(center=(center_point[0], center_point[1]),
zoom=13)
-
self.m.clear_layers()
- self.m.tile_layer(
- url_template=r'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
- options={
- 'maxZoom': 21,
- 'attribution': '© OpenStreetMap contributors'
- },
- )
-
+ self.current_basemap: TileLayer | None = None
+ self.toggle_basemap()
self.field_layers: list[list] = []
self.robot_marker = None
self.drawn_marker = None
@@ -91,7 +86,7 @@ def handle_draw(e: events.GenericEventArguments):
point_list = []
for point in coordinates[0]:
point_list.append([point['lat'], point['lng']])
- field = Field(id=f'{str(uuid.uuid4())}', name=f'{str(uuid.uuid4())}',
+ field = Field(id=f'{str(uuid.uuid4())}', name=f'field_{len(self.field_provider.fields)+1}',
outline_wgs84=point_list, reference_lat=point_list[0][0], reference_lon=point_list[0][1])
self.field_provider.add_field(field)
@@ -99,6 +94,14 @@ def handle_draw(e: events.GenericEventArguments):
m.on('draw:created', handle_draw)
self.gnss.ROBOT_POSITION_LOCATED.register(self.update_robot_position)
+ def buttons(self) -> None:
+ """Builds additional buttons to interact with the map."""
+ ui.button(icon='my_location', on_click=self.zoom_to_robot).props('dense flat')
+ ui.button(icon='satellite', on_click=self.toggle_basemap).props('dense flat') \
+ .bind_visibility_from(self, 'current_basemap', lambda x: x is not None and 'openstreetmap' not in x.url_template)
+ ui.button(icon='map', on_click=self.toggle_basemap).props('dense flat') \
+ .bind_visibility_from(self, 'current_basemap', lambda x: x is not None and 'openstreetmap' in x.url_template)
+
def set_simulated_reference(self, latlon, dialog):
dialog.close()
self.m.remove_layer(self.drawn_marker)
@@ -165,11 +168,39 @@ def update_robot_position(self) -> None:
self.robot_marker.run_method(':setIcon', icon)
self.robot_marker.move(self.gnss.record.latitude, self.gnss.record.longitude)
- def change_basemap(self) -> None:
- return
- # TODO: add a button in leaflet map to change basemap layer and implement the functionality here
-
- # this is the ESRI satellite image as free satellite image
- # Esri_WorldImagery = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
- # attribution: 'Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
- # })
+ def zoom_to_robot(self) -> None:
+ self.m.center = (self.gnss.record.latitude, self.gnss.record.longitude)
+ self.m.set_zoom(self.current_basemap.options['maxZoom'] - 1)
+
+ def toggle_basemap(self) -> None:
+ use_satellite = app.storage.user.get('use_satellite', False)
+ if self.current_basemap is not None:
+ self.m.remove_layer(self.current_basemap)
+ use_satellite = not use_satellite
+ app.storage.user['use_satellite'] = use_satellite
+ if use_satellite:
+ # ESRI satellite image provides free usage
+ self.current_basemap = self.m.tile_layer(
+ url_template='https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
+ options={
+ 'maxZoom': 21,
+ 'attribution': 'Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
+ })
+ # self.current_basemap = self.m.tile_layer(
+ # url_template=r'http://{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',
+ # options={
+ # 'maxZoom': 20,
+ # 'subdomains': ['mt0', 'mt1', 'mt2', 'mt3'],
+ # 'attribution': '© Google Maps'
+ # },
+ # )
+ else:
+ self.current_basemap = self.m.tile_layer(
+ url_template=r'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
+ options={
+ 'maxZoom': 20,
+ 'attribution': '© OpenStreetMap contributors'
+ },
+ )
+ if self.current_basemap.options['maxZoom'] - 1 < self.m.zoom:
+ self.m.set_zoom(self.current_basemap.options['maxZoom'] - 1)
diff --git a/field_friend/interface/components/monitoring.py b/field_friend/interface/components/monitoring.py
index fc899662..2eace261 100644
--- a/field_friend/interface/components/monitoring.py
+++ b/field_friend/interface/components/monitoring.py
@@ -31,7 +31,8 @@ def __init__(self,
field_friend: FieldFriend,
system: 'System',
*,
- shrink_factor: int = 1) -> None:
+ shrink_factor: int = 1,
+ ) -> None:
self.log = logging.getLogger('field_friend.monitoring')
self.usb_camera_provider = usb_camera_provider
self.mjpg_camera_provider = mjpeg_camera_provider
@@ -69,10 +70,12 @@ def __init__(self,
ui.label('Animal count:').classes('text-2xl text-bold').bind_text_from(self,
'animal_count', backward=lambda x: f'Animal count: {x}')
ui.space()
- ui.switch('Person detection').bind_value(
- self, 'monitoring_active').bind_enabled_from(self.automator, 'is_running', backward=lambda x: not x)
- ui.switch('Plant detection').bind_value(self.plant_locator, 'is_paused', forward=lambda x: not x,
- backward=lambda x: not x).bind_enabled_from(self.automator, 'is_running', backward=lambda x: not x)
+ ui.switch('Person detection') \
+ .bind_value(self, 'monitoring_active') \
+ .bind_enabled_from(self.automator, 'is_running', backward=lambda x: not x)
+ ui.switch('Plant detection') \
+ .bind_value(self.plant_locator, 'is_paused', forward=lambda x: not x, backward=lambda x: not x) \
+ .bind_enabled_from(self.automator, 'is_running', backward=lambda x: not x)
with ui.row().classes('w-full items-stretch gap-0'):
column_classes = 'w-1/3 items-center mt-[50px]'
diff --git a/field_friend/interface/components/operation.py b/field_friend/interface/components/operation.py
index bbb5d210..9919ba85 100644
--- a/field_friend/interface/components/operation.py
+++ b/field_friend/interface/components/operation.py
@@ -2,7 +2,7 @@
import logging
from typing import TYPE_CHECKING
-from nicegui import ui
+from nicegui import app, events, ui
from .automation_controls import automation_controls
from .key_controls import KeyControls
@@ -30,8 +30,10 @@ def __init__(self, system: 'System', leaflet_map: leaflet_map) -> None:
@ui.refreshable
def center_map_button() -> None:
if self.field_provider.active_field is not None and len(self.field_provider.active_field.outline_wgs84) > 0:
- ui.button(on_click=lambda: self.leaflet_map.m.set_center(self.field_provider.active_field.outline_wgs84[0])).props(
- 'icon=place color=primary fab-mini flat').tooltip('center map on point').classes('ml-0')
+ ui.button(on_click=lambda: self.leaflet_map.m.set_center(self.field_provider.active_field.outline_wgs84[0])) \
+ .props('icon=place color=primary fab-mini flat').tooltip('center map on point').classes('ml-0')
+ ui.button(on_click=lambda: self.leaflet_map.m.set_center(self.field_provider.active_field.outline_wgs84[0])) \
+ .props('icon=place color=primary fab-mini flat').tooltip('center map on point').classes('ml-0')
else:
ui.icon('place').props('size=sm color=grey').classes('ml-2')
center_map_button()
@@ -41,98 +43,299 @@ def center_map_button() -> None:
if self.field_provider.fields is not None and len(self.field_provider.fields) > 0:
for field in self.system.field_provider.fields:
field_selection_dict[field.id] = field.name
- self.initial_value = None if self.field_provider.active_field is None else self.field_provider.active_field.id
+ active = self.field_provider.active_field
+ self.initial_value = app.storage.user.get('field') if active is None else active.id
self.field_selection = None
@ui.refreshable
def show_field_selection() -> None:
self.field_selection = ui.select(
field_selection_dict,
- with_input=True, on_change=self.set_field, label='Field', value=self.initial_value).tooltip(
- 'Select the field to work on').classes('w-24')
+ with_input=True,
+ on_change=self.set_field,
+ label='Field')\
+ .tooltip('Select the field to work on').classes('w-24')
+ self.field_selection.value = self.initial_value
show_field_selection()
self.field_provider.FIELDS_CHANGED.register(show_field_selection.refresh)
ui.separator()
with ui.row():
- ui.label("Automation").classes('text-xl')
+ ui.label('Automation').classes('text-xl')
+ ui.label('Automation').classes('text-xl')
with ui.row().classes('w-full'):
- self.automations_toggle = ui.select(
- [key for key in self.system.automations.keys()],
- value='weeding').bind_value(
- self.system.automator, 'default_automation', forward=lambda key: self.system.automations[key],
- backward=lambda automation: next(
- key for key, value in self.system.automations.items() if value == automation)).classes('w-full border pl-2').style('border: 2px solid #6E93D6; border-radius: 5px; background-color: #EEF4FA')
-
+ self.automations_toggle = ui.select([key for key in self.system.automations.keys()],
+ on_change=self.handle_automation_changed) \
+ .classes('w-full border pl-2').style('border: 2px solid #6E93D6; border-radius: 5px; background-color: #EEF4FA')
+ self.automations_toggle.value = app.storage.user.get('automation', 'weeding')
with ui.column().bind_visibility_from(self.automations_toggle, 'value', value='mowing'):
with ui.row():
- ui.number('Padding', value=0.5, step=0.1, min=0.0, format='%.1f').props('dense outlined suffix=m').classes(
- 'w-24').bind_value(system.mowing, 'padding').tooltip('Set the padding for the mowing automation')
- ui.number('Lane distance', value=0.5, step=0.1, min=0.0, format='%.1f').props('dense outlined suffix=m').classes(
- 'w-24').bind_value(system.mowing, 'lane_distance').tooltip('Set the lane distance for the system. automation')
- ui.number('Number of outer lanes', value=3, step=1, min=3, format='%.0f').props('dense outlined').classes(
- 'w-28').bind_value(system.mowing, 'number_of_outer_lanes').tooltip('Set the number of outer lanes for the mowing automation')
- ui.number('Min. turning radius', format='%.2f', value=0.5, step=0.1, min=0.1, max=1.0).props(
- 'dense outlined suffix=m').classes('w-32').bind_value(
- self.system.mowing, 'turning_radius').tooltip(
- 'Set the turning radius for the mowing automation')
+ ui.number('Padding', value=0.5, step=0.1, min=0.0, format='%.1f') \
+ .props('dense outlined suffix=m').classes('w-24') \
+ .bind_value(system.mowing, 'padding') \
+ .tooltip('Set the padding for the mowing automation')
+ ui.number('Lane distance', value=0.5, step=0.1, min=0.0, format='%.1f') \
+ .props('dense outlined suffix=m') \
+ .classes('w-24').bind_value(system.mowing, 'lane_distance') \
+ .tooltip('Set the lane distance for the system. automation')
+ ui.number('Number of outer lanes', value=3, step=1, min=3, format='%.0f') \
+ .props('dense outlined').classes('w-28') \
+ .bind_value(system.mowing, 'number_of_outer_lanes') \
+ .tooltip('Set the number of outer lanes for the mowing automation')
+ ui.number('Min. turning radius', format='%.2f', value=0.5, step=0.05, min=0.1, max=2.0) \
+ .props('dense outlined suffix=m') \
+ .classes('w-32') \
+ .bind_value(self.system.mowing, 'minimum_turning_radius') \
+ .tooltip('Set the turning radius for the mowing automation')
+ ui.number('Padding', value=0.5, step=0.1, min=0.0, format='%.1f') \
+ .props('dense outlined suffix=m').classes('w-24') \
+ .bind_value(system.mowing, 'padding') \
+ .tooltip('Set the padding for the mowing automation')
+ ui.number('Lane distance', value=0.5, step=0.1, min=0.0, format='%.1f') \
+ .props('dense outlined suffix=m') \
+ .classes('w-24').bind_value(system.mowing, 'lane_distance') \
+ .tooltip('Set the lane distance for the system. automation')
+ ui.number('Number of outer lanes', value=3, step=1, min=3, format='%.0f') \
+ .props('dense outlined').classes('w-28') \
+ .bind_value(system.mowing, 'number_of_outer_lanes') \
+ .tooltip('Set the number of outer lanes for the mowing automation')
+ ui.number('Min. turning radius', format='%.2f', value=0.5, step=0.05, min=0.1, max=2.0) \
+ .props('dense outlined suffix=m') \
+ .classes('w-32') \
+ .bind_value(self.system.mowing, 'minimum_turning_radius') \
+ .tooltip('Set the turning radius for the mowing automation')
with ui.column().bind_visibility_from(self.automations_toggle, 'value', value='weeding'):
- with ui.column():
+ ui.separator()
+ ui.markdown('Field settings').style('color: #6E93D6')
+ with ui.row():
self.with_field_planning = ui.checkbox('Use field planning', value=True).bind_value(
self.system.weeding, 'use_field_planning').tooltip('Set the weeding automation to use the field planning with GNSS')
with ui.row().bind_visibility_from(self.with_field_planning, 'value', value=True):
self.show_start_row()
self.show_end_row()
- ui.number('Min. turning radius', format='%.2f', value=0.5, step=0.1, min=0.1, max=1.0).props(
- 'dense outlined suffix=m').classes('w-30').bind_value(
- self.system.weeding, 'turning_radius').tooltip(
- 'Set the turning radius for the weeding automation')
- with ui.row():
- ui.number('Tornado angle', format='%.0f', value=180, step=1, min=1, max=180).props(
- 'dense outlined suffix=°').classes('w-24').bind_value(
- self.system.weeding, 'tornado_angle').tooltip(
- 'Set the angle for the tornado drill')
- ui.checkbox('Only monitoring').bind_value(
- self.system.weeding, 'only_monitoring').tooltip(
- 'Set the weeding automation to only monitor the field')
-
- with ui.column().bind_visibility_from(self.automations_toggle, 'value', value='monitoring'):
- with ui.column():
- self.with_field_planning_monitor = ui.checkbox('Use field planning', value=True).bind_value(
- self.system.monitoring, 'use_field_planning').tooltip('Set the monitoring automation to use the field planning with GNSS')
-
- with ui.row().bind_visibility_from(self.with_field_planning_monitor, 'value', value=True):
- self.show_start_row()
- self.show_end_row()
- ui.number('Min. turning radius', format='%.2f', value=0.5, step=0.1, min=0.1, max=1.0).props(
- 'dense outlined suffix=m').classes('w-30').bind_value(
- self.system.monitoring, 'turning_radius').tooltip(
- 'Set the turning radius for the monitoring automation')
+ ui.number('Min. turning radius', format='%.2f', value=0.5, step=0.05, min=0.05, max=2.0) \
+ .props('dense outlined suffix=m').classes('w-30').bind_value(
+ self.system.weeding, 'minimum_turning_radius').tooltip(
+ ui.number('Min. turning radius', format='%.2f',
+ value=0.5, step=0.05, min=0.05, max=2.0)
+ .props('dense outlined suffix=m').classes('w-30').bind_value(
+ self.system.weeding, 'minimum_turning_radius').tooltip(
+ 'Set the turning radius for the weeding automation')
+ ui.number('turn_offset', format='%.2f', value=0.4, step=0.05, min=0.05, max=2.0)
+ .props('dense outlined suffix=m').classes('w-30').bind_value(
+ self.system.weeding, 'turn_offset').tooltip(
+ 'Set the turning offset for the weeding automation')
+ ui.separator()
+ ui.markdown('Detector settings').style('color: #6E93D6')
+ with ui.row():
+ ui.number('Min. weed confidence', format='%.2f',
+ value=0.8, step=0.05, min=0.0, max=1.0)
+ .props('dense outlined').classes('w-24').bind_value(
+ self.system.plant_locator, 'minimum_weed_confidence').tooltip(
+ 'Set the minimum weed confidence for the weeding automation')
+ ui.number('Min. crop confidence', format='%.2f',
+ value=0.4, step=0.05, min=0.0, max=1.0)
+ .props('dense outlined').classes('w-24').bind_value(
+ self.system.plant_locator, 'minimum_crop_confidence').tooltip(
+ 'Set the minimum crop confidence for the weeding automation')
+ ui.separator()
+ ui.markdown('Tool settings').style('color: #6E93D6')
+ with ui.row():
+ if self.system.field_friend.tool == 'tornado':
+ ui.number('turn_offset', format='%.2f', value=0.4, step=0.05, min=0.05, max=2.0)
+ .props('dense outlined suffix=m').classes('w-30').bind_value(
+ self.system.weeding, 'turn_offset').tooltip(
+ 'Set the turning offset for the weeding automation')
+ ui.separator()
+ ui.markdown('Detector settings').style('color: #6E93D6')
+ with ui.row():
+ ui.number('Min. weed confidence', format='%.2f',
+ value=0.8, step=0.05, min=0.0, max=1.0)
+ .props('dense outlined').classes('w-24').bind_value(
+ self.system.plant_locator, 'minimum_weed_confidence').tooltip(
+ 'Set the minimum weed confidence for the weeding automation')
+ ui.number('Min. crop confidence', format='%.2f',
+ value=0.4, step=0.05, min=0.0, max=1.0)
+ .props('dense outlined').classes('w-24').bind_value(
+ self.system.plant_locator, 'minimum_crop_confidence').tooltip(
+ 'Set the minimum crop confidence for the weeding automation')
+ ui.separator()
+ ui.markdown('Tool settings').style('color: #6E93D6')
+ with ui.row():
+ if self.system.field_friend.tool == 'tornado':
+ ui.number('Tornado angle', format='%.0f', value=180, step=1, min=1, max=180).props(
+ 'dense outlined suffix=°').classes('w-24').bind_value(
+ self.system.weeding, 'tornado_angle').tooltip(
+ 'Set the angle for the tornado drill')
+ elif self.system.field_friend.tool in ['weed_screw', 'dual_mechanism']:
+ ui.number('Drill depth', value=0.02, format='%.2f', step=0.01,
+ min=self.system.field_friend.z_axis.max_position, max=self.system.field_friend.z_axis.min_position*-1)
+ .props('dense outlined suffix=°').classes('w-24').bind_value(
+ self.system.weeding, 'weed_screw_depth').tooltip(
+ 'Set the drill depth for the weeding automation')
+ ui.number('Crop safety distance', value=0.01, step=0.01, min=0.0, max=0.05, format='%.2f').props(
+ 'dense outlined suffix=m').classes('w-24').bind_value(
+ self.system.weeding, 'crop_safety_distance').tooltip(
+ 'Set the crop safety distance for the weeding automation')
- with ui.column().bind_visibility_from(self.automations_toggle, 'value', value='collecting (demo)'):
- with ui.row():
- ui.number(
- 'Drill angle', format='%.0f', value=100, step=1, min=1, max=180).props(
- 'dense outlined suffix=°').classes('w-24').bind_value(
- self.system.coin_collecting, 'angle').tooltip(
- 'Set the drill depth for the weeding automation')
- ui.checkbox('with drilling', value=True).bind_value(
- self.system.coin_collecting, 'with_drilling')
+ ui.separator()
+ ui.markdown('Workflow settings').style('color: #6E93D6')
+ with ui.row():
+ if self.system.field_friend.tool == 'tornado':
+ ui.checkbox('Drill 2x with open torando', value=False, on_change=system.weeding.invalidate).bind_value(
+ self.system.weeding, 'drill_with_open_tornado').tooltip(
+ 'Set the weeding automation to drill a second time with open tornado')
+ ui.checkbox('Drill between crops', value=False).bind_value(
+ self.system.weeding, 'drill_between_crops').tooltip(
+ 'Set the weeding automation to drill between crops')
+ elif self.system.field_friend.tool == 'dual_mechanism':
+
+ ui.checkbox('Drilling', value=False).bind_value(
+ self.system.weeding, 'with_drilling').tooltip(
+ 'Set the weeding automation to with drill')
+ ui.checkbox('Chopping', value=False).bind_value(
+ self.system.weeding, 'with_chopping').tooltip(
+ 'Set the weeding automation to with chop')
+
+ ui.checkbox('Chop if no crops', value=False).bind_value(
+ self.system.weeding, 'chop_if_no_crops').tooltip(
+ 'Set the weeding automation to chop also if no crops seen')
+
+ ui.checkbox('Only monitoring').bind_value(
+ self.system.weeding, 'only_monitoring').tooltip(
+ 'Set the weeding automation to only monitor the field')
+ ui.separator()
+ ui.markdown('**Driver settings**').style('color: #6E93D6')
+ with ui.row():
+ ui.number('linear_speed_on_row', value=0.5, step=0.1, min=0.1, format='%.1f')
+ .props('dense outlined suffix=m/s').classes('w-24')
+ .bind_value(self.system.weeding, 'linear_speed_on_row')
+ .tooltip('Set the linear speed on row for the weeding automation')
+ ui.number('linear_speed_between_rows', value=0.5, step=0.1, min=0.1, format='%.1f')
+ .props('dense outlined suffix=m/s').classes('w-24')
+ .bind_value(self.system.weeding, 'linear_speed_between_rows')
+ .tooltip('Set the linear speed between rows for the weeding automation')
+ ui.number('angular_speed_on_row', value=0.5, step=0.1, min=0.1, format='%.1f')
+ .props('dense outlined suffix=°/s').classes('w-24')
+ .bind_value(self.system.weeding, 'angular_speed_on_row')
+ .tooltip('Set the angular speed on row for the weeding automation')
+ ui.number('angular_speed_between_rows', value=0.5, step=0.1, min=0.1, format='%.1f')
+ .props('dense outlined suffix=°/s').classes('w-24')
+ .bind_value(self.system.weeding, 'angular_speed_between_rows')
+ .tooltip('Set the angular speed between rows for the weeding automation')
+ elif self.system.field_friend.tool in ['weed_screw', 'dual_mechanism']:
+ ui.number('Drill depth', value=0.02, format='%.2f', step=0.01,
+ min=self.system.field_friend.z_axis.max_position, max=self.system.field_friend.z_axis.min_position*-1)
+ .props('dense outlined suffix=°').classes('w-24').bind_value(
+ self.system.weeding, 'weed_screw_depth').tooltip(
+ 'Set the drill depth for the weeding automation')
+ ui.number('Crop safety distance', value=0.01, step=0.01, min=0.0, max=0.05, format='%.2f').props(
+ 'dense outlined suffix=m').classes('w-24').bind_value(
+ self.system.weeding, 'crop_safety_distance').tooltip(
+ 'Set the crop safety distance for the weeding automation')
+
+ ui.separator()
+ ui.markdown('Workflow settings').style('color: #6E93D6')
+ with ui.row():
+ if self.system.field_friend.tool == 'tornado':
+ ui.checkbox('Drill 2x with open torando', value=False, on_change=system.weeding.invalidate).bind_value(
+ self.system.weeding, 'drill_with_open_tornado').tooltip(
+ 'Set the weeding automation to drill a second time with open tornado')
+ ui.checkbox('Drill between crops', value=False).bind_value(
+ self.system.weeding, 'drill_between_crops').tooltip(
+ 'Set the weeding automation to drill between crops')
+ elif self.system.field_friend.tool == 'dual_mechanism':
+
+ ui.checkbox('Drilling', value=False).bind_value(
+ self.system.weeding, 'with_drilling').tooltip(
+ 'Set the weeding automation to with drill')
+ ui.checkbox('Chopping', value=False).bind_value(
+ self.system.weeding, 'with_chopping').tooltip(
+ 'Set the weeding automation to with chop')
+
+ ui.checkbox('Chop if no crops', value=False).bind_value(
+ self.system.weeding, 'chop_if_no_crops').tooltip(
+ 'Set the weeding automation to chop also if no crops seen')
+
+ ui.checkbox('Only monitoring').bind_value(
+ self.system.weeding, 'only_monitoring').tooltip(
+ 'Set the weeding automation to only monitor the field')
+ ui.separator()
+ ui.markdown('**Driver settings**').style('color: #6E93D6')
+ with ui.row():
+ ui.number('linear_speed_on_row', value=0.5, step=0.1, min=0.1, format='%.1f')
+ .props('dense outlined suffix=m/s').classes('w-24')
+ .bind_value(self.system.weeding, 'linear_speed_on_row')
+ .tooltip('Set the linear speed on row for the weeding automation')
+ ui.number('linear_speed_between_rows', value=0.5, step=0.1, min=0.1, format='%.1f')
+ .props('dense outlined suffix=m/s').classes('w-24')
+ .bind_value(self.system.weeding, 'linear_speed_between_rows')
+ .tooltip('Set the linear speed between rows for the weeding automation')
+ ui.number('angular_speed_on_row', value=0.5, step=0.1, min=0.1, format='%.1f')
+ .props('dense outlined suffix=°/s').classes('w-24')
+ .bind_value(self.system.weeding, 'angular_speed_on_row')
+ .tooltip('Set the angular speed on row for the weeding automation')
+ ui.number('angular_speed_between_rows', value=0.5, step=0.1, min=0.1, format='%.1f')
+ .props('dense outlined suffix=°/s').classes('w-24')
+ .bind_value(self.system.weeding, 'angular_speed_between_rows')
+ .tooltip('Set the angular speed between rows for the weeding automation')
+
+ with ui.column().bind_visibility_from(self.automations_toggle, 'value', value='monitoring'):
+ with ui.column():
+ self.with_field_planning_monitor=ui.checkbox('Use field planning', value=True)
+ .bind_value(self.system.monitoring, 'use_field_planning').tooltip('Set the monitoring automation to use the field planning with GNSS')
+ self.with_field_planning_monitor=ui.checkbox('Use field planning', value=True)
+ .bind_value(self.system.monitoring, 'use_field_planning').tooltip('Set the monitoring automation to use the field planning with GNSS')
+
+ with ui.row().bind_visibility_from(self.with_field_planning_monitor, 'value', value=True):
+ self.show_start_row()
+ self.show_end_row()
+ ui.number('Min. turning radius', format='%.2f',
+ value=0.5, step=0.05, min=0.05, max=2.0)
+ .props('dense outlined suffix=m').classes('w-30')
+ .bind_value(self.system.monitoring, 'minimum_turning_radius')
+ .tooltip('Set the turning radius for the monitoring automation')
+ ui.number('Min. turning radius', format='%.2f',
+ value=0.5, step=0.05, min=0.05, max=2.0)
+ .props('dense outlined suffix=m').classes('w-30')
+ .bind_value(self.system.monitoring, 'minimum_turning_radius')
+ .tooltip('Set the turning radius for the monitoring automation')
+
+ with ui.column().bind_visibility_from(self.automations_toggle, 'value', value='collecting (demo)'):
+ with ui.row():
+ ui.number(
+ 'Drill angle', format='%.0f', value=100, step=1, min=1, max=180)
+ .props('dense outlined suffix=°').classes('w-24')
+ .bind_value(self.system.coin_collecting, 'angle')
+ .tooltip('Set the drill depth for the weeding automation')
+ ui.checkbox('with drilling', value=True)
+ .bind_value(self.system.coin_collecting, 'with_drilling')
+ 'Drill angle', format='%.0f', value=100, step=1, min=1, max=180) \
+ .props('dense outlined suffix=°').classes('w-24') \
+ .bind_value(self.system.coin_collecting, 'angle') \
+ .tooltip('Set the drill depth for the weeding automation')
+ ui.checkbox('with drilling', value=True) \
+ .bind_value(self.system.coin_collecting, 'with_drilling')
ui.space()
with ui.row().style("margin: 1rem; width: calc(100% - 2rem);"):
with ui.column():
- ui.button('emergency stop', on_click=lambda: system.field_friend.estop.set_soft_estop(True)).props('color=red').classes(
- 'py-3 px-6 text-lg').bind_visibility_from(system.field_friend.estop, 'is_soft_estop_active', value=False)
- ui.button('emergency reset', on_click=lambda: system.field_friend.estop.set_soft_estop(False)).props(
- 'color=red-700 outline').classes('py-3 px-6 text-lg').bind_visibility_from(system.field_friend.estop,
- 'is_soft_estop_active', value=True)
+ ui.button('emergency stop', on_click=lambda: system.field_friend.estop.set_soft_estop(True)).props('color=red') \
+ .classes('py-3 px-6 text-lg').bind_visibility_from(system.field_friend.estop, 'is_soft_estop_active', value=False)
+ ui.button('emergency reset', on_click=lambda: system.field_friend.estop.set_soft_estop(False)) \
+ .props('color=red-700 outline').classes('py-3 px-6 text-lg') \
+ .bind_visibility_from(system.field_friend.estop, 'is_soft_estop_active', value=True)
+ ui.button('emergency stop', on_click=lambda: system.field_friend.estop.set_soft_estop(True)).props('color=red') \
+ .classes('py-3 px-6 text-lg').bind_visibility_from(system.field_friend.estop, 'is_soft_estop_active', value=False)
+ ui.button('emergency reset', on_click=lambda: system.field_friend.estop.set_soft_estop(False)) \
+ .props('color=red-700 outline').classes('py-3 px-6 text-lg') \
+ .bind_visibility_from(system.field_friend.estop, 'is_soft_estop_active', value=True)
ui.space()
with ui.row():
- automation_controls(self.system, can_start=self.ensure_start)
+ automation_controls(self.system, can_start=self.can_start)
with ui.dialog() as self.dialog, ui.card():
- self.dialog_label = ui.label(f'Do you want to continue the canceled automation').classes(
- 'text-lg')
+ self.dialog_label = ui.label('Do you want to continue the canceled automation').classes('text-lg')
+ self.dialog_label = ui.label('Do you want to continue the canceled automation').classes('text-lg')
with ui.row():
ui.button('Yes', on_click=lambda: self.dialog.submit('Yes'))
ui.button('No', on_click=lambda: self.dialog.submit('No'))
@@ -141,25 +344,34 @@ def show_field_selection() -> None:
@ui.refreshable
def show_start_row(self) -> None:
if self.field_provider.active_field is not None:
- ui.select({row.id: row.name for row in self.field_provider.active_field.rows}, label='Start row').bind_value(self.system.weeding, 'start_row_id').classes(
- 'w-24').tooltip('Select the row to start on')
+ ui.select({row.id: row.name for row in self.field_provider.active_field.rows}, label='Start row') \
+ .bind_value(self.system.weeding, 'start_row_id').classes('w-24').tooltip('Select the row to start on')
+ ui.select({row.id: row.name for row in self.field_provider.active_field.rows}, label='Start row') \
+ .bind_value(self.system.weeding, 'start_row_id').classes('w-24').tooltip('Select the row to start on')
else:
- ui.select([None], label='Start row').bind_value(self.system.weeding, 'start_row').classes(
- 'w-24').tooltip('Select the row to start on')
+ ui.select([None], label='Start row')\
+ .bind_value(self.system.weeding, 'start_row').classes('w-24').tooltip('Select the row to start on')
+ ui.select([None], label='Start row')\
+ .bind_value(self.system.weeding, 'start_row').classes('w-24').tooltip('Select the row to start on')
@ui.refreshable
def show_end_row(self) -> None:
if self.field_provider.active_field is not None:
- ui.select({row.id: row.name for row in self.field_provider.active_field.rows}, label='End row').bind_value(self.system.weeding, 'end_row_id').classes(
- 'w-24').tooltip('Select the row to end on')
+ ui.select({row.id: row.name for row in self.field_provider.active_field.rows}, label='End row') \
+ .bind_value(self.system.weeding, 'end_row_id').classes('w-24').tooltip('Select the row to end on')
+ ui.select({row.id: row.name for row in self.field_provider.active_field.rows}, label='End row') \
+ .bind_value(self.system.weeding, 'end_row_id').classes('w-24').tooltip('Select the row to end on')
else:
- ui.select([None], label='End row').bind_value(self.system.weeding, 'end_row').classes(
- 'w-24').tooltip('Select the row to end on')
+ ui.select([None], label='End row') \
+ .bind_value(self.system.weeding, 'end_row').classes('w-24').tooltip('Select the row to end on')
+ ui.select([None], label='End row') \
+ .bind_value(self.system.weeding, 'end_row').classes('w-24').tooltip('Select the row to end on')
def set_field(self) -> None:
for field in self.system.field_provider.fields:
if field.id == self.field_selection.value:
self.field_provider.select_field(field)
+ app.storage.user['field'] = field.id
if len(field.outline_wgs84) > 0:
self.system.gnss.set_reference(field.outline_wgs84[0][0], field.outline_wgs84[0][1])
# TODO das hier noch auf das active field umbauen, damit auch diese werte im weeding auf das active field registriert sind
@@ -168,16 +380,16 @@ def set_field(self) -> None:
self.show_start_row.refresh()
self.show_end_row.refresh()
- async def ensure_start(self) -> bool:
- self.log.info('Ensuring start of automation')
+ async def can_start(self) -> bool:
+ self.log.info('Checking if automation can be started')
if self.automations_toggle.value == 'mowing':
- return await self.ensure_mowing_start()
+ return await self.can_mowing_start()
elif self.automations_toggle.value == 'weeding':
- return await self.ensure_weeding_start()
+ return await self.can_weeding_start()
return True
- async def ensure_mowing_start(self) -> bool:
- self.log.info('Ensuring start of mowing automation')
+ async def can_mowing_start(self) -> bool:
+ self.log.info('Checking mowing automation')
if self.system.mowing.current_path_segment is None:
self.system.mowing.continue_mowing = False
return True
@@ -191,8 +403,8 @@ async def ensure_mowing_start(self) -> bool:
return False
return True
- async def ensure_weeding_start(self) -> bool:
- self.log.info('Ensuring start of weeding automation')
+ async def can_weeding_start(self) -> bool:
+ self.log.info('Checking weeding automation')
if not self.system.weeding.current_row or not self.system.weeding.current_segment:
self.system.weeding.continue_canceled_weeding = False
return True
@@ -205,3 +417,7 @@ async def ensure_weeding_start(self) -> bool:
elif result == 'Cancel':
return False
return True
+
+ def handle_automation_changed(self, e: events.ValueChangeEventArguments) -> None:
+ app.storage.user.update({'automation': e.value})
+ self.system.automator.default_automation = self.system.automations[e.value]
diff --git a/field_friend/interface/components/plant_object.py b/field_friend/interface/components/plant_object.py
index 4f2c02bd..2dc78bc2 100644
--- a/field_friend/interface/components/plant_object.py
+++ b/field_friend/interface/components/plant_object.py
@@ -1,8 +1,10 @@
import logging
+
from nicegui.elements.scene_objects import Group, Sphere
from ...automations import PlantProvider
+
class plant_objects(Group):
def __init__(self, plant_provider: PlantProvider, weed_category_names: list[str]) -> None:
@@ -23,6 +25,11 @@ def update(self) -> None:
obj.delete()
for id, plant in in_world.items():
if id not in rendered:
- Sphere(0.02).with_name(f'plant_{plant.type}:{id}') \
- .material('#ef1208' if plant.type in self.weed_category_names else '#11ede3') \
- .move(plant.position.x, plant.position.y, 0.02)
+ if plant.type in self.weed_category_names:
+ Sphere(0.02).with_name(f'plant_{plant.type}:{id}') \
+ .material('#ef1208') \
+ .move(plant.position.x, plant.position.y, 0.02)
+ else:
+ Sphere(0.035).with_name(f'plant_{plant.type}:{id}') \
+ .material('#11ede3') \
+ .move(plant.position.x, plant.position.y, 0.035)
diff --git a/field_friend/interface/components/robot_scene.py b/field_friend/interface/components/robot_scene.py
index 5fec61b2..e01c893b 100644
--- a/field_friend/interface/components/robot_scene.py
+++ b/field_friend/interface/components/robot_scene.py
@@ -1,4 +1,6 @@
import logging
+from typing import TYPE_CHECKING
+
import rosys
from nicegui import events, ui
@@ -7,7 +9,6 @@
from .plant_object import plant_objects
from .visualizer_object import visualizer_object
-from typing import TYPE_CHECKING
if TYPE_CHECKING:
from field_friend.system import System
@@ -24,18 +25,19 @@ def __init__(self, system: 'System'):
with self.scene_card.tight().classes('w-full place-items-center').style('max-width: 100%; overflow: hidden;'):
def toggle_lock():
self.locked_view = not self.locked_view
- self.lock_view_button.props(
- f'flat color={"primary" if self.locked_view else "grey"}')
- self.lock_view_button = ui.button(icon='sym_o_visibility_lock', on_click=toggle_lock).props('flat color=primary').style(
- 'position: absolute; left: 1px; top: 1px; z-index: 500;').tooltip('Lock view to robot')
+ self.lock_view_button.props(f'flat color={"primary" if self.locked_view else "grey"}')
+ self.lock_view_button = ui.button(icon='sym_o_visibility_lock', on_click=toggle_lock).props('flat color=primary') \
+ .style('position: absolute; left: 1px; top: 1px; z-index: 500;').tooltip('Lock view to robot')
with ui.scene(200, 200, on_click=self.handle_click, grid=False).classes('w-full') as self.scene:
field_friend_object(self.system.odometer, self.system.usb_camera_provider, self.system.field_friend)
rosys.driving.driver_object(self.system.driver)
- plant_objects(self.system.plant_provider, self.system.big_weed_category_names +
- self.system.small_weed_category_names)
- visualizer_object(self.system.automator, self.system.path_provider,
- self.system.mowing, self.system.weeding)
+ plant_objects(self.system.plant_provider,
+ self.system.big_weed_category_names + self.system.small_weed_category_names)
+ visualizer_object(self.system.automator,
+ self.system.path_provider,
+ self.system.mowing,
+ self.system.weeding)
field_object(self.system.field_provider)
self.scene.move_camera(-0.5, -1, 2)
diff --git a/field_friend/interface/components/status_dev.py b/field_friend/interface/components/status_dev.py
index 58c462df..0e7e068a 100644
--- a/field_friend/interface/components/status_dev.py
+++ b/field_friend/interface/components/status_dev.py
@@ -115,6 +115,9 @@ def status_dev_page(robot: FieldFriend, system: 'System'):
with ui.row().classes('place-items-center'):
ui.markdown('**Current Field:**').style('color: #EDF4FB')
current_field_label = ui.label()
+ with ui.row().classes('place-items-center'):
+ ui.markdown('**Worked Area:**').style('color: #EDF4FB')
+ worked_area_label = ui.label()
with ui.row().classes('place-items-center'):
ui.markdown('**Current Row:**').style('color: #EDF4FB')
current_row_label = ui.label()
@@ -133,9 +136,16 @@ def status_dev_page(robot: FieldFriend, system: 'System'):
with ui.row().classes('place-items-center'):
ui.markdown('**Weeds Detected:**').style('color: #EDF4FB')
kpi_weeds_detected_label = ui.label()
+ with ui.row().classes('place-items-center'):
+ ui.markdown('**Weeds Removed:**').style('color: #EDF4FB')
+ kpi_weeds_removed_label = ui.label()
with ui.row().classes('place-items-center'):
ui.markdown('**Punches:**').style('color: #EDF4FB')
kpi_punches_label = ui.label()
+ if robot.tool == 'dual_mechanism':
+ with ui.row().classes('place-items-center'):
+ ui.markdown('**Chops:**').style('color: #EDF4FB')
+ kpi_chops_label = ui.label()
with ui.card().style('background-color: #3E63A6; color: white;'):
ui.markdown('**Positioning**').style('color: #6E93D6').classes('w-full text-center')
@@ -205,8 +215,8 @@ def update_status() -> None:
'end_bottom' if robot.z_axis.end_bottom else '',
'ref_motor' if robot.z_axis.ref_motor else '',
'ref_gear' if robot.z_axis.ref_gear else '',
- 'ref_t' if robot.z_axis.ref_t else '',
- 'ref_b' if robot.z_axis.ref_b else '',
+ 'ref_knife_stop' if robot.z_axis.ref_knife_stop else '',
+ 'ref_knife_ground' if robot.z_axis.ref_knife_ground else '',
f'{robot.z_axis.position_z:.2f}m' if robot.z_axis.z_is_referenced else '',
f'{robot.z_axis.position_turn:.2f}°' if robot.z_axis.turn_is_referenced else '',
]
@@ -255,19 +265,26 @@ def get_jetson_cpu_temperature():
if system.automator.is_running:
if system.field_provider.active_field is not None:
current_field_label.text = system.field_provider.active_field.name
- kpi_fieldtime_label.text = system.kpi_provider.current_weeding_kpis.time
- kpi_distance_label.text = system.kpi_provider.current_weeding_kpis.distance
+ kpi_fieldtime_label.text = f'{system.kpi_provider.current_weeding_kpis.time:.2f}s'
+ kpi_distance_label.text = f'{system.kpi_provider.current_weeding_kpis.distance:.0f}m'
current_automation = next(key for key, value in system.automations.items()
if value == system.automator.default_automation)
if current_automation == 'weeding' or current_automation == 'monitoring':
- if system.field_provider.active_object is not None and system.field_provider.active_object['object'] is not None:
- current_row_label.text = system.field_provider.active_object['object'].name
+ if current_automation == 'weeding':
+ current_row_label.text = system.weeding.current_row.name if system.weeding.current_row is not None else 'No row'
+ worked_area_label.text = f'{system.weeding.field.worked_area(system.kpi_provider.current_weeding_kpis.rows_weeded):.2f}m²/{system.weeding.field.area():.2f}m²' if system.weeding.field is not None else 'No field'
+ elif current_automation == 'monitoring':
+ current_row_label.text = system.monitoring.current_row.name if system.monitoring.current_row is not None else 'No row'
+ worked_area_label.text = f'{system.monitoring.field.worked_area(system.kpi_provider.current_weeding_kpis.rows_weeded):.2f}m²/{system.monitoring.field.area():.2f}m²' if system.monitoring.field is not None else 'No field'
kpi_weeds_detected_label.text = system.kpi_provider.current_weeding_kpis.weeds_detected
kpi_crops_detected_label.text = system.kpi_provider.current_weeding_kpis.crops_detected
+ kpi_weeds_removed_label.text = system.kpi_provider.current_weeding_kpis.weeds_removed
kpi_rows_weeded_label.text = system.kpi_provider.current_weeding_kpis.rows_weeded
if current_automation == 'weeding':
kpi_punches_label.text = system.kpi_provider.current_weeding_kpis.punches
+ if robot.tool == 'dual_mechanism':
+ kpi_chops_label.text = system.kpi_provider.current_weeding_kpis.chops
gnss_device_label.text = 'No connection' if system.gnss.device is None else 'Connected'
reference_position_label.text = 'No reference' if system.gnss.reference_lat is None else 'Set'
diff --git a/field_friend/interface/components/status_drawer.py b/field_friend/interface/components/status_drawer.py
index b9c2e781..a7868cfe 100644
--- a/field_friend/interface/components/status_drawer.py
+++ b/field_friend/interface/components/status_drawer.py
@@ -179,12 +179,12 @@ def update_status() -> None:
'' if robot.z_axis.is_referenced else 'not referenced',
'' if robot.z_axis.z_is_referenced else 'z not referenced',
'' if robot.z_axis.turn_is_referenced else 'turn not referenced',
- 'end_top' if robot.z_axis.end_top else '',
- 'end_bottom' if robot.z_axis.end_bottom else '',
+ 'top' if robot.z_axis.end_top else '',
+ 'bottom' if robot.z_axis.end_bottom else '',
'ref_motor' if robot.z_axis.ref_motor else '',
'ref_gear' if robot.z_axis.ref_gear else '',
- 'ref_t' if robot.z_axis.ref_t else '',
- 'ref_b' if robot.z_axis.ref_b else '',
+ 'knife_stop' if robot.z_axis.ref_knife_stop else '',
+ 'knife_ground' if robot.z_axis.ref_knife_ground else '',
f'{robot.z_axis.position_z:.2f}m' if robot.z_axis.z_is_referenced else '',
f'{robot.z_axis.position_turn:.2f}°' if robot.z_axis.turn_is_referenced else '',
]
diff --git a/field_friend/interface/pages/field_planner_page.py b/field_friend/interface/pages/field_planner_page.py
index 4501f99c..feec9881 100644
--- a/field_friend/interface/pages/field_planner_page.py
+++ b/field_friend/interface/pages/field_planner_page.py
@@ -24,5 +24,7 @@ def content(self) -> None:
with ui.row().classes('items-stretch justify-items-stretch').style('flex-wrap:nowrap; height:40%; max-height:40%;'):
leaflet_map_field = leaflet_map(self.system, True)
leaflet_map_field.m.style('height: 100%; max-height:100%;')
+ with ui.column():
+ leaflet_map_field.buttons()
with ui.row().classes('items-stretch justify-items-stretch').style('flex-wrap:nowrap; height: 60%; max-height:60%;'):
field_planner(self.system.field_provider, self.system.odometer, self.system.gnss, leaflet_map_field)
diff --git a/field_friend/interface/pages/main_page.py b/field_friend/interface/pages/main_page.py
index 5148fae5..058d6779 100644
--- a/field_friend/interface/pages/main_page.py
+++ b/field_friend/interface/pages/main_page.py
@@ -26,8 +26,9 @@ def content(self, devmode) -> None:
with splitter.before:
with ui.column().classes('h-full p-2').style('width: 100%;'):
leaflet_map_landing = leaflet_map(self.system, False)
- leaflet_map_landing.m.classes(
- 'h-full w-full')
+ leaflet_map_landing.m.classes('h-full w-full')
+ with ui.row():
+ leaflet_map_landing.buttons()
with splitter.after:
with ui.row().classes('h-full ml-2 m-2').style('width: calc(100% - 1rem)'):
with ui.column().style('width: 55%; height: 100%; flex-wrap: nowrap;'):
@@ -36,7 +37,11 @@ def content(self, devmode) -> None:
with ui.card().classes('w-full h-full p-0').style('margin-bottom: 10px;'):
with ui.scroll_area().classes('w-full h-full'):
with ui.card().classes('w-full'):
- camera_card(self.system.usb_camera_provider, self.system.automator, self.system.detector, self.system.plant_locator, self.system.field_friend,
+ camera_card(self.system.usb_camera_provider,
+ self.system.automator,
+ self.system.detector,
+ self.system.plant_locator,
+ self.system.field_friend,
self.system.puncher)
with ui.card().classes('w-full'):
robot_scene(self.system)
diff --git a/field_friend/interface/pages/monitor_page.py b/field_friend/interface/pages/monitor_page.py
index d4eb6b19..388e73ec 100644
--- a/field_friend/interface/pages/monitor_page.py
+++ b/field_friend/interface/pages/monitor_page.py
@@ -20,9 +20,13 @@ def page() -> None:
self.content()
def content(self) -> None:
- # ui.query('body').classes('bg-black text-white')
- # ui.query('.nicegui-content').classes('p-0 h-screen')
ui.colors(primary='#6E93D6', secondary='#53B689', accent='#111B1E', positive='#53B689')
- if self.system.is_real:
- monitoring(self.system.usb_camera_provider, self.system.mjpeg_camera_provider,
- self.system.detector, self.system.monitoring_detector, self.system.plant_locator, self.system.automator, self.system.field_friend, self.system)
+ monitoring(self.system.usb_camera_provider,
+ self.system.mjpeg_camera_provider,
+ self.system.detector,
+ self.system.monitoring_detector,
+ self.system.plant_locator,
+ self.system.automator,
+ self.system.field_friend,
+ self.system,
+ )
diff --git a/field_friend/kpi_generator.py b/field_friend/kpi_generator.py
index e9f74589..31c15dad 100644
--- a/field_friend/kpi_generator.py
+++ b/field_friend/kpi_generator.py
@@ -1,8 +1,10 @@
from datetime import datetime, timedelta
from random import randint
-from .automations import KpiProvider
+
from rosys.analysis import Day, Month, date_to_str
+from .automations import KpiProvider
+
def generate_kpis(kpi_provider: KpiProvider) -> None:
kpi_provider.days = [
@@ -23,7 +25,9 @@ def generate_kpis(kpi_provider: KpiProvider) -> None:
'mowing_completed': randint(0, 10),
'crops_detected': randint(0, 100),
'weeds_detected': randint(0, 500),
+ 'weeds_removed': randint(0, 500),
'punches': randint(0, 200),
+ 'chops': randint(0, 200),
},
)
for i in range(7 * 12)
@@ -49,7 +53,9 @@ def generate_kpis(kpi_provider: KpiProvider) -> None:
'mowing_completed': randint(0, 10) * 7 * 4,
'crops_detected': randint(0, 100) * 7 * 4,
'weeds_detected': randint(0, 500) * 7 * 4,
+ 'weeds_removed': randint(0, 500) * 7 * 4,
'punches': randint(0, 200) * 7 * 4,
+ 'chops': randint(0, 200) * 7 * 4,
},
)
for i in range(4, 10)
diff --git a/field_friend/navigation/gnss.py b/field_friend/navigation/gnss.py
index 457595ee..a78542ac 100644
--- a/field_friend/navigation/gnss.py
+++ b/field_friend/navigation/gnss.py
@@ -5,7 +5,7 @@
from abc import ABC, abstractmethod
from copy import deepcopy
from dataclasses import dataclass
-from typing import Any, Optional, Protocol
+from typing import Optional, Protocol
import numpy as np
import pynmea2
@@ -14,7 +14,7 @@
from geographiclib.geodesic import Geodesic
from serial.tools import list_ports
-from field_friend.navigation.point_transformation import cartesian_to_wgs84, wgs84_to_cartesian
+from field_friend.navigation.point_transformation import cartesian_to_wgs84, get_new_position, wgs84_to_cartesian
@dataclass
@@ -30,7 +30,7 @@ class GNSSRecord:
speed_kmh: float = 0.0
-class Gnss(rosys.persistence.PersistentModule, ABC):
+class Gnss(ABC):
def __init__(self) -> None:
super().__init__()
@@ -58,26 +58,6 @@ def __init__(self) -> None:
rosys.on_repeat(self.update, 0.01)
rosys.on_repeat(self.try_connection, 3.0)
- def backup(self) -> dict:
- return {
- 'record': rosys.persistence.to_dict(self.record)
- }
-
- def restore(self, data: dict[str, Any]) -> None:
- record = data.get('record')
- if not isinstance(record, dict):
- self.log.error('No record data found')
- return
- self.record.timestamp = record["timestamp"]
- self.record.latitude = record["latitude"]
- self.record.longitude = record["longitude"]
- self.record.mode = record["mode"]
- self.record.gps_qual = record["gps_qual"]
- self.record.altitude = record["altitude"]
- self.record.separation = record["separation"]
- self.record.heading = record["heading"]
- self.record.speed_kmh = record["speed_kmh"]
-
@abstractmethod
async def update(self) -> None:
pass
@@ -93,7 +73,6 @@ def set_reference(self, lat: float, lon: float) -> None:
def clear_reference(self) -> None:
self.reference_lat = None
self.reference_lon = None
- self.request_backup()
def get_reference(self) -> tuple[Optional[float], Optional[float]]:
return self.reference_lat, self.reference_lon
@@ -109,9 +88,10 @@ class GnssHardware(Gnss):
PORT = '/dev/cu.usbmodem36307295'
TYPES_NEEDED = {'GGA', 'GNS', 'HDT'}
- def __init__(self, odometer: rosys.driving.Odometer) -> None:
+ def __init__(self, odometer: rosys.driving.Odometer, antenna_offset: float) -> None:
super().__init__()
self.odometer = odometer
+ self.antenna_offset = antenna_offset
def __del__(self) -> None:
if self.ser is not None:
@@ -171,7 +151,7 @@ async def update(self) -> None:
self.log.debug('No data received')
return
try:
- msg = await rosys.run.cpu_bound(pynmea2.parse, line)
+ msg = pynmea2.parse(line)
if not hasattr(msg, 'sentence_type'):
self.log.debug(f'No sentence type: {msg}')
return
@@ -196,7 +176,7 @@ async def update(self) -> None:
# print(f'The GNSS message: {msg.mode_indicator}')
has_location = True
if msg.sentence_type == 'HDT' and getattr(msg, 'heading', None):
- record.heading = msg.heading
+ record.heading = float(msg.heading)
has_heading = True
except pynmea2.ParseError as e:
self.log.info(f'Parse error: {e}')
@@ -206,10 +186,10 @@ async def update(self) -> None:
self.device = None
return
if self.record.gps_qual > 0 and record.gps_qual == 0:
- self.log.info('GNSS lost')
+ self.log.warning('GNSS lost')
self.GNSS_CONNECTION_LOST.emit()
if self.record.gps_qual == 4 and record.gps_qual != 4:
- self.log.info('GNSS RTK fix lost')
+ self.log.warning('GNSS RTK fix lost')
self.RTK_FIX_LOST.emit()
self.record = deepcopy(record)
if has_location:
@@ -218,13 +198,18 @@ async def update(self) -> None:
self.log.info(f'GNSS reference set to {record.latitude}, {record.longitude}')
self.set_reference(record.latitude, record.longitude)
else:
- cartesian_coordinates = wgs84_to_cartesian([self.reference_lat, self.reference_lon], [
- record.latitude, record.longitude])
if has_heading:
- yaw = np.deg2rad(float(-record.heading))
+ yaw = np.deg2rad(-record.heading)
else:
yaw = self.odometer.get_pose(time=record.timestamp).yaw
# TODO: Better INS implementation if no heading provided by GNSS
+ # correct the gnss coordinat by antenna offset
+ corrected_coordinates = get_new_position([
+ record.latitude, record.longitude], self.antenna_offset, yaw+np.pi/2)
+ self.record.latitude = deepcopy(corrected_coordinates[0])
+ self.record.longitude = deepcopy(corrected_coordinates[1])
+ cartesian_coordinates = wgs84_to_cartesian([self.reference_lat, self.reference_lon], [
+ self.record.latitude, self.record.longitude])
pose = rosys.geometry.Pose(
x=cartesian_coordinates[0],
y=cartesian_coordinates[1],
@@ -244,7 +229,6 @@ async def update(self) -> None:
def set_reference(self, lat: float, lon: float) -> None:
self.reference_lat = lat
self.reference_lon = lon
- self.request_backup()
class PoseProvider(Protocol):
@@ -268,7 +252,11 @@ async def update(self) -> None:
if self.reference_lon is None:
self.reference_lon = 7.434212
pose = deepcopy(self.pose_provider.pose)
- pose.time = rosys.time()
+ try:
+ pose.time = rosys.time()
+ except Exception:
+ self.log.error('Pose provider has no time attribute')
+ return
current_position = cartesian_to_wgs84([self.reference_lat, self.reference_lon], [pose.x, pose.y])
self.record.timestamp = pose.time
@@ -288,4 +276,3 @@ def set_reference(self, lat: float, lon: float) -> None:
self.record.latitude = lat
self.record.longitude = lon
self.ROBOT_POSITION_LOCATED.emit()
- self.request_backup()
diff --git a/field_friend/navigation/point_transformation.py b/field_friend/navigation/point_transformation.py
index d29c73af..21a6fbd0 100644
--- a/field_friend/navigation/point_transformation.py
+++ b/field_friend/navigation/point_transformation.py
@@ -1,6 +1,7 @@
+from typing import List
+
import numpy as np
from geographiclib.geodesic import Geodesic
-from typing import List
def wgs84_to_cartesian(reference, point) -> List[float]:
@@ -18,3 +19,21 @@ def cartesian_to_wgs84(reference, point) -> List[float]:
r = Geodesic.WGS84.Direct(r['lat2'], r['lon2'], 0.0, point[1])
wgs84_coords = [r['lat2'], r['lon2']]
return wgs84_coords
+
+
+def get_new_position(reference, distance, yaw):
+ """
+ Calculate a new position given a reference point, distance, and yaw (direction in radians).
+
+ Parameters:
+ - reference: Tuple containing the latitude and longitude of the reference point (lat, lon).
+ - distance: Distance to move from the reference point in meters.
+ - yaw: Direction of movement in radians from the north.
+
+ Returns:
+ - Tuple containing the latitude and longitude of the new position (lat, lon).
+ """
+ azimuth_deg = np.degrees(-yaw)
+ result = Geodesic.WGS84.Direct(reference[0], reference[1], azimuth_deg, distance)
+ new_position = [result['lat2'], result['lon2']]
+ return new_position
diff --git a/field_friend/system.py b/field_friend/system.py
index d3551091..87a02401 100644
--- a/field_friend/system.py
+++ b/field_friend/system.py
@@ -32,7 +32,7 @@ def __init__(self) -> None:
self.monitoring_detector = rosys.vision.DetectorHardware(port=8005)
self.camera_configurator = CameraConfigurator(self.usb_camera_provider)
else:
- version = 'rb33' # insert here your field friend version to be simulated
+ version = 'rb27' # insert here your field friend version to be simulated
self.field_friend = FieldFriendSimulation(robot_id=version)
self.usb_camera_provider = SimulatedCamProvider()
self.usb_camera_provider.remove_all_cameras()
@@ -43,23 +43,22 @@ def __init__(self) -> None:
yaw=np.deg2rad(90)))
self.detector = rosys.vision.DetectorSimulation(self.usb_camera_provider)
self.camera_configurator = CameraConfigurator(self.usb_camera_provider, robot_id=version)
- # self.circle_sight = None
self.plant_provider = PlantProvider()
self.steerer = rosys.driving.Steerer(self.field_friend.wheels, speed_scaling=0.25)
self.odometer = rosys.driving.Odometer(self.field_friend.wheels)
self.gnss: GnssHardware | GnssSimulation
if self.is_real:
- self.gnss = GnssHardware(self.odometer)
+ self.gnss = GnssHardware(self.odometer, self.field_friend.ANTENNA_OFFSET)
else:
self.gnss = GnssSimulation(self.field_friend.wheels)
self.gnss.ROBOT_POSE_LOCATED.register(self.odometer.handle_detection)
self.driver = rosys.driving.Driver(self.field_friend.wheels, self.odometer)
- self.driver.parameters.linear_speed_limit = 0.1
- self.driver.parameters.angular_speed_limit = 0.5
+ self.driver.parameters.linear_speed_limit = 0.3
+ self.driver.parameters.angular_speed_limit = 0.8
self.driver.parameters.can_drive_backwards = True
- self.driver.parameters.minimum_turning_radius = 0.02
- self.driver.parameters.hook_offset = 0.6
- self.driver.parameters.carrot_distance = 0.2
+ self.driver.parameters.minimum_turning_radius = 0.01
+ self.driver.parameters.hook_offset = 0.45
+ self.driver.parameters.carrot_distance = 0.15
self.driver.parameters.carrot_offset = self.driver.parameters.hook_offset + self.driver.parameters.carrot_distance
self.kpi_provider = KpiProvider(self.plant_provider)
@@ -76,10 +75,15 @@ def watch_robot() -> None:
self.big_weed_category_names = ['thistle', 'big_weed', 'orache']
self.small_weed_category_names = ['weed', 'coin']
self.crop_category_names = ['sugar_beet', 'crop', 'coin_with_hole']
- self.plant_locator = PlantLocator(self.usb_camera_provider, self.detector,
- self.plant_provider, self.odometer)
+ self.plant_locator = PlantLocator(self.usb_camera_provider,
+ self.detector,
+ self.plant_provider,
+ self.odometer,
+ )
self.plant_locator.weed_category_names = self.big_weed_category_names + self.small_weed_category_names
self.plant_locator.crop_category_names = self.crop_category_names
+ self.plant_locator.minimum_weed_confidence = 0.8
+ self.plant_locator.minimum_crop_confidence = 0.40
rosys.on_repeat(watch_robot, 1.0)
diff --git a/field_friend/vision/camera_configurator.py b/field_friend/vision/camera_configurator.py
index 91a36b0f..ac2cafe0 100644
--- a/field_friend/vision/camera_configurator.py
+++ b/field_friend/vision/camera_configurator.py
@@ -12,7 +12,8 @@
class CameraConfigurator:
def __init__(self,
camera_provider: rosys.vision.CameraProvider,
- robot_id: str | None = None):
+ robot_id: str | None = None,
+ ):
self.log = logging.getLogger('field_friend.camera_configurator')
self.camera_provider = camera_provider
if not robot_id:
@@ -23,7 +24,7 @@ def __init__(self,
async def update_camera_config(self):
await rosys.sleep(15)
- self.log.info(f'updating camera config')
+ self.log.info('updating camera config')
camera = None
start_time = rosys.time()
while not camera:
diff --git a/main.py b/main.py
index aab6933c..96a4ec9a 100755
--- a/main.py
+++ b/main.py
@@ -1,4 +1,5 @@
#!/usr/bin/env python3
+import rosys
from nicegui import app, ui
from rosys.analysis import logging_page
@@ -15,37 +16,31 @@ def startup() -> None:
system = System()
def page_wrapper() -> None:
- drawer = status_drawer(system, system.field_friend, system.gnss,
- system.odometer, system.automator)
+ drawer = status_drawer(system, system.field_friend, system.gnss, system.odometer, system.automator)
header_bar(system, drawer)
system_bar()
- # /
- interface.pages.main_page(page_wrapper, system)
- # /field
- interface.pages.field_planner_page(page_wrapper, system)
- # /path
- interface.pages.path_planner_page(page_wrapper, system)
- # /dev
- interface.pages.dev_page(page_wrapper, system)
- # /test
- interface.pages.test_page(page_wrapper, system)
- # /kpis
- interface.pages.kpi_page(page_wrapper, system)
- # /monitor
- interface.pages.monitor_page(page_wrapper, system)
- # /status
-
- @app.get('/status')
+ interface.pages.main_page(page_wrapper, system) # /
+ interface.pages.field_planner_page(page_wrapper, system) # /field
+ interface.pages.path_planner_page(page_wrapper, system) # /path
+ interface.pages.dev_page(page_wrapper, system) # /dev
+ interface.pages.test_page(page_wrapper, system) # /test
+ interface.pages.kpi_page(page_wrapper, system) # /kpis
+ interface.pages.monitor_page(page_wrapper, system) # /monitor
+
+ @app.get('/status') # /status
def status():
return {'status': 'ok'}
- # /logging
- logging_page(['field_friend', 'rosys'])
+ logging_page(['field_friend', 'rosys']) # /logging
app.on_startup(startup)
-ui.run(title='Field Friend', port=80, favicon='assets/favicon.ico',
+ui.run(title='Field Friend',
+ port=80,
+ storage_secret='feldfreund',
+ favicon='assets/favicon.ico',
binding_refresh_interval=0.3,
- reconnect_timeout=10)
+ reconnect_timeout=10,
+ )