From 2a76303d82e54eee13d290ee06fe65395fe9a267 Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Wed, 14 Jul 2021 17:23:14 +0300 Subject: [PATCH 01/29] [ADD] two base structs --- pallets/ds-maps/src/lib.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pallets/ds-maps/src/lib.rs b/pallets/ds-maps/src/lib.rs index 3387b10..4cea995 100644 --- a/pallets/ds-maps/src/lib.rs +++ b/pallets/ds-maps/src/lib.rs @@ -217,6 +217,22 @@ impl< } } +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(Encode, Decode, Clone, Default)] +pub struct Waypoint { + pub location: Point3D, + pub arrival: Moment, +} + + +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(Encode, Decode, Clone, Default)] +pub struct Route { + pub id: RouteId, + pub route: Vec>, + pub owner: OwnerId, +} + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(Encode, Decode, Default, Clone, Copy, Debug)] pub struct RootBox { From 77db582f51637c492254364b9232822b0c931f9c Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Thu, 15 Jul 2021 15:46:30 +0300 Subject: [PATCH 02/29] [ADD] added base function --- pallets/ds-maps/src/lib.rs | 33 ++++++++++++++++++++++++++++----- pallets/ds-maps/src/mock.rs | 2 +- runtime/src/lib.rs | 1 + 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/pallets/ds-maps/src/lib.rs b/pallets/ds-maps/src/lib.rs index 4cea995..0d5e6d6 100644 --- a/pallets/ds-maps/src/lib.rs +++ b/pallets/ds-maps/src/lib.rs @@ -218,7 +218,7 @@ impl< } #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[derive(Encode, Decode, Clone, Default)] +#[derive(Encode, Decode, Clone, Default, Debug, PartialEq, Eq)] pub struct Waypoint { pub location: Point3D, pub arrival: Moment, @@ -226,9 +226,8 @@ pub struct Waypoint { #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[derive(Encode, Decode, Clone, Default)] -pub struct Route { - pub id: RouteId, +#[derive(Encode, Decode, Clone, Default, Debug, PartialEq, Eq)] +pub struct Route { pub route: Vec>, pub owner: OwnerId, } @@ -992,11 +991,13 @@ pub trait Trait: accounts::Trait { + Sub + Div; + type RouteId: Default + Parameter + Copy; + type RawCoord: Default + Parameter + Into + Copy; - + /// This allows us to have a top border for zones type MaxBuildingsInArea: Get; @@ -1035,6 +1036,7 @@ decl_storage! { pub type PageOf = Page<::Coord>; pub type RootBoxOf = RootBox<::Coord>; pub type ZoneOf = Zone<::Coord>; +pub type RouteOf = Route<::Coord, ::AccountId, ::Moment>; // Pallets use events to inform users when important changes are made. // https://substrate.dev/docs/en/knowledgebase/runtime/events @@ -1042,6 +1044,8 @@ decl_event!( pub enum Event where AccountId = ::AccountId, + Moment = ::Moment, + Coord = ::Coord, { // Event documentation should end with an array that provides descriptive names for event parameters. /// New root box has been created [box number, who] @@ -1054,6 +1058,8 @@ decl_event!( RootRemoved(RootId, AccountId), /// Zone was removed from storage ZoneRemoved(ZoneId, AccountId), + /// New route was submitted [start, destination, start, arrival, rootId, who] + RouteAdded(Point3D, Point3D, Moment, Moment, RootId, AccountId), } ); @@ -1401,6 +1407,23 @@ decl_module! { Self::deposit_event(RawEvent::AreaTypeChanged(area_type, area_id, root_id, who)); Ok(()) } + + /// Adds new route for UAV + #[weight = ::WeightInfo::change_area_type()] + pub fn route_add(origin, + waypoints: Vec::Moment>>, + estimate_time: ::Moment, + root_id: RootId) -> dispatch::DispatchResult { + let who = ensure_signed(origin)?; + ensure!(>::account_is(&who, REGISTRAR_ROLE.into()), Error::::NotAuthorized); + ensure!(RootBoxes::contains_key(root_id), Error::::RootDoesNotExist); + + // AreaData::mutate(root_id, area_id, |ar| { + // ar.area_type = area_type; + // }); + Self::deposit_event(RawEvent::RouteAdded(start_point, end_point, start_time, estimate_time, root_id, who)); + Ok(()) + } } } diff --git a/pallets/ds-maps/src/mock.rs b/pallets/ds-maps/src/mock.rs index 82e1ea9..138ec5a 100644 --- a/pallets/ds-maps/src/mock.rs +++ b/pallets/ds-maps/src/mock.rs @@ -107,7 +107,7 @@ impl Trait for Test { type Event = Event; type WeightInfo = (); type Coord = I10F22; - + type RouteId = u32; type RawCoord = i32; type MaxBuildingsInArea = MaxBuildingsInArea; type MaxHeight = MaxHeight; diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index ef36438..b1bbbeb 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -313,6 +313,7 @@ impl pallet_ds_maps::Trait for Runtime { type Event = Event; type WeightInfo = (); type RawCoord = i32; + type RouteId = u32; type Coord = I10F22; type MaxBuildingsInArea = MaxBuildingsInArea; type MaxHeight = MaxHeight; From 7104782c6a26390ee6ae840a1abf97794a933f9a Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Thu, 15 Jul 2021 16:27:02 +0300 Subject: [PATCH 03/29] [ADD] lot of required checks --- pallets/ds-maps/src/lib.rs | 41 ++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/pallets/ds-maps/src/lib.rs b/pallets/ds-maps/src/lib.rs index 0d5e6d6..d01876b 100644 --- a/pallets/ds-maps/src/lib.rs +++ b/pallets/ds-maps/src/lib.rs @@ -185,10 +185,16 @@ pub struct Point3D { alt: Coord, } -impl Point3D { +impl< + Coord: PartialOrd + Sub + Signed + IntDiv + > Point3D { pub fn new(lat: Coord, lon: Coord, alt: Coord) -> Self { Point3D{lat, lon, alt} } + + pub fn project(self) -> Point2D { + Point2D::new(self.lat, self.lon) + } } #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] @@ -207,12 +213,8 @@ impl< /// Gets rect 2D projection from a box pub fn projection_on_plane(self) -> Rect2D { - let south_west = - Point2D::new(self.south_west.lat, - self.south_west.lon); - let north_east = - Point2D::new(self.north_east.lat, - self.north_east.lon); + let south_west = self.south_west.project(); + let north_east = self.north_east.project(); Rect2D::new(south_west, north_east) } } @@ -1097,6 +1099,10 @@ decl_error! { ZoneDoesntFit, /// Zone you are trying to access is not in storage ZoneDoesntExist, + /// Wrong time bounds are supplied + WrongTimeSupplied, + /// Route contain 1 or more wpoints, which lie outside of root + RouteDoesNotFitToRoot, // Add additional errors below } } @@ -1412,16 +1418,31 @@ decl_module! { #[weight = ::WeightInfo::change_area_type()] pub fn route_add(origin, waypoints: Vec::Moment>>, - estimate_time: ::Moment, root_id: RootId) -> dispatch::DispatchResult { let who = ensure_signed(origin)?; + // TODO consider role for route addition ensure!(>::account_is(&who, REGISTRAR_ROLE.into()), Error::::NotAuthorized); - ensure!(RootBoxes::contains_key(root_id), Error::::RootDoesNotExist); + ensure!(RootBoxes::::contains_key(root_id), Error::::RootDoesNotExist); + ensure!(waypoints.len() <= 2, Error::::InvalidData); + let start_waypoint = &waypoints[0]; + let end_waypoint = &waypoints[waypoints.len() - 1]; + // Getting all time bounds + let start_time = start_waypoint.arrival; + let arrival_time = end_waypoint.arrival; + let current_timestamp = >::get(); + // TODO wp[n].arrival < wp[n + 1].arrival + ensure!((arrival_time > current_timestamp) && (arrival_time > start_time), Error::::WrongTimeSupplied); + let root = RootBoxes::::get(root_id); + let start_area = root.detect_intersected_area(start_waypoint.location.project()); + let end_area = root.detect_intersected_area(end_waypoint.location.project()); + // TODO each wp[n].location shall be inside one Root + ensure!((start_area != 0) && (end_area != 0), Error::::RouteDoesNotFitToRoot); + // for now we assume that there is only two waypoints // AreaData::mutate(root_id, area_id, |ar| { // ar.area_type = area_type; // }); - Self::deposit_event(RawEvent::RouteAdded(start_point, end_point, start_time, estimate_time, root_id, who)); + // Self::deposit_event(RawEvent::RouteAdded(start_point, end_point, start_time, arrival_time, root_id, who)); Ok(()) } } From cf7bb8653b50f3bb3b997f2d67afccb0e9ff34cc Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Thu, 15 Jul 2021 16:36:44 +0300 Subject: [PATCH 04/29] [FIX] added event emit --- pallets/ds-maps/src/lib.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/pallets/ds-maps/src/lib.rs b/pallets/ds-maps/src/lib.rs index d01876b..8267a10 100644 --- a/pallets/ds-maps/src/lib.rs +++ b/pallets/ds-maps/src/lib.rs @@ -1430,19 +1430,17 @@ decl_module! { let start_time = start_waypoint.arrival; let arrival_time = end_waypoint.arrival; let current_timestamp = >::get(); - // TODO wp[n].arrival < wp[n + 1].arrival + // TODO (n>2) wp[n].arrival < wp[n + 1].arrival ensure!((arrival_time > current_timestamp) && (arrival_time > start_time), Error::::WrongTimeSupplied); let root = RootBoxes::::get(root_id); let start_area = root.detect_intersected_area(start_waypoint.location.project()); let end_area = root.detect_intersected_area(end_waypoint.location.project()); - // TODO each wp[n].location shall be inside one Root + // TODO (n>2) each wp[n].location shall be inside one Root ensure!((start_area != 0) && (end_area != 0), Error::::RouteDoesNotFitToRoot); - // for now we assume that there is only two waypoints - // AreaData::mutate(root_id, area_id, |ar| { - // ar.area_type = area_type; - // }); - // Self::deposit_event(RawEvent::RouteAdded(start_point, end_point, start_time, arrival_time, root_id, who)); + + Self::deposit_event(RawEvent::RouteAdded(start_waypoint.location, + end_waypoint.location, start_time, arrival_time, root_id, who)); Ok(()) } } From 42f1c3d82c14b838119f636acddc8c3e441bb4e5 Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Thu, 15 Jul 2021 16:36:59 +0300 Subject: [PATCH 05/29] [ADD] started writing tests --- pallets/ds-maps/src/tests.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pallets/ds-maps/src/tests.rs b/pallets/ds-maps/src/tests.rs index 6d468d4..288a6f0 100644 --- a/pallets/ds-maps/src/tests.rs +++ b/pallets/ds-maps/src/tests.rs @@ -748,3 +748,21 @@ fn it_dispatchable_get_root_index() { }); } +#[test] +fn it_add_route_wrong_timelines() { + new_test_ext().execute_with(|| { + assert_ok!( + DSAccountsModule::account_add( + Origin::signed(ADMIN_ACCOUNT_ID), + REGISTRAR_1_ACCOUNT_ID, + super::REGISTRAR_ROLE + )); + assert_ok!( + DSMapsModule::root_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + construct_testing_box(), + coord(DELTA), + )); + + }); +} \ No newline at end of file From c5852fbcec9b2897bc95ad935605aab4eb7b6fa7 Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Thu, 15 Jul 2021 18:35:26 +0300 Subject: [PATCH 06/29] [ADD] new() method for wp --- pallets/ds-maps/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pallets/ds-maps/src/lib.rs b/pallets/ds-maps/src/lib.rs index 8267a10..ade8f2b 100644 --- a/pallets/ds-maps/src/lib.rs +++ b/pallets/ds-maps/src/lib.rs @@ -226,6 +226,11 @@ pub struct Waypoint { pub arrival: Moment, } +impl Waypoint { + pub fn new(location: Point3D, arrival: Moment) -> Self { + Waypoint{location, arrival} + } +} #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(Encode, Decode, Clone, Default, Debug, PartialEq, Eq)] From 724b83405f45baccccfdb5b39d37f79d808c6bec Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Thu, 15 Jul 2021 18:35:46 +0300 Subject: [PATCH 07/29] [UPDATE] first test finished --- pallets/ds-maps/src/tests.rs | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/pallets/ds-maps/src/tests.rs b/pallets/ds-maps/src/tests.rs index 288a6f0..f02de8d 100644 --- a/pallets/ds-maps/src/tests.rs +++ b/pallets/ds-maps/src/tests.rs @@ -3,6 +3,7 @@ use crate::{ Page, Point3D, Box3D, Point2D, Rect2D, + Waypoint, }; use frame_support::{ assert_noop, assert_ok, @@ -52,13 +53,18 @@ use sp_std::str::FromStr; // (55.390, 37,380) type Error = super::Error; +// TODO find out how to connect this types w mock pub type Coord = I10F22; +type Moment = u64; // Constants to make tests more readable const ADMIN_ACCOUNT_ID: u64 = 1; const REGISTRAR_1_ACCOUNT_ID: u64 = 2; pub const ROOT_ID: u64 = 0b0001_0101_1010_0001_0000_1110_1001_1001_0001_0101_1101_1000_0000_1110_1100_1110; -// this value, and values in construct_testing_..() were calculated +// Values in construct_testing_..() pre-calculated +// construct_custom_..() same functionality, but custom numbers +// These consts also pre-calculated + const AREA_ID: u16 = 58; const DEFAULT_HEIGHT: u32 = 30; @@ -105,6 +111,21 @@ pub fn construct_custom_rect(sw_lat: &str, sw_lon: &str, ne_lat: &str, ne_lon: & Rect2D::new(south_west, north_east) } +pub fn construct_testing_waypoints() -> Vec> { + let start_location = Point3D::new(coord("55.395"), + coord("37.385"), + coord("1")); + let end_location = Point3D::new(coord("55.397"), + coord("37.387"), + coord("1")); + let start_time = 100_u64; + let end_time = 110_u64; + let start_wp = Waypoint::new(start_location, start_time); + let end_wp = Waypoint::new(end_location, end_time); + vec![start_wp, end_wp] + +} + #[test] fn it_try_to_add_root_unauthorized() { new_test_ext().execute_with(|| { @@ -763,6 +784,13 @@ fn it_add_route_wrong_timelines() { construct_testing_box(), coord(DELTA), )); - + let waypoints = construct_testing_waypoints(); + + assert_ok!( + DSMapsModule::route_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + waypoints, + ROOT_ID, + )); }); } \ No newline at end of file From ae7721ca93c1eccda69392e4a37a09fa8cde295f Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Thu, 15 Jul 2021 18:40:58 +0300 Subject: [PATCH 08/29] [ADD] custom waypoint constructor fn --- pallets/ds-maps/src/tests.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pallets/ds-maps/src/tests.rs b/pallets/ds-maps/src/tests.rs index f02de8d..b0a30b9 100644 --- a/pallets/ds-maps/src/tests.rs +++ b/pallets/ds-maps/src/tests.rs @@ -123,7 +123,21 @@ pub fn construct_testing_waypoints() -> Vec> { let start_wp = Waypoint::new(start_location, start_time); let end_wp = Waypoint::new(end_location, end_time); vec![start_wp, end_wp] +} +// Assume that alt is const for now +pub fn construct_custom_waypoints(sw_lat: &str, sw_lon: &str, + ne_lat: &str, ne_lon: &str, + start_time: u64, end_time: u64) -> Vec> { + let start_location = Point3D::new(coord(sw_lat), + coord(sw_lon), + coord("1")); + let end_location = Point3D::new(coord(ne_lat), + coord(ne_lon), + coord("1")); + let start_wp = Waypoint::new(start_location, start_time); + let end_wp = Waypoint::new(end_location, end_time); + vec![start_wp, end_wp] } #[test] From 0ceecef823007bad586499700d63f46e448b5d62 Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Thu, 15 Jul 2021 21:24:34 +0300 Subject: [PATCH 09/29] [FIX] less to more --- pallets/ds-maps/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/ds-maps/src/lib.rs b/pallets/ds-maps/src/lib.rs index ade8f2b..c322e86 100644 --- a/pallets/ds-maps/src/lib.rs +++ b/pallets/ds-maps/src/lib.rs @@ -1428,7 +1428,7 @@ decl_module! { // TODO consider role for route addition ensure!(>::account_is(&who, REGISTRAR_ROLE.into()), Error::::NotAuthorized); ensure!(RootBoxes::::contains_key(root_id), Error::::RootDoesNotExist); - ensure!(waypoints.len() <= 2, Error::::InvalidData); + ensure!(waypoints.len() >= 2, Error::::InvalidData); let start_waypoint = &waypoints[0]; let end_waypoint = &waypoints[waypoints.len() - 1]; // Getting all time bounds From 83fd9ac2b4c88b481e5bf9c883b8dcaa7cf1452f Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Thu, 15 Jul 2021 21:24:45 +0300 Subject: [PATCH 10/29] [ADD] more tests --- pallets/ds-maps/src/tests.rs | 117 ++++++++++++++++++++++++++++++++--- 1 file changed, 109 insertions(+), 8 deletions(-) diff --git a/pallets/ds-maps/src/tests.rs b/pallets/ds-maps/src/tests.rs index b0a30b9..3ec6887 100644 --- a/pallets/ds-maps/src/tests.rs +++ b/pallets/ds-maps/src/tests.rs @@ -125,15 +125,16 @@ pub fn construct_testing_waypoints() -> Vec> { vec![start_wp, end_wp] } -// Assume that alt is const for now -pub fn construct_custom_waypoints(sw_lat: &str, sw_lon: &str, - ne_lat: &str, ne_lon: &str, +// Assume that alt is const for now. +// TODO (n>2) replace &str in signature to an array [[&str; 4]; n] +pub fn construct_custom_waypoints(start_lat: &str, start_lon: &str, + end_lat: &str, end_lon: &str, start_time: u64, end_time: u64) -> Vec> { - let start_location = Point3D::new(coord(sw_lat), - coord(sw_lon), + let start_location = Point3D::new(coord(start_lat), + coord(start_lon), coord("1")); - let end_location = Point3D::new(coord(ne_lat), - coord(ne_lon), + let end_location = Point3D::new(coord(end_lat), + coord(end_lon), coord("1")); let start_wp = Waypoint::new(start_location, start_time); let end_wp = Waypoint::new(end_location, end_time); @@ -783,8 +784,9 @@ fn it_dispatchable_get_root_index() { }); } +// Not sure if there's need to try this unauthorized #[test] -fn it_add_route_wrong_timelines() { +fn it_add_route_by_registrar() { new_test_ext().execute_with(|| { assert_ok!( DSAccountsModule::account_add( @@ -799,12 +801,111 @@ fn it_add_route_wrong_timelines() { coord(DELTA), )); let waypoints = construct_testing_waypoints(); + assert_ok!( + DSMapsModule::route_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + waypoints, + ROOT_ID, + )); + }); +} +#[test] +fn it_add_route_wrong_root() { + new_test_ext().execute_with(|| { assert_ok!( + DSAccountsModule::account_add( + Origin::signed(ADMIN_ACCOUNT_ID), + REGISTRAR_1_ACCOUNT_ID, + super::REGISTRAR_ROLE + )); + assert_ok!( + DSMapsModule::root_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + construct_testing_box(), + coord(DELTA), + )); + let waypoints = construct_testing_waypoints(); + assert_noop!( + DSMapsModule::route_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + waypoints, + ROOT_ID + 1, + ), + Error::RootDoesNotExist + ); + }); +} + +#[test] +fn it_add_route_wrong_timestamps() { + new_test_ext().execute_with(|| { + assert_ok!( + DSAccountsModule::account_add( + Origin::signed(ADMIN_ACCOUNT_ID), + REGISTRAR_1_ACCOUNT_ID, + super::REGISTRAR_ROLE + )); + assert_ok!( + DSMapsModule::root_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + construct_testing_box(), + coord(DELTA), + )); + // coords are same as in testing wp + let waypoints = construct_custom_waypoints( + "55.395", "37.385", + "55.397", "37.387", + 120, 100); + assert_noop!( DSMapsModule::route_add( Origin::signed(REGISTRAR_1_ACCOUNT_ID), waypoints, ROOT_ID, + ), + Error::WrongTimeSupplied + ); + }); +} + +#[test] +fn it_add_route_wrong_waypoints() { + new_test_ext().execute_with(|| { + assert_ok!( + DSAccountsModule::account_add( + Origin::signed(ADMIN_ACCOUNT_ID), + REGISTRAR_1_ACCOUNT_ID, + super::REGISTRAR_ROLE + )); + assert_ok!( + DSMapsModule::root_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + construct_testing_box(), + coord(DELTA), )); + let location = Point3D::new(coord("55.395"), + coord("37.385"), + coord("1")); + let single_waypoint = vec![Waypoint::new(location, 10)]; + assert_noop!( + DSMapsModule::route_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + single_waypoint, + ROOT_ID, + ), + Error::InvalidData); + + let waypoints = construct_custom_waypoints( + "55.395", "37.385", + "10", "37", + 100, 120); + assert_noop!( + DSMapsModule::route_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + waypoints, + ROOT_ID, + ), + Error::RouteDoesNotFitToRoot + ); }); } \ No newline at end of file From c296cb32fa24742b4ada98f62bc70702ea9cf1a0 Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Fri, 16 Jul 2021 14:49:28 +0300 Subject: [PATCH 11/29] [ADD] Line structure --- pallets/ds-maps/src/lib.rs | 39 ++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/pallets/ds-maps/src/lib.rs b/pallets/ds-maps/src/lib.rs index c322e86..7e4ef7c 100644 --- a/pallets/ds-maps/src/lib.rs +++ b/pallets/ds-maps/src/lib.rs @@ -7,7 +7,7 @@ use frame_support::{ codec::{Decode, Encode}, storage::StorageDoubleMap, dispatch::fmt::Debug, - sp_runtime::sp_std::{ops::{Sub, Div}, vec::Vec}, + sp_runtime::sp_std::{ops::{Sub, Div, Mul}, vec::Vec}, decl_error, decl_event, decl_module, decl_storage, dispatch, ensure, weights::Weight, Parameter, @@ -239,6 +239,18 @@ pub struct Route { pub owner: OwnerId, } +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[derive(Encode, Decode, Clone, Default, Debug, PartialEq, Eq)] +pub struct Line { + pub a: Coord, + pub b: Coord, + pub c: Coord, +} + +impl Line { + +} + #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(Encode, Decode, Default, Clone, Copy, Debug)] pub struct RootBox { @@ -248,7 +260,7 @@ pub struct RootBox { } impl< - Coord: PartialOrd + Sub + Signed + IntDiv + Div + Copy + Coord: PartialOrd + Sub + Signed + IntDiv + Mul + Div + Copy, > RootBox { pub fn new(id: RootId, bounding_box: Box3D, delta: Coord) -> Self { RootBox{id, bounding_box, delta} @@ -302,6 +314,16 @@ impl< (total_rows * (column - 1)) + row } + + pub fn get_route_areas(self, waypoint_locations: Vec>) -> Vec{ + // Form coefficients (y1 - y2)x + (x2 - x1)y + (x1y2 - x2y1) = 0 + // TODO (n>2) in a cycle + let a = waypoint_locations[0].lat - waypoint_locations[1].lat; + let b = waypoint_locations[1].lon - waypoint_locations[0].lon; + let c = (waypoint_locations[0].lon * waypoint_locations[1].lat) - (waypoint_locations[1].lon * waypoint_locations[0].lat); + + Vec::new() + } #[cfg(test)] pub fn is_active(&self) -> bool { @@ -996,7 +1018,8 @@ pub trait Trait: accounts::Trait { + FromRaw + CastToType + Sub - + Div; + + Div + + Mul; type RouteId: Default + Parameter + Copy; @@ -1419,7 +1442,7 @@ decl_module! { Ok(()) } - /// Adds new route for UAV + /// creates new route for UAV #[weight = ::WeightInfo::change_area_type()] pub fn route_add(origin, waypoints: Vec::Moment>>, @@ -1444,8 +1467,12 @@ decl_module! { // TODO (n>2) each wp[n].location shall be inside one Root ensure!((start_area != 0) && (end_area != 0), Error::::RouteDoesNotFitToRoot); - Self::deposit_event(RawEvent::RouteAdded(start_waypoint.location, - end_waypoint.location, start_time, arrival_time, root_id, who)); + let route_areas: Vec = root.get_route_areas(vec![start_waypoint.location, end_waypoint.location]); + + Self::deposit_event(RawEvent::RouteAdded( + start_waypoint.location, end_waypoint.location, + start_time, arrival_time, root_id, who + )); Ok(()) } } From 5c0d1a2f21bf283ca6ff58cec5c3eb4d37d2c841 Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Fri, 16 Jul 2021 16:13:18 +0300 Subject: [PATCH 12/29] [ADD] Supporting type BigCoord for calculations --- dsky-utils/src/lib.rs | 29 ++++++++++++++++++++++++++++- pallets/ds-maps/src/lib.rs | 21 +++++++++++++++------ pallets/ds-maps/src/mock.rs | 3 ++- runtime/src/lib.rs | 3 ++- 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/dsky-utils/src/lib.rs b/dsky-utils/src/lib.rs index 3aa5b60..b584c65 100644 --- a/dsky-utils/src/lib.rs +++ b/dsky-utils/src/lib.rs @@ -1,6 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std)] -use substrate_fixed::types::I10F22; +use substrate_fixed::{types::{I10F22, I42F22}, traits::FromFixed}; +// Set of traits, required for Coord struct, used in maps pallet pub trait IntDiv { fn integer_division_u16(self, rhs: RHS) -> u16; @@ -19,6 +20,18 @@ pub trait CastToType { fn to_u32_with_frac_part(self, cell_size: u32, max_digits_in_frac_part: u8) -> u32; } +// TODO consider naming of two traits, as they are used in pair +pub trait ToBigCoord { + type Output; + fn ToBigCoord(self) -> Self::Output; +} + +pub trait FromBigCoord { + type Output; + fn try_into(self) -> Self::Output; +} + +// Here comes the implementations // Want to change Coord type => impl trait for it here impl IntDiv for I10F22 { fn integer_division_u16(self, rhs: I10F22) -> u16 { @@ -63,3 +76,17 @@ impl CastToType for I10F22 { integer_part + frac_part } } + +impl ToBigCoord for I10F22 { + type Output = I42F22; + fn ToBigCoord(self) -> Self::Output { + self.into() + } +} +// TODO handle possible errors through checked_from_fixed() +impl FromBigCoord for I42F22 { + type Output = I10F22; + fn try_into(self) -> Self::Output { + I10F22::from_fixed(self) + } +} \ No newline at end of file diff --git a/pallets/ds-maps/src/lib.rs b/pallets/ds-maps/src/lib.rs index 7e4ef7c..009da75 100644 --- a/pallets/ds-maps/src/lib.rs +++ b/pallets/ds-maps/src/lib.rs @@ -7,7 +7,7 @@ use frame_support::{ codec::{Decode, Encode}, storage::StorageDoubleMap, dispatch::fmt::Debug, - sp_runtime::sp_std::{ops::{Sub, Div, Mul}, vec::Vec}, + sp_runtime::sp_std::{ops::{Sub, Div, Mul, Add}, vec::Vec}, decl_error, decl_event, decl_module, decl_storage, dispatch, ensure, weights::Weight, Parameter, @@ -20,7 +20,7 @@ use sp_std::{ vec, }; -use dsky_utils::{CastToType, FromRaw, IntDiv, Signed}; +use dsky_utils::{CastToType, FromRaw, IntDiv, Signed, ToBigCoord, FromBigCoord}; use frame_system::ensure_signed; use pallet_ds_accounts as accounts; use accounts::REGISTRAR_ROLE; @@ -1013,13 +1013,22 @@ pub trait Trait: accounts::Trait { + PartialOrd + PartialEq + FromStr - + IntDiv + Signed - + FromRaw - + CastToType + Sub + Div - + Mul; + + Mul + // Traits from dsky-utils + + IntDiv + + FromRaw + + CastToType + + ToBigCoord; + + // Required for global calculations, where Coord is not enough. Not for common usage. + type BigCoord: Sub + + Div + + Mul + + Add + + FromBigCoord; type RouteId: Default + Parameter + Copy; diff --git a/pallets/ds-maps/src/mock.rs b/pallets/ds-maps/src/mock.rs index 138ec5a..9713ee8 100644 --- a/pallets/ds-maps/src/mock.rs +++ b/pallets/ds-maps/src/mock.rs @@ -12,7 +12,7 @@ use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, }; -use substrate_fixed::types::I10F22; +use substrate_fixed::types::{I10F22, I42F22}; use pallet_ds_accounts::ADMIN_ROLE; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; @@ -107,6 +107,7 @@ impl Trait for Test { type Event = Event; type WeightInfo = (); type Coord = I10F22; + type BigCoord = I42F22; type RouteId = u32; type RawCoord = i32; type MaxBuildingsInArea = MaxBuildingsInArea; diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index b1bbbeb..8f50510 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -50,7 +50,7 @@ pub use pallet_ds_accounts; /// Import the DS maps pallet. pub use pallet_ds_maps; /// Import fixed point for GPS coords -use substrate_fixed::types::I10F22; +use substrate_fixed::types::{I10F22, I42F22}; /// An index to a block. pub type BlockNumber = u32; @@ -315,6 +315,7 @@ impl pallet_ds_maps::Trait for Runtime { type RawCoord = i32; type RouteId = u32; type Coord = I10F22; + type BigCoord = I42F22; type MaxBuildingsInArea = MaxBuildingsInArea; type MaxHeight = MaxHeight; } From 10566b05338cc42af769a03e4db7bd8ce7a4b37d Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Sat, 17 Jul 2021 15:50:16 +0300 Subject: [PATCH 13/29] [ADD] constructor for Line --- dsky-utils/src/lib.rs | 4 ++-- pallets/ds-maps/src/lib.rs | 24 ++++++++++++++++-------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/dsky-utils/src/lib.rs b/dsky-utils/src/lib.rs index b584c65..6a13540 100644 --- a/dsky-utils/src/lib.rs +++ b/dsky-utils/src/lib.rs @@ -23,7 +23,7 @@ pub trait CastToType { // TODO consider naming of two traits, as they are used in pair pub trait ToBigCoord { type Output; - fn ToBigCoord(self) -> Self::Output; + fn to_big_coord(self) -> Self::Output; } pub trait FromBigCoord { @@ -79,7 +79,7 @@ impl CastToType for I10F22 { impl ToBigCoord for I10F22 { type Output = I42F22; - fn ToBigCoord(self) -> Self::Output { + fn to_big_coord(self) -> Self::Output { self.into() } } diff --git a/pallets/ds-maps/src/lib.rs b/pallets/ds-maps/src/lib.rs index 009da75..347b378 100644 --- a/pallets/ds-maps/src/lib.rs +++ b/pallets/ds-maps/src/lib.rs @@ -241,14 +241,27 @@ pub struct Route { #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(Encode, Decode, Clone, Default, Debug, PartialEq, Eq)] -pub struct Line { +pub struct Line { pub a: Coord, pub b: Coord, pub c: Coord, + _phantom: PhantomData } -impl Line { - +impl + Copy, + BigCoord: Mul + Sub + FromBigCoord + > Line { + pub fn new(point_0: Point3D, point_1: Point3D) -> Self { + // Form coefficients (y1 - y2)x + (x2 - x1)y + (x1y2 - x2y1) = 0 + // TODO (n>2) in a cycle + let a = (point_0.lat.to_big_coord() - point_1.lat.to_big_coord()).try_into(); + let b = (point_1.lon.to_big_coord() - point_0.lon.to_big_coord()).try_into(); + let c = + ((point_0.lon.to_big_coord() * point_1.lat.to_big_coord()) - + (point_1.lon.to_big_coord() * point_0.lat.to_big_coord())).try_into(); + + Line {a: a, b: b, c: c, _phantom: PhantomData} + } } #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] @@ -316,11 +329,6 @@ impl< } pub fn get_route_areas(self, waypoint_locations: Vec>) -> Vec{ - // Form coefficients (y1 - y2)x + (x2 - x1)y + (x1y2 - x2y1) = 0 - // TODO (n>2) in a cycle - let a = waypoint_locations[0].lat - waypoint_locations[1].lat; - let b = waypoint_locations[1].lon - waypoint_locations[0].lon; - let c = (waypoint_locations[0].lon * waypoint_locations[1].lat) - (waypoint_locations[1].lon * waypoint_locations[0].lat); Vec::new() } From 0079637b6e18e6efe943c95a45ae3dd919cfb758 Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Mon, 19 Jul 2021 21:20:33 +0300 Subject: [PATCH 14/29] [ADD] Area calculations implemented --- pallets/ds-maps/src/lib.rs | 134 +++++++++++++++++++++++++++++++---- pallets/ds-maps/src/tests.rs | 4 +- 2 files changed, 122 insertions(+), 16 deletions(-) diff --git a/pallets/ds-maps/src/lib.rs b/pallets/ds-maps/src/lib.rs index 347b378..390cd98 100644 --- a/pallets/ds-maps/src/lib.rs +++ b/pallets/ds-maps/src/lib.rs @@ -239,16 +239,20 @@ pub struct Route { pub owner: OwnerId, } +// Actually, this is line section, so we need limits #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(Encode, Decode, Clone, Default, Debug, PartialEq, Eq)] pub struct Line { pub a: Coord, pub b: Coord, pub c: Coord, + pub start_point: Point2D, + pub end_point: Point2D, _phantom: PhantomData } -impl + Copy, +impl< + Coord: ToBigCoord + PartialOrd + Signed + IntDiv + Mul + Sub + Add + Div + Copy, BigCoord: Mul + Sub + FromBigCoord > Line { pub fn new(point_0: Point3D, point_1: Point3D) -> Self { @@ -256,11 +260,110 @@ impl + Copy, // TODO (n>2) in a cycle let a = (point_0.lat.to_big_coord() - point_1.lat.to_big_coord()).try_into(); let b = (point_1.lon.to_big_coord() - point_0.lon.to_big_coord()).try_into(); - let c = - ((point_0.lon.to_big_coord() * point_1.lat.to_big_coord()) - - (point_1.lon.to_big_coord() * point_0.lat.to_big_coord())).try_into(); - - Line {a: a, b: b, c: c, _phantom: PhantomData} + let c = ( + (point_0.lon.to_big_coord() * point_1.lat.to_big_coord()) - + (point_1.lon.to_big_coord() * point_0.lat.to_big_coord()) + ).try_into(); + // TODO if point_0.lat > point_1.lat, swap points + Line { + a: a, + b: b, + c: c, + start_point: point_0.project(), + end_point: point_1.project(), + _phantom: PhantomData + } + } + + //TODO optimize fn to get ONLY intersected areas, for now it gets everythig from the rect + pub fn get_route_areas(self, root: RootBox) -> Vec { + let start_area = root.detect_intersected_area(self.start_point); + let end_area = root.detect_intersected_area(self.end_point); + // In case everything is in one area + if start_area == end_area {return vec![start_area]} + let mut output = Vec::new(); + let delta = root.delta; + // let lat_area = (self.end_point.lat - self.start_point.lat) / root.delta; + // // TODO handle negative substraction + // let lon_area = (self.end_point.lon - self.start_point.lon) / root.delta; + + let start_vector = root.bounding_box.south_west.project().get_distance_vector(self.start_point); + let start_row = start_vector.lat.integer_division_u16(root.delta) + 1; + let start_column = start_vector.lon.integer_division_u16(root.delta) + 1; + + let end_vector = root.bounding_box.south_west.project().get_distance_vector(self.end_point); + let end_row = end_vector.lat.integer_division_u16(root.delta) + 1; + let end_column = end_vector.lon.integer_division_u16(root.delta) + 1; + + let mut current_point = self.start_point; + let mut row_counter = start_row; + + while row_counter <= end_row { + let mut column_counter = start_column; + while column_counter <= end_column { + output.push(root.detect_intersected_area(current_point)); + current_point.lon = current_point.lon + delta; + column_counter += 1; + } + current_point.lon = self.start_point.lon; + current_point.lat = current_point.lat + delta; + row_counter += 1; + } + output + } +} + +#[cfg(test)] +mod line_tests { + use super::*; + use crate::tests::{construct_custom_box, coord}; + // TODO draw rect in ascii as an illustration + + #[test] + fn get_route_areas_in_square() { + let rect = construct_custom_box("0", "0", "4", "4"); + let root = RootBox::new(1, rect, coord("1")); + let first_point = Point3D::new(coord("0.5"), + coord("0.5"), + coord("1")); + let second_point = Point3D::new(coord("2.5"), + coord("2.5"), + coord("1")); + let line = Line::new(first_point, second_point); + let areas = line.get_route_areas(root); + assert_eq!(areas, vec![1, 5, 9, 2, 6, 10, 3, 7, 11]); + + + } + + #[test] + fn get_route_areas_in_line() { + let rect = construct_custom_box("0", "0", "4", "4"); + let root = RootBox::new(1, rect, coord("1")); + let first_point = Point3D::new(coord("0.1"), + coord("0.1"), + coord("1")); + let second_point = Point3D::new(coord("3.1"), + coord("0.2"), + coord("1")); + let line = Line::new(first_point, second_point); + let areas = line.get_route_areas(root); + assert_eq!(areas, vec![1, 2, 3, 4]); + } + + #[test] + fn get_route_single_area() { + let rect = construct_custom_box("0", "0", "4", "4"); + let root = RootBox::new(1, rect, coord("1")); + let first_point = Point3D::new(coord("0.1"), + coord("0.1"), + coord("1")); + let second_point = Point3D::new(coord("0.3"), + coord("0.2"), + coord("1")); + let line = Line::new(first_point, second_point); + let areas = line.get_route_areas(root); + assert_eq!(areas, vec![1]); } } @@ -327,11 +430,6 @@ impl< (total_rows * (column - 1)) + row } - - pub fn get_route_areas(self, waypoint_locations: Vec>) -> Vec{ - - Vec::new() - } #[cfg(test)] pub fn is_active(&self) -> bool { @@ -1025,6 +1123,7 @@ pub trait Trait: accounts::Trait { + Sub + Div + Mul + + Add // Traits from dsky-utils + IntDiv + FromRaw @@ -1483,9 +1582,16 @@ decl_module! { let end_area = root.detect_intersected_area(end_waypoint.location.project()); // TODO (n>2) each wp[n].location shall be inside one Root ensure!((start_area != 0) && (end_area != 0), Error::::RouteDoesNotFitToRoot); - - let route_areas: Vec = root.get_route_areas(vec![start_waypoint.location, end_waypoint.location]); - + // TODO (n>2) vec! in here + let route_line = Line::new(start_waypoint.location, end_waypoint.location); + // We receive all areas, containing list of zones which could be intersected + let route_areas: Vec = route_line.get_route_areas(root); + // Loop through areas, check each existing zone + for area_id in route_areas.iter() { + if AreaData::contains_key(root_id, area_id) { + + } + } Self::deposit_event(RawEvent::RouteAdded( start_waypoint.location, end_waypoint.location, start_time, arrival_time, root_id, who diff --git a/pallets/ds-maps/src/tests.rs b/pallets/ds-maps/src/tests.rs index 3ec6887..f8aa9da 100644 --- a/pallets/ds-maps/src/tests.rs +++ b/pallets/ds-maps/src/tests.rs @@ -68,7 +68,7 @@ pub const ROOT_ID: u64 = 0b0001_0101_1010_0001_0000_1110_1001_1001_0001_0101_110 const AREA_ID: u16 = 58; const DEFAULT_HEIGHT: u32 = 30; -const DELTA: &str = "0.01"; +pub const DELTA: &str = "0.01"; // shortcut for &str -> Coord pub fn coord(s: &str) -> Coord @@ -908,4 +908,4 @@ fn it_add_route_wrong_waypoints() { Error::RouteDoesNotFitToRoot ); }); -} \ No newline at end of file +} From 964573e7b99f7850791c80cbebcafb2a91c79df6 Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Mon, 19 Jul 2021 23:53:48 +0300 Subject: [PATCH 15/29] [ADD] zone iterations, error --- pallets/ds-maps/src/lib.rs | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/pallets/ds-maps/src/lib.rs b/pallets/ds-maps/src/lib.rs index 390cd98..6631f01 100644 --- a/pallets/ds-maps/src/lib.rs +++ b/pallets/ds-maps/src/lib.rs @@ -241,7 +241,7 @@ pub struct Route { // Actually, this is line section, so we need limits #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[derive(Encode, Decode, Clone, Default, Debug, PartialEq, Eq)] +#[derive(Encode, Decode, Clone, Copy, Default, Debug, PartialEq, Eq)] pub struct Line { pub a: Coord, pub b: Coord, @@ -253,9 +253,9 @@ pub struct Line { impl< Coord: ToBigCoord + PartialOrd + Signed + IntDiv + Mul + Sub + Add + Div + Copy, - BigCoord: Mul + Sub + FromBigCoord + BigCoord: Mul + Sub + FromBigCoord + Copy > Line { - pub fn new(point_0: Point3D, point_1: Point3D) -> Self { + pub fn new(point_0: Point2D, point_1: Point2D) -> Self { // Form coefficients (y1 - y2)x + (x2 - x1)y + (x1y2 - x2y1) = 0 // TODO (n>2) in a cycle let a = (point_0.lat.to_big_coord() - point_1.lat.to_big_coord()).try_into(); @@ -269,8 +269,8 @@ impl< a: a, b: b, c: c, - start_point: point_0.project(), - end_point: point_1.project(), + start_point: point_0, + end_point: point_1, _phantom: PhantomData } } @@ -311,6 +311,10 @@ impl< } output } + + pub fn intersect_zone(&self, zone: Rect2D) -> bool { + true + } } #[cfg(test)] @@ -1135,7 +1139,8 @@ pub trait Trait: accounts::Trait { + Div + Mul + Add - + FromBigCoord; + + FromBigCoord + + Copy; type RouteId: Default + Parameter + Copy; @@ -1247,6 +1252,8 @@ decl_error! { WrongTimeSupplied, /// Route contain 1 or more wpoints, which lie outside of root RouteDoesNotFitToRoot, + /// Route intersect 1 or more zones + RouteIntersectRedZone, // Add additional errors below } } @@ -1583,15 +1590,22 @@ decl_module! { // TODO (n>2) each wp[n].location shall be inside one Root ensure!((start_area != 0) && (end_area != 0), Error::::RouteDoesNotFitToRoot); // TODO (n>2) vec! in here - let route_line = Line::new(start_waypoint.location, end_waypoint.location); + let route_line = Line::new(start_waypoint.location.project(), end_waypoint.location.project()); // We receive all areas, containing list of zones which could be intersected let route_areas: Vec = route_line.get_route_areas(root); // Loop through areas, check each existing zone + let mut clear_path = true; for area_id in route_areas.iter() { if AreaData::contains_key(root_id, area_id) { - + let mut zone_id = Self::pack_index(root_id, *area_id, 0); + while RedZones::::contains_key(zone_id) { + // TODO + clear_path = route_line.intersect_zone(RedZones::::get(zone_id).rect); + zone_id += 1; + } } } + ensure!(clear_path, Error::::RouteIntersectRedZone); Self::deposit_event(RawEvent::RouteAdded( start_waypoint.location, end_waypoint.location, start_time, arrival_time, root_id, who From 3f144c479c8ecf6c793b711e7c6544817832a925 Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Wed, 21 Jul 2021 00:16:36 +0300 Subject: [PATCH 16/29] [UPDATE] rename fn in utils --- dsky-utils/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dsky-utils/src/lib.rs b/dsky-utils/src/lib.rs index 6a13540..ea2c890 100644 --- a/dsky-utils/src/lib.rs +++ b/dsky-utils/src/lib.rs @@ -23,12 +23,12 @@ pub trait CastToType { // TODO consider naming of two traits, as they are used in pair pub trait ToBigCoord { type Output; - fn to_big_coord(self) -> Self::Output; + fn try_into(self) -> Self::Output; } pub trait FromBigCoord { type Output; - fn try_into(self) -> Self::Output; + fn try_from(self) -> Self::Output; } // Here comes the implementations @@ -79,14 +79,14 @@ impl CastToType for I10F22 { impl ToBigCoord for I10F22 { type Output = I42F22; - fn to_big_coord(self) -> Self::Output { + fn try_into(self) -> Self::Output { self.into() } } // TODO handle possible errors through checked_from_fixed() impl FromBigCoord for I42F22 { type Output = I10F22; - fn try_into(self) -> Self::Output { + fn try_from(self) -> Self::Output { I10F22::from_fixed(self) } } \ No newline at end of file From 5c470b358d260e1971924fd6827b59ae1f4ff20e Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Wed, 21 Jul 2021 00:17:03 +0300 Subject: [PATCH 17/29] [ADD] Implemented raw intersection algorithm --- pallets/ds-maps/src/lib.rs | 151 ++++++++++++++++++++++++++++++------- 1 file changed, 122 insertions(+), 29 deletions(-) diff --git a/pallets/ds-maps/src/lib.rs b/pallets/ds-maps/src/lib.rs index 6631f01..de2f4d1 100644 --- a/pallets/ds-maps/src/lib.rs +++ b/pallets/ds-maps/src/lib.rs @@ -253,17 +253,17 @@ pub struct Line { impl< Coord: ToBigCoord + PartialOrd + Signed + IntDiv + Mul + Sub + Add + Div + Copy, - BigCoord: Mul + Sub + FromBigCoord + Copy + BigCoord: Mul + Sub + FromBigCoord + Copy + Default + PartialOrd > Line { pub fn new(point_0: Point2D, point_1: Point2D) -> Self { // Form coefficients (y1 - y2)x + (x2 - x1)y + (x1y2 - x2y1) = 0 // TODO (n>2) in a cycle - let a = (point_0.lat.to_big_coord() - point_1.lat.to_big_coord()).try_into(); - let b = (point_1.lon.to_big_coord() - point_0.lon.to_big_coord()).try_into(); + let a = (point_0.lat.try_into() - point_1.lat.try_into()).try_from(); + let b = (point_1.lon.try_into() - point_0.lon.try_into()).try_from(); let c = ( - (point_0.lon.to_big_coord() * point_1.lat.to_big_coord()) - - (point_1.lon.to_big_coord() * point_0.lat.to_big_coord()) - ).try_into(); + (point_0.lon.try_into() * point_1.lat.try_into()) - + (point_1.lon.try_into() * point_0.lat.try_into()) + ).try_from(); // TODO if point_0.lat > point_1.lat, swap points Line { a: a, @@ -311,28 +311,79 @@ impl< } output } + // Basically we split rect to 4 lines(maybe 2 is enough?), and check each one for intersection + pub fn intersects_rect(&self, rect: Rect2D) -> bool { + // true + let south_east = Point2D::new(rect.north_east.lat, rect.south_west.lon); + let north_west = Point2D::new(rect.south_west.lat, rect.north_east.lon); + + let west_line = Line::new(rect.south_west, north_west); + let north_line = Line::new(north_west, rect.north_east); + let east_line = Line::new(rect.north_east, south_east); + let south_line = Line::new(south_east, rect.south_west); + + let rect_lines = vec![west_line, north_line, east_line, south_line]; + let mut crossed = false; + for line in rect_lines.iter() { + crossed = self.is_lines_cross(*line); + } + crossed + } + // TODO change BigCoord::default to custom epsilon in Trait + pub fn is_lines_cross(&self, line: Line) -> bool { + Line::intersect_1(self.start_point.lat, self.end_point.lat, line.start_point.lat, line.end_point.lat) && + Line::intersect_1(self.start_point.lon, self.end_point.lon, line.start_point.lon, line.end_point.lon) && + ((Line::area(self.start_point, self.end_point, line.start_point) * + Line::area(self.start_point, self.end_point, line.end_point)) <= BigCoord::default()) && + ((Line::area(line.start_point, line.end_point, self.start_point) * + Line::area(line.start_point, line.end_point, self.end_point)) <= BigCoord::default()) + } - pub fn intersect_zone(&self, zone: Rect2D) -> bool { - true + // Okay, this is just unreadable code + fn intersect_1(_a: Coord, _b: Coord, _c: Coord, _d: Coord) -> bool { + let (mut a, mut b, mut c, mut d) = (_a.clone(), _b.clone(), _c.clone(), _d.clone()); + if a > b { + let temp = a; + a = b; + b = temp; + } + if c > d { + let temp = c; + c = d; + d = temp; + } + Line::max(a,c) <= Line::min(b,d) + } + + // Area of a triangle + fn area(a: Point2D, b: Point2D, c: Point2D) -> BigCoord { + (b.lat.try_into() - a.lat.try_into()) * (c.lon.try_into() - a.lon.try_into()) - + (b.lon.try_into() - a.lon.try_into()) * (c.lat.try_into() - a.lat.try_into()) + } + + fn max(a: Coord, b: Coord) -> Coord { + if a > b {a} else {b} + } + + fn min(a: Coord, b: Coord) -> Coord { + if a > b {b} else {a} } } #[cfg(test)] mod line_tests { use super::*; - use crate::tests::{construct_custom_box, coord}; + use crate::tests::{construct_custom_box, coord, Coord}; // TODO draw rect in ascii as an illustration #[test] fn get_route_areas_in_square() { let rect = construct_custom_box("0", "0", "4", "4"); let root = RootBox::new(1, rect, coord("1")); - let first_point = Point3D::new(coord("0.5"), - coord("0.5"), - coord("1")); - let second_point = Point3D::new(coord("2.5"), - coord("2.5"), - coord("1")); + let first_point = Point2D::new(coord("0.5"), + coord("0.5")); + let second_point = Point2D::new(coord("2.5"), + coord("2.5")); let line = Line::new(first_point, second_point); let areas = line.get_route_areas(root); assert_eq!(areas, vec![1, 5, 9, 2, 6, 10, 3, 7, 11]); @@ -344,12 +395,10 @@ mod line_tests { fn get_route_areas_in_line() { let rect = construct_custom_box("0", "0", "4", "4"); let root = RootBox::new(1, rect, coord("1")); - let first_point = Point3D::new(coord("0.1"), - coord("0.1"), - coord("1")); - let second_point = Point3D::new(coord("3.1"), - coord("0.2"), - coord("1")); + let first_point = Point2D::new(coord("0.1"), + coord("0.1")); + let second_point = Point2D::new(coord("3.1"), + coord("0.2")); let line = Line::new(first_point, second_point); let areas = line.get_route_areas(root); assert_eq!(areas, vec![1, 2, 3, 4]); @@ -359,16 +408,59 @@ mod line_tests { fn get_route_single_area() { let rect = construct_custom_box("0", "0", "4", "4"); let root = RootBox::new(1, rect, coord("1")); - let first_point = Point3D::new(coord("0.1"), - coord("0.1"), - coord("1")); - let second_point = Point3D::new(coord("0.3"), - coord("0.2"), - coord("1")); + let first_point = Point2D::new(coord("0.1"), + coord("0.1")); + let second_point = Point2D::new(coord("0.3"), + coord("0.2")); let line = Line::new(first_point, second_point); let areas = line.get_route_areas(root); assert_eq!(areas, vec![1]); } + + #[test] + fn lines_cross() { + let a_first_point: Point2D = Point2D::new(coord("0.1"), + coord("0.1")); + let a_second_point: Point2D = Point2D::new(coord("1"), + coord("1")); + let b_first_point: Point2D = Point2D::new(coord("0.1"), + coord("1")); + let b_second_point: Point2D = Point2D::new(coord("1"), + coord("0.2")); + let a = Line::new(a_first_point, a_second_point); + let b = Line::new(b_first_point, b_second_point); + assert!(a.is_lines_cross(b)); + } + + #[test] + fn long_lines_cross() { + let a_first_point: Point2D = Point2D::new(coord("10"), + coord("0.1")); + let a_second_point: Point2D = Point2D::new(coord("10"), + coord("10")); + let b_first_point: Point2D = Point2D::new(coord("5"), + coord("5")); + let b_second_point: Point2D = Point2D::new(coord("15"), + coord("5")); + let a = Line::new(a_first_point, a_second_point); + let b = Line::new(b_first_point, b_second_point); + assert!(a.is_lines_cross(b)); + } + + #[test] + fn lines_do_not_cross() { + let a_first_point: Point2D = Point2D::new(coord("0.1"), + coord("0.1")); + let a_second_point: Point2D = Point2D::new(coord("1"), + coord("1")); + let b_first_point: Point2D = Point2D::new(coord("1.3"), + coord("1.3")); + let b_second_point: Point2D = Point2D::new(coord("2"), + coord("4")); + let a = Line::new(a_first_point, a_second_point); + let b = Line::new(b_first_point, b_second_point); + assert!(!a.is_lines_cross(b)); + } } #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] @@ -1140,6 +1232,8 @@ pub trait Trait: accounts::Trait { + Mul + Add + FromBigCoord + + Default + + PartialOrd + Copy; type RouteId: Default + Parameter + Copy; @@ -1599,8 +1693,7 @@ decl_module! { if AreaData::contains_key(root_id, area_id) { let mut zone_id = Self::pack_index(root_id, *area_id, 0); while RedZones::::contains_key(zone_id) { - // TODO - clear_path = route_line.intersect_zone(RedZones::::get(zone_id).rect); + clear_path = route_line.intersects_rect(RedZones::::get(zone_id).rect); zone_id += 1; } } From 8900e031a5e98399f9f31b80ac8e5861be9c666f Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Wed, 21 Jul 2021 14:50:18 +0300 Subject: [PATCH 18/29] [REMOVE] koefficients from Line<> --- pallets/ds-maps/src/lib.rs | 215 ++++++++++++++++++++++--------------- 1 file changed, 127 insertions(+), 88 deletions(-) diff --git a/pallets/ds-maps/src/lib.rs b/pallets/ds-maps/src/lib.rs index de2f4d1..d28a254 100644 --- a/pallets/ds-maps/src/lib.rs +++ b/pallets/ds-maps/src/lib.rs @@ -243,9 +243,9 @@ pub struct Route { #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(Encode, Decode, Clone, Copy, Default, Debug, PartialEq, Eq)] pub struct Line { - pub a: Coord, - pub b: Coord, - pub c: Coord, + // pub a: Coord, + // pub b: Coord, + // pub c: Coord, pub start_point: Point2D, pub end_point: Point2D, _phantom: PhantomData @@ -258,17 +258,17 @@ impl< pub fn new(point_0: Point2D, point_1: Point2D) -> Self { // Form coefficients (y1 - y2)x + (x2 - x1)y + (x1y2 - x2y1) = 0 // TODO (n>2) in a cycle - let a = (point_0.lat.try_into() - point_1.lat.try_into()).try_from(); - let b = (point_1.lon.try_into() - point_0.lon.try_into()).try_from(); - let c = ( - (point_0.lon.try_into() * point_1.lat.try_into()) - - (point_1.lon.try_into() * point_0.lat.try_into()) - ).try_from(); + // let a = (point_0.lat.try_into() - point_1.lat.try_into()).try_from(); + // let b = (point_1.lon.try_into() - point_0.lon.try_into()).try_from(); + // let c = ( + // (point_0.lon.try_into() * point_1.lat.try_into()) - + // (point_1.lon.try_into() * point_0.lat.try_into()) + // ).try_from(); // TODO if point_0.lat > point_1.lat, swap points Line { - a: a, - b: b, - c: c, + // a: a, + // b: b, + // c: c, start_point: point_0, end_point: point_1, _phantom: PhantomData @@ -376,90 +376,129 @@ mod line_tests { use crate::tests::{construct_custom_box, coord, Coord}; // TODO draw rect in ascii as an illustration - #[test] - fn get_route_areas_in_square() { - let rect = construct_custom_box("0", "0", "4", "4"); - let root = RootBox::new(1, rect, coord("1")); - let first_point = Point2D::new(coord("0.5"), - coord("0.5")); - let second_point = Point2D::new(coord("2.5"), - coord("2.5")); - let line = Line::new(first_point, second_point); - let areas = line.get_route_areas(root); - assert_eq!(areas, vec![1, 5, 9, 2, 6, 10, 3, 7, 11]); + // In this section we try test calculations for intersecting areas with line + mod route_area_tests { + use super::*; + + #[test] + fn get_route_areas_in_square() { + let rect = construct_custom_box("0", "0", "4", "4"); + let root = RootBox::new(1, rect, coord("1")); + let first_point = Point2D::new(coord("0.5"), + coord("0.5")); + let second_point = Point2D::new(coord("2.5"), + coord("2.5")); + let line = Line::new(first_point, second_point); + let areas = line.get_route_areas(root); + assert_eq!(areas, vec![1, 5, 9, 2, 6, 10, 3, 7, 11]); + } + #[test] + fn get_route_areas_in_line() { + let rect = construct_custom_box("0", "0", "4", "4"); + let root = RootBox::new(1, rect, coord("1")); + let first_point = Point2D::new(coord("0.1"), + coord("0.1")); + let second_point = Point2D::new(coord("3.1"), + coord("0.2")); + let line = Line::new(first_point, second_point); + let areas = line.get_route_areas(root); + assert_eq!(areas, vec![1, 2, 3, 4]); + } + #[test] + fn get_route_single_area() { + let rect = construct_custom_box("0", "0", "4", "4"); + let root = RootBox::new(1, rect, coord("1")); + let first_point = Point2D::new(coord("0.1"), + coord("0.1")); + let second_point = Point2D::new(coord("0.3"), + coord("0.2")); + let line = Line::new(first_point, second_point); + let areas = line.get_route_areas(root); + assert_eq!(areas, vec![1]); + } } - #[test] - fn get_route_areas_in_line() { - let rect = construct_custom_box("0", "0", "4", "4"); - let root = RootBox::new(1, rect, coord("1")); - let first_point = Point2D::new(coord("0.1"), - coord("0.1")); - let second_point = Point2D::new(coord("3.1"), - coord("0.2")); - let line = Line::new(first_point, second_point); - let areas = line.get_route_areas(root); - assert_eq!(areas, vec![1, 2, 3, 4]); - } + // In this section we try crossing different lines + mod crossing_line_tests { + use super::*; + + #[test] + fn lines_cross() { + let a_first_point: Point2D = Point2D::new(coord("0.1"), + coord("0.1")); + let a_second_point: Point2D = Point2D::new(coord("1"), + coord("1")); + let b_first_point: Point2D = Point2D::new(coord("0.1"), + coord("1")); + let b_second_point: Point2D = Point2D::new(coord("1"), + coord("0.2")); + let a = Line::new(a_first_point, a_second_point); + let b = Line::new(b_first_point, b_second_point); + assert!(a.is_lines_cross(b)); + } - #[test] - fn get_route_single_area() { - let rect = construct_custom_box("0", "0", "4", "4"); - let root = RootBox::new(1, rect, coord("1")); - let first_point = Point2D::new(coord("0.1"), - coord("0.1")); - let second_point = Point2D::new(coord("0.3"), - coord("0.2")); - let line = Line::new(first_point, second_point); - let areas = line.get_route_areas(root); - assert_eq!(areas, vec![1]); - } + #[test] + fn long_lines_cross() { + let a_first_point: Point2D = Point2D::new(coord("10"), + coord("0.1")); + let a_second_point: Point2D = Point2D::new(coord("10"), + coord("10")); + let b_first_point: Point2D = Point2D::new(coord("5"), + coord("5")); + let b_second_point: Point2D = Point2D::new(coord("15"), + coord("5")); + let a = Line::new(a_first_point, a_second_point); + let b = Line::new(b_first_point, b_second_point); + assert!(a.is_lines_cross(b)); + } - #[test] - fn lines_cross() { - let a_first_point: Point2D = Point2D::new(coord("0.1"), - coord("0.1")); - let a_second_point: Point2D = Point2D::new(coord("1"), - coord("1")); - let b_first_point: Point2D = Point2D::new(coord("0.1"), - coord("1")); - let b_second_point: Point2D = Point2D::new(coord("1"), - coord("0.2")); - let a = Line::new(a_first_point, a_second_point); - let b = Line::new(b_first_point, b_second_point); - assert!(a.is_lines_cross(b)); - } + #[test] + fn lines_do_not_cross() { + let a_first_point: Point2D = Point2D::new(coord("0.1"), + coord("0.1")); + let a_second_point: Point2D = Point2D::new(coord("1"), + coord("1")); + let b_first_point: Point2D = Point2D::new(coord("1.3"), + coord("1.3")); + let b_second_point: Point2D = Point2D::new(coord("2"), + coord("4")); + let a = Line::new(a_first_point, a_second_point); + let b = Line::new(b_first_point, b_second_point); + assert!(!a.is_lines_cross(b)); + } - #[test] - fn long_lines_cross() { - let a_first_point: Point2D = Point2D::new(coord("10"), - coord("0.1")); - let a_second_point: Point2D = Point2D::new(coord("10"), - coord("10")); - let b_first_point: Point2D = Point2D::new(coord("5"), - coord("5")); - let b_second_point: Point2D = Point2D::new(coord("15"), - coord("5")); - let a = Line::new(a_first_point, a_second_point); - let b = Line::new(b_first_point, b_second_point); - assert!(a.is_lines_cross(b)); - } + #[test] + fn lines_cross_at_high_angle() { + let a_first_point: Point2D = Point2D::new(coord("10"), + coord("0.1")); + let a_second_point: Point2D = Point2D::new(coord("10"), + coord("10")); + let b_first_point: Point2D = Point2D::new(coord("9.8"), + coord("0.1")); + let b_second_point: Point2D = Point2D::new(coord("10.1"), + coord("10")); + let a = Line::new(a_first_point, a_second_point); + let b = Line::new(b_first_point, b_second_point); + assert!(a.is_lines_cross(b)); + } - #[test] - fn lines_do_not_cross() { - let a_first_point: Point2D = Point2D::new(coord("0.1"), - coord("0.1")); - let a_second_point: Point2D = Point2D::new(coord("1"), - coord("1")); - let b_first_point: Point2D = Point2D::new(coord("1.3"), - coord("1.3")); - let b_second_point: Point2D = Point2D::new(coord("2"), - coord("4")); - let a = Line::new(a_first_point, a_second_point); - let b = Line::new(b_first_point, b_second_point); - assert!(!a.is_lines_cross(b)); + #[test] + fn line_cross_rect() { + // It is becoming kinda long, but there's a lot of coords in use + let a_first_point: Point2D = Point2D::new(coord("0.1"), + coord("0.1")); + let a_second_point: Point2D = Point2D::new(coord("1"), + coord("1")); + let b_first_point: Point2D = Point2D::new(coord("1.3"), + coord("1.3")); + let b_second_point: Point2D = Point2D::new(coord("2"), + coord("4")); + let a = Line::new(a_first_point, a_second_point); + let b = Line::new(b_first_point, b_second_point); + assert!(!a.is_lines_cross(b)); + } } } From 53ad9ed31999250af566c23ad5cb1da1fd06e7da Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Wed, 21 Jul 2021 17:11:04 +0300 Subject: [PATCH 19/29] [ADD] ALl unit-tests for Line<> --- pallets/ds-maps/src/lib.rs | 100 +++++++++++++++++++++++++---------- pallets/ds-maps/src/tests.rs | 2 +- 2 files changed, 73 insertions(+), 29 deletions(-) diff --git a/pallets/ds-maps/src/lib.rs b/pallets/ds-maps/src/lib.rs index d28a254..89dacbe 100644 --- a/pallets/ds-maps/src/lib.rs +++ b/pallets/ds-maps/src/lib.rs @@ -264,7 +264,6 @@ impl< // (point_0.lon.try_into() * point_1.lat.try_into()) - // (point_1.lon.try_into() * point_0.lat.try_into()) // ).try_from(); - // TODO if point_0.lat > point_1.lat, swap points Line { // a: a, // b: b, @@ -323,11 +322,11 @@ impl< let south_line = Line::new(south_east, rect.south_west); let rect_lines = vec![west_line, north_line, east_line, south_line]; - let mut crossed = false; + for line in rect_lines.iter() { - crossed = self.is_lines_cross(*line); + if self.is_lines_cross(*line) { return true; } } - crossed + false } // TODO change BigCoord::default to custom epsilon in Trait pub fn is_lines_cross(&self, line: Line) -> bool { @@ -373,7 +372,7 @@ impl< #[cfg(test)] mod line_tests { use super::*; - use crate::tests::{construct_custom_box, coord, Coord}; + use crate::tests::{construct_custom_box, construct_testing_rect, coord, Coord}; // TODO draw rect in ascii as an illustration // In this section we try test calculations for intersecting areas with line @@ -426,13 +425,14 @@ mod line_tests { #[test] fn lines_cross() { + // I'm not sure, why do i have to specify variable type in each fn let a_first_point: Point2D = Point2D::new(coord("0.1"), coord("0.1")); - let a_second_point: Point2D = Point2D::new(coord("1"), + let a_second_point = Point2D::new(coord("1"), coord("1")); - let b_first_point: Point2D = Point2D::new(coord("0.1"), + let b_first_point = Point2D::new(coord("0.1"), coord("1")); - let b_second_point: Point2D = Point2D::new(coord("1"), + let b_second_point = Point2D::new(coord("1"), coord("0.2")); let a = Line::new(a_first_point, a_second_point); let b = Line::new(b_first_point, b_second_point); @@ -443,11 +443,11 @@ mod line_tests { fn long_lines_cross() { let a_first_point: Point2D = Point2D::new(coord("10"), coord("0.1")); - let a_second_point: Point2D = Point2D::new(coord("10"), + let a_second_point = Point2D::new(coord("10"), coord("10")); - let b_first_point: Point2D = Point2D::new(coord("5"), + let b_first_point = Point2D::new(coord("5"), coord("5")); - let b_second_point: Point2D = Point2D::new(coord("15"), + let b_second_point = Point2D::new(coord("15"), coord("5")); let a = Line::new(a_first_point, a_second_point); let b = Line::new(b_first_point, b_second_point); @@ -458,11 +458,11 @@ mod line_tests { fn lines_do_not_cross() { let a_first_point: Point2D = Point2D::new(coord("0.1"), coord("0.1")); - let a_second_point: Point2D = Point2D::new(coord("1"), + let a_second_point = Point2D::new(coord("1"), coord("1")); - let b_first_point: Point2D = Point2D::new(coord("1.3"), + let b_first_point = Point2D::new(coord("1.3"), coord("1.3")); - let b_second_point: Point2D = Point2D::new(coord("2"), + let b_second_point = Point2D::new(coord("2"), coord("4")); let a = Line::new(a_first_point, a_second_point); let b = Line::new(b_first_point, b_second_point); @@ -473,11 +473,11 @@ mod line_tests { fn lines_cross_at_high_angle() { let a_first_point: Point2D = Point2D::new(coord("10"), coord("0.1")); - let a_second_point: Point2D = Point2D::new(coord("10"), + let a_second_point = Point2D::new(coord("10"), coord("10")); - let b_first_point: Point2D = Point2D::new(coord("9.8"), + let b_first_point = Point2D::new(coord("9.8"), coord("0.1")); - let b_second_point: Point2D = Point2D::new(coord("10.1"), + let b_second_point = Point2D::new(coord("10.1"), coord("10")); let a = Line::new(a_first_point, a_second_point); let b = Line::new(b_first_point, b_second_point); @@ -485,19 +485,63 @@ mod line_tests { } #[test] - fn line_cross_rect() { + fn line_through_rect() { // It is becoming kinda long, but there's a lot of coords in use - let a_first_point: Point2D = Point2D::new(coord("0.1"), - coord("0.1")); - let a_second_point: Point2D = Point2D::new(coord("1"), + let rect_first_point: Point2D = Point2D::new(coord("1"), coord("1")); - let b_first_point: Point2D = Point2D::new(coord("1.3"), - coord("1.3")); - let b_second_point: Point2D = Point2D::new(coord("2"), - coord("4")); - let a = Line::new(a_first_point, a_second_point); - let b = Line::new(b_first_point, b_second_point); - assert!(!a.is_lines_cross(b)); + let rect_second_point = Point2D::new(coord("2"), + coord("15")); + let first_point = Point2D::new(coord("0.1"), + coord("3")); + let second_point = Point2D::new(coord("4"), + coord("3")); + let rect = Rect2D::new(rect_first_point, rect_second_point); + let line = Line::new(first_point, second_point); + assert!(line.intersects_rect(rect)); + } + + #[test] + fn line_partially_inside_rect() { + let rect_first_point: Point2D = Point2D::new(coord("1"), + coord("1")); + let rect_second_point = Point2D::new(coord("2"), + coord("15")); + let first_point = Point2D::new(coord("0.1"), + coord("3")); + let second_point = Point2D::new(coord("1.5"), + coord("3")); + let rect = Rect2D::new(rect_first_point, rect_second_point); + let line = Line::new(first_point, second_point); + assert!(line.intersects_rect(rect)); + } + + + #[test] + fn line_outside_rect() { + let rect_first_point: Point2D = Point2D::new(coord("1"), + coord("1")); + let rect_second_point = Point2D::new(coord("2"), + coord("15")); + let first_point = Point2D::new(coord("3"), + coord("3")); + let second_point = Point2D::new(coord("4"), + coord("6")); + let rect = Rect2D::new(rect_first_point, rect_second_point); + let line = Line::new(first_point, second_point); + assert!(!line.intersects_rect(rect)); + } + + #[test] + fn line_rect_gps_coords() { + let first_point = Point2D::new(coord("55.392"), + coord("37.386")); + let second_point = Point2D::new(coord("55.396"), + coord("37.386")); + let rect = construct_testing_rect(); + let intersecting_line = Line::new(first_point, second_point); + assert!(intersecting_line.intersects_rect(rect)); + let third_point = Point2D::new(coord("55.393"), coord("37.386")); + let outside_line = Line::new(first_point, third_point); } } } diff --git a/pallets/ds-maps/src/tests.rs b/pallets/ds-maps/src/tests.rs index f8aa9da..ff1b518 100644 --- a/pallets/ds-maps/src/tests.rs +++ b/pallets/ds-maps/src/tests.rs @@ -95,7 +95,7 @@ pub fn construct_custom_box(sw_lat: &str, sw_lon: &str, ne_lat: &str, ne_lon: &s Box3D::new(south_west, north_east) } -fn construct_testing_rect() -> Rect2D { +pub fn construct_testing_rect() -> Rect2D { let south_west = Point2D::new(coord("55.395"), coord("37.385")); let north_east = Point2D::new(coord("55.396"), From 6c2718331d55b377feec4fd4986927112758f1cb Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Wed, 21 Jul 2021 18:34:32 +0300 Subject: [PATCH 20/29] [FIX] test assertion --- pallets/ds-maps/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pallets/ds-maps/src/lib.rs b/pallets/ds-maps/src/lib.rs index 89dacbe..c79783d 100644 --- a/pallets/ds-maps/src/lib.rs +++ b/pallets/ds-maps/src/lib.rs @@ -542,6 +542,7 @@ mod line_tests { assert!(intersecting_line.intersects_rect(rect)); let third_point = Point2D::new(coord("55.393"), coord("37.386")); let outside_line = Line::new(first_point, third_point); + assert!(!outside_line.intersects_rect(rect)); } } } @@ -1771,17 +1772,16 @@ decl_module! { // We receive all areas, containing list of zones which could be intersected let route_areas: Vec = route_line.get_route_areas(root); // Loop through areas, check each existing zone - let mut clear_path = true; for area_id in route_areas.iter() { if AreaData::contains_key(root_id, area_id) { let mut zone_id = Self::pack_index(root_id, *area_id, 0); while RedZones::::contains_key(zone_id) { - clear_path = route_line.intersects_rect(RedZones::::get(zone_id).rect); + // TODO ask about ensure!() usage in cycle + ensure!(!route_line.intersects_rect(RedZones::::get(zone_id).rect), Error::::RouteIntersectRedZone); zone_id += 1; } } } - ensure!(clear_path, Error::::RouteIntersectRedZone); Self::deposit_event(RawEvent::RouteAdded( start_waypoint.location, end_waypoint.location, start_time, arrival_time, root_id, who From 461087c37172cf4096e0ed46c54faf36ae1668ce Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Wed, 21 Jul 2021 18:34:51 +0300 Subject: [PATCH 21/29] [ADD] it tests for routes --- pallets/ds-maps/src/tests.rs | 131 +++++++++++++++++++++++++++++++++-- 1 file changed, 125 insertions(+), 6 deletions(-) diff --git a/pallets/ds-maps/src/tests.rs b/pallets/ds-maps/src/tests.rs index ff1b518..d05b15f 100644 --- a/pallets/ds-maps/src/tests.rs +++ b/pallets/ds-maps/src/tests.rs @@ -32,17 +32,17 @@ use sp_std::str::FromStr; // =0.01 +-> O----+----+----+---------------+----+ // Origin(55.37, 37.37) // -// +// (wp1, wp2) is a testing waypoints // Area 58 (55.400, 37.390) // +-----------------------------------o // | | -// | | -// | | +// | (x) | +// | (wp2) | // | rect2D | // | +--------o(55.396, | // | | | 37.386) | -// | |testing | | -// | | zone | | +// | | (x) | | +// | | (wp1) | | // | | | | // | o--------+ | // | (55.395, | @@ -893,7 +893,8 @@ fn it_add_route_wrong_waypoints() { single_waypoint, ROOT_ID, ), - Error::InvalidData); + Error::InvalidData + ); let waypoints = construct_custom_waypoints( "55.395", "37.385", @@ -909,3 +910,121 @@ fn it_add_route_wrong_waypoints() { ); }); } + +// There is no zones to block the way, so it'll pass +#[test] +fn it_add_route_without_zones() { + new_test_ext().execute_with(|| { + assert_ok!( + DSAccountsModule::account_add( + Origin::signed(ADMIN_ACCOUNT_ID), + REGISTRAR_1_ACCOUNT_ID, + super::REGISTRAR_ROLE + )); + assert_ok!( + DSMapsModule::root_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + construct_testing_box(), + coord(DELTA), + )); + let waypoints = construct_testing_waypoints(); + assert_ok!( + DSMapsModule::route_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + waypoints, + ROOT_ID, + )); + }); +} + +// This test contain two zones, one is intersecting route, and another is not +#[test] +fn it_add_route_one_area() { + new_test_ext().execute_with(|| { + assert_ok!( + DSAccountsModule::account_add( + Origin::signed(ADMIN_ACCOUNT_ID), + REGISTRAR_1_ACCOUNT_ID, + super::REGISTRAR_ROLE + )); + assert_ok!( + DSMapsModule::root_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + construct_testing_box(), + coord(DELTA), + )); + let waypoints = construct_testing_waypoints(); + // This one do not block the way, and test will pass + assert_ok!(DSMapsModule::zone_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + construct_custom_rect("55.391", "37.381", "55.392", "37.382"), + DEFAULT_HEIGHT, + ROOT_ID, + )); + assert_ok!( + DSMapsModule::route_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + waypoints.clone(), + ROOT_ID, + )); + // But this one will fail, as it blocks the way + assert_ok!(DSMapsModule::zone_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + construct_testing_rect(), + DEFAULT_HEIGHT, + ROOT_ID, + )); + assert_noop!( + DSMapsModule::route_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + waypoints, + ROOT_ID, + ), Error::RouteIntersectRedZone + ); + }); +} +#[test] +fn it_add_route_multiple_areas() { + new_test_ext().execute_with(|| { + assert_ok!( + DSAccountsModule::account_add( + Origin::signed(ADMIN_ACCOUNT_ID), + REGISTRAR_1_ACCOUNT_ID, + super::REGISTRAR_ROLE + )); + assert_ok!( + DSMapsModule::root_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + construct_testing_box(), + coord(DELTA), + )); + // Route starts at 1 area, and ends in testing rect + let waypoints = construct_custom_waypoints("55.371", "37.371", "55.396", "37.386", 10, 20); + assert_ok!(DSMapsModule::zone_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + construct_custom_rect("55.391", "37.381", "55.392", "37.382"), + DEFAULT_HEIGHT, + ROOT_ID, + )); + assert_ok!( + DSMapsModule::route_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + waypoints.clone(), + ROOT_ID, + )); + // But this one will + assert_ok!(DSMapsModule::zone_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + construct_testing_rect(), + DEFAULT_HEIGHT, + ROOT_ID, + )); + assert_noop!( + DSMapsModule::route_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + waypoints, + ROOT_ID, + ), Error::RouteIntersectRedZone + ); + }); +} From 094bc68af223cff915259989094c7bfdc8a1aac8 Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Wed, 21 Jul 2021 18:37:59 +0300 Subject: [PATCH 22/29] [REMOVE] unused route structure --- pallets/ds-maps/src/lib.rs | 10 ---------- pallets/ds-maps/src/mock.rs | 1 - runtime/src/lib.rs | 1 - 3 files changed, 12 deletions(-) diff --git a/pallets/ds-maps/src/lib.rs b/pallets/ds-maps/src/lib.rs index c79783d..c9482e4 100644 --- a/pallets/ds-maps/src/lib.rs +++ b/pallets/ds-maps/src/lib.rs @@ -232,13 +232,6 @@ impl Waypoint { } } -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[derive(Encode, Decode, Clone, Default, Debug, PartialEq, Eq)] -pub struct Route { - pub route: Vec>, - pub owner: OwnerId, -} - // Actually, this is line section, so we need limits #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(Encode, Decode, Clone, Copy, Default, Debug, PartialEq, Eq)] @@ -1319,8 +1312,6 @@ pub trait Trait: accounts::Trait { + Default + PartialOrd + Copy; - - type RouteId: Default + Parameter + Copy; type RawCoord: Default + Parameter @@ -1365,7 +1356,6 @@ decl_storage! { pub type PageOf = Page<::Coord>; pub type RootBoxOf = RootBox<::Coord>; pub type ZoneOf = Zone<::Coord>; -pub type RouteOf = Route<::Coord, ::AccountId, ::Moment>; // Pallets use events to inform users when important changes are made. // https://substrate.dev/docs/en/knowledgebase/runtime/events diff --git a/pallets/ds-maps/src/mock.rs b/pallets/ds-maps/src/mock.rs index 9713ee8..51e546b 100644 --- a/pallets/ds-maps/src/mock.rs +++ b/pallets/ds-maps/src/mock.rs @@ -108,7 +108,6 @@ impl Trait for Test { type WeightInfo = (); type Coord = I10F22; type BigCoord = I42F22; - type RouteId = u32; type RawCoord = i32; type MaxBuildingsInArea = MaxBuildingsInArea; type MaxHeight = MaxHeight; diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 8f50510..9b43d2c 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -313,7 +313,6 @@ impl pallet_ds_maps::Trait for Runtime { type Event = Event; type WeightInfo = (); type RawCoord = i32; - type RouteId = u32; type Coord = I10F22; type BigCoord = I42F22; type MaxBuildingsInArea = MaxBuildingsInArea; From f1740fdca09da55c930ccb5d22b71cb67276851f Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Wed, 21 Jul 2021 21:00:38 +0300 Subject: [PATCH 23/29] [ADD] integrational tests for routes --- pallets/ds-maps/src/tests.rs | 106 +++++++++++++++++++++++++++-------- 1 file changed, 83 insertions(+), 23 deletions(-) diff --git a/pallets/ds-maps/src/tests.rs b/pallets/ds-maps/src/tests.rs index d05b15f..900ee9e 100644 --- a/pallets/ds-maps/src/tests.rs +++ b/pallets/ds-maps/src/tests.rs @@ -12,7 +12,7 @@ use substrate_fixed::types::I10F22; use sp_std::str::FromStr; // Explanation for all hardcoded values down here -// Root P2(55.92, 37.90) +// Root P2(55.921, 37.901) // +----+-------------------------+----O // |2861| |2915| // | | Area 2861 | | @@ -30,7 +30,7 @@ use sp_std::str::FromStr; // | | 1 | 2 | 3 | | 55 | // delta | | | | | | | // =0.01 +-> O----+----+----+---------------+----+ -// Origin(55.37, 37.37) +// Origin(55.371, 37.371) // // (wp1, wp2) is a testing waypoints // Area 58 (55.400, 37.390) @@ -955,11 +955,12 @@ fn it_add_route_one_area() { )); let waypoints = construct_testing_waypoints(); // This one do not block the way, and test will pass - assert_ok!(DSMapsModule::zone_add( - Origin::signed(REGISTRAR_1_ACCOUNT_ID), - construct_custom_rect("55.391", "37.381", "55.392", "37.382"), - DEFAULT_HEIGHT, - ROOT_ID, + assert_ok!( + DSMapsModule::zone_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + construct_custom_rect("55.391", "37.381", "55.392", "37.382"), + DEFAULT_HEIGHT, + ROOT_ID, )); assert_ok!( DSMapsModule::route_add( @@ -968,11 +969,12 @@ fn it_add_route_one_area() { ROOT_ID, )); // But this one will fail, as it blocks the way - assert_ok!(DSMapsModule::zone_add( - Origin::signed(REGISTRAR_1_ACCOUNT_ID), - construct_testing_rect(), - DEFAULT_HEIGHT, - ROOT_ID, + assert_ok!( + DSMapsModule::zone_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + construct_testing_rect(), + DEFAULT_HEIGHT, + ROOT_ID, )); assert_noop!( DSMapsModule::route_add( @@ -983,6 +985,7 @@ fn it_add_route_one_area() { ); }); } + #[test] fn it_add_route_multiple_areas() { new_test_ext().execute_with(|| { @@ -999,12 +1002,14 @@ fn it_add_route_multiple_areas() { coord(DELTA), )); // Route starts at 1 area, and ends in testing rect - let waypoints = construct_custom_waypoints("55.371", "37.371", "55.396", "37.386", 10, 20); - assert_ok!(DSMapsModule::zone_add( - Origin::signed(REGISTRAR_1_ACCOUNT_ID), - construct_custom_rect("55.391", "37.381", "55.392", "37.382"), - DEFAULT_HEIGHT, - ROOT_ID, + let waypoints = construct_custom_waypoints("55.373", "37.373", "55.396", "37.386", 10, 20); + // This one located far from our route, so no intersection + assert_ok!( + DSMapsModule::zone_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + construct_custom_rect("55.411", "37.372", "55.416", "37.375"), + DEFAULT_HEIGHT, + ROOT_ID, )); assert_ok!( DSMapsModule::route_add( @@ -1013,11 +1018,12 @@ fn it_add_route_multiple_areas() { ROOT_ID, )); // But this one will - assert_ok!(DSMapsModule::zone_add( - Origin::signed(REGISTRAR_1_ACCOUNT_ID), - construct_testing_rect(), - DEFAULT_HEIGHT, - ROOT_ID, + assert_ok!( + DSMapsModule::zone_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + construct_testing_rect(), + DEFAULT_HEIGHT, + ROOT_ID, )); assert_noop!( DSMapsModule::route_add( @@ -1028,3 +1034,57 @@ fn it_add_route_multiple_areas() { ); }); } + +// Here we show that zone exist, and then we remove it from storage +#[test] +fn it_add_route_and_remove_zone() { + new_test_ext().execute_with(|| { + assert_ok!( + DSAccountsModule::account_add( + Origin::signed(ADMIN_ACCOUNT_ID), + REGISTRAR_1_ACCOUNT_ID, + super::REGISTRAR_ROLE + ) + ); + assert_ok!( + DSMapsModule::root_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + construct_testing_box(), + coord(DELTA), + ) + ); + let waypoints = construct_custom_waypoints("55.373", "37.373", "55.396", "37.386", 10, 20); + assert_ok!( + DSMapsModule::zone_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + construct_testing_rect(), + DEFAULT_HEIGHT, + ROOT_ID, + ) + ); + // Can't add it, zone is blocking the way + assert_noop!( + DSMapsModule::route_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + waypoints.clone(), + ROOT_ID, + ), + Error::RouteIntersectRedZone + ); + let zone_index = DSMapsModule::pack_index(ROOT_ID, AREA_ID, 0); + // Now we remove it, and path is clear + assert_ok!( + DSMapsModule::zone_remove( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + zone_index, + ) + ); + assert_ok!( + DSMapsModule::route_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + waypoints, + ROOT_ID, + ) + ); + }); +} \ No newline at end of file From 18e49353b389872cb5f3cd43989b163fe2f3fb2a Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Wed, 21 Jul 2021 21:43:39 +0300 Subject: [PATCH 24/29] [FIX] linter notes --- pallets/ds-maps/src/lib.rs | 37 ++++++++++++------------------------ pallets/ds-maps/src/tests.rs | 3 +-- 2 files changed, 13 insertions(+), 27 deletions(-) diff --git a/pallets/ds-maps/src/lib.rs b/pallets/ds-maps/src/lib.rs index c9482e4..2301496 100644 --- a/pallets/ds-maps/src/lib.rs +++ b/pallets/ds-maps/src/lib.rs @@ -17,7 +17,9 @@ use frame_support::{ use sp_std::{ str::FromStr, marker::PhantomData, - vec, + vec, + mem::swap, + cmp::{max, min} }; use dsky_utils::{CastToType, FromRaw, IntDiv, Signed, ToBigCoord, FromBigCoord}; @@ -245,7 +247,7 @@ pub struct Line { } impl< - Coord: ToBigCoord + PartialOrd + Signed + IntDiv + Mul + Sub + Add + Div + Copy, + Coord: ToBigCoord + Ord + Signed + IntDiv + Mul + Sub + Add + Div + Copy, BigCoord: Mul + Sub + FromBigCoord + Copy + Default + PartialOrd > Line { pub fn new(point_0: Point2D, point_1: Point2D) -> Self { @@ -275,9 +277,7 @@ impl< if start_area == end_area {return vec![start_area]} let mut output = Vec::new(); let delta = root.delta; - // let lat_area = (self.end_point.lat - self.start_point.lat) / root.delta; - // // TODO handle negative substraction - // let lon_area = (self.end_point.lon - self.start_point.lon) / root.delta; + // TODO handle negative substraction let start_vector = root.bounding_box.south_west.project().get_distance_vector(self.start_point); let start_row = start_vector.lat.integer_division_u16(root.delta) + 1; @@ -303,6 +303,7 @@ impl< } output } + // Basically we split rect to 4 lines(maybe 2 is enough?), and check each one for intersection pub fn intersects_rect(&self, rect: Rect2D) -> bool { // true @@ -333,18 +334,11 @@ impl< // Okay, this is just unreadable code fn intersect_1(_a: Coord, _b: Coord, _c: Coord, _d: Coord) -> bool { - let (mut a, mut b, mut c, mut d) = (_a.clone(), _b.clone(), _c.clone(), _d.clone()); - if a > b { - let temp = a; - a = b; - b = temp; - } - if c > d { - let temp = c; - c = d; - d = temp; - } - Line::max(a,c) <= Line::min(b,d) + let (mut a, mut b, mut c, mut d) = (_a, _b, _c, _d); + if a > b { swap(&mut a, &mut b) } + if c > d { swap(&mut c, &mut d) } + + max(a,c) <= min(b,d) } // Area of a triangle @@ -352,14 +346,6 @@ impl< (b.lat.try_into() - a.lat.try_into()) * (c.lon.try_into() - a.lon.try_into()) - (b.lon.try_into() - a.lon.try_into()) * (c.lat.try_into() - a.lat.try_into()) } - - fn max(a: Coord, b: Coord) -> Coord { - if a > b {a} else {b} - } - - fn min(a: Coord, b: Coord) -> Coord { - if a > b {b} else {a} - } } #[cfg(test)] @@ -1290,6 +1276,7 @@ pub trait Trait: accounts::Trait { + Parameter + Copy + PartialOrd + + Ord + PartialEq + FromStr + Signed diff --git a/pallets/ds-maps/src/tests.rs b/pallets/ds-maps/src/tests.rs index 900ee9e..0000f7b 100644 --- a/pallets/ds-maps/src/tests.rs +++ b/pallets/ds-maps/src/tests.rs @@ -775,10 +775,9 @@ fn it_dispatchable_get_root_index() { )); // 55.395 - 232343470 // 37.385 - 156804055 - // TODO add explanation, for why this is true let root_id = DSMapsModule::get_root_index([232343470, 156804055]); assert_eq!(root_id, 1558542996706168526); - // For now, this proof will do. We can see, that by this index we get valid, active root + // Proof, that everything is right: by this index we get active root let root = DSMapsModule::root_box_data(root_id); assert!(root.is_active()); }); From c7cb1591bea58aea491e71315960125508617c43 Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Thu, 22 Jul 2021 19:58:13 +0300 Subject: [PATCH 25/29] [ADD] Added fn weight --- dsky-utils/src/lib.rs | 11 ++++ pallets/ds-maps/src/default_weight.rs | 4 +- pallets/ds-maps/src/lib.rs | 32 +++++++----- pallets/ds-maps/src/tests.rs | 72 ++++++++++++++++++++++++++- 4 files changed, 103 insertions(+), 16 deletions(-) diff --git a/dsky-utils/src/lib.rs b/dsky-utils/src/lib.rs index ea2c890..065cca3 100644 --- a/dsky-utils/src/lib.rs +++ b/dsky-utils/src/lib.rs @@ -31,6 +31,10 @@ pub trait FromBigCoord { fn try_from(self) -> Self::Output; } +pub trait GetEpsilon { + fn get_epsilon() -> Self; +} + // Here comes the implementations // Want to change Coord type => impl trait for it here impl IntDiv for I10F22 { @@ -83,10 +87,17 @@ impl ToBigCoord for I10F22 { self.into() } } + // TODO handle possible errors through checked_from_fixed() impl FromBigCoord for I42F22 { type Output = I10F22; fn try_from(self) -> Self::Output { I10F22::from_fixed(self) } +} + +impl GetEpsilon for I42F22 { + fn get_epsilon() -> I42F22 { + I42F22::from_num(0.00001f64) + } } \ No newline at end of file diff --git a/pallets/ds-maps/src/default_weight.rs b/pallets/ds-maps/src/default_weight.rs index 6354bd7..6aca0f3 100644 --- a/pallets/ds-maps/src/default_weight.rs +++ b/pallets/ds-maps/src/default_weight.rs @@ -16,5 +16,7 @@ impl crate::WeightInfo for () { fn change_area_type() -> Weight { 100_000_u64.saturating_add(DbWeight::get().writes(1)) } - + fn route_add() -> Weight { + 100_000_u64.saturating_add(DbWeight::get().writes(1)) + } } diff --git a/pallets/ds-maps/src/lib.rs b/pallets/ds-maps/src/lib.rs index 2301496..13e6ab9 100644 --- a/pallets/ds-maps/src/lib.rs +++ b/pallets/ds-maps/src/lib.rs @@ -22,7 +22,7 @@ use sp_std::{ cmp::{max, min} }; -use dsky_utils::{CastToType, FromRaw, IntDiv, Signed, ToBigCoord, FromBigCoord}; +use dsky_utils::{CastToType, FromRaw, IntDiv, Signed, ToBigCoord, FromBigCoord, GetEpsilon}; use frame_system::ensure_signed; use pallet_ds_accounts as accounts; use accounts::REGISTRAR_ROLE; @@ -248,7 +248,7 @@ pub struct Line { impl< Coord: ToBigCoord + Ord + Signed + IntDiv + Mul + Sub + Add + Div + Copy, - BigCoord: Mul + Sub + FromBigCoord + Copy + Default + PartialOrd + BigCoord: Mul + Sub + FromBigCoord + Copy + PartialOrd + GetEpsilon > Line { pub fn new(point_0: Point2D, point_1: Point2D) -> Self { // Form coefficients (y1 - y2)x + (x2 - x1)y + (x1y2 - x2y1) = 0 @@ -322,18 +322,21 @@ impl< } false } - // TODO change BigCoord::default to custom epsilon in Trait + // This method allows to proof intersection, but can't find intersection points pub fn is_lines_cross(&self, line: Line) -> bool { - Line::intersect_1(self.start_point.lat, self.end_point.lat, line.start_point.lat, line.end_point.lat) && - Line::intersect_1(self.start_point.lon, self.end_point.lon, line.start_point.lon, line.end_point.lon) && + // Checking if the segments are collinear + Line::projection_intersect(self.start_point.lat, self.end_point.lat, line.start_point.lat, line.end_point.lat) && + Line::projection_intersect(self.start_point.lon, self.end_point.lon, line.start_point.lon, line.end_point.lon) && + // Check, that points of AB lies on different sides of CD, and vice versa + // TODO basically we need only signs and zero-condition, consider refactor ((Line::area(self.start_point, self.end_point, line.start_point) * - Line::area(self.start_point, self.end_point, line.end_point)) <= BigCoord::default()) && + Line::area(self.start_point, self.end_point, line.end_point)) <= BigCoord::get_epsilon()) && ((Line::area(line.start_point, line.end_point, self.start_point) * - Line::area(line.start_point, line.end_point, self.end_point)) <= BigCoord::default()) + Line::area(line.start_point, line.end_point, self.end_point)) <= BigCoord::get_epsilon()) } - // Okay, this is just unreadable code - fn intersect_1(_a: Coord, _b: Coord, _c: Coord, _d: Coord) -> bool { + // Checks for projections not to intersect + fn projection_intersect(_a: Coord, _b: Coord, _c: Coord, _d: Coord) -> bool { let (mut a, mut b, mut c, mut d) = (_a, _b, _c, _d); if a > b { swap(&mut a, &mut b) } if c > d { swap(&mut c, &mut d) } @@ -341,7 +344,8 @@ impl< max(a,c) <= min(b,d) } - // Area of a triangle + // BigCoord required, cause this math is out of bounds for Coord + // Signed area of a triangle, oriented clockwise (same as vector multiplication) fn area(a: Point2D, b: Point2D, c: Point2D) -> BigCoord { (b.lat.try_into() - a.lat.try_into()) * (c.lon.try_into() - a.lon.try_into()) - (b.lon.try_into() - a.lon.try_into()) * (c.lat.try_into() - a.lat.try_into()) @@ -1295,10 +1299,11 @@ pub trait Trait: accounts::Trait { + Div + Mul + Add - + FromBigCoord + Default + PartialOrd - + Copy; + + Copy + + FromBigCoord + + GetEpsilon; type RawCoord: Default + Parameter @@ -1318,6 +1323,7 @@ pub trait WeightInfo { fn root_remove() -> Weight; fn zone_remove() -> Weight; fn change_area_type() -> Weight; + fn route_add() -> Weight; } decl_storage! { @@ -1721,7 +1727,7 @@ decl_module! { } /// creates new route for UAV - #[weight = ::WeightInfo::change_area_type()] + #[weight = ::WeightInfo::route_add()] pub fn route_add(origin, waypoints: Vec::Moment>>, root_id: RootId) -> dispatch::DispatchResult { diff --git a/pallets/ds-maps/src/tests.rs b/pallets/ds-maps/src/tests.rs index 0000f7b..2ce41d4 100644 --- a/pallets/ds-maps/src/tests.rs +++ b/pallets/ds-maps/src/tests.rs @@ -1034,7 +1034,7 @@ fn it_add_route_multiple_areas() { }); } -// Here we show that zone exist, and then we remove it from storage +// Here we add zone, and then we remove it from storage #[test] fn it_add_route_and_remove_zone() { new_test_ext().execute_with(|| { @@ -1071,7 +1071,75 @@ fn it_add_route_and_remove_zone() { Error::RouteIntersectRedZone ); let zone_index = DSMapsModule::pack_index(ROOT_ID, AREA_ID, 0); - // Now we remove it, and path is clear + // Now we remove it, clearing the path + assert_ok!( + DSMapsModule::zone_remove( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + zone_index, + ) + ); + assert_ok!( + DSMapsModule::route_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + waypoints, + ROOT_ID, + ) + ); + }); +} + +#[test] +fn it_add_lots_of_zones() { + new_test_ext().execute_with(|| { + assert_ok!( + DSAccountsModule::account_add( + Origin::signed(ADMIN_ACCOUNT_ID), + REGISTRAR_1_ACCOUNT_ID, + super::REGISTRAR_ROLE + ) + ); + assert_ok!( + DSMapsModule::root_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + construct_testing_box(), + coord(DELTA), + ) + ); + let delta: Coord = coord(DELTA); + let waypoints = construct_custom_waypoints("55.373", "37.373", "55.396", "37.386", 10, 20); + let mut testing_rect: Rect2D = construct_testing_rect(); + for n in 1..11 { + testing_rect.north_east.lon += delta; + testing_rect.south_west.lon += delta; + assert_ok!( + DSMapsModule::zone_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + testing_rect, + DEFAULT_HEIGHT, + ROOT_ID, + ) + ); + } + + assert_ok!( + DSMapsModule::zone_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + construct_testing_rect(), + DEFAULT_HEIGHT, + ROOT_ID, + ) + ); + // Can't add it, zone is blocking the way + assert_noop!( + DSMapsModule::route_add( + Origin::signed(REGISTRAR_1_ACCOUNT_ID), + waypoints.clone(), + ROOT_ID, + ), + Error::RouteIntersectRedZone + ); + let zone_index = DSMapsModule::pack_index(ROOT_ID, AREA_ID, 0); + // Now we remove it, clearing the path assert_ok!( DSMapsModule::zone_remove( Origin::signed(REGISTRAR_1_ACCOUNT_ID), From 02a9e89182180919c4cfc6c4a5029e5b9a3171ef Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Thu, 22 Jul 2021 20:37:34 +0300 Subject: [PATCH 26/29] [ADD] weights to mock --- pallets/ds-maps/src/lib.rs | 12 ++++++------ pallets/ds-maps/src/mock.rs | 3 +++ pallets/ds-maps/src/tests.rs | 17 ++++------------- 3 files changed, 13 insertions(+), 19 deletions(-) diff --git a/pallets/ds-maps/src/lib.rs b/pallets/ds-maps/src/lib.rs index 13e6ab9..796fd26 100644 --- a/pallets/ds-maps/src/lib.rs +++ b/pallets/ds-maps/src/lib.rs @@ -269,7 +269,7 @@ impl< } } - //TODO optimize fn to get ONLY intersected areas, for now it gets everythig from the rect + // This fn reuqires few days of research for proper work, this is unoptimized version pub fn get_route_areas(self, root: RootBox) -> Vec { let start_area = root.detect_intersected_area(self.start_point); let end_area = root.detect_intersected_area(self.end_point); @@ -277,7 +277,6 @@ impl< if start_area == end_area {return vec![start_area]} let mut output = Vec::new(); let delta = root.delta; - // TODO handle negative substraction let start_vector = root.bounding_box.south_west.project().get_distance_vector(self.start_point); let start_row = start_vector.lat.integer_division_u16(root.delta) + 1; @@ -289,7 +288,7 @@ impl< let mut current_point = self.start_point; let mut row_counter = start_row; - + // Iterating through all areas from start to end points while row_counter <= end_row { let mut column_counter = start_column; while column_counter <= end_column { @@ -1726,7 +1725,7 @@ decl_module! { Ok(()) } - /// creates new route for UAV + /// Creates new route for UAV #[weight = ::WeightInfo::route_add()] pub fn route_add(origin, waypoints: Vec::Moment>>, @@ -1736,8 +1735,8 @@ decl_module! { ensure!(>::account_is(&who, REGISTRAR_ROLE.into()), Error::::NotAuthorized); ensure!(RootBoxes::::contains_key(root_id), Error::::RootDoesNotExist); ensure!(waypoints.len() >= 2, Error::::InvalidData); - let start_waypoint = &waypoints[0]; - let end_waypoint = &waypoints[waypoints.len() - 1]; + let start_waypoint = &waypoints.first().unwrap(); + let end_waypoint = &waypoints.last().unwrap(); // Getting all time bounds let start_time = start_waypoint.arrival; let arrival_time = end_waypoint.arrival; @@ -1758,6 +1757,7 @@ decl_module! { for area_id in route_areas.iter() { if AreaData::contains_key(root_id, area_id) { let mut zone_id = Self::pack_index(root_id, *area_id, 0); + // Loop through zones, maybe add constraint to MaxBuildingsInArea while RedZones::::contains_key(zone_id) { // TODO ask about ensure!() usage in cycle ensure!(!route_line.intersects_rect(RedZones::::get(zone_id).rect), Error::::RouteIntersectRedZone); diff --git a/pallets/ds-maps/src/mock.rs b/pallets/ds-maps/src/mock.rs index 51e546b..bee2120 100644 --- a/pallets/ds-maps/src/mock.rs +++ b/pallets/ds-maps/src/mock.rs @@ -95,6 +95,9 @@ impl crate::WeightInfo for WeightInfo { fn change_area_type() -> Weight { <() as crate::WeightInfo>::change_area_type() } + fn route_add() -> Weight { + <() as crate::WeightInfo>::route_add() + } } // After researches, consider placing here max grid sizes diff --git a/pallets/ds-maps/src/tests.rs b/pallets/ds-maps/src/tests.rs index 2ce41d4..2389c51 100644 --- a/pallets/ds-maps/src/tests.rs +++ b/pallets/ds-maps/src/tests.rs @@ -1108,9 +1108,7 @@ fn it_add_lots_of_zones() { let delta: Coord = coord(DELTA); let waypoints = construct_custom_waypoints("55.373", "37.373", "55.396", "37.386", 10, 20); let mut testing_rect: Rect2D = construct_testing_rect(); - for n in 1..11 { - testing_rect.north_east.lon += delta; - testing_rect.south_west.lon += delta; + for _n in 1..11 { assert_ok!( DSMapsModule::zone_add( Origin::signed(REGISTRAR_1_ACCOUNT_ID), @@ -1119,17 +1117,10 @@ fn it_add_lots_of_zones() { ROOT_ID, ) ); + testing_rect.north_east.lon += delta; + testing_rect.south_west.lon += delta; } - - assert_ok!( - DSMapsModule::zone_add( - Origin::signed(REGISTRAR_1_ACCOUNT_ID), - construct_testing_rect(), - DEFAULT_HEIGHT, - ROOT_ID, - ) - ); - // Can't add it, zone is blocking the way + // Can't add it, one of this zones is blocking the way assert_noop!( DSMapsModule::route_add( Origin::signed(REGISTRAR_1_ACCOUNT_ID), From b5634a8d65466735219bcd7bc28840fd7d12ea57 Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Fri, 23 Jul 2021 17:43:53 +0300 Subject: [PATCH 27/29] [FIX] written few comments --- pallets/ds-maps/src/lib.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pallets/ds-maps/src/lib.rs b/pallets/ds-maps/src/lib.rs index 796fd26..177526c 100644 --- a/pallets/ds-maps/src/lib.rs +++ b/pallets/ds-maps/src/lib.rs @@ -269,7 +269,7 @@ impl< } } - // This fn reuqires few days of research for proper work, this is unoptimized version + // Realisation of Bresenham's line algorithm pub fn get_route_areas(self, root: RootBox) -> Vec { let start_area = root.detect_intersected_area(self.start_point); let end_area = root.detect_intersected_area(self.end_point); @@ -288,6 +288,7 @@ impl< let mut current_point = self.start_point; let mut row_counter = start_row; + // TODO fix inverted stepping // Iterating through all areas from start to end points while row_counter <= end_row { let mut column_counter = start_column; @@ -356,7 +357,9 @@ mod line_tests { use super::*; use crate::tests::{construct_custom_box, construct_testing_rect, coord, Coord}; // TODO draw rect in ascii as an illustration - + // +---+ + // | | + // +---+ // In this section we try test calculations for intersecting areas with line mod route_area_tests { use super::*; From 8182f7bda3e067f1fdec0866a77d6d9cd34bcfaf Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Fri, 23 Jul 2021 20:13:33 +0300 Subject: [PATCH 28/29] [UPDATE] Bresenham's resterization algorithm --- dsky-utils/src/lib.rs | 7 +++- pallets/ds-maps/src/lib.rs | 78 +++++++++++++++++++++++++++----------- 2 files changed, 61 insertions(+), 24 deletions(-) diff --git a/dsky-utils/src/lib.rs b/dsky-utils/src/lib.rs index 065cca3..743c437 100644 --- a/dsky-utils/src/lib.rs +++ b/dsky-utils/src/lib.rs @@ -10,6 +10,7 @@ pub trait IntDiv { pub trait Signed { fn abs(self) -> Self; + fn signum(self) -> Self; } pub trait FromRaw { @@ -57,6 +58,10 @@ impl Signed for I10F22 { fn abs(self) -> Self { self.abs() } + /// returns sign (-1, 1, 0) + fn signum(self) -> Self { + self.signum() + } } impl CastToType for I10F22 { @@ -100,4 +105,4 @@ impl GetEpsilon for I42F22 { fn get_epsilon() -> I42F22 { I42F22::from_num(0.00001f64) } -} \ No newline at end of file +} diff --git a/pallets/ds-maps/src/lib.rs b/pallets/ds-maps/src/lib.rs index 177526c..6d86467 100644 --- a/pallets/ds-maps/src/lib.rs +++ b/pallets/ds-maps/src/lib.rs @@ -247,7 +247,7 @@ pub struct Line { } impl< - Coord: ToBigCoord + Ord + Signed + IntDiv + Mul + Sub + Add + Div + Copy, + Coord: FromStr + Default + ToBigCoord + Ord + Signed + IntDiv + Mul + Sub + Add + Div + Copy, BigCoord: Mul + Sub + FromBigCoord + Copy + PartialOrd + GetEpsilon > Line { pub fn new(point_0: Point2D, point_1: Point2D) -> Self { @@ -266,8 +266,14 @@ impl< start_point: point_0, end_point: point_1, _phantom: PhantomData - } + } } + fn coord_from_str (s: &str) -> Coord { + match Coord::from_str(s) { + Ok(v) => v, + Err(_) => Default::default(), + } + } // Realisation of Bresenham's line algorithm pub fn get_route_areas(self, root: RootBox) -> Vec { @@ -277,29 +283,42 @@ impl< if start_area == end_area {return vec![start_area]} let mut output = Vec::new(); let delta = root.delta; - - let start_vector = root.bounding_box.south_west.project().get_distance_vector(self.start_point); - let start_row = start_vector.lat.integer_division_u16(root.delta) + 1; - let start_column = start_vector.lon.integer_division_u16(root.delta) + 1; - - let end_vector = root.bounding_box.south_west.project().get_distance_vector(self.end_point); - let end_row = end_vector.lat.integer_division_u16(root.delta) + 1; - let end_column = end_vector.lon.integer_division_u16(root.delta) + 1; + // Simplification, it's still lat/lon + let mut dx = self.end_point.lat - self.start_point.lat; + let mut dy = self.end_point.lon - self.start_point.lon; + let incx = dx.signum() * delta; + let incy = dy.signum() * delta; + dx = dx.abs(); + dy = dy.abs(); + let pdx: Coord; + let pdy: Coord; + let es: Coord; + let el: Coord; + let zero: Coord = Line::coord_from_str("0"); + if dx > dy { + pdx = incx; pdy = zero; + es = dy; el = dx; + } else { + pdx = zero; pdy = incy; + es = dx; el = dy; + } let mut current_point = self.start_point; - let mut row_counter = start_row; - // TODO fix inverted stepping - // Iterating through all areas from start to end points - while row_counter <= end_row { - let mut column_counter = start_column; - while column_counter <= end_column { - output.push(root.detect_intersected_area(current_point)); - current_point.lon = current_point.lon + delta; - column_counter += 1; + output.push(root.detect_intersected_area(current_point)); + let mut count: Coord = zero; + let mut err = el / Line::coord_from_str("2"); + while count < el { + count = count + delta; + err = err - es; + if err < zero { + err = err + el; + current_point.lat = current_point.lat + incx; + current_point.lon = current_point.lon + incy; + } else { + current_point.lat = current_point.lat + pdx; + current_point.lon = current_point.lon + pdy; } - current_point.lon = self.start_point.lon; - current_point.lat = current_point.lat + delta; - row_counter += 1; + output.push(root.detect_intersected_area(current_point)); } output } @@ -374,7 +393,7 @@ mod line_tests { coord("2.5")); let line = Line::new(first_point, second_point); let areas = line.get_route_areas(root); - assert_eq!(areas, vec![1, 5, 9, 2, 6, 10, 3, 7, 11]); + assert_eq!(areas, vec![1, 6, 11]); } #[test] @@ -390,6 +409,19 @@ mod line_tests { assert_eq!(areas, vec![1, 2, 3, 4]); } + #[test] + fn get_route_areas_in_frac_delta() { + let rect = construct_custom_box("0", "0", "4", "4"); + let root = RootBox::new(1, rect, coord("0.1")); + let first_point = Point2D::new(coord("0.01"), + coord("0.01")); + let second_point = Point2D::new(coord("0.31"), + coord("0.02")); + let line = Line::new(first_point, second_point); + let areas = line.get_route_areas(root); + assert_eq!(areas, vec![1, 2, 3, 4, 5]); + } + #[test] fn get_route_single_area() { let rect = construct_custom_box("0", "0", "4", "4"); From 2dcd1549c8b84a0b52b8d664cd9bd6f834a4fd06 Mon Sep 17 00:00:00 2001 From: Karuzzzo Date: Tue, 27 Jul 2021 22:50:52 +0300 Subject: [PATCH 29/29] [ADD] Crutch for web --- pallets/ds-maps/src/lib.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/pallets/ds-maps/src/lib.rs b/pallets/ds-maps/src/lib.rs index 6d86467..20a2665 100644 --- a/pallets/ds-maps/src/lib.rs +++ b/pallets/ds-maps/src/lib.rs @@ -363,7 +363,7 @@ impl< max(a,c) <= min(b,d) } - // BigCoord required, cause this math is out of bounds for Coord + // Yea, this fn hurts // Signed area of a triangle, oriented clockwise (same as vector multiplication) fn area(a: Point2D, b: Point2D, c: Point2D) -> BigCoord { (b.lat.try_into() - a.lat.try_into()) * (c.lon.try_into() - a.lon.try_into()) - @@ -1806,6 +1806,28 @@ decl_module! { )); Ok(()) } + + #[weight = ::WeightInfo::route_add()] + pub fn raw_route_add(origin, + raw_waypoints: [T::RawCoord; 4], + start_time: T::Moment, + arrival_time: T::Moment, + root_id: RootId) -> dispatch::DispatchResult { + let start_location = Point3D::new( + T::Coord::from_raw(raw_waypoints[0].into()), + T::Coord::from_raw(raw_waypoints[1].into()), + Self::coord_from_str("1")); + + let start_waypoint = Waypoint::new(start_location, start_time); + + let arrival_location = Point3D::new( + T::Coord::from_raw(raw_waypoints[2].into()), + T::Coord::from_raw(raw_waypoints[3].into()), + Self::coord_from_str("1")); + + let arrival_waypoint = Waypoint::new(arrival_location, arrival_time); + Module::::route_add(origin, vec![start_waypoint, arrival_waypoint], root_id) + } } }