11use itertools:: { Itertools , chain} ;
2- use monoxide_curves:: { CubicBezier , SpiroCurve , stroke:: StrokedSpiroCurve , xform:: AffineExt } ;
2+ use linesweeper:: { FillRule , binary_op} ;
3+ use monoxide_curves:: {
4+ CubicBezier , SpiroCurve , convert:: spiro_to_cube, stroke:: StrokedSpiroCurve , xform:: AffineExt ,
5+ } ;
36
47use crate :: { ast:: OutlineExpr , trace:: EvalTracer } ;
58
@@ -21,8 +24,7 @@ pub fn eval_outline<E: EvalTracer>(
2124 let output_size_before = out. len ( ) ;
2225 let id = dbg. spiro_to_bezier ( evaled. id ) ;
2326 for spiro in spiros {
24- let bez = monoxide_curves:: convert:: spiro_to_cube ( & spiro. points )
25- . map_err ( |e| EvalError :: CurveError ( id, e) ) ?;
27+ let bez = spiro_to_cube ( & spiro. points ) . map_err ( |e| EvalError :: CurveError ( id, e) ) ?;
2628 out. extend ( bez) ;
2729 }
2830 dbg. intermediate_output ( id, & out[ output_size_before..] ) ;
@@ -71,8 +73,8 @@ impl<Id: Copy> EvalValue<Id> {
7173 let mut beziers = vec ! [ ] ;
7274 let id = dbg. spiro_to_bezier ( self . id ) ;
7375 for spiro in & spiros {
74- let bez = monoxide_curves :: convert :: spiro_to_cube ( & spiro . points )
75- . map_err ( |e| EvalError :: CurveError ( id, e) ) ?;
76+ let bez =
77+ spiro_to_cube ( & spiro . points ) . map_err ( |e| EvalError :: CurveError ( id, e) ) ?;
7678 beziers. extend ( bez) ;
7779 }
7880 dbg. intermediate_output ( id, & beziers) ;
@@ -89,11 +91,27 @@ pub enum EvalError<Id> {
8991 ) ]
9092 StrokingABezier ( Id ) ,
9193
94+ #[ error(
95+ "boolean operation on multiple beziers unimplemented at {0}: try using a single bezier"
96+ ) ]
97+ BoolingMultipleBeziers ( Id ) ,
98+
99+ #[ error( "boolean operation error at {0}: {1}" ) ]
100+ Boolean ( Id , #[ source] linesweeper:: Error ) ,
101+
92102 #[ error( "curve evaluation error at {0}: {1}" ) ]
93103 CurveError ( Id , #[ source] monoxide_curves:: error:: Error ) ,
94104}
95105
96106fn eval_outline_internal < E : EvalTracer > ( expr : & OutlineExpr , dbg : & mut E ) -> EvalResult < E > {
107+ let to_bez = |spiro : & SpiroCurve , id, dbg : & mut E | {
108+ let bez = spiro_to_cube ( & spiro. points ) . map_err ( |e| EvalError :: CurveError ( id, e) ) ?;
109+ if E :: needs_evaluate_intermediate ( ) {
110+ dbg. intermediate_output ( id, & bez) ;
111+ }
112+ Ok ( bez)
113+ } ;
114+
97115 match expr {
98116 OutlineExpr :: Bezier ( cubic_bezier) => {
99117 let id = dbg. constructed_bezier ( cubic_bezier) ;
@@ -102,15 +120,42 @@ fn eval_outline_internal<E: EvalTracer>(expr: &OutlineExpr, dbg: &mut E) -> Eval
102120 OutlineExpr :: Spiro ( spiro) => {
103121 let id = dbg. constructed_spiro ( & spiro. points ) ;
104122
105- if E :: needs_evaluate_intermediate ( ) {
106- // convert to beziers if needed
107- let bez = monoxide_curves:: convert:: spiro_to_cube ( & spiro. points )
108- . map_err ( |e| EvalError :: CurveError ( id, e) ) ?;
109- dbg. intermediate_output ( id, & bez) ;
110- }
123+ // convert to beziers if needed
124+ to_bez ( spiro, id, dbg) ?;
111125
112126 Ok ( EvalValue :: spiro ( spiro. clone ( ) , id) )
113127 }
128+ OutlineExpr :: Bool ( op, lhs, rhs) => {
129+ let mut eval_half = |outline| {
130+ let evaled = eval_outline_internal ( outline, dbg) ?;
131+ let bez = match evaled. kind {
132+ EvalValueKind :: Beziers ( bez) => Ok ( bez) ,
133+ EvalValueKind :: Spiros ( spiro) => spiro
134+ . into_iter ( )
135+ . map ( |s| to_bez ( & s, evaled. id , dbg) )
136+ . flatten_ok ( )
137+ . try_collect ( ) ,
138+ } ?;
139+ let [ bez] = & bez[ ..] else {
140+ return Err ( EvalError :: BoolingMultipleBeziers ( evaled. id ) ) ;
141+ } ;
142+ Ok ( ( evaled. id , bez. to_kurbo ( ) ) )
143+ } ;
144+
145+ let ( lhs_id, lhs) = eval_half ( lhs) ?;
146+ let ( rhs_id, rhs) = eval_half ( rhs) ?;
147+
148+ let id = dbg. boolean_added ( & [ lhs_id, rhs_id] ) ;
149+ let merged = binary_op ( & lhs, & rhs, FillRule :: NonZero , * op)
150+ . map_err ( |e| EvalError :: Boolean ( id, e) ) ?
151+ . contours ( )
152+ . map ( |it| CubicBezier :: from_kurbo ( & it. path ) )
153+ . collect ( ) ;
154+ Ok ( EvalValue {
155+ kind : EvalValueKind :: Beziers ( merged) ,
156+ id,
157+ } )
158+ }
114159 OutlineExpr :: Stroked ( outline_expr, width) => {
115160 let evaled = eval_outline_internal ( outline_expr, dbg) ?;
116161 match evaled. kind {
@@ -176,10 +221,7 @@ fn eval_stroked<E: EvalTracer>(
176221 if E :: needs_evaluate_intermediate ( ) {
177222 // Convert both original spiro and the stroked spiro to beziers
178223 let bezs: Vec < _ > = chain ! ( eval_spiros, & out_spiros)
179- . map ( |spiro| {
180- monoxide_curves:: convert:: spiro_to_cube ( & spiro. points )
181- . map_err ( |e| EvalError :: CurveError ( id, e) )
182- } )
224+ . map ( |spiro| spiro_to_cube ( & spiro. points ) . map_err ( |e| EvalError :: CurveError ( id, e) ) )
183225 . flatten_ok ( )
184226 . try_collect ( ) ?;
185227 dbg. intermediate_output ( id, & bezs) ;
0 commit comments