blockworx/widget/
scene.rs1use std::collections::HashSet;
2
3use blockworx_doc::id::RouteId;
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_label: Option<RouteId>,
41 blocks: Option<LayerFn<'p, R>>,
42 pins: Option<LayerFn<'p, R>>,
43 routes: Option<LayerFn<'p, R>>,
44 overlay: Option<LayerFn<'p, R>>,
45}
46
47impl<'p, 'd, R: Renderer> DrawingPasses<'p, 'd, R> {
48 pub fn new(data: &'p Drawing<'d>) -> Self {
49 Self {
50 data,
51 shape_mode: Box::new(|_| RenderMode::Normal),
52 route_mode: Box::new(|_| RouteRenderMode::Normal),
53 suppressed_label: None,
54 blocks: None,
55 pins: None,
56 routes: None,
57 overlay: None,
58 }
59 }
60
61 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 pub fn route_mode(mut self, f: impl Fn(RouteId) -> RouteRenderMode + 'p) -> Self {
70 self.route_mode = Box::new(f);
71 self
72 }
73
74 pub fn suppress_route_label(mut self, route: RouteId) -> Self {
76 self.suppressed_label = Some(route);
77 self
78 }
79
80 pub fn blocks(mut self, f: impl FnOnce(&mut Style<'_, R>) + 'p) -> Self {
83 self.blocks = Some(Box::new(f));
84 self
85 }
86
87 pub fn pins(mut self, f: impl FnOnce(&mut Style<'_, R>) + 'p) -> Self {
90 self.pins = Some(Box::new(f));
91 self
92 }
93
94 pub fn routes(mut self, f: impl FnOnce(&mut Style<'_, R>) + 'p) -> Self {
97 self.routes = Some(Box::new(f));
98 self
99 }
100
101 pub fn overlay(mut self, f: impl FnOnce(&mut Style<'_, R>) + 'p) -> Self {
104 self.overlay = Some(Box::new(f));
105 self
106 }
107
108 pub fn draw(self, painter: &mut Style<'_, R>) {
109 let data = self.data;
110 let shape_mode = self.shape_mode;
111 let visible: Option<HashSet<HitId>> = painter
116 .visible_world_bounds()
117 .and_then(|viewport| data.visible_ids(viewport));
118 let shape_visible = |id: ShapeId| {
119 visible
120 .as_ref()
121 .is_none_or(|v| v.contains(&HitId::Shape(id)))
122 };
123 let route_visible = |id: RouteId| {
124 visible
125 .as_ref()
126 .is_none_or(|v| v.contains(&HitId::Route(id)))
127 };
128 {
129 let _s = tracing::info_span!("pass_images").entered();
130 for (id, shape) in data.images_layer() {
131 if shape_visible(id) {
132 shape.render_ng(data.shape_accents(), shape_mode(id), painter);
133 }
134 }
135 }
136 {
137 let _s = tracing::info_span!("pass_areas").entered();
138 for (id, area) in data.areas() {
139 if shape_visible(id) {
140 area.render_ng(data.shape_accents(), shape_mode(id), painter);
141 }
142 }
143 }
144 {
145 let _s = tracing::info_span!("pass_block_bodies").entered();
146 match self.blocks {
147 Some(f) => f(painter),
148 None => {
149 for (id, shape) in data.blocks_layer() {
150 if shape_visible(id) {
151 shape.render_ng(data.shape_accents(), shape_mode(id), painter);
152 }
153 }
154 }
155 }
156 }
157 {
158 let _s = tracing::info_span!("pass_icons").entered();
161 for (id, shape) in data.icons() {
162 if shape_visible(id) {
163 shape.render_ng(data.shape_accents(), shape_mode(id), painter);
164 }
165 }
166 }
167 {
168 let _s = tracing::info_span!("pass_block_pins").entered();
169 match self.pins {
170 Some(f) => f(painter),
171 None => {
172 for (id, shape) in data.blocks_layer() {
173 if shape_visible(id) {
174 shape.render_pins_ng(data.shape_accents(), shape_mode(id), painter);
175 }
176 }
177 }
178 }
179 }
180 {
181 let _s = tracing::info_span!("pass_ports").entered();
182 for (id, shape) in data.ports_layer() {
183 if shape_visible(id) {
184 shape.render_ng(data.shape_accents(), shape_mode(id), painter);
185 }
186 }
187 }
188 {
189 let _s = tracing::info_span!("pass_routes").entered();
190 match self.routes {
191 Some(f) => f(painter),
192 None => {
193 for (id, route) in data.auto_routes() {
194 if route_visible(id) {
195 let Some(geometry) = data.route_geometry(id) else {
197 continue;
198 };
199 let labels = if Some(id) == self.suppressed_label {
200 LabelPass::Skip
201 } else {
202 LabelPass::Draw
203 };
204 render_route(painter, &route, geometry, (self.route_mode)(id), labels);
205 }
206 }
207 }
208 }
209 }
210 {
211 let _s = tracing::info_span!("pass_texts").entered();
212 for (id, shape) in data.texts_layer() {
213 if shape_visible(id) {
214 shape.render_ng(data.shape_accents(), shape_mode(id), painter);
215 }
216 }
217 }
218 if let Some(f) = self.overlay {
219 f(painter);
220 }
221 }
222}