@@ -104,201 +104,94 @@ impl From<Table<GradientStops>> for Graphic {
104104 }
105105}
106106
107- // Local trait to convert types to Table<Graphic> (avoids orphan rule issues)
108- pub trait IntoGraphicTable {
109- fn into_graphic_table ( self ) -> Table < Graphic > ;
107+ /// Deeply flattens a graphic table, collecting only elements matching a specific variant (extracted by `extract_variant`)
108+ /// and discarding all other non-matching content. Recursion through `Graphic::Graphic` sub-tables composes transforms and opacity.
109+ fn flatten_graphic_table < T > ( content : Table < Graphic > , extract_variant : fn ( Graphic ) -> Option < Table < T > > ) -> Table < T > {
110+ fn compose_alpha_blending ( parent : AlphaBlending , child : AlphaBlending ) -> AlphaBlending {
111+ AlphaBlending {
112+ blend_mode : child. blend_mode ,
113+ opacity : parent. opacity * child. opacity ,
114+ fill : child. fill ,
115+ clip : child. clip ,
116+ }
117+ }
110118
111- /// Deeply flattens any vector content within a graphic table, discarding non-vector content, and returning a table of only vector elements.
112- fn into_flattened_vector_table ( self ) -> Table < Vector >
113- where
114- Self : std:: marker:: Sized ,
115- {
116- let content = self . into_graphic_table ( ) ;
117-
118- // TODO: Avoid mutable reference, instead return a new Table<Graphic>?
119- fn flatten_table ( output_vector_table : & mut Table < Vector > , current_graphic_table : Table < Graphic > ) {
120- for current_graphic_row in current_graphic_table. iter ( ) {
121- let current_graphic = current_graphic_row. element . clone ( ) ;
122- let source_node_id = * current_graphic_row. source_node_id ;
123-
124- match current_graphic {
125- // If we're allowed to recurse, flatten any tables we encounter
126- Graphic :: Graphic ( mut current_graphic_table) => {
127- // Apply the parent graphic's transform to all child elements
128- for graphic in current_graphic_table. iter_mut ( ) {
129- * graphic. transform = * current_graphic_row. transform * * graphic. transform ;
130- }
119+ fn flatten_recursive < T > ( output : & mut Table < T > , current_graphic_table : Table < Graphic > , extract_variant : fn ( Graphic ) -> Option < Table < T > > ) {
120+ for current_graphic_row in current_graphic_table. into_iter ( ) {
121+ let source_node_id = current_graphic_row. source_node_id ;
131122
132- flatten_table ( output_vector_table, current_graphic_table) ;
123+ match current_graphic_row. element {
124+ // Recurse into nested graphic tables, composing the parent's transform onto each child
125+ Graphic :: Graphic ( mut sub_table) => {
126+ for graphic in sub_table. iter_mut ( ) {
127+ * graphic. transform = current_graphic_row. transform * * graphic. transform ;
128+ * graphic. alpha_blending = compose_alpha_blending ( current_graphic_row. alpha_blending , * graphic. alpha_blending ) ;
133129 }
134- // Push any leaf Vector elements we encounter
135- Graphic :: Vector ( vector_table) => {
136- for current_vector_row in vector_table. iter ( ) {
137- output_vector_table. push ( TableRow {
138- element : current_vector_row. element . clone ( ) ,
139- transform : * current_graphic_row. transform * * current_vector_row. transform ,
140- alpha_blending : AlphaBlending {
141- blend_mode : current_vector_row. alpha_blending . blend_mode ,
142- opacity : current_graphic_row. alpha_blending . opacity * current_vector_row. alpha_blending . opacity ,
143- fill : current_vector_row. alpha_blending . fill ,
144- clip : current_vector_row. alpha_blending . clip ,
145- } ,
130+
131+ flatten_recursive ( output, sub_table, extract_variant) ;
132+ }
133+ // Try to extract the target variant; if it matches, push its rows with composed transform and opacity
134+ other => {
135+ if let Some ( typed_table) = extract_variant ( other) {
136+ for row in typed_table. into_iter ( ) {
137+ output. push ( TableRow {
138+ element : row. element ,
139+ transform : current_graphic_row. transform * row. transform ,
140+ alpha_blending : compose_alpha_blending ( current_graphic_row. alpha_blending , row. alpha_blending ) ,
146141 source_node_id,
147142 } ) ;
148143 }
149144 }
150- _ => { }
151145 }
152146 }
153147 }
154-
155- let mut output = Table :: new ( ) ;
156- flatten_table ( & mut output, content) ;
157- output
158148 }
159149
160- /// Deeply flattens any raster content within a graphic table, discarding non-raster content, and returning a table of only raster elements.
161- fn into_flattened_raster_table ( self ) -> Table < Raster < CPU > >
162- where
163- Self : std:: marker:: Sized ,
164- {
165- let content = self . into_graphic_table ( ) ;
166-
167- fn flatten_table ( output_raster_table : & mut Table < Raster < CPU > > , current_graphic_table : Table < Graphic > ) {
168- for current_graphic_row in current_graphic_table. iter ( ) {
169- let current_graphic = current_graphic_row. element . clone ( ) ;
170- let source_node_id = * current_graphic_row. source_node_id ;
171-
172- match current_graphic {
173- // If we're allowed to recurse, flatten any tables we encounter
174- Graphic :: Graphic ( mut current_graphic_table) => {
175- // Apply the parent graphic's transform to all child elements
176- for graphic in current_graphic_table. iter_mut ( ) {
177- * graphic. transform = * current_graphic_row. transform * * graphic. transform ;
178- }
150+ let mut output = Table :: new ( ) ;
151+ flatten_recursive ( & mut output, content, extract_variant) ;
152+ output
153+ }
179154
180- flatten_table ( output_raster_table, current_graphic_table) ;
181- }
182- // Push any leaf RasterCPU elements we encounter
183- Graphic :: RasterCPU ( raster_table) => {
184- for current_raster_row in raster_table. iter ( ) {
185- output_raster_table. push ( TableRow {
186- element : current_raster_row. element . clone ( ) ,
187- transform : * current_graphic_row. transform * * current_raster_row. transform ,
188- alpha_blending : AlphaBlending {
189- blend_mode : current_raster_row. alpha_blending . blend_mode ,
190- opacity : current_graphic_row. alpha_blending . opacity * current_raster_row. alpha_blending . opacity ,
191- fill : current_raster_row. alpha_blending . fill ,
192- clip : current_raster_row. alpha_blending . clip ,
193- } ,
194- source_node_id,
195- } ) ;
196- }
197- }
198- _ => { }
199- }
200- }
201- }
155+ /// Maps from a concrete element type to its corresponding `Graphic` enum variant,
156+ /// enabling type-directed casting of typed tables from a `Graphic` value.
157+ pub trait TryFromGraphic : Clone + Sized {
158+ fn try_from_graphic ( graphic : Graphic ) -> Option < Table < Self > > ;
159+ }
202160
203- let mut output = Table :: new ( ) ;
204- flatten_table ( & mut output , content ) ;
205- output
161+ impl TryFromGraphic for Vector {
162+ fn try_from_graphic ( graphic : Graphic ) -> Option < Table < Self > > {
163+ if let Graphic :: Vector ( t ) = graphic { Some ( t ) } else { None }
206164 }
165+ }
207166
208- /// Deeply flattens any color content within a graphic table, discarding non-color content, and returning a table of only color elements.
209- fn into_flattened_color_table ( self ) -> Table < Color >
210- where
211- Self : std:: marker:: Sized ,
212- {
213- let content = self . into_graphic_table ( ) ;
214-
215- fn flatten_table ( output_color_table : & mut Table < Color > , current_graphic_table : Table < Graphic > ) {
216- for current_graphic_row in current_graphic_table. iter ( ) {
217- let current_graphic = current_graphic_row. element . clone ( ) ;
218- let source_node_id = * current_graphic_row. source_node_id ;
219-
220- match current_graphic {
221- // If we're allowed to recurse, flatten any tables we encounter
222- Graphic :: Graphic ( mut current_graphic_table) => {
223- // Apply the parent graphic's transform to all child elements
224- for graphic in current_graphic_table. iter_mut ( ) {
225- * graphic. transform = * current_graphic_row. transform * * graphic. transform ;
226- }
167+ impl TryFromGraphic for Raster < CPU > {
168+ fn try_from_graphic ( graphic : Graphic ) -> Option < Table < Self > > {
169+ if let Graphic :: RasterCPU ( t) = graphic { Some ( t) } else { None }
170+ }
171+ }
227172
228- flatten_table ( output_color_table, current_graphic_table) ;
229- }
230- // Push any leaf Color elements we encounter
231- Graphic :: Color ( color_table) => {
232- for current_color_row in color_table. iter ( ) {
233- output_color_table. push ( TableRow {
234- element : * current_color_row. element ,
235- transform : * current_graphic_row. transform * * current_color_row. transform ,
236- alpha_blending : AlphaBlending {
237- blend_mode : current_color_row. alpha_blending . blend_mode ,
238- opacity : current_graphic_row. alpha_blending . opacity * current_color_row. alpha_blending . opacity ,
239- fill : current_color_row. alpha_blending . fill ,
240- clip : current_color_row. alpha_blending . clip ,
241- } ,
242- source_node_id,
243- } ) ;
244- }
245- }
246- _ => { }
247- }
248- }
249- }
173+ impl TryFromGraphic for Color {
174+ fn try_from_graphic ( graphic : Graphic ) -> Option < Table < Self > > {
175+ if let Graphic :: Color ( t) = graphic { Some ( t) } else { None }
176+ }
177+ }
250178
251- let mut output = Table :: new ( ) ;
252- flatten_table ( & mut output , content ) ;
253- output
179+ impl TryFromGraphic for GradientStops {
180+ fn try_from_graphic ( graphic : Graphic ) -> Option < Table < Self > > {
181+ if let Graphic :: Gradient ( t ) = graphic { Some ( t ) } else { None }
254182 }
183+ }
255184
256- /// Deeply flattens any gradient content within a graphic table, discarding non-gradient content, and returning a table of only gradient elements.
257- fn into_flattened_gradient_table ( self ) -> Table < GradientStops >
185+ // Local trait to convert types to Table<Graphic> (avoids orphan rule issues)
186+ pub trait IntoGraphicTable {
187+ fn into_graphic_table ( self ) -> Table < Graphic > ;
188+
189+ /// Deeply flattens any content of type `T` within a graphic table, discarding all other content, and returning a flat table of only `T` elements.
190+ fn into_flattened_table < T : TryFromGraphic > ( self ) -> Table < T >
258191 where
259192 Self : std:: marker:: Sized ,
260193 {
261- let content = self . into_graphic_table ( ) ;
262-
263- fn flatten_table ( output_gradient_table : & mut Table < GradientStops > , current_graphic_table : Table < Graphic > ) {
264- for current_graphic_row in current_graphic_table. iter ( ) {
265- let current_graphic = current_graphic_row. element . clone ( ) ;
266- let source_node_id = * current_graphic_row. source_node_id ;
267-
268- match current_graphic {
269- // If we're allowed to recurse, flatten any tables we encounter
270- Graphic :: Graphic ( mut current_graphic_table) => {
271- // Apply the parent graphic's transform to all child elements
272- for graphic in current_graphic_table. iter_mut ( ) {
273- * graphic. transform = * current_graphic_row. transform * * graphic. transform ;
274- }
275-
276- flatten_table ( output_gradient_table, current_graphic_table) ;
277- }
278- // Push any leaf GradientStops elements we encounter
279- Graphic :: Gradient ( gradient_table) => {
280- for current_gradient_row in gradient_table. iter ( ) {
281- output_gradient_table. push ( TableRow {
282- element : current_gradient_row. element . clone ( ) ,
283- transform : * current_graphic_row. transform * * current_gradient_row. transform ,
284- alpha_blending : AlphaBlending {
285- blend_mode : current_gradient_row. alpha_blending . blend_mode ,
286- opacity : current_graphic_row. alpha_blending . opacity * current_gradient_row. alpha_blending . opacity ,
287- fill : current_gradient_row. alpha_blending . fill ,
288- clip : current_gradient_row. alpha_blending . clip ,
289- } ,
290- source_node_id,
291- } ) ;
292- }
293- }
294- _ => { }
295- }
296- }
297- }
298-
299- let mut output = Table :: new ( ) ;
300- flatten_table ( & mut output, content) ;
301- output
194+ flatten_graphic_table ( self . into_graphic_table ( ) , T :: try_from_graphic)
302195 }
303196}
304197
0 commit comments