Skip to content

Commit 9455812

Browse files
Merge pull request #67 from iShape-Rust/feature/outline_tests
add more outline tests
2 parents 625bb3b + 4d83183 commit 9455812

1 file changed

Lines changed: 167 additions & 11 deletions

File tree

iOverlay/src/mesh/outline/offset.rs

Lines changed: 167 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -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)]
364361
mod 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

Comments
 (0)