1use std::cell::RefCell;
2
3use lyon::geom::{CubicBezierSegment, Point, QuadraticBezierSegment};
4
5use geom::{
6 Angle, ArrowCap, Bounds, Circle, Distance, InfiniteLine, Line, PolyLine, Polygon, Pt2D,
7 Tessellation,
8};
9use map_model::{BufferType, Direction, DrivingSide, Lane, LaneID, LaneType, Map, Road, TurnID};
10use widgetry::{Color, Drawable, GeomBatch, GfxCtx, Prerender, RewriteColor};
11
12use crate::render::{DrawOptions, Renderable, OUTLINE_THICKNESS};
13use crate::{AppLike, ID};
14
15pub struct DrawLane {
16 pub id: LaneID,
17 pub polygon: Polygon,
18 zorder: isize,
19
20 draw_default: RefCell<Option<Drawable>>,
21}
22
23impl DrawLane {
24 pub fn new(lane: &Lane, road: &Road) -> DrawLane {
25 DrawLane {
26 id: lane.id,
27 polygon: lane.get_thick_polygon(),
28 zorder: road.zorder,
29 draw_default: RefCell::new(None),
30 }
31 }
32
33 pub fn render<P: AsRef<Prerender>>(&self, prerender: &P, app: &dyn AppLike) -> GeomBatch {
34 let map = app.map();
35 let lane = map.get_l(self.id);
36 let road = map.get_r(lane.id.road);
37 let rank = road.get_rank();
38 let mut batch = GeomBatch::new();
39
40 if !lane.is_light_rail() {
41 batch.push(
42 app.cs().zoomed_road_surface(lane.lane_type, rank),
43 self.polygon.clone(),
44 );
45 }
46 let general_road_marking = app.cs().general_road_marking;
47
48 match lane.lane_type {
49 LaneType::Sidewalk | LaneType::Shoulder => {
50 if lane.is_sidewalk() {
52 batch.extend(app.cs().sidewalk_lines, calculate_sidewalk_lines(lane));
53 }
54 if app.cs().road_outlines {
55 let width = Distance::meters(0.2);
57 let mut shift = (lane.width - width) / 2.0;
58 if map.get_config().driving_side == DrivingSide::Right {
59 shift *= -1.0;
60 }
61 if let Ok(pl) = lane.lane_center_pts.shift_either_direction(shift) {
62 batch.push(app.cs().curb(rank), pl.make_polygons(width));
63 }
64 }
65 }
66 LaneType::Parking => {
67 batch.extend(general_road_marking, calculate_parking_lines(lane, map));
68 }
69 LaneType::Driving => {
70 batch.extend(general_road_marking, calculate_driving_lines(lane, road));
71 batch.extend(general_road_marking, calculate_turn_markings(map, lane));
72 batch.extend(general_road_marking, calculate_one_way_markings(lane, road));
73 }
74 LaneType::Bus => {
75 batch.extend(general_road_marking, calculate_driving_lines(lane, road));
76 batch.extend(general_road_marking, calculate_turn_markings(map, lane));
77 batch.extend(general_road_marking, calculate_one_way_markings(lane, road));
78 for (pt, angle) in lane
79 .lane_center_pts
80 .step_along(Distance::meters(30.0), Distance::meters(5.0))
81 {
82 batch.append(
83 GeomBatch::load_svg(prerender, "system/assets/map/bus_only.svg")
84 .scale(0.06)
85 .centered_on(pt)
86 .rotate(angle.shortest_rotation_towards(Angle::degrees(-90.0))),
87 );
88 }
89 }
90 LaneType::Biking => {
91 for (pt, angle) in lane
92 .lane_center_pts
93 .step_along(Distance::meters(30.0), Distance::meters(5.0))
94 {
95 batch.append(
96 GeomBatch::load_svg(prerender, "system/assets/meters/bike.svg")
97 .scale(0.06)
98 .centered_on(pt)
99 .rotate(angle.shortest_rotation_towards(Angle::degrees(-90.0))),
100 );
101 }
102 }
103 LaneType::SharedLeftTurn => {
104 let thickness = Distance::meters(0.25);
105 let center_line = app.cs().road_center_line(map);
106 batch.push(
107 center_line,
108 lane.lane_center_pts
109 .must_shift_right((lane.width - thickness) / 2.0)
110 .make_polygons(thickness),
111 );
112 batch.push(
113 center_line,
114 lane.lane_center_pts
115 .must_shift_left((lane.width - thickness) / 2.0)
116 .make_polygons(thickness),
117 );
118 for (pt, angle) in lane
119 .lane_center_pts
120 .step_along(Distance::meters(30.0), Distance::meters(5.0))
121 {
122 batch.append(
123 GeomBatch::load_svg(prerender, "system/assets/map/shared_left_turn.svg")
124 .autocrop()
125 .scale(0.003)
126 .centered_on(pt)
127 .rotate(angle.shortest_rotation_towards(Angle::degrees(-90.0))),
128 );
129 }
130 }
131 LaneType::Construction => {
132 for (pt, angle) in lane
133 .lane_center_pts
134 .step_along(Distance::meters(30.0), Distance::meters(5.0))
135 {
136 batch.append(
138 GeomBatch::load_svg(prerender, "system/assets/map/under_construction.svg")
139 .scale(0.05)
140 .rotate_around_batch_center(
141 angle.shortest_rotation_towards(Angle::degrees(-90.0)),
142 )
143 .autocrop()
144 .centered_on(pt),
145 );
146 }
147 }
148 LaneType::LightRail => {
149 let track_width = lane.width / 4.0;
150 batch.push(
151 app.cs().light_rail_track,
152 lane.lane_center_pts
153 .must_shift_right((lane.width - track_width) / 2.5)
154 .make_polygons(track_width),
155 );
156 batch.push(
157 app.cs().light_rail_track,
158 lane.lane_center_pts
159 .must_shift_left((lane.width - track_width) / 2.5)
160 .make_polygons(track_width),
161 );
162
163 for (pt, angle) in lane
164 .lane_center_pts
165 .step_along(Distance::meters(3.0), Distance::meters(3.0))
166 {
167 let pt2 = pt.project_away(Distance::meters(1.0), angle);
169 batch.push(
170 app.cs().light_rail_track,
171 perp_line(Line::must_new(pt, pt2), lane.width).make_polygons(track_width),
172 );
173 }
174 }
175 LaneType::Buffer(style) => {
176 calculate_buffer_markings(app, style, lane, &mut batch);
177 }
178 LaneType::Footway | LaneType::SharedUse => {
179 for dir in [-1.0, 1.0] {
181 let pl = lane
182 .lane_center_pts
183 .shift_either_direction(dir * lane.width / 2.0)
184 .unwrap();
185 batch.extend(
186 Color::BLACK,
187 pl.exact_dashed_polygons(
188 Distance::meters(0.25),
189 Distance::meters(1.0),
190 Distance::meters(1.5),
191 ),
192 );
193 }
194 }
195 }
196
197 if road.is_private() {
198 if let Some(color) = app.cs().private_road {
199 batch.push(color.alpha(0.5), self.polygon.clone());
200 }
201 }
202
203 if self.zorder < 0 {
204 batch = batch.color(RewriteColor::ChangeAlpha(0.5));
205 }
206
207 batch
208 }
209
210 pub fn clear_rendering(&mut self) {
211 *self.draw_default.borrow_mut() = None;
212 }
213}
214
215impl Renderable for DrawLane {
216 fn get_id(&self) -> ID {
217 ID::Lane(self.id)
218 }
219
220 fn draw(&self, g: &mut GfxCtx, app: &dyn AppLike, _: &DrawOptions) {
221 let mut draw = self.draw_default.borrow_mut();
224 if draw.is_none() {
225 *draw = Some(g.upload(self.render(g, app)));
226 }
227 g.redraw(draw.as_ref().unwrap());
228 }
229
230 fn get_outline(&self, map: &Map) -> Tessellation {
231 let lane = map.get_l(self.id);
232 lane.lane_center_pts
233 .to_thick_boundary(lane.width, OUTLINE_THICKNESS)
234 .unwrap_or_else(|| Tessellation::from(self.polygon.clone()))
235 }
236
237 fn get_bounds(&self, map: &Map) -> Bounds {
238 map.get_l(self.id).get_thick_polygon().get_bounds()
239 }
240
241 fn contains_pt(&self, pt: Pt2D, _: &Map) -> bool {
242 self.polygon.contains_pt(pt)
243 }
244
245 fn get_zorder(&self) -> isize {
246 self.zorder
247 }
248}
249
250fn perp_line(l: Line, length: Distance) -> Line {
252 let pt1 = l.shift_right(length / 2.0).pt1();
253 let pt2 = l.shift_left(length / 2.0).pt1();
254 Line::must_new(pt1, pt2)
255}
256
257fn calculate_sidewalk_lines(lane: &Lane) -> Vec<Polygon> {
258 lane.lane_center_pts
259 .step_along(lane.width, lane.width)
260 .into_iter()
261 .map(|(pt, angle)| {
262 let pt2 = pt.project_away(Distance::meters(1.0), angle);
264 perp_line(Line::must_new(pt, pt2), lane.width).make_polygons(Distance::meters(0.25))
265 })
266 .collect()
267}
268
269fn calculate_parking_lines(lane: &Lane, map: &Map) -> Vec<Polygon> {
270 let leg_length = Distance::meters(1.0);
271
272 let mut result = Vec::new();
273 let num_spots = lane.number_parking_spots(map.get_config());
274 if num_spots > 0 {
275 for idx in 0..=num_spots {
276 let (pt, lane_angle) = lane
277 .lane_center_pts
278 .must_dist_along(map.get_config().street_parking_spot_length * (1.0 + idx as f64));
279 let perp_angle = if map.get_config().driving_side == DrivingSide::Right {
280 lane_angle.rotate_degs(270.0)
281 } else {
282 lane_angle.rotate_degs(90.0)
283 };
284 let t_pt = pt.project_away(lane.width * 0.4, perp_angle);
288 let p1 = t_pt.project_away(leg_length, perp_angle.opposite());
290 result.push(Line::must_new(t_pt, p1).make_polygons(Distance::meters(0.25)));
291 let p2 = t_pt.project_away(leg_length, lane_angle);
293 result.push(Line::must_new(t_pt, p2).make_polygons(Distance::meters(0.25)));
294 let p3 = t_pt.project_away(leg_length, lane_angle.opposite());
296 result.push(Line::must_new(t_pt, p3).make_polygons(Distance::meters(0.25)));
297 }
298 }
299
300 result
301}
302
303fn calculate_driving_lines(lane: &Lane, road: &Road) -> Vec<Polygon> {
307 let idx = lane.id.offset;
308
309 if idx == 0
312 || lane.dir != road.lanes[idx - 1].dir
313 || lane.lane_type != road.lanes[idx - 1].lane_type
314 {
315 return Vec::new();
316 }
317
318 let lane_edge_pts = if lane.dir == Direction::Fwd {
319 lane.lane_center_pts.must_shift_left(lane.width / 2.0)
320 } else {
321 lane.lane_center_pts.must_shift_right(lane.width / 2.0)
322 };
323 lane_edge_pts.dashed_lines(
324 Distance::meters(0.25),
325 Distance::meters(1.0),
326 Distance::meters(1.5),
327 )
328}
329
330fn calculate_turn_markings(map: &Map, lane: &Lane) -> Vec<Polygon> {
331 let i = map.get_i(lane.dst_i);
334 if i.outgoing_lanes.iter().all(|l| {
335 let l = map.get_l(*l);
336 l.lane_type != lane.lane_type
337 || l.id.road == lane.id.road
338 || map
339 .maybe_get_t(TurnID {
340 parent: i.id,
341 src: lane.id,
342 dst: l.id,
343 })
344 .is_some()
345 }) {
346 return Vec::new();
347 }
348
349 let mut turn_angles_roads: Vec<_> = map
351 .get_turns_from_lane(lane.id)
352 .iter()
353 .map(|t| (t.id.dst.road, t.angle()))
354 .collect();
355 turn_angles_roads.dedup_by(|(r1, _), (r2, _)| r1 == r2);
356
357 let turn_angles: Vec<_> = turn_angles_roads.iter().map(|(_, a)| a).collect();
358
359 let mut results = Vec::new();
360 let thickness = Distance::meters(0.2);
361
362 let location = Distance::meters(4.0);
364 let length_max = Distance::meters(3.0);
366 let width_max = Distance::meters(3.0)
368 .min(lane.width - 4.0 * thickness)
369 .max(Distance::ZERO);
370 let (left, right) = turn_angles
372 .iter()
373 .map(|a| {
374 width_max / 2.0
375 * (a.simple_shortest_rotation_towards(Angle::ZERO) / 2.0)
376 .to_radians()
377 .sin()
378 .min(0.5)
379 })
380 .fold((Distance::ZERO, Distance::ZERO), |(min, max), s| {
381 (s.min(min), s.max(max))
382 });
383 let offset = (right + left) / 2.0;
385
386 if lane.length() < length_max + location {
388 return Vec::new();
389 }
390
391 let (start_pt_unshifted, start_angle) = lane
392 .lane_center_pts
393 .must_dist_along(lane.length() - (length_max + location));
394 let start_pt = start_pt_unshifted.project_away(
395 offset.abs(),
396 start_angle.rotate_degs(if offset > Distance::ZERO { -90.0 } else { 90.0 }),
397 );
398
399 for turn_angle in turn_angles {
400 let half_angle =
401 Angle::degrees(turn_angle.simple_shortest_rotation_towards(Angle::ZERO) / 2.0);
402
403 let end_pt = start_pt
404 .project_away(
405 half_angle.normalized_radians().cos() * length_max,
406 start_angle,
407 )
408 .project_away(
409 half_angle.normalized_radians().sin().abs().min(0.5) * width_max,
410 start_angle
411 + if half_angle > Angle::ZERO {
412 Angle::degrees(90.0)
413 } else {
414 Angle::degrees(-90.0)
415 },
416 );
417
418 fn to_pt(pt: Pt2D) -> Point<f64> {
419 Point::new(pt.x(), pt.y())
420 }
421
422 fn from_pt(pt: Point<f64>) -> Pt2D {
423 Pt2D::new(pt.x, pt.y)
424 }
425
426 let intersection = InfiniteLine::from_pt_angle(start_pt, start_angle)
427 .intersection(&InfiniteLine::from_pt_angle(
428 end_pt,
429 start_angle + *turn_angle,
430 ))
431 .unwrap_or(start_pt);
432 let curve = if turn_angle.approx_parallel(
433 Angle::ZERO,
434 (length_max / (width_max / 2.0)).atan().to_degrees(),
435 ) || start_pt.approx_eq(intersection, geom::EPSILON_DIST)
436 {
437 CubicBezierSegment {
438 from: to_pt(start_pt),
439 ctrl1: to_pt(start_pt.project_away(length_max / 2.0, start_angle)),
440 ctrl2: to_pt(
441 end_pt.project_away(length_max / 2.0, (start_angle + *turn_angle).opposite()),
442 ),
443 to: to_pt(end_pt),
444 }
445 } else {
446 QuadraticBezierSegment {
447 from: to_pt(start_pt),
448 ctrl: to_pt(intersection),
449 to: to_pt(end_pt),
450 }
451 .to_cubic()
452 };
453
454 let pieces = 5;
455 let mut curve_pts: Vec<_> = (0..=pieces)
456 .map(|i| from_pt(curve.sample(1.0 / f64::from(pieces) * f64::from(i))))
457 .collect();
458 curve_pts.push(
460 curve_pts
461 .last()
462 .unwrap()
463 .project_away(thickness, start_angle + *turn_angle),
464 );
465 curve_pts.dedup();
466
467 results.push(
468 PolyLine::new(curve_pts)
469 .unwrap()
470 .make_arrow(thickness, ArrowCap::Triangle),
471 );
472 }
473
474 results
475}
476
477fn calculate_one_way_markings(lane: &Lane, road: &Road) -> Vec<Tessellation> {
478 let mut results = Vec::new();
479 if road
480 .lanes
481 .iter()
482 .any(|l| l.dir != lane.dir && l.lane_type == LaneType::Driving)
483 {
484 return results;
486 }
487
488 let arrow_len = Distance::meters(1.75);
489 let thickness = Distance::meters(0.25);
490 for (pt, angle) in lane.lane_center_pts.step_along_start_end(
492 Distance::meters(30.0),
493 arrow_len,
494 arrow_len + Distance::meters(8.0),
495 ) {
496 results.push(
497 PolyLine::must_new(vec![
498 pt.project_away(arrow_len / 2.0, angle.opposite()),
499 pt.project_away(arrow_len / 2.0, angle),
500 ])
501 .make_arrow(thickness * 2.0, ArrowCap::Triangle)
502 .to_outline(thickness / 2.0),
503 );
504 }
505 results
506}
507
508fn calculate_buffer_markings(
509 app: &dyn AppLike,
510 style: BufferType,
511 lane: &Lane,
512 batch: &mut GeomBatch,
513) {
514 let color = app.cs().general_road_marking;
515
516 let side_lines = |batch: &mut GeomBatch| {
517 let thickness = Distance::meters(0.25);
518 batch.push(
519 color,
520 lane.lane_center_pts
521 .must_shift_right((lane.width - thickness) / 2.0)
522 .make_polygons(thickness),
523 );
524 batch.push(
525 color,
526 lane.lane_center_pts
527 .must_shift_left((lane.width - thickness) / 2.0)
528 .make_polygons(thickness),
529 );
530 };
531
532 let stripes = |batch: &mut GeomBatch, step_size, buffer_ends| {
533 for (center, angle) in lane.lane_center_pts.step_along(step_size, buffer_ends) {
534 let thickness = Distance::meters(0.25);
536 let left = center.project_away(lane.width / 2.0 + thickness, angle.rotate_degs(45.0));
537 let right = center.project_away(
538 lane.width / 2.0 + thickness,
539 angle.rotate_degs(45.0).opposite(),
540 );
541 batch.push(
542 color,
543 Line::must_new(left, right).make_polygons(Distance::meters(0.3)),
544 );
545 }
546 };
547
548 let dark_grey = Color::grey(0.6);
549 let light_grey = Color::grey(0.8);
550 match style {
551 BufferType::Stripes | BufferType::Verge => {
554 side_lines(batch);
555 stripes(batch, Distance::meters(3.0), Distance::meters(5.0));
556 }
557 BufferType::FlexPosts => {
558 side_lines(batch);
559 stripes(batch, Distance::meters(3.0), Distance::meters(2.5));
560 for (pt, _) in lane
561 .lane_center_pts
562 .step_along(Distance::meters(3.0), Distance::meters(2.5 + 1.5))
563 {
564 let circle = Circle::new(pt, 0.3 * lane.width);
565 batch.push(light_grey, circle.to_polygon());
566 if let Ok(poly) = circle.to_outline(Distance::meters(0.25)) {
567 batch.push(dark_grey, poly);
568 }
569 }
570 }
571 BufferType::Planters => {
572 side_lines(batch);
573 stripes(batch, Distance::meters(3.0), Distance::meters(5.0));
575 for poly in lane.lane_center_pts.dashed_lines(
576 0.6 * lane.width,
577 Distance::meters(2.0),
578 Distance::meters(2.5),
579 ) {
580 batch.push(Color::hex("#108833"), poly.clone());
581 batch.push(
582 Color::hex("#A8882A"),
583 poly.to_outline(Distance::meters(0.25)),
584 );
585 }
586 }
587 BufferType::JerseyBarrier => {
588 let buffer_ends = Distance::meters(2.0);
589 if let Ok(pl) = lane
590 .lane_center_pts
591 .maybe_exact_slice(buffer_ends, lane.length() - buffer_ends)
592 {
593 batch.push(dark_grey, pl.make_polygons(0.8 * lane.width));
594 batch.push(light_grey, pl.make_polygons(0.5 * lane.width));
595 }
596 }
597 BufferType::Curb => {
598 batch.push(dark_grey, lane.get_thick_polygon());
599 }
600 }
601}