diff --git a/geom/src/polyline3.rs b/geom/src/polyline3.rs index 77a1d7cd..d914f1a4 100644 --- a/geom/src/polyline3.rs +++ b/geom/src/polyline3.rs @@ -376,16 +376,19 @@ impl PolyLine3 { } } - pub fn split(mut self, dst: f32) -> (Self, Self) { - let start = self.cut_start(dst); - let n = start.n_points(); - *self.points.get_mut(n - 1).unwrap() = start.last(); - self.points.drain(..n - 1); - self.l -= start.length(); - (start, self) - } - - // dst is distance from start to cut + /// Split the polyline3 into two at the given distance from the start + /// The polylined returned look like this + /// ([start ... cut], [cut ... end]) + pub fn split(mut self, dst_from_start: f32) -> (Self, Self) { + let end = self.cut_start(dst_from_start); + self.points.truncate(self.points.len() - end.n_points() + 1); + self.points.push(end.first()); + self.l -= end.length(); + (self, end) + } + + /// dst is distance from start to cut + /// Returns the end of the points after cutting part of the start pub fn cut_start(&self, mut dst: f32) -> Self { if dst == 0.0 { return self.clone(); diff --git a/native_app/src/debug_gui/debug_window.rs b/native_app/src/debug_gui/debug_window.rs index f16375e4..ab01e751 100644 --- a/native_app/src/debug_gui/debug_window.rs +++ b/native_app/src/debug_gui/debug_window.rs @@ -15,7 +15,7 @@ use engine::{PerfCountersStatic, Tesselator}; use geom::{Camera, Color, LinearColor, Spline3, Vec2}; use prototypes::{GameDuration, GameTime, SECONDS_PER_DAY}; use simulation::map::{ - IntersectionID, Map, MapSubscriber, NetworkObjectID, RoadSegmentKind, TraverseKind, UpdateType, + IntersectionID, Map, MapSubscriber, NetworkObjectID, TraverseKind, UpdateType, }; use simulation::transportation::train::TrainReservations; use simulation::world_command::WorldCommand; @@ -44,7 +44,6 @@ impl Default for DebugObjs { (false, "Debug electricity", debug_electricity), (false, "Debug spatialmap", debug_spatialmap), (false, "Debug transport grid", debug_transport_grid), - (false, "Debug splines", debug_spline), (false, "Debug lots", debug_lots), (false, "Debug road points", debug_road_points), (false, "Debug parking", debug_parking), @@ -275,26 +274,6 @@ fn debug(window: egui::Window<'_>, ui: &egui::Context, uiworld: &UiWorld, sim: & }); } -pub fn debug_spline(tess: &mut Tesselator, sim: &Simulation, _: &UiWorld) -> Option<()> { - for road in sim.map().roads().values() { - if let RoadSegmentKind::Curved((fr_dr, to_der)) = road.segment { - let fr = road.points.first(); - let to = road.points.last(); - draw_spline( - tess, - Spline3 { - from: fr, - to, - from_derivative: fr_dr.z0(), - to_derivative: to_der.z0(), - }, - ); - } - } - - Some(()) -} - pub fn debug_lots(tess: &mut Tesselator, sim: &Simulation, _: &UiWorld) -> Option<()> { tess.set_color(Color::RED); for lot in sim.map().lots().values() { @@ -410,6 +389,7 @@ pub fn debug_connectivity(tess: &mut Tesselator, sim: &Simulation, uiw: &UiWorld Some(()) } +#[allow(unused)] fn draw_spline(tess: &mut Tesselator, mut sp: Spline3) { sp.from = sp.from.up(0.3); sp.to = sp.to.up(0.3); diff --git a/simulation/src/lib.rs b/simulation/src/lib.rs index 4cbee884..ade65a5d 100644 --- a/simulation/src/lib.rs +++ b/simulation/src/lib.rs @@ -2,7 +2,7 @@ #![allow(clippy::type_complexity)] use crate::init::{GSYSTEMS, INIT_FUNCS, SAVELOAD_FUNCS}; -use crate::map::{BuildingKind, Map}; +use crate::map::{BuildingKind, Heightmap, Map}; use crate::map_dynamic::{Itinerary, ItineraryLeader}; use crate::souls::add_souls_to_empty_buildings; use crate::utils::resources::{Ref, RefMut, Resources}; @@ -288,6 +288,9 @@ impl Simulation { pub fn load_from_disk(save_name: &str) -> Option { let sim: Simulation = common::saveload::CompressedBincode::load(save_name).ok()?; + if sim.resources.try_read::().ok()?.environment.size().0 == 0 { + return None; + } Some(sim) } diff --git a/simulation/src/map/map.rs b/simulation/src/map/map.rs index 9e879e5d..504faacd 100644 --- a/simulation/src/map/map.rs +++ b/simulation/src/map/map.rs @@ -5,10 +5,10 @@ use crate::map::{ Building, BuildingID, BuildingKind, Environment, Intersection, IntersectionID, Lane, LaneID, LaneKind, LanePattern, Lot, LotID, LotKind, MapSubscriber, MapSubscribers, ParkingSpotID, ParkingSpots, ProjectFilter, ProjectKind, Road, RoadID, RoadSegmentKind, SpatialMap, - SubscriberChunkID, TerraformKind, UpdateType, Zone, + SubscriberChunkID, TerraformKind, UpdateType, Zone, ROAD_Z_OFFSET, }; use geom::OBB; -use geom::{Spline3, Vec2, Vec3}; +use geom::{Vec2, Vec3}; use ordered_float::OrderedFloat; use prototypes::{BuildingGen, Tick}; use serde::{Deserialize, Serialize}; @@ -488,48 +488,13 @@ impl Map { self.parking.remove_to_reuse(id); } - let id = self.add_intersection(pos); - - let src_id = r.src; - - let (r1, r2) = match r.segment { - RoadSegmentKind::Straight => ( - self.connect(src_id, id, &pat, RoadSegmentKind::Straight)?, - self.connect(id, r.dst, &pat, RoadSegmentKind::Straight)?, - ), - RoadSegmentKind::Curved((from_derivative, to_derivative)) => { - let s = Spline3 { - from: r.points.first(), - to: r.points.last(), - from_derivative: from_derivative.z0(), - to_derivative: to_derivative.z0(), - }; - let t_approx = s.project_t(pos, 1.0); - - let (s_from, s_to) = s.split_at(t_approx); - - ( - self.connect( - src_id, - id, - &pat, - RoadSegmentKind::Curved(( - s_from.from_derivative.xy(), - s_from.to_derivative.xy(), - )), - )?, - self.connect( - id, - r.dst, - &pat, - RoadSegmentKind::Curved(( - s_to.from_derivative.xy(), - s_to.to_derivative.xy(), - )), - )?, - ) - } - }; + let id = self.add_intersection(pos - Vec3::z(ROAD_Z_OFFSET)); + + let dist_along = r.points.length_at_proj(pos); + let (before, after) = r.points.split(dist_along); + + let r1 = self.connect(r.src, id, &pat, RoadSegmentKind::Arbitrary(before))?; + let r2 = self.connect(id, r.dst, &pat, RoadSegmentKind::Arbitrary(after))?; log::info!( "{} parking spots reused when splitting", @@ -874,7 +839,7 @@ impl Map { .first() .up(-crate::map::ROAD_Z_OFFSET) .is_close(src.pos, 0.001), - "{:?} {:?} {:?} {:?}", + "First road point should be close to src intersection's position {:?} {:?} {:?} {:?}", road.points.first().up(-crate::map::ROAD_Z_OFFSET), src.pos, road.id, @@ -885,7 +850,7 @@ impl Map { .last() .up(-crate::map::ROAD_Z_OFFSET) .is_close(dst.pos, 0.001), - "{:?} {:?} {:?} {:?}", + "Last road point should be close to dst intersection's position {:?} {:?} {:?} {:?}", road.points.last().up(-crate::map::ROAD_Z_OFFSET), dst.pos, road.id, diff --git a/simulation/src/map/objects/road.rs b/simulation/src/map/objects/road.rs index a9adb1fc..2a9d54a8 100644 --- a/simulation/src/map/objects/road.rs +++ b/simulation/src/map/objects/road.rs @@ -14,10 +14,11 @@ new_key_type! { pub struct RoadID; } -#[derive(Copy, Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub enum RoadSegmentKind { Straight, Curved((Vec2, Vec2)), // The two derivatives for the spline + Arbitrary(PolyLine3), } impl RoadSegmentKind { @@ -35,8 +36,6 @@ pub struct Road { pub src: IntersectionID, pub dst: IntersectionID, - pub segment: RoadSegmentKind, - /// always from src to dst /// don't try to make points go away from the road as it would be impossible to split them correctly afterward pub points: PolyLine3, @@ -97,7 +96,6 @@ impl Road { dst: dst.id, src_interface: 9.0, dst_interface: 9.0, - segment, width, lanes_forward: vec![], lanes_backward: vec![], @@ -477,6 +475,12 @@ impl Road { from_derivative, to_derivative, }, + RoadSegmentKind::Arbitrary(pos) => { + if pos.len() < 2 { + unreachable!("Arbitrary road segment with less than 2 points") + } + return (pos, None); + } }; let iter = spline.smart_points(if precise { 0.1 } else { 1.0 }, 0.0, 1.0);