1use std::collections::HashSet;
2
3use blockworx_doc::id::{RouteId, RouteLabelId};
4
5use crate::{
6 render::{LabelPass, RouteRenderMode, render_route},
7 shape::ShapeId,
8 state::RenderMode,
9 theme::Style,
10 widget::{drawing::Drawing, spatial::HitId},
11};
12use blockworx_paint::Renderer;
13
14type LayerFn<'p, R> = Box<dyn FnOnce(&mut Style<'_, R>) + 'p>;
15
16pub struct DrawingPasses<'p, 'd, R: Renderer> {
35 data: &'p Drawing<'d>,
36 shape_mode: Box<dyn Fn(ShapeId) -> RenderMode + 'p>,
37 route_mode: Box<dyn Fn(RouteId) -> RouteRenderMode + 'p>,
38 suppressed: Option<(RouteId, LabelPass)>,
40 blocks: Option<LayerFn<'p, R>>,
41 pins: Option<LayerFn<'p, R>>,
42 routes: Option<LayerFn<'p, R>>,
43 overlay: Option<LayerFn<'p, R>>,
44}
45
46impl<'p, 'd, R: Renderer> DrawingPasses<'p, 'd, R> {
47 pub fn new(data: &'p Drawing<'d>) -> Self {
48 Self {
49 data,
50 shape_mode: Box::new(|_| RenderMode::Normal),
51 route_mode: Box::new(|_| RouteRenderMode::Normal),
52 suppressed: None,
53 blocks: None,
54 pins: None,
55 routes: None,
56 overlay: None,
57 }
58 }
59
60 #[must_use]
63 pub fn shape_mode(mut self, f: impl Fn(ShapeId) -> RenderMode + 'p) -> Self {
64 self.shape_mode = Box::new(f);
65 self
66 }
67
68 #[must_use]
70 pub fn route_mode(mut self, f: impl Fn(RouteId) -> RouteRenderMode + 'p) -> Self {
71 self.route_mode = Box::new(f);
72 self
73 }
74
75 #[must_use]
78 pub fn suppress_route_labels(mut self, route: RouteId) -> Self {
79 self.suppressed = Some((route, LabelPass::Skip));
80 self
81 }
82
83 #[must_use]
86 pub fn suppress_route_label(mut self, route: RouteId, label: RouteLabelId) -> Self {
87 self.suppressed = Some((route, LabelPass::Omit(label)));
88 self
89 }
90
91 #[must_use]
94 pub fn blocks(mut self, f: impl FnOnce(&mut Style<'_, R>) + 'p) -> Self {
95 self.blocks = Some(Box::new(f));
96 self
97 }
98
99 #[must_use]
102 pub fn pins(mut self, f: impl FnOnce(&mut Style<'_, R>) + 'p) -> Self {
103 self.pins = Some(Box::new(f));
104 self
105 }
106
107 #[must_use]
110 pub fn routes(mut self, f: impl FnOnce(&mut Style<'_, R>) + 'p) -> Self {
111 self.routes = Some(Box::new(f));
112 self
113 }
114
115 #[must_use]
118 pub fn overlay(mut self, f: impl FnOnce(&mut Style<'_, R>) + 'p) -> Self {
119 self.overlay = Some(Box::new(f));
120 self
121 }
122
123 pub fn draw(self, painter: &mut Style<'_, R>) {
124 let _s = tracing::info_span!("scene").entered();
125 let data = self.data;
126 let shape_mode = self.shape_mode;
127 let visible: Option<HashSet<HitId>> = painter
132 .visible_world_bounds()
133 .and_then(|viewport| data.visible_ids(viewport));
134 let shape_visible = |id: ShapeId| {
135 visible
136 .as_ref()
137 .is_none_or(|v| v.contains(&HitId::Shape(id)))
138 };
139 let route_visible = |id: RouteId| {
140 visible
141 .as_ref()
142 .is_none_or(|v| v.contains(&HitId::Route(id)))
143 };
144 let visible_routes: Vec<RouteId> = data
147 .scope_route_ids()
148 .into_iter()
149 .filter(|&id| route_visible(id))
150 .collect();
151 {
152 let _s = tracing::info_span!("pass_images").entered();
153 for (id, shape) in data.images_layer() {
154 if shape_visible(id) {
155 shape.render_ng(data.shape_accents(), shape_mode(id), painter);
156 }
157 }
158 }
159 {
160 let _s = tracing::info_span!("pass_areas").entered();
161 for (id, area) in data.areas() {
162 if shape_visible(id) {
163 area.render_ng(data.shape_accents(), shape_mode(id), painter);
164 }
165 }
166 }
167 {
168 let _s = tracing::info_span!("pass_block_bodies").entered();
169 match self.blocks {
170 Some(f) => f(painter),
171 None => {
172 for (id, shape) in data.blocks_layer() {
173 if shape_visible(id) {
174 shape.render_ng(data.shape_accents(), shape_mode(id), painter);
175 }
176 }
177 }
178 }
179 }
180 {
181 let _s = tracing::info_span!("pass_icons").entered();
184 for (id, shape) in data.icons() {
185 if shape_visible(id) {
186 shape.render_ng(data.shape_accents(), shape_mode(id), painter);
187 }
188 }
189 }
190 {
191 let _s = tracing::info_span!("pass_block_pins").entered();
192 match self.pins {
193 Some(f) => f(painter),
194 None => {
195 for (id, shape) in data.blocks_layer() {
196 if shape_visible(id) {
197 shape.render_pins_ng(data.shape_accents(), shape_mode(id), painter);
198 }
199 }
200 }
201 }
202 }
203 {
204 let _s = tracing::info_span!("pass_ports").entered();
205 for (id, shape) in data.ports_layer() {
206 if shape_visible(id) {
207 shape.render_ng(data.shape_accents(), shape_mode(id), painter);
208 }
209 }
210 }
211 {
212 let _s = tracing::info_span!("pass_routes").entered();
213 if let Some(f) = self.routes {
214 f(painter);
215 } else {
216 let hops = data.hops(&visible_routes);
217 for &id in &visible_routes {
218 let Some(route) = data.auto_route(id) else {
219 continue;
220 };
221 let Some(course) = data.course(id, hops.of(id)) else {
222 continue;
223 };
224 let labels = match self.suppressed {
225 Some((route, pass)) if route == id => pass,
226 _ => LabelPass::Draw,
227 };
228 render_route(painter, &route, course, (self.route_mode)(id), labels);
229 }
230 }
231 }
232 {
233 let _s = tracing::info_span!("pass_texts").entered();
234 for (id, shape) in data.texts_layer() {
235 if shape_visible(id) {
236 shape.render_ng(data.shape_accents(), shape_mode(id), painter);
237 }
238 }
239 }
240 {
241 let _s = tracing::info_span!("pass_standing_conflicts").entered();
251 let shapes: Vec<ShapeId> = data
252 .child_blocks()
253 .into_iter()
254 .map(|(id, _)| ShapeId::Rect(id))
255 .chain(
256 data.block_pins(data.current_scope())
257 .into_iter()
258 .map(|(id, _)| ShapeId::Port(id)),
259 )
260 .filter(|&id| shape_visible(id))
261 .collect();
262 let conflicts = data.standing_conflicts(&shapes);
263 for mark in conflicts.overlaps.into_iter().chain(conflicts.crossings) {
264 crate::render::draw_standing_conflict(mark, painter);
265 }
266 }
267 if let Some(f) = self.overlay {
268 f(painter);
269 }
270 }
271}