Skip to content

Commit

Permalink
Merge roads
Browse files Browse the repository at this point in the history
Fixes #111
  • Loading branch information
Uriopass committed Jul 14, 2024
1 parent 986c156 commit 798e644
Show file tree
Hide file tree
Showing 15 changed files with 303 additions and 63 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ yakui-core = { git = "https://github.com/Uriopass/yakui", branch = "dev" }
yakui-widgets = { git = "https://github.com/Uriopass/yakui", branch = "dev" }
itertools = { version = "0.13.0", default-features = false }
mlua = { version = "0.9.4", features = ["luau"] }
# rerun = { version = "0.17.0", default-features = false, features = ["sdk"] }

# Set the settings for build scripts and proc-macros.
[profile.dev.build-override]
Expand Down
4 changes: 2 additions & 2 deletions native_app/src/gui/hud.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::time::Instant;
use std::time::{Duration, Instant};

use goryak::{image_button, minrow, on_secondary_container, textc};
use ordered_float::OrderedFloat;
Expand Down Expand Up @@ -47,7 +47,7 @@ pub fn render_newgui(uiworld: &UiWorld, sim: &Simulation) {
}

fn auto_save(uiworld: &UiWorld) {
let every = uiworld.read::<Settings>().auto_save_every.into();
let every: Option<Duration> = uiworld.read::<Settings>().auto_save_every.into();
let mut gui = uiworld.write::<GuiState>();
if let Some(every) = every {
if gui.last_save.elapsed() > every {
Expand Down
21 changes: 15 additions & 6 deletions native_app/src/gui/tools/roadbuild.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,9 +368,9 @@ pub struct RoadBuildResource {

#[derive(Default, Clone, Copy)]
pub enum Snapping {
#[default]
None,
SnapToGrid,
#[default]
SnapToAngle,
}

Expand All @@ -387,7 +387,7 @@ fn check_angle(map: &Map, from: MapProject, to: Vec2, is_rail: bool) -> bool {
let max_turn_angle = if is_rail {
0.0
} else {
30.0 * std::f32::consts::PI / 180.0
25.0 * std::f32::consts::PI / 180.0
};

match from.kind {
Expand All @@ -401,7 +401,7 @@ fn check_angle(map: &Map, from: MapProject, to: Vec2, is_rail: bool) -> bool {
.roads
.iter()
.map(|road_id| map.roads()[*road_id].dir_from(i))
.any(|v| v.angle(dir).abs() >= max_turn_angle)
.all(|v| v.angle(dir).abs() >= max_turn_angle)
}
Road(r) => {
let Some(r) = map.roads().get(r) else {
Expand Down Expand Up @@ -536,9 +536,18 @@ impl RoadBuildResource {
.color(col);
}

immdraw.circle(p.first(), patwidth * 0.5).color(col);
immdraw.circle(p.last(), patwidth * 0.5).color(col);
immdraw.polyline(p.into_vec(), patwidth, false).color(col);
immdraw.circle(p.first().up(0.1), patwidth * 0.5).color(col);
immdraw.circle(p.last().up(0.1), patwidth * 0.5).color(col);
immdraw
.polyline(
p.into_vec()
.into_iter()
.map(|v| v.up(0.1))
.collect::<Vec<_>>(),
patwidth,
false,
)
.color(col);
}

pub fn possible_interpolations(&self, map: &Map, mousepos: Vec3) -> Option<Vec<Vec3>> {
Expand Down
2 changes: 1 addition & 1 deletion native_app/src/rendering/map_rendering/map_mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -766,7 +766,7 @@ impl MapBuilders {
LotKind::Residential => simulation::colors().lot_residential_col,
};
tess_lots.set_color(col);
tess_lots.draw_filled_polygon(&lot.shape.corners, lot.height + 0.3);
tess_lots.draw_filled_polygon(&lot.shape.corners, lot.height + 0.28);
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions simulation/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ arc-swap = "1.3.0"
derive_more = { workspace = true }
bitflags = "2.4.1"
itertools = { workspace = true }
diff = "0.1.13"
# rerun = { workspace = true }


[dev-dependencies]
Expand Down
2 changes: 2 additions & 0 deletions simulation/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ use serde::de::DeserializeOwned;
use serde::Serialize;

pub fn init() {
//crate::rerun::init_rerun();

// # Safety
// This function is called only once, before any other function in this crate.
unsafe {
Expand Down
3 changes: 2 additions & 1 deletion simulation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#![allow(clippy::type_complexity)]

use crate::init::{GSYSTEMS, INIT_FUNCS, SAVELOAD_FUNCS};
use crate::map::{BuildingKind, Heightmap, Map};
use crate::map::{BuildingKind, Map};
use crate::map_dynamic::{Itinerary, ItineraryLeader};
use crate::souls::add_souls_to_empty_buildings;
use crate::utils::resources::{Ref, RefMut, Resources};
Expand Down Expand Up @@ -42,6 +42,7 @@ pub mod init;
pub mod map;
pub mod map_dynamic;
pub mod multiplayer;
mod rerun;
pub mod souls;
#[cfg(test)]
mod tests;
Expand Down
172 changes: 150 additions & 22 deletions simulation/src/map/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ use geom::{Vec2, Vec3};
use ordered_float::OrderedFloat;
use prototypes::{BuildingGen, Tick};
use serde::{Deserialize, Serialize};
use slotmapd::HopSlotMap;
use slotmapd::SlotMap;

pub type Roads = HopSlotMap<RoadID, Road>;
pub type Lanes = HopSlotMap<LaneID, Lane>;
pub type Intersections = HopSlotMap<IntersectionID, Intersection>;
pub type Buildings = HopSlotMap<BuildingID, Building>;
pub type Lots = HopSlotMap<LotID, Lot>;
pub type Roads = SlotMap<RoadID, Road>;
pub type Lanes = SlotMap<LaneID, Lane>;
pub type Intersections = SlotMap<IntersectionID, Intersection>;
pub type Buildings = SlotMap<BuildingID, Building>;
pub type Lots = SlotMap<LotID, Lot>;

#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct MapProject {
Expand Down Expand Up @@ -160,6 +160,7 @@ impl Map {
{
return None;
}
info!("make_connection {:?} {:?} {:?}", from, to, interpoint);

let connection_segment = match interpoint {
Some(x) => RoadSegmentKind::from_elbow(from.pos.xy(), to.pos.xy(), x),
Expand Down Expand Up @@ -189,10 +190,24 @@ impl Map {
return None;
};

info!(
"connect {:?}({:?}) {:?}({:?}) {:?} {:?}: {:?}",
from, from_id, to, to_id, pattern, &interpoint, r
);
self.invalidate(from_id);
self.invalidate(to_id);

let mut maybe_merge = |inter_id| {
let inter = &self.intersections[inter_id];
if let [r1_id, r2_id] = *inter.roads {
let r1 = &self.roads[r1_id];
let r2 = &self.roads[r2_id];
// only merge if angle is shallow
if r1.dir_from(inter_id).dot(r2.dir_from(inter_id)) < -0.9 {
self.merge_road(r1_id, r2_id);
}
}
};
maybe_merge(from_id);
maybe_merge(to_id);

info!("connected {:?} {:?}: {:?}", from_id, to_id, r);

self.check_invariants();

Expand Down Expand Up @@ -476,12 +491,13 @@ impl Map {
) -> Option<IntersectionID> {
info!("split_road {:?} {:?}", split_road_id, pos);

let pat = self.roads.get(split_road_id)?.pattern(&self.lanes);

let r = unwrap_or!(self.remove_raw_road(split_road_id), {
log::error!("Trying to split unexisting road");
if !self.roads.contains_key(split_road_id) {
log::error!("splitting non-existing road {:?}", split_road_id);
return None;
});
}

let pat = self.roads.get(split_road_id)?.pattern(&self.lanes);
let r = self.remove_raw_road(split_road_id)?;
self.subscribers.dispatch(UpdateType::Road, &r);

for (id, _) in r.lanes_iter() {
Expand All @@ -496,6 +512,10 @@ impl Map {
let r1 = self.connect(r.src, id, &pat, RoadSegmentKind::Arbitrary(before))?;
let r2 = self.connect(id, r.dst, &pat, RoadSegmentKind::Arbitrary(after))?;

self.invalidate(r.src);
self.invalidate(r.dst);
self.invalidate(id);

log::info!(
"{} parking spots reused when splitting",
self.parking.clean_reuse()
Expand Down Expand Up @@ -556,6 +576,109 @@ impl Map {
Some(id)
}

pub(crate) fn merge_road(&mut self, road1: RoadID, road2: RoadID) -> Option<RoadID> {
info!("merge_road {:?} {:?}", road1, road2);

let r1 = self.roads.get(road1)?;
let r2 = self.roads.get(road2)?;

let same_inter = if r1.src == r2.src || r1.src == r2.dst {
r1.src
} else if r1.dst == r2.src || r1.dst == r2.dst {
r1.dst
} else {
warn!("trying to merge roads that are not connected to each other");
return None;
};

let mut pat1 = r1.pattern(&self.lanes);
let mut pat2 = r2.pattern(&self.lanes);

if r1.src == same_inter {
std::mem::swap(&mut pat1.lanes_backward, &mut pat1.lanes_forward);
}

if r2.src != same_inter {
std::mem::swap(&mut pat2.lanes_backward, &mut pat2.lanes_forward);
}

if pat1 != pat2 {
log::info!("merge refused because patterns don't match");
return None;
}

let r1_extremity = if r1.src == same_inter { r1.dst } else { r1.src };
let r2_extremity = if r2.src == same_inter { r2.dst } else { r2.src };

if r1_extremity == r2_extremity {
log::info!("merge refused because connecting to itself");
return None;
}

let r1 = self.remove_raw_road(road1)?;
let r2 = self.remove_raw_road(road2)?;

self.remove_intersection_inner(same_inter);

self.subscribers.dispatch(UpdateType::Road, &r1);
self.subscribers.dispatch(UpdateType::Road, &r2);

for (id, _) in r2.lanes_iter().chain(r1.lanes_iter()) {
self.parking.remove_to_reuse(id);
}

let mut new_polyline = r1.points;
if r1.src == same_inter {
new_polyline.reverse();
}

if r2.src == same_inter {
new_polyline.extend(r2.points.iter().skip(1))
} else {
new_polyline.extend(r2.points.iter().rev().skip(1))
}

let new_r = self.connect(
r1_extremity,
r2_extremity,
&pat1,
RoadSegmentKind::Arbitrary(new_polyline),
)?;

let new_r = &mut self.roads[new_r];
log::info!("merge_road new road is {:?}", new_r.id);

log::info!(
"{} parking spots reused when merging",
self.parking.clean_reuse()
);

for b in r1
.connected_buildings
.iter()
.chain(r2.connected_buildings.iter())
{
let b = self.buildings.get_mut(*b)?;
b.connected_road = Some(new_r.id);
new_r.connected_buildings.push(b.id);

self.electricity.add_edge(b.id, new_r.id);
}

for lot in &mut self.lots.values_mut() {
if lot.parent == road1 || lot.parent == road2 {
lot.parent = new_r.id;
}
}

let new_id = new_r.id;

self.invalidate(r1_extremity);
self.invalidate(r2_extremity);

Some(new_id)
}

/// Returns None if one of the intersections don't exist
pub(crate) fn connect(
&mut self,
Expand All @@ -567,6 +690,8 @@ impl Map {
let src = self.intersections.get(src_id)?;
let dst = self.intersections.get(dst_id)?;

let gen_lots = !matches!(segment, RoadSegmentKind::Arbitrary(_));

let rid = Road::make(
src,
dst,
Expand All @@ -589,11 +714,10 @@ impl Map {
self.intersections.get_mut(src_id)?.add_road(&self.roads, r);
self.intersections.get_mut(dst_id)?.add_road(&self.roads, r);

self.invalidate(src_id);
self.invalidate(dst_id);

Lot::remove_intersecting_lots(self, rid);
Lot::generate_along_road(self, rid);
if gen_lots {
Lot::generate_along_road(self, rid);
}

#[allow(clippy::indexing_slicing)]
let r = &self.roads[rid];
Expand Down Expand Up @@ -787,9 +911,9 @@ impl Map {
assert_eq!(turn.id.parent, inter.id);
assert!(
self.lanes.contains_key(turn.id.src),
"{:?} {:?}",
"Turn src doesn't exist for inter:{:?} src:{:?}",
inter.id,
turn.id.src
turn.id.src,
);
assert!(
self.lanes.contains_key(turn.id.dst),
Expand Down Expand Up @@ -911,7 +1035,11 @@ impl Map {
for lot in self.lots.values() {
log::debug!("{:?}", lot.id);
assert!(lot.shape.axis().iter().all(|x| x.mag() > 0.0));
assert!(self.roads.contains_key(lot.parent), "{:?}", lot.parent);
assert!(
self.roads.contains_key(lot.parent),
"Lot parent {:?} doesn't exist",
lot.parent
);
assert!(self.spatial_map.contains(lot.id));
}

Expand Down
Loading

0 comments on commit 798e644

Please sign in to comment.