mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-29 01:13:53 +03:00
split parking lot code from building code, for #231
This commit is contained in:
parent
a4a545cc79
commit
eb4acdc2f6
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -445,8 +445,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "contour"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/dabreegster/contour-rs#356b770587504de761a5ded7bb7684e779232ae7"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"geojson 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1044,7 +1044,7 @@ dependencies = [
|
||||
"chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clipboard 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"colorous 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"contour 0.1.0 (git+https://github.com/dabreegster/contour-rs)",
|
||||
"contour 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"downcast-rs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"enumset 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ezgui 0.1.0",
|
||||
@ -3963,7 +3963,7 @@ dependencies = [
|
||||
"checksum colorous 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ebeb47d6d3334179ee49ef9a1f3a03d177450b25705854d92e2e1d128b49c736"
|
||||
"checksum const-random 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "2f1af9ac737b2dd2d577701e59fd09ba34822f6f2ebdb30a7647405d9e55e16a"
|
||||
"checksum const-random-macro 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "25e4c606eb459dd29f7c57b2e0879f2b6f14ee130918c2b78ccb58a9624e6c7a"
|
||||
"checksum contour 0.1.0 (git+https://github.com/dabreegster/contour-rs)" = "<none>"
|
||||
"checksum contour 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "32505c91a846e14e02e458fc4ecdb6755858bfaf876717968d35cf623f6f554c"
|
||||
"checksum core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171"
|
||||
"checksum core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
|
||||
"checksum core-graphics 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "59e78b2e0aaf43f08e7ae0d6bc96895ef72ff0921c7d4ff4762201b2dba376dd"
|
||||
|
@ -17,7 +17,7 @@ built = { version = "0.4.2", optional = true, features=["chrono"] }
|
||||
chrono = "0.4.10"
|
||||
clipboard = { version = "0.5.0", optional = true }
|
||||
colorous = "1.0.1"
|
||||
contour = { git = "https://github.com/dabreegster/contour-rs" }
|
||||
contour = "0.2.0"
|
||||
downcast-rs = "1.1.1"
|
||||
enumset = "1.0.0"
|
||||
ezgui = { path = "../ezgui", default-features=false }
|
||||
|
@ -183,7 +183,7 @@ impl CommuterPatterns {
|
||||
BlockSelection::NothingSelected => None,
|
||||
};
|
||||
|
||||
return match base_block_id {
|
||||
match base_block_id {
|
||||
None => (ctx.upload(batch), None),
|
||||
Some(base_block_id) => {
|
||||
let base_block = &self.blocks[base_block_id];
|
||||
@ -290,7 +290,7 @@ impl CommuterPatterns {
|
||||
};
|
||||
(ctx.upload(batch), Some(composite_data))
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn redraw_composite(&mut self, state: Option<&CompositeState>, ctx: &mut EventCtx, app: &App) {
|
||||
|
@ -1,11 +1,10 @@
|
||||
use crate::make::match_points_to_lanes;
|
||||
use crate::raw::{OriginalBuilding, RawBuilding, RawParkingLot};
|
||||
use crate::raw::{OriginalBuilding, RawBuilding};
|
||||
use crate::{
|
||||
osm, Building, BuildingID, BuildingType, FrontPath, LaneID, LaneType, Map, OffstreetParking,
|
||||
ParkingLot, ParkingLotID, Position, NORMAL_LANE_THICKNESS, PARKING_LOT_SPOT_LENGTH,
|
||||
};
|
||||
use abstutil::{Tags, Timer};
|
||||
use geom::{Angle, Distance, FindClosest, HashablePt2D, Line, PolyLine, Polygon, Pt2D, Ring};
|
||||
use geom::{Distance, HashablePt2D, Line, PolyLine, Polygon};
|
||||
use rand::{Rng, SeedableRng};
|
||||
use rand_xorshift::XorShiftRng;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashSet};
|
||||
@ -127,145 +126,6 @@ pub fn make_all_buildings(
|
||||
results
|
||||
}
|
||||
|
||||
pub fn make_all_parking_lots(
|
||||
input: &Vec<RawParkingLot>,
|
||||
aisles: &Vec<Vec<Pt2D>>,
|
||||
map: &Map,
|
||||
timer: &mut Timer,
|
||||
) -> Vec<ParkingLot> {
|
||||
timer.start("convert parking lots");
|
||||
let mut center_per_lot: Vec<HashablePt2D> = Vec::new();
|
||||
let mut query: HashSet<HashablePt2D> = HashSet::new();
|
||||
for lot in input {
|
||||
let center = lot.polygon.center().to_hashable();
|
||||
center_per_lot.push(center);
|
||||
query.insert(center);
|
||||
}
|
||||
|
||||
let sidewalk_buffer = Distance::meters(7.5);
|
||||
let driveway_buffer = Distance::meters(7.0);
|
||||
let sidewalk_pts = match_points_to_lanes(
|
||||
map.get_bounds(),
|
||||
query,
|
||||
map.all_lanes(),
|
||||
|l| l.is_sidewalk(),
|
||||
sidewalk_buffer,
|
||||
Distance::meters(1000.0),
|
||||
timer,
|
||||
);
|
||||
|
||||
let mut results = Vec::new();
|
||||
timer.start_iter("create parking lot driveways", center_per_lot.len());
|
||||
for (lot_center, orig) in center_per_lot.into_iter().zip(input.iter()) {
|
||||
timer.next();
|
||||
// TODO Refactor this
|
||||
if let Some(sidewalk_pos) = sidewalk_pts.get(&lot_center) {
|
||||
let sidewalk_line = match Line::new(lot_center.to_pt2d(), sidewalk_pos.pt(map)) {
|
||||
Some(l) => trim_path(&orig.polygon, l),
|
||||
None => {
|
||||
timer.warn(format!(
|
||||
"Skipping parking lot {} because front path has 0 length",
|
||||
orig.osm_id
|
||||
));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// Can this lot have a driveway? If it's not next to a driving lane, then no.
|
||||
let mut driveway: Option<(PolyLine, Position)> = None;
|
||||
let sidewalk_lane = sidewalk_pos.lane();
|
||||
if let Ok(driving_lane) = map
|
||||
.get_parent(sidewalk_lane)
|
||||
.find_closest_lane(sidewalk_lane, vec![LaneType::Driving])
|
||||
{
|
||||
let driving_pos = sidewalk_pos.equiv_pos(driving_lane, Distance::ZERO, map);
|
||||
|
||||
if driving_pos.dist_along() > driveway_buffer
|
||||
&& map.get_l(driving_lane).length() - driving_pos.dist_along() > driveway_buffer
|
||||
{
|
||||
driveway = Some((
|
||||
PolyLine::must_new(vec![
|
||||
sidewalk_line.pt1(),
|
||||
sidewalk_line.pt2(),
|
||||
driving_pos.pt(map),
|
||||
]),
|
||||
driving_pos,
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some((driveway_line, driving_pos)) = driveway {
|
||||
let id = ParkingLotID(results.len());
|
||||
results.push(ParkingLot {
|
||||
id,
|
||||
polygon: orig.polygon.clone(),
|
||||
aisles: Vec::new(),
|
||||
osm_id: orig.osm_id,
|
||||
spots: Vec::new(),
|
||||
|
||||
driveway_line,
|
||||
driving_pos,
|
||||
sidewalk_line,
|
||||
sidewalk_pos: *sidewalk_pos,
|
||||
});
|
||||
} else {
|
||||
timer.warn(format!(
|
||||
"Parking lot from OSM way {} can't have a driveway.",
|
||||
orig.osm_id
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
timer.note(format!(
|
||||
"Discarded {} parking lots that weren't close enough to a sidewalk",
|
||||
input.len() - results.len()
|
||||
));
|
||||
|
||||
let mut closest: FindClosest<ParkingLotID> = FindClosest::new(map.get_bounds());
|
||||
for lot in &results {
|
||||
closest.add(lot.id, lot.polygon.points());
|
||||
}
|
||||
timer.start_iter("match parking aisles", aisles.len());
|
||||
for pts in aisles {
|
||||
timer.next();
|
||||
// Use the center of all the aisle points to match it to a lot
|
||||
let candidates: Vec<ParkingLotID> = closest
|
||||
.all_close_pts(Pt2D::center(&pts), Distance::meters(500.0))
|
||||
.into_iter()
|
||||
.map(|(id, _, _)| id)
|
||||
.collect();
|
||||
|
||||
let (polylines, rings) = Ring::split_points(pts).unwrap();
|
||||
'PL: for pl in polylines {
|
||||
for id in &candidates {
|
||||
let lot = &mut results[id.0];
|
||||
for segment in lot.polygon.clip_polyline(&pl) {
|
||||
lot.aisles.push(segment);
|
||||
continue 'PL;
|
||||
}
|
||||
}
|
||||
}
|
||||
'RING: for ring in rings {
|
||||
for id in &candidates {
|
||||
let lot = &mut results[id.0];
|
||||
for segment in lot.polygon.clip_ring(&ring) {
|
||||
lot.aisles.push(segment);
|
||||
continue 'RING;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
timer.start_iter("generate parking lot spots", results.len());
|
||||
for lot in results.iter_mut() {
|
||||
timer.next();
|
||||
lot.spots = infer_spots(&lot.polygon, &lot.aisles);
|
||||
}
|
||||
|
||||
timer.stop("convert parking lots");
|
||||
|
||||
results
|
||||
}
|
||||
|
||||
// Adjust the path to start on the building's border, not center
|
||||
fn trim_path(poly: &Polygon, path: Line) -> Line {
|
||||
for bldg_line in poly.points().windows(2) {
|
||||
@ -289,82 +149,6 @@ fn get_address(tags: &Tags, sidewalk: LaneID, map: &Map) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
fn infer_spots(lot_polygon: &Polygon, aisles: &Vec<Vec<Pt2D>>) -> Vec<(Pt2D, Angle)> {
|
||||
let mut spots = Vec::new();
|
||||
let mut finalized_lines = Vec::new();
|
||||
|
||||
for aisle in aisles {
|
||||
let aisle_thickness = NORMAL_LANE_THICKNESS / 2.0;
|
||||
let pl = PolyLine::unchecked_new(aisle.clone());
|
||||
|
||||
for rotate in vec![90.0, -90.0] {
|
||||
// Blindly generate all of the lines
|
||||
let lines = {
|
||||
let mut lines = Vec::new();
|
||||
let mut start = Distance::ZERO;
|
||||
while start + NORMAL_LANE_THICKNESS < pl.length() {
|
||||
let (pt, angle) = pl.must_dist_along(start);
|
||||
start += NORMAL_LANE_THICKNESS;
|
||||
let theta = angle.rotate_degs(rotate);
|
||||
lines.push(Line::must_new(
|
||||
pt.project_away(aisle_thickness / 2.0, theta),
|
||||
pt.project_away(aisle_thickness / 2.0 + PARKING_LOT_SPOT_LENGTH, theta),
|
||||
));
|
||||
}
|
||||
lines
|
||||
};
|
||||
|
||||
for pair in lines.windows(2) {
|
||||
let l1 = &pair[0];
|
||||
let l2 = &pair[1];
|
||||
let back = Line::must_new(l1.pt2(), l2.pt2());
|
||||
if l1.intersection(&l2).is_none()
|
||||
&& l1.angle().approx_eq(l2.angle(), 5.0)
|
||||
&& line_valid(lot_polygon, aisles, l1, &finalized_lines)
|
||||
&& line_valid(lot_polygon, aisles, l2, &finalized_lines)
|
||||
&& line_valid(lot_polygon, aisles, &back, &finalized_lines)
|
||||
{
|
||||
let avg_angle = (l1.angle() + l2.angle()) / 2.0;
|
||||
spots.push((back.middle().unwrap(), avg_angle.opposite()));
|
||||
finalized_lines.push(l1.clone());
|
||||
finalized_lines.push(l2.clone());
|
||||
finalized_lines.push(back);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
spots
|
||||
}
|
||||
|
||||
fn line_valid(
|
||||
lot_polygon: &Polygon,
|
||||
aisles: &Vec<Vec<Pt2D>>,
|
||||
line: &Line,
|
||||
finalized_lines: &Vec<Line>,
|
||||
) -> bool {
|
||||
// Don't leak out of the parking lot
|
||||
// TODO Entire line
|
||||
if !lot_polygon.contains_pt(line.pt1()) || !lot_polygon.contains_pt(line.pt2()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't let this line hit another line
|
||||
if finalized_lines.iter().any(|other| line.crosses(other)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't hit an aisle
|
||||
if aisles.iter().any(|pts| {
|
||||
PolyLine::unchecked_new(pts.clone())
|
||||
.intersection(&line.to_polyline())
|
||||
.is_some()
|
||||
}) {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn classify_bldg(
|
||||
tags: Tags,
|
||||
amenities: &BTreeSet<(String, String)>,
|
||||
|
@ -1,6 +1,7 @@
|
||||
mod bridges;
|
||||
mod buildings;
|
||||
pub mod initial;
|
||||
mod parking_lots;
|
||||
mod remove_disconnected;
|
||||
pub mod traffic_signals;
|
||||
mod transit;
|
||||
@ -280,8 +281,12 @@ impl Map {
|
||||
map.lanes[lane.0].building_paths = bldgs;
|
||||
}
|
||||
|
||||
map.parking_lots =
|
||||
buildings::make_all_parking_lots(&raw.parking_lots, &raw.parking_aisles, &map, timer);
|
||||
map.parking_lots = parking_lots::make_all_parking_lots(
|
||||
&raw.parking_lots,
|
||||
&raw.parking_aisles,
|
||||
&map,
|
||||
timer,
|
||||
);
|
||||
|
||||
map.zones = Zone::make_all(&map);
|
||||
|
||||
|
239
map_model/src/make/parking_lots.rs
Normal file
239
map_model/src/make/parking_lots.rs
Normal file
@ -0,0 +1,239 @@
|
||||
use crate::make::match_points_to_lanes;
|
||||
use crate::raw::RawParkingLot;
|
||||
use crate::{
|
||||
LaneType, Map, ParkingLot, ParkingLotID, Position, NORMAL_LANE_THICKNESS,
|
||||
PARKING_LOT_SPOT_LENGTH,
|
||||
};
|
||||
use abstutil::Timer;
|
||||
use geom::{Angle, Distance, FindClosest, HashablePt2D, Line, PolyLine, Polygon, Pt2D, Ring};
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub fn make_all_parking_lots(
|
||||
input: &Vec<RawParkingLot>,
|
||||
aisles: &Vec<Vec<Pt2D>>,
|
||||
map: &Map,
|
||||
timer: &mut Timer,
|
||||
) -> Vec<ParkingLot> {
|
||||
timer.start("convert parking lots");
|
||||
let mut center_per_lot: Vec<HashablePt2D> = Vec::new();
|
||||
let mut query: HashSet<HashablePt2D> = HashSet::new();
|
||||
for lot in input {
|
||||
let center = lot.polygon.center().to_hashable();
|
||||
center_per_lot.push(center);
|
||||
query.insert(center);
|
||||
}
|
||||
|
||||
let sidewalk_buffer = Distance::meters(7.5);
|
||||
let driveway_buffer = Distance::meters(7.0);
|
||||
let sidewalk_pts = match_points_to_lanes(
|
||||
map.get_bounds(),
|
||||
query,
|
||||
map.all_lanes(),
|
||||
|l| l.is_sidewalk(),
|
||||
sidewalk_buffer,
|
||||
Distance::meters(1000.0),
|
||||
timer,
|
||||
);
|
||||
|
||||
let mut results = Vec::new();
|
||||
timer.start_iter("create parking lot driveways", center_per_lot.len());
|
||||
for (lot_center, orig) in center_per_lot.into_iter().zip(input.iter()) {
|
||||
timer.next();
|
||||
// TODO Refactor this
|
||||
if let Some(sidewalk_pos) = sidewalk_pts.get(&lot_center) {
|
||||
let sidewalk_line = match Line::new(lot_center.to_pt2d(), sidewalk_pos.pt(map)) {
|
||||
Some(l) => trim_path(&orig.polygon, l),
|
||||
None => {
|
||||
timer.warn(format!(
|
||||
"Skipping parking lot {} because front path has 0 length",
|
||||
orig.osm_id
|
||||
));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// Can this lot have a driveway? If it's not next to a driving lane, then no.
|
||||
let mut driveway: Option<(PolyLine, Position)> = None;
|
||||
let sidewalk_lane = sidewalk_pos.lane();
|
||||
if let Ok(driving_lane) = map
|
||||
.get_parent(sidewalk_lane)
|
||||
.find_closest_lane(sidewalk_lane, vec![LaneType::Driving])
|
||||
{
|
||||
let driving_pos = sidewalk_pos.equiv_pos(driving_lane, Distance::ZERO, map);
|
||||
|
||||
if driving_pos.dist_along() > driveway_buffer
|
||||
&& map.get_l(driving_lane).length() - driving_pos.dist_along() > driveway_buffer
|
||||
{
|
||||
driveway = Some((
|
||||
PolyLine::must_new(vec![
|
||||
sidewalk_line.pt1(),
|
||||
sidewalk_line.pt2(),
|
||||
driving_pos.pt(map),
|
||||
]),
|
||||
driving_pos,
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some((driveway_line, driving_pos)) = driveway {
|
||||
let id = ParkingLotID(results.len());
|
||||
results.push(ParkingLot {
|
||||
id,
|
||||
polygon: orig.polygon.clone(),
|
||||
aisles: Vec::new(),
|
||||
osm_id: orig.osm_id,
|
||||
spots: Vec::new(),
|
||||
|
||||
driveway_line,
|
||||
driving_pos,
|
||||
sidewalk_line,
|
||||
sidewalk_pos: *sidewalk_pos,
|
||||
});
|
||||
} else {
|
||||
timer.warn(format!(
|
||||
"Parking lot from OSM way {} can't have a driveway.",
|
||||
orig.osm_id
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
timer.note(format!(
|
||||
"Discarded {} parking lots that weren't close enough to a sidewalk",
|
||||
input.len() - results.len()
|
||||
));
|
||||
|
||||
let mut closest: FindClosest<ParkingLotID> = FindClosest::new(map.get_bounds());
|
||||
for lot in &results {
|
||||
closest.add(lot.id, lot.polygon.points());
|
||||
}
|
||||
timer.start_iter("match parking aisles", aisles.len());
|
||||
for pts in aisles {
|
||||
timer.next();
|
||||
// Use the center of all the aisle points to match it to a lot
|
||||
let candidates: Vec<ParkingLotID> = closest
|
||||
.all_close_pts(Pt2D::center(&pts), Distance::meters(500.0))
|
||||
.into_iter()
|
||||
.map(|(id, _, _)| id)
|
||||
.collect();
|
||||
|
||||
let (polylines, rings) = Ring::split_points(pts).unwrap();
|
||||
'PL: for pl in polylines {
|
||||
for id in &candidates {
|
||||
let lot = &mut results[id.0];
|
||||
for segment in lot.polygon.clip_polyline(&pl) {
|
||||
lot.aisles.push(segment);
|
||||
continue 'PL;
|
||||
}
|
||||
}
|
||||
}
|
||||
'RING: for ring in rings {
|
||||
for id in &candidates {
|
||||
let lot = &mut results[id.0];
|
||||
for segment in lot.polygon.clip_ring(&ring) {
|
||||
lot.aisles.push(segment);
|
||||
continue 'RING;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
timer.start_iter("generate parking lot spots", results.len());
|
||||
for lot in results.iter_mut() {
|
||||
timer.next();
|
||||
lot.spots = infer_spots(&lot.polygon, &lot.aisles);
|
||||
}
|
||||
|
||||
timer.stop("convert parking lots");
|
||||
|
||||
results
|
||||
}
|
||||
|
||||
// Adjust the path to start on the building's border, not center
|
||||
fn trim_path(poly: &Polygon, path: Line) -> Line {
|
||||
for bldg_line in poly.points().windows(2) {
|
||||
if let Some(l1) = Line::new(bldg_line[0], bldg_line[1]) {
|
||||
if let Some(hit) = l1.intersection(&path) {
|
||||
if let Some(l2) = Line::new(hit, path.pt2()) {
|
||||
return l2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Just give up
|
||||
path
|
||||
}
|
||||
|
||||
fn infer_spots(lot_polygon: &Polygon, aisles: &Vec<Vec<Pt2D>>) -> Vec<(Pt2D, Angle)> {
|
||||
let mut spots = Vec::new();
|
||||
let mut finalized_lines = Vec::new();
|
||||
|
||||
for aisle in aisles {
|
||||
let aisle_thickness = NORMAL_LANE_THICKNESS / 2.0;
|
||||
let pl = PolyLine::unchecked_new(aisle.clone());
|
||||
|
||||
for rotate in vec![90.0, -90.0] {
|
||||
// Blindly generate all of the lines
|
||||
let lines = {
|
||||
let mut lines = Vec::new();
|
||||
let mut start = Distance::ZERO;
|
||||
while start + NORMAL_LANE_THICKNESS < pl.length() {
|
||||
let (pt, angle) = pl.must_dist_along(start);
|
||||
start += NORMAL_LANE_THICKNESS;
|
||||
let theta = angle.rotate_degs(rotate);
|
||||
lines.push(Line::must_new(
|
||||
pt.project_away(aisle_thickness / 2.0, theta),
|
||||
pt.project_away(aisle_thickness / 2.0 + PARKING_LOT_SPOT_LENGTH, theta),
|
||||
));
|
||||
}
|
||||
lines
|
||||
};
|
||||
|
||||
for pair in lines.windows(2) {
|
||||
let l1 = &pair[0];
|
||||
let l2 = &pair[1];
|
||||
let back = Line::must_new(l1.pt2(), l2.pt2());
|
||||
if l1.intersection(&l2).is_none()
|
||||
&& l1.angle().approx_eq(l2.angle(), 5.0)
|
||||
&& line_valid(lot_polygon, aisles, l1, &finalized_lines)
|
||||
&& line_valid(lot_polygon, aisles, l2, &finalized_lines)
|
||||
&& line_valid(lot_polygon, aisles, &back, &finalized_lines)
|
||||
{
|
||||
let avg_angle = (l1.angle() + l2.angle()) / 2.0;
|
||||
spots.push((back.middle().unwrap(), avg_angle.opposite()));
|
||||
finalized_lines.push(l1.clone());
|
||||
finalized_lines.push(l2.clone());
|
||||
finalized_lines.push(back);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
spots
|
||||
}
|
||||
|
||||
fn line_valid(
|
||||
lot_polygon: &Polygon,
|
||||
aisles: &Vec<Vec<Pt2D>>,
|
||||
line: &Line,
|
||||
finalized_lines: &Vec<Line>,
|
||||
) -> bool {
|
||||
// Don't leak out of the parking lot
|
||||
// TODO Entire line
|
||||
if !lot_polygon.contains_pt(line.pt1()) || !lot_polygon.contains_pt(line.pt2()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't let this line hit another line
|
||||
if finalized_lines.iter().any(|other| line.crosses(other)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't hit an aisle
|
||||
if aisles.iter().any(|pts| {
|
||||
PolyLine::unchecked_new(pts.clone())
|
||||
.intersection(&line.to_polyline())
|
||||
.is_some()
|
||||
}) {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
Loading…
Reference in New Issue
Block a user