@@ -288,26 +288,23 @@ impl<P: FloatPointCompatible<T> + 'static, T: FloatNumber + 'static> OutlineSolv
288288 offset_overlay. clear ( ) ;
289289 segments. clear ( ) ;
290290
291- if area < 0 {
291+ let contour_fill_rule = if area < 0 {
292292 offset_overlay. options . output_direction = ContourDirection :: CounterClockwise ;
293293 segments. reserve ( self . outer_builder . capacity ( path. len ( ) ) ) ;
294294 self . outer_builder . build ( path, & self . adapter , & mut segments) ;
295-
296- offset_overlay. add_segments ( & segments) ;
297-
298- if let Some ( graph) = offset_overlay. build_graph_view ( FillRule :: Positive ) {
299- graph. extract_contours_into ( OverlayRule :: Subject , & mut bool_buffer, & mut flat_buffer) ;
300- }
295+ FillRule :: Positive
301296 } else {
302297 offset_overlay. options . output_direction = ContourDirection :: Clockwise ;
303298 segments. reserve ( self . inner_builder . capacity ( path. len ( ) ) ) ;
304299 self . inner_builder . build ( path, & self . adapter , & mut segments) ;
305300
306- offset_overlay. add_segments ( & segments) ;
301+ FillRule :: Negative
302+ } ;
307303
308- if let Some ( graph) = offset_overlay. build_graph_view ( FillRule :: Negative ) {
309- graph. extract_contours_into ( OverlayRule :: Subject , & mut bool_buffer, & mut flat_buffer) ;
310- }
304+ offset_overlay. add_segments ( & segments) ;
305+
306+ if let Some ( graph) = offset_overlay. build_graph_view ( contour_fill_rule) {
307+ graph. extract_contours_into ( OverlayRule :: Subject , & mut bool_buffer, & mut flat_buffer) ;
311308 }
312309
313310 overlay. add_flat_buffer ( & flat_buffer, Subject ) ;
@@ -362,12 +359,17 @@ impl<P: FloatPointCompatible<T> + 'static, T: FloatNumber + 'static> OutlineSolv
362359
363360#[ cfg( test) ]
364361mod tests {
362+ use crate :: core:: fill_rule:: FillRule ;
363+ use crate :: float:: simplify:: SimplifyShape ;
365364 use crate :: mesh:: outline:: offset:: OutlineOffset ;
366365 use crate :: mesh:: style:: { LineJoin , OutlineStyle } ;
367366 use alloc:: vec;
367+ use alloc:: vec:: Vec ;
368368 use core:: f32:: consts:: PI ;
369+ use i_shape:: base:: data:: { Path , Shape } ;
369370 use i_shape:: flat:: float:: FloatFlatContoursBuffer ;
370371 use i_shape:: float:: area:: Area ;
372+ use rand:: RngExt ;
371373
372374 #[ test]
373375 fn test_doc ( ) {
@@ -887,6 +889,119 @@ mod tests {
887889 } ;
888890 }
889891
892+ #[ test]
893+ fn test_star_bevel_0 ( ) {
894+ let r0 = 5.0 ;
895+ let r1 = 20.0 ;
896+ let pi = core:: f64:: consts:: PI ;
897+ for count in 8 ..24 {
898+ let mut angle = 0.0 ;
899+ while angle < pi {
900+ let shape = create_star ( r0, r1, count, angle) ;
901+ let mut offset = 0.0 ;
902+ let mut prev_area = 0.0 ;
903+ while offset < 10.0 {
904+ let min_area = pi * ( r0 + offset) . powi ( 2 ) ;
905+ let max_area = pi * ( r1 + offset) . powi ( 2 ) ;
906+
907+ let style = OutlineStyle :: new ( offset) ;
908+ let outline_shapes = shape. outline ( & style) ;
909+ let area = outline_shapes. area ( ) ;
910+
911+ assert ! ( area >= min_area) ;
912+ assert ! ( area <= max_area) ;
913+ assert ! ( prev_area < area) ;
914+
915+ offset += 0.5 ;
916+ prev_area = area;
917+ }
918+ angle += 1.0 ;
919+ }
920+ }
921+ }
922+
923+ #[ test]
924+ fn test_star_round_0 ( ) {
925+ let r0 = 5.0 ;
926+ let r1 = 20.0 ;
927+ let pi = core:: f64:: consts:: PI ;
928+ let join_angle = pi / 3.0 ;
929+ for count in 8 ..24 {
930+ let mut angle = 0.0 ;
931+ while angle < pi {
932+ let shape = create_star ( r0, r1, count, angle) ;
933+ let mut offset = 0.0 ;
934+ let mut prev_area = 0.0 ;
935+ while offset < 10.0 {
936+ let min_area = pi * ( r0 + offset) . powi ( 2 ) ;
937+ let max_area = pi * ( r1 + offset) . powi ( 2 ) ;
938+ let style = OutlineStyle :: new ( offset) . line_join ( LineJoin :: Round ( join_angle) ) ;
939+
940+ let outline_shapes = shape. outline ( & style) ;
941+ let area = outline_shapes. area ( ) ;
942+
943+ assert ! ( area >= min_area) ;
944+ assert ! ( area <= max_area) ;
945+ assert ! ( prev_area < area) ;
946+
947+ offset += 0.5 ;
948+ prev_area = area;
949+ }
950+ angle += 1.0 ;
951+ }
952+ }
953+ }
954+
955+ #[ test]
956+ fn test_random_0 ( ) {
957+ let style = OutlineStyle :: new ( 10.0 ) ;
958+ for _ in 0 ..100 {
959+ let shapes = random_float ( 100.0 , 100 ) . simplify_shape ( FillRule :: NonZero ) ;
960+ let base_area = shapes. area ( ) ;
961+ let outline_shapes = shapes. outline ( & style) ;
962+ let area = outline_shapes. area ( ) ;
963+ assert ! ( base_area < area) ;
964+ }
965+ }
966+
967+ #[ test]
968+ fn test_random_1 ( ) {
969+ let join_angle = core:: f64:: consts:: PI / 3.0 ;
970+ let style = OutlineStyle :: new ( 10.0 ) . line_join ( LineJoin :: Round ( join_angle) ) ;
971+ for _ in 0 ..100 {
972+ let shapes = random_float ( 100.0 , 100 ) . simplify_shape ( FillRule :: NonZero ) ;
973+ let base_area = shapes. area ( ) ;
974+ let outline_shapes = shapes. outline ( & style) ;
975+ let area = outline_shapes. area ( ) ;
976+ assert ! ( base_area < area) ;
977+ }
978+ }
979+
980+ #[ test]
981+ fn test_random_2 ( ) {
982+ let style = OutlineStyle :: new ( -10.0 ) ;
983+ for _ in 0 ..100 {
984+ let shapes = random_float ( 100.0 , 100 ) . simplify_shape ( FillRule :: NonZero ) ;
985+ let base_area = shapes. area ( ) ;
986+ let outline_shapes = shapes. outline ( & style) ;
987+ let area = outline_shapes. area ( ) ;
988+ assert ! ( base_area >= area) ;
989+ }
990+ }
991+
992+ #[ test]
993+ fn test_random_3 ( ) {
994+ let join_angle = core:: f64:: consts:: PI / 3.0 ;
995+ let style = OutlineStyle :: new ( -10.0 ) . line_join ( LineJoin :: Round ( join_angle) ) ;
996+ for _ in 0 ..100 {
997+ let shapes = random_float ( 100.0 , 100 ) . simplify_shape ( FillRule :: NonZero ) ;
998+ let base_area = shapes. area ( ) ;
999+ let outline_shapes = shapes. outline ( & style) ;
1000+ let area = outline_shapes. area ( ) ;
1001+ assert ! ( base_area >= area) ;
1002+ }
1003+ }
1004+
8901005 #[ test]
8911006 fn test_real_case_0 ( ) {
8921007 let main = vec ! [
@@ -1097,4 +1212,45 @@ mod tests {
10971212 assert ! ( shape[ 0 ] . len( ) < 1_000 ) ;
10981213 } ;
10991214 }
1215+
1216+ fn create_star ( r0 : f64 , r1 : f64 , count : usize , angle : f64 ) -> Shape < [ f64 ; 2 ] > {
1217+ let da = core:: f64:: consts:: PI / count as f64 ;
1218+ let mut a = angle;
1219+
1220+ let mut points = Vec :: new ( ) ;
1221+
1222+ for _ in 0 ..count {
1223+ let ( sn, cs) = a. sin_cos ( ) ;
1224+
1225+ let xr0 = r0 * cs;
1226+ let yr0 = r0 * sn;
1227+
1228+ a += da;
1229+
1230+ let ( sn, cs) = a. sin_cos ( ) ;
1231+ let xr1 = r1 * cs;
1232+ let yr1 = r1 * sn;
1233+
1234+ a += da;
1235+
1236+ points. push ( [ xr0, yr0] ) ;
1237+ points. push ( [ xr1, yr1] ) ;
1238+ }
1239+
1240+ [ points] . to_vec ( )
1241+ }
1242+
1243+ fn random_float ( radius : f64 , n : usize ) -> Path < [ f64 ; 2 ] > {
1244+ let a = 0.5 * radius;
1245+ let range = -a..=a;
1246+ let mut points = Vec :: with_capacity ( n) ;
1247+ let mut rng = rand:: rng ( ) ;
1248+ for _ in 0 ..n {
1249+ let x = rng. random_range ( range. clone ( ) ) ;
1250+ let y = rng. random_range ( range. clone ( ) ) ;
1251+ points. push ( [ x, y] )
1252+ }
1253+
1254+ points
1255+ }
11001256}
0 commit comments