├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── examples ├── overlay_editor │ ├── .gitignore │ ├── Cargo.toml │ ├── run_build-wasm.sh │ └── src │ │ ├── app │ │ ├── boolean │ │ │ ├── content.rs │ │ │ ├── control.rs │ │ │ ├── mod.rs │ │ │ └── workspace.rs │ │ ├── design.rs │ │ ├── fill_option.rs │ │ ├── main.rs │ │ ├── mod.rs │ │ ├── outline │ │ │ ├── content.rs │ │ │ ├── control.rs │ │ │ ├── mod.rs │ │ │ └── workspace.rs │ │ ├── solver_option.rs │ │ ├── string │ │ │ ├── content.rs │ │ │ ├── control.rs │ │ │ ├── mod.rs │ │ │ └── workspace.rs │ │ └── stroke │ │ │ ├── content.rs │ │ │ ├── control.rs │ │ │ ├── mod.rs │ │ │ └── workspace.rs │ │ ├── data │ │ ├── boolean.rs │ │ ├── mod.rs │ │ ├── outline.rs │ │ ├── resource.rs │ │ ├── string.rs │ │ └── stroke.rs │ │ ├── draw │ │ ├── mod.rs │ │ ├── path.rs │ │ ├── shape.rs │ │ ├── varicolored.rs │ │ └── vectors.rs │ │ ├── geom │ │ ├── camera.rs │ │ ├── mod.rs │ │ └── vector.rs │ │ ├── lib.rs │ │ ├── main.rs │ │ ├── point_editor │ │ ├── mod.rs │ │ ├── point.rs │ │ ├── state.rs │ │ └── widget.rs │ │ ├── sheet │ │ ├── mod.rs │ │ ├── state.rs │ │ └── widget.rs │ │ └── web.rs ├── tests │ ├── boolean │ │ ├── test_0.json │ │ ├── test_1.json │ │ ├── test_10.json │ │ ├── test_2.json │ │ ├── test_3.json │ │ ├── test_4.json │ │ ├── test_5.json │ │ ├── test_6.json │ │ ├── test_7.json │ │ ├── test_8.json │ │ └── test_9.json │ ├── outline │ │ ├── test_0.json │ │ ├── test_1.json │ │ ├── test_2.json │ │ ├── test_3.json │ │ └── test_4.json │ ├── sc_boolean_to_web.py │ ├── sc_clean_boolean_schema.py │ ├── sc_outline_to_web.py │ ├── sc_string_to_web.py │ ├── sc_stroke_to_web.py │ ├── string │ │ ├── test_0.json │ │ ├── test_1.json │ │ ├── test_2.json │ │ └── test_3.json │ └── stroke │ │ ├── test_0.json │ │ ├── test_1.json │ │ ├── test_2.json │ │ ├── test_3.json │ │ ├── test_4.json │ │ ├── test_5.json │ │ ├── test_6.json │ │ └── test_7.json └── web_tests │ ├── boolean_tests.json │ ├── outline_tests.json │ ├── string_tests.json │ └── stroke_tests.json ├── iOverlay ├── CHANGELOG.md ├── Cargo.toml ├── README.md ├── readme │ ├── ab.svg │ ├── balloons.svg │ ├── difference_ab.svg │ ├── difference_ba.svg │ ├── example_clip.svg │ ├── example_offseting_path.svg │ ├── example_offseting_polygon.svg │ ├── example_slice.svg │ ├── example_union.svg │ ├── exclusion.svg │ ├── intersection.svg │ ├── line_cap_butt.svg │ ├── line_cap_custom.svg │ ├── line_cap_round.svg │ ├── line_cap_square.svg │ ├── line_join_bevel.svg │ ├── line_join_mitter.svg │ ├── line_join_round.svg │ ├── overlay_rules.md │ └── union.svg ├── src │ ├── bind │ │ ├── mod.rs │ │ ├── segment.rs │ │ └── solver.rs │ ├── build │ │ ├── boolean.rs │ │ ├── builder.rs │ │ ├── graph.rs │ │ ├── mod.rs │ │ ├── offset.rs │ │ ├── string.rs │ │ └── util.rs │ ├── core │ │ ├── divide.rs │ │ ├── extract.rs │ │ ├── fill_rule.rs │ │ ├── graph.rs │ │ ├── link.rs │ │ ├── mod.rs │ │ ├── nearest_vector.rs │ │ ├── overlay.rs │ │ ├── overlay_rule.rs │ │ ├── simplify.rs │ │ └── solver.rs │ ├── float │ │ ├── clip.rs │ │ ├── graph.rs │ │ ├── mod.rs │ │ ├── overlay.rs │ │ ├── simplify.rs │ │ ├── single.rs │ │ ├── slice.rs │ │ ├── string_graph.rs │ │ └── string_overlay.rs │ ├── geom │ │ ├── end.rs │ │ ├── id_point.rs │ │ ├── line_range.rs │ │ ├── mod.rs │ │ ├── v_segment.rs │ │ └── x_segment.rs │ ├── lib.rs │ ├── mesh │ │ ├── extract.rs │ │ ├── graph.rs │ │ ├── math.rs │ │ ├── miter.rs │ │ ├── mod.rs │ │ ├── outline │ │ │ ├── builder.rs │ │ │ ├── builder_join.rs │ │ │ ├── mod.rs │ │ │ ├── offset.rs │ │ │ └── section.rs │ │ ├── overlay.rs │ │ ├── rotator.rs │ │ ├── stroke │ │ │ ├── builder.rs │ │ │ ├── builder_cap.rs │ │ │ ├── builder_join.rs │ │ │ ├── mod.rs │ │ │ ├── offset.rs │ │ │ └── section.rs │ │ ├── style.rs │ │ └── subject.rs │ ├── segm │ │ ├── boolean.rs │ │ ├── build.rs │ │ ├── merge.rs │ │ ├── mod.rs │ │ ├── offset.rs │ │ ├── segment.rs │ │ ├── string.rs │ │ └── winding.rs │ ├── split │ │ ├── cross_solver.rs │ │ ├── fragment.rs │ │ ├── grid_layout.rs │ │ ├── line_mark.rs │ │ ├── mod.rs │ │ ├── snap_radius.rs │ │ ├── solver.rs │ │ ├── solver_fragment.rs │ │ ├── solver_list.rs │ │ └── solver_tree.rs │ ├── string │ │ ├── clip.rs │ │ ├── extract.rs │ │ ├── filter.rs │ │ ├── graph.rs │ │ ├── line.rs │ │ ├── mod.rs │ │ ├── overlay.rs │ │ ├── rule.rs │ │ ├── slice.rs │ │ └── split.rs │ ├── util │ │ ├── log.rs │ │ ├── mod.rs │ │ └── sort.rs │ └── vector │ │ ├── edge.rs │ │ ├── extract_vectors.rs │ │ └── mod.rs └── tests │ ├── board_tests.rs │ ├── boolean │ ├── test_0.json │ ├── test_1.json │ ├── test_10.json │ ├── test_100.json │ ├── test_101.json │ ├── test_102.json │ ├── test_103.json │ ├── test_104.json │ ├── test_105.json │ ├── test_106.json │ ├── test_107.json │ ├── test_108.json │ ├── test_109.json │ ├── test_11.json │ ├── test_110.json │ ├── test_111.json │ ├── test_112.json │ ├── test_113.json │ ├── test_114.json │ ├── test_115.json │ ├── test_116.json │ ├── test_117.json │ ├── test_118.json │ ├── test_119.json │ ├── test_12.json │ ├── test_120.json │ ├── test_121.json │ ├── test_122.json │ ├── test_123.json │ ├── test_124.json │ ├── test_125.json │ ├── test_126.json │ ├── test_127.json │ ├── test_128.json │ ├── test_129.json │ ├── test_13.json │ ├── test_130.json │ ├── test_131.json │ ├── test_132.json │ ├── test_133.json │ ├── test_134.json │ ├── test_135.json │ ├── test_136.json │ ├── test_137.json │ ├── test_138.json │ ├── test_139.json │ ├── test_14.json │ ├── test_140.json │ ├── test_141.json │ ├── test_142.json │ ├── test_143.json │ ├── test_144.json │ ├── test_145.json │ ├── test_146.json │ ├── test_147.json │ ├── test_148.json │ ├── test_149.json │ ├── test_15.json │ ├── test_150.json │ ├── test_151.json │ ├── test_152.json │ ├── test_153.json │ ├── test_154.json │ ├── test_155.json │ ├── test_156.json │ ├── test_157.json │ ├── test_158.json │ ├── test_159.json │ ├── test_16.json │ ├── test_160.json │ ├── test_161.json │ ├── test_17.json │ ├── test_18.json │ ├── test_19.json │ ├── test_2.json │ ├── test_20.json │ ├── test_21.json │ ├── test_22.json │ ├── test_23.json │ ├── test_24.json │ ├── test_25.json │ ├── test_26.json │ ├── test_27.json │ ├── test_28.json │ ├── test_29.json │ ├── test_3.json │ ├── test_30.json │ ├── test_31.json │ ├── test_32.json │ ├── test_33.json │ ├── test_34.json │ ├── test_35.json │ ├── test_36.json │ ├── test_37.json │ ├── test_38.json │ ├── test_39.json │ ├── test_4.json │ ├── test_40.json │ ├── test_41.json │ ├── test_42.json │ ├── test_43.json │ ├── test_44.json │ ├── test_45.json │ ├── test_46.json │ ├── test_47.json │ ├── test_48.json │ ├── test_49.json │ ├── test_5.json │ ├── test_50.json │ ├── test_51.json │ ├── test_52.json │ ├── test_53.json │ ├── test_54.json │ ├── test_55.json │ ├── test_56.json │ ├── test_57.json │ ├── test_58.json │ ├── test_59.json │ ├── test_6.json │ ├── test_60.json │ ├── test_61.json │ ├── test_62.json │ ├── test_63.json │ ├── test_64.json │ ├── test_65.json │ ├── test_66.json │ ├── test_67.json │ ├── test_68.json │ ├── test_69.json │ ├── test_7.json │ ├── test_70.json │ ├── test_71.json │ ├── test_72.json │ ├── test_73.json │ ├── test_74.json │ ├── test_75.json │ ├── test_76.json │ ├── test_77.json │ ├── test_78.json │ ├── test_79.json │ ├── test_8.json │ ├── test_80.json │ ├── test_81.json │ ├── test_82.json │ ├── test_83.json │ ├── test_84.json │ ├── test_85.json │ ├── test_86.json │ ├── test_87.json │ ├── test_88.json │ ├── test_89.json │ ├── test_9.json │ ├── test_90.json │ ├── test_91.json │ ├── test_92.json │ ├── test_93.json │ ├── test_94.json │ ├── test_95.json │ ├── test_96.json │ ├── test_97.json │ ├── test_98.json │ └── test_99.json │ ├── data.rs │ ├── direction_tests.rs │ ├── doc_tests.rs │ ├── dynamic_tests.rs │ ├── empty_tests.rs │ ├── fill_rule_tests.rs │ ├── float_overlay_tests.rs │ ├── fragment_tests.rs │ ├── outline │ ├── test_0.json │ ├── test_1.json │ ├── test_10.json │ ├── test_2.json │ ├── test_3.json │ ├── test_4.json │ ├── test_5.json │ ├── test_6.json │ ├── test_7.json │ ├── test_8.json │ └── test_9.json │ ├── overlay_tests.rs │ ├── simplify_tests.rs │ ├── slice_tests.rs │ ├── string │ ├── test_0.json │ ├── test_1.json │ ├── test_10.json │ ├── test_11.json │ ├── test_2.json │ ├── test_3.json │ ├── test_4.json │ ├── test_5.json │ ├── test_6.json │ ├── test_7.json │ ├── test_8.json │ └── test_9.json │ ├── string_tests.rs │ ├── stroke │ ├── test_0.json │ ├── test_1.json │ ├── test_2.json │ ├── test_3.json │ └── test_4.json │ ├── util.rs │ └── vector_tests.rs ├── performance └── rust_app │ ├── Cargo.toml │ ├── run.sh │ └── src │ ├── main.rs │ └── test │ ├── mod.rs │ ├── test_0_checkerboard.rs │ ├── test_1_not_overlap.rs │ ├── test_2_lines_net.rs │ ├── test_3_spiral.rs │ ├── test_4_windows.rs │ ├── test_5_nested_squares.rs │ ├── test_6_corrosion.rs │ ├── test_7_concentric.rs │ ├── test_8_wind_mill.rs │ └── util.rs └── readme ├── .DS_Store ├── ab.svg ├── balloons.svg ├── difference_ab.svg ├── difference_ba.svg ├── example_clip.svg ├── example_offseting_path.svg ├── example_offseting_polygon.svg ├── example_slice.svg ├── example_union.svg ├── exclusion.svg ├── intersection.svg ├── line_cap_butt.svg ├── line_cap_custom.svg ├── line_cap_round.svg ├── line_cap_square.svg ├── line_join_bevel.svg ├── line_join_mitter.svg ├── line_join_round.svg ├── overlay_rules.md └── union.svg /.gitattributes: -------------------------------------------------------------------------------- 1 | /performance/* linguist-vendored 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | # IntelliJ IDEA specific 17 | **/.idea/ 18 | **/*.iml 19 | 20 | # Visual Studio Code specific 21 | **/.vscode/ 22 | 23 | # macOS system files 24 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 iShape-Rust 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | iOverlay/README.md -------------------------------------------------------------------------------- /examples/overlay_editor/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | # IntelliJ IDEA specific 17 | **/.idea/ 18 | **/*.iml 19 | 20 | # Visual Studio Code specific 21 | **/.vscode/ 22 | 23 | # macOS system files 24 | .DS_Store -------------------------------------------------------------------------------- /examples/overlay_editor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "overlay_editor" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [profile.release] 8 | opt-level = 3 9 | lto = false 10 | codegen-units = 1 11 | 12 | [lib] 13 | crate-type = ["cdylib"] 14 | 15 | [dependencies] 16 | 17 | #iced = { path = "../../../../iced", features = ["wgpu", "advanced"] } 18 | iced = { git = "https://github.com/iced-rs/iced", branch = "master", features = ["wgpu", "advanced", "fira-sans"] } 19 | 20 | serde = { version = "^1.0", features = ["derive"] } 21 | serde_json = "^1.0" 22 | 23 | wasm-bindgen = "~0.2.95" 24 | 25 | log = "0.4.22" 26 | console_log = "^1.0.0" 27 | console_error_panic_hook = "^0" 28 | 29 | i_mesh = "^0.3.0" 30 | i_triangle = { version = "^0.35.0", features = ["serde"] } 31 | 32 | #i_triangle = { path = "../../../../iShape/iTriangle/iTriangle", default-features = true, features = ["serde"] } 33 | #i_mesh = { path = "../../../../iShape/iMesh/iMesh" } 34 | 35 | #ICED_BACKEND=wgpu cargo r -r -------------------------------------------------------------------------------- /examples/overlay_editor/run_build-wasm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | wasm-pack build --release --target web 4 | wasm-pack pack -------------------------------------------------------------------------------- /examples/overlay_editor/src/app/boolean/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod content; 2 | mod workspace; 3 | mod control; -------------------------------------------------------------------------------- /examples/overlay_editor/src/app/fill_option.rs: -------------------------------------------------------------------------------- 1 | use i_triangle::i_overlay::core::fill_rule::FillRule; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] 4 | pub(crate) enum FillOption { 5 | #[default] 6 | NonZero, 7 | EvenOdd, 8 | Positive, 9 | Negative, 10 | } 11 | 12 | impl FillOption { 13 | pub(crate) const ALL: [FillOption; 4] = [ 14 | FillOption::NonZero, 15 | FillOption::EvenOdd, 16 | FillOption::Positive, 17 | FillOption::Negative, 18 | ]; 19 | 20 | pub(crate) fn fill_rule(&self) -> FillRule { 21 | match self { 22 | FillOption::NonZero => FillRule::NonZero, 23 | FillOption::EvenOdd => FillRule::EvenOdd, 24 | FillOption::Positive => FillRule::Positive, 25 | FillOption::Negative => FillRule::Negative, 26 | } 27 | } 28 | } 29 | 30 | impl std::fmt::Display for FillOption { 31 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 32 | write!( 33 | f, 34 | "{}", 35 | match self { 36 | FillOption::NonZero => "NonZero", 37 | FillOption::EvenOdd => "EvenOdd", 38 | FillOption::Positive => "Positive", 39 | FillOption::Negative => "Negative", 40 | } 41 | ) 42 | } 43 | } -------------------------------------------------------------------------------- /examples/overlay_editor/src/app/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod main; 2 | mod design; 3 | mod boolean; 4 | mod string; 5 | mod fill_option; 6 | mod solver_option; 7 | mod stroke; 8 | mod outline; -------------------------------------------------------------------------------- /examples/overlay_editor/src/app/outline/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod content; 2 | mod workspace; 3 | mod control; -------------------------------------------------------------------------------- /examples/overlay_editor/src/app/solver_option.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] 2 | pub(crate) enum SolverOption { 3 | #[default] 4 | Auto, 5 | Average, 6 | Precise, 7 | } 8 | 9 | impl SolverOption { 10 | pub(crate) const ALL: [SolverOption; 3] = [ 11 | SolverOption::Auto, 12 | SolverOption::Average, 13 | SolverOption::Precise 14 | ]; 15 | } 16 | 17 | impl std::fmt::Display for SolverOption { 18 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 19 | write!( 20 | f, 21 | "{}", 22 | match self { 23 | SolverOption::Auto => "Auto", 24 | SolverOption::Average => "Average", 25 | SolverOption::Precise => "Precise", 26 | } 27 | ) 28 | } 29 | } -------------------------------------------------------------------------------- /examples/overlay_editor/src/app/string/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod content; 2 | mod workspace; 3 | mod control; -------------------------------------------------------------------------------- /examples/overlay_editor/src/app/stroke/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod content; 2 | mod workspace; 3 | mod control; -------------------------------------------------------------------------------- /examples/overlay_editor/src/data/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod resource; 2 | pub(crate) mod boolean; 3 | pub(crate) mod string; 4 | pub(crate) mod stroke; 5 | pub(crate) mod outline; -------------------------------------------------------------------------------- /examples/overlay_editor/src/data/resource.rs: -------------------------------------------------------------------------------- 1 | use crate::data::string::StringResource; 2 | use crate::data::boolean::BooleanResource; 3 | use crate::data::stroke::StrokeResource; 4 | use crate::data::outline::OutlineResource; 5 | 6 | pub struct AppResource { 7 | pub(crate) boolean: BooleanResource, 8 | pub(crate) string: StringResource, 9 | pub(crate) stroke: StrokeResource, 10 | pub(crate) outline: OutlineResource, 11 | } 12 | 13 | impl AppResource { 14 | #[cfg(not(target_arch = "wasm32"))] 15 | pub(crate) fn with_paths(boolean: &str, string: &str, stroke: &str, outline: &str) -> Self { 16 | Self { 17 | boolean: BooleanResource::with_path(boolean), 18 | string: StringResource::with_path(string), 19 | stroke: StrokeResource::with_path(stroke), 20 | outline: OutlineResource::with_path(outline), 21 | } 22 | } 23 | 24 | #[cfg(target_arch = "wasm32")] 25 | pub fn with_content(boolean: &String, string: &String, stroke: &String, outline: &String) -> Self { 26 | Self { 27 | boolean: BooleanResource::with_content(boolean), 28 | string: StringResource::with_content(string), 29 | stroke: StrokeResource::with_content(stroke), 30 | outline: OutlineResource::with_content(outline), 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /examples/overlay_editor/src/draw/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod shape; 2 | pub(crate) mod path; 3 | pub(crate) mod varicolored; 4 | pub(crate) mod vectors; -------------------------------------------------------------------------------- /examples/overlay_editor/src/geom/camera.rs: -------------------------------------------------------------------------------- 1 | use i_triangle::i_overlay::i_float::int::point::IntPoint; 2 | use i_triangle::i_overlay::i_float::int::rect::IntRect; 3 | use iced::{Size, Vector}; 4 | 5 | #[derive(Debug, Clone, Copy)] 6 | pub(crate) struct Camera { 7 | pub(crate) scale: f32, 8 | pub(crate) i_scale: f32, 9 | pub(crate) size: Size, 10 | pub(crate) pos: Vector, 11 | } 12 | 13 | impl Camera { 14 | 15 | pub(crate) fn empty() -> Self { 16 | Self { scale: 0.0, i_scale: 0.0, size: Size::ZERO, pos: Vector::new(0.0 ,0.0) } 17 | } 18 | 19 | pub(crate) fn is_empty(&self) -> bool { 20 | self.scale < 0.000_000_000_1 21 | } 22 | 23 | pub(crate) fn set_scale(&mut self, scale: f32) { 24 | self.scale = scale; 25 | self.i_scale = 1.0 / scale; 26 | } 27 | 28 | pub(crate) fn is_not_empty(&self) -> bool { 29 | self.scale > 0.0 30 | } 31 | 32 | pub(crate) fn new(rect: IntRect, size: Size) -> Self { 33 | let w_pow = rect.width().ilog2() as usize; 34 | let h_pow = rect.height().ilog2() as usize; 35 | 36 | let width = (1 << w_pow) as f32; 37 | let height = (1 << h_pow) as f32; 38 | let sw = size.width / width; 39 | let sh = size.height / height; 40 | 41 | let scale = 0.25 * sw.min(sh); 42 | let i_scale = 1.0 / scale; 43 | let x = 0.5 * (rect.min_x + rect.max_x) as f32; 44 | let y = 0.5 * (rect.min_y + rect.max_y) as f32; 45 | let pos = Vector::new(x, y); 46 | 47 | Camera { scale, i_scale, size, pos } 48 | } 49 | 50 | #[inline] 51 | pub(crate) fn world_to_screen(&self, view_left_top: Vector, world: IntPoint) -> Vector { 52 | let x = self.scale * (world.x as f32 - self.pos.x) + view_left_top.x + 0.5 * self.size.width; 53 | let y = self.scale * (self.pos.y - world.y as f32) + view_left_top.y + 0.5 * self.size.height; 54 | Vector { x, y } 55 | } 56 | 57 | #[inline] 58 | pub(crate) fn int_world_to_view(&self, world: IntPoint) -> Vector { 59 | self.world_to_view(Vector::new(world.x as f32, world.y as f32)) 60 | } 61 | 62 | #[inline] 63 | pub(crate) fn world_to_view(&self, world: Vector) -> Vector { 64 | let x = self.scale * (world.x - self.pos.x) + 0.5 * self.size.width; 65 | let y = self.scale * (self.pos.y - world.y) + 0.5 * self.size.height; 66 | Vector { x, y } 67 | } 68 | 69 | #[inline] 70 | pub(crate) fn view_to_world(&self, view: Vector) -> Vector { 71 | let x = self.i_scale * (view.x - 0.5 * self.size.width) + self.pos.x; 72 | let y = self.i_scale * (0.5 * self.size.height - view.y) + self.pos.y; 73 | Vector { x, y } 74 | } 75 | 76 | #[inline] 77 | pub(crate) fn view_distance_to_world(&self, view_distance: Vector) -> Vector { 78 | let x = view_distance.x * self.i_scale; 79 | let y = -view_distance.y * self.i_scale; 80 | Vector { x, y } 81 | } 82 | } -------------------------------------------------------------------------------- /examples/overlay_editor/src/geom/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod camera; 2 | pub(crate) mod vector; -------------------------------------------------------------------------------- /examples/overlay_editor/src/geom/vector.rs: -------------------------------------------------------------------------------- 1 | use i_triangle::i_overlay::i_float::int::point::IntPoint; 2 | use iced::{Point, Vector}; 3 | 4 | pub(crate) trait VectorExt { 5 | fn round(&self) -> IntPoint; 6 | fn point(point: Point) -> Self; 7 | } 8 | 9 | impl VectorExt for Vector { 10 | fn round(&self) -> IntPoint { 11 | IntPoint::new( 12 | self.x.round() as i32, 13 | self.y.round() as i32, 14 | ) 15 | } 16 | fn point(point: Point) -> Self { 17 | Self { x: point.x, y: point.y } 18 | } 19 | } -------------------------------------------------------------------------------- /examples/overlay_editor/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod web; 2 | mod data; 3 | mod app; 4 | mod draw; 5 | mod point_editor; 6 | mod sheet; 7 | mod geom; -------------------------------------------------------------------------------- /examples/overlay_editor/src/main.rs: -------------------------------------------------------------------------------- 1 | mod data; 2 | mod app; 3 | mod draw; 4 | mod point_editor; 5 | mod sheet; 6 | mod geom; 7 | 8 | use iced::application; 9 | use crate::app::main::EditorApp; 10 | use crate::data::resource::AppResource; 11 | 12 | #[cfg(not(target_arch = "wasm32"))] 13 | fn main() -> iced::Result { 14 | run_desktop() 15 | } 16 | 17 | 18 | 19 | #[cfg(not(target_arch = "wasm32"))] 20 | fn run_desktop() -> iced::Result { 21 | 22 | let app_initializer = move || { 23 | let app_resource = AppResource::with_paths( 24 | "../tests/boolean", 25 | "../tests/string", 26 | "../tests/stroke", 27 | "../tests/outline" 28 | ); 29 | let app = EditorApp::with_resource(app_resource); 30 | (app, iced::Task::none()) 31 | }; 32 | 33 | application(app_initializer, EditorApp::update, EditorApp::view) 34 | .resizable(true) 35 | .centered() 36 | .title("iOverlay Editor") 37 | .subscription(EditorApp::subscription) 38 | .run() 39 | } 40 | 41 | -------------------------------------------------------------------------------- /examples/overlay_editor/src/point_editor/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod widget; 2 | pub(crate) mod state; 3 | pub(crate) mod point; -------------------------------------------------------------------------------- /examples/overlay_editor/src/point_editor/point.rs: -------------------------------------------------------------------------------- 1 | use i_triangle::i_overlay::i_float::int::point::IntPoint; 2 | use i_triangle::i_overlay::i_shape::int::path::IntPaths; 3 | 4 | #[derive(Debug, Clone)] 5 | pub(crate) struct MultiIndex { 6 | pub(crate) point_index: usize, 7 | pub(crate) path_index: usize, 8 | pub(crate) group_index: usize, // subj or clip 9 | } 10 | 11 | #[derive(Debug, Clone)] 12 | pub(crate) struct EditorPoint { 13 | pub(crate) pos: IntPoint, 14 | pub(crate) index: MultiIndex, 15 | } 16 | 17 | pub(crate) trait PathsToEditorPoints { 18 | fn feed_edit_points(&self, group_index: usize, edit_points: &mut Vec); 19 | } 20 | 21 | impl PathsToEditorPoints for IntPaths { 22 | fn feed_edit_points(&self, group_index: usize, edit_points: &mut Vec) { 23 | for (path_index, path) in self.iter().enumerate() { 24 | for (point_index, &pos) in path.iter().enumerate() { 25 | let index = MultiIndex { point_index, path_index, group_index }; 26 | edit_points.push(EditorPoint { pos, index }) 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/overlay_editor/src/sheet/mod.rs: -------------------------------------------------------------------------------- 1 | mod state; 2 | pub(crate) mod widget; -------------------------------------------------------------------------------- /examples/overlay_editor/src/sheet/state.rs: -------------------------------------------------------------------------------- 1 | use iced::{Size, Vector}; 2 | use iced::mouse::ScrollDelta; 3 | use crate::geom::camera::Camera; 4 | 5 | struct Drag { 6 | start_screen: Vector, 7 | start_world: Vector, 8 | } 9 | 10 | enum DragState { 11 | Drag(Drag), 12 | None, 13 | } 14 | 15 | pub(super) struct SheetState { 16 | drag_state: DragState, 17 | } 18 | 19 | impl SheetState { 20 | pub(super) fn mouse_press(&mut self, camera: Camera, view_cursor: Vector) { 21 | self.drag_state = DragState::Drag(Drag { start_screen: view_cursor, start_world: camera.pos }); 22 | } 23 | 24 | pub(super) fn mouse_release(&mut self) { 25 | self.drag_state = DragState::None; 26 | } 27 | 28 | pub(super) fn mouse_move(&mut self, camera: Camera, view_cursor: Vector) -> Option> { 29 | if let DragState::Drag(drag) = &self.drag_state { 30 | let translate = drag.start_screen - view_cursor; 31 | let world_dist = camera.view_distance_to_world(translate); 32 | let new_pos = Vector::new( 33 | drag.start_world.x + world_dist.x, 34 | drag.start_world.y + world_dist.y, 35 | ); 36 | Some(new_pos) 37 | } else { 38 | None 39 | } 40 | } 41 | 42 | pub(super) fn mouse_wheel_scrolled(&mut self, camera: Camera, viewport_size: Size, delta: ScrollDelta, view_cursor: Vector) -> Option { 43 | if let ScrollDelta::Pixels { x: _ , y } = delta { 44 | 45 | let s = 1.0 + y / viewport_size.height; 46 | let mut new_camera = camera; 47 | new_camera.set_scale(s * camera.scale); 48 | 49 | let world_pos = camera.view_to_world(view_cursor); 50 | let new_view_pos = new_camera.world_to_view(world_pos); 51 | 52 | let view_distance = view_cursor - new_view_pos; 53 | let world_distance = new_camera.view_distance_to_world(view_distance); 54 | 55 | new_camera.pos = new_camera.pos - world_distance; 56 | 57 | Some(new_camera) 58 | } else { 59 | None 60 | } 61 | } 62 | } 63 | 64 | impl Default for SheetState { 65 | fn default() -> Self { 66 | Self { 67 | drag_state: DragState::None, 68 | } 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /examples/overlay_editor/src/web.rs: -------------------------------------------------------------------------------- 1 | use std::panic; 2 | use std::sync::Once; 3 | use wasm_bindgen::prelude::*; 4 | 5 | 6 | #[wasm_bindgen] 7 | pub struct WebApp {} 8 | 9 | static INIT_LOGGER: Once = Once::new(); 10 | 11 | #[cfg(target_arch = "wasm32")] 12 | #[wasm_bindgen] 13 | impl WebApp { 14 | #[wasm_bindgen(constructor)] 15 | pub fn create() -> Self { Self {} } 16 | 17 | #[wasm_bindgen] 18 | pub fn start( 19 | &mut self, 20 | boolean_data: String, 21 | string_data: String, 22 | stroke_data: String, 23 | outline_data: String, 24 | ) { 25 | use log::info; 26 | use iced::application; 27 | 28 | use crate::app::main::EditorApp; 29 | use crate::data::resource::AppResource; 30 | 31 | panic::set_hook(Box::new(console_error_panic_hook::hook)); 32 | INIT_LOGGER.call_once(|| { 33 | console_log::init_with_level(log::Level::Debug).expect("error initializing log"); 34 | }); 35 | 36 | info!("wasm start"); 37 | 38 | let app_initializer = move || { 39 | info!("wasm init"); 40 | let app_resource = AppResource::with_content( 41 | &boolean_data, 42 | &string_data, 43 | &stroke_data, 44 | &outline_data, 45 | ); 46 | let app = EditorApp::with_resource(app_resource); 47 | 48 | (app, iced::Task::none()) 49 | }; 50 | 51 | application(app_initializer, EditorApp::update, EditorApp::view) 52 | .resizable(true) 53 | .centered() 54 | .title("iOverlay Editor") 55 | .subscription(EditorApp::subscription) 56 | .run() 57 | .unwrap(); 58 | 59 | info!("wasm app run"); 60 | } 61 | } -------------------------------------------------------------------------------- /examples/tests/outline/test_0.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "outline": [ 4 | [ 5 | [-15.0, -15.0], 6 | [ 15.0, -15.0], 7 | [ 15.0, 15.0], 8 | [-15.0, 15.0] 9 | ], 10 | [ 11 | [-5.0, -5.0], 12 | [-5.0, 5.0], 13 | [ 5.0, 5.0], 14 | [ 5.0, -5.0] 15 | ] 16 | ] 17 | } -------------------------------------------------------------------------------- /examples/tests/outline/test_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "outline": [ 4 | [ 5 | [0.0, 0.0], 6 | [1.0, 0.0], 7 | [1.0, 1.0], 8 | [0.0, 1.0] 9 | ], 10 | [ 11 | [1.0, 2.0], 12 | [1.0, 3.0], 13 | [0.0, 3.0], 14 | [0.0, 2.0] 15 | ], 16 | [ 17 | [1.0, 4.0], 18 | [1.0, 5.0], 19 | [0.0, 5.0], 20 | [0.0, 4.0] 21 | ], 22 | [ 23 | [3.0, 0.0], 24 | [3.0, 1.0], 25 | [2.0, 1.0], 26 | [2.0, 0.0] 27 | ], 28 | [ 29 | [3.0, 2.0], 30 | [3.0, 3.0], 31 | [2.0, 3.0], 32 | [2.0, 2.0] 33 | ], 34 | [ 35 | [3.0, 4.0], 36 | [3.0, 5.0], 37 | [2.0, 5.0], 38 | [2.0, 4.0] 39 | ], 40 | [ 41 | [5.0, 0.0], 42 | [5.0, 1.0], 43 | [4.0, 1.0], 44 | [4.0, 0.0] 45 | ], 46 | [ 47 | [5.0, 2.0], 48 | [5.0, 3.0], 49 | [4.0, 3.0], 50 | [4.0, 2.0] 51 | ], 52 | [ 53 | [5.0, 4.0], 54 | [5.0, 5.0], 55 | [4.0, 5.0], 56 | [4.0, 4.0] 57 | ] 58 | ] 59 | } -------------------------------------------------------------------------------- /examples/tests/outline/test_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "outline": [ 4 | [ 5 | [ 0.0, 0.0], 6 | [25.0, 0.0], 7 | [25.0, 25.0], 8 | [ 0.0, 25.0] 9 | ], 10 | [ 11 | [ 5.0, 10.0], 12 | [10.0, 10.0], 13 | [10.0, 5.0], 14 | [ 5.0, 5.0] 15 | ], 16 | [ 17 | [ 5.0, 20.0], 18 | [10.0, 20.0], 19 | [10.0, 15.0], 20 | [ 5.0, 15.0] 21 | 22 | ], 23 | [ 24 | [15.0, 10.0], 25 | [20.0, 10.0], 26 | [20.0, 5.0], 27 | [15.0, 5.0] 28 | ], 29 | [ 30 | [15.0, 20.0], 31 | [20.0, 20.0], 32 | [20.0, 15.0], 33 | [15.0, 15.0] 34 | ] 35 | ] 36 | } -------------------------------------------------------------------------------- /examples/tests/outline/test_3.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "outline": [ 4 | [ 5 | [-10.0, -20.0], 6 | [ 10.0, -20.0], 7 | [ 10.0, -10.0], 8 | [ 20.0, -10.0], 9 | [ 20.0, 10.0], 10 | [ 10.0, 10.0], 11 | [ 10.0, 20.0], 12 | [-10.0, 20.0], 13 | [-10.0, 10.0], 14 | [-20.0, 10.0], 15 | [-20.0, -10.0], 16 | [-10.0, -10.0] 17 | ] 18 | ] 19 | } -------------------------------------------------------------------------------- /examples/tests/outline/test_4.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "outline": [ 4 | [ 5 | [2.0, 1.0], 6 | [4.0, 1.0], 7 | [5.0, 2.0], 8 | [13.0, 2.0], 9 | [13.0, 3.0], 10 | [12.0, 3.0], 11 | [12.0, 4.0], 12 | [11.0, 4.0], 13 | [11.0, 3.0], 14 | [10.0, 3.0], 15 | [9.0, 4.0], 16 | [8.0, 4.0], 17 | [8.0, 3.0], 18 | [5.0, 3.0], 19 | [5.0, 4.0], 20 | [4.0, 5.0], 21 | [2.0, 5.0], 22 | [1.0, 4.0], 23 | [1.0, 2.0] 24 | ], 25 | [ 26 | [2.0, 4.0], 27 | [4.0, 4.0], 28 | [4.0, 2.0], 29 | [2.0, 2.0] 30 | ] 31 | ] 32 | } -------------------------------------------------------------------------------- /examples/tests/sc_boolean_to_web.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | 4 | # Path to the folder with cleaned JSON files 5 | DIRECTORY = "./boolean" 6 | OUTPUT_FILE = "../../web_tests/boolean_tests.json" 7 | 8 | all_data = [] 9 | 10 | # Iterate through all JSON files in the folder 11 | for filename in sorted(os.listdir(DIRECTORY)): 12 | if filename.endswith(".json"): 13 | file_path = os.path.join(DIRECTORY, filename) 14 | 15 | with open(file_path, 'r', encoding='utf-8') as f: 16 | try: 17 | data = json.load(f) 18 | if "subjPaths" in data and "clipPaths" in data: 19 | all_data.append(data) 20 | else: 21 | print(f"Skipping incomplete file: {filename}") 22 | except json.JSONDecodeError as e: 23 | print(f"Skipping invalid JSON: {filename} ({e})") 24 | 25 | # Write the aggregated result to a new JSON file 26 | with open(os.path.join(DIRECTORY, OUTPUT_FILE), 'w', encoding='utf-8') as f: 27 | json.dump(all_data, f, indent=4) 28 | 29 | print(f"Aggregated {len(all_data)} JSON files into {OUTPUT_FILE}") 30 | -------------------------------------------------------------------------------- /examples/tests/sc_clean_boolean_schema.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | 4 | # Hardcoded directory path where your JSON files are located 5 | DIRECTORY = "./boolean" 6 | 7 | # Loop through each file in the directory 8 | for filename in os.listdir(DIRECTORY): 9 | if filename.endswith(".json"): 10 | file_path = os.path.join(DIRECTORY, filename) 11 | 12 | with open(file_path, 'r', encoding='utf-8') as f: 13 | try: 14 | data = json.load(f) 15 | except json.JSONDecodeError as e: 16 | print(f"Skipping invalid JSON: {filename} ({e})") 17 | continue 18 | 19 | # Keep only subjPaths and clipPaths 20 | new_data = { 21 | "subjPaths": data.get("subjPaths"), 22 | "clipPaths": data.get("clipPaths") 23 | } 24 | 25 | # Write the cleaned JSON back to the file 26 | with open(file_path, 'w', encoding='utf-8') as f: 27 | json.dump(new_data, f, indent=4) 28 | 29 | print("Finished updating all JSON files.") 30 | -------------------------------------------------------------------------------- /examples/tests/sc_outline_to_web.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | 4 | # Path to the folder with cleaned JSON files 5 | DIRECTORY = "./outline" 6 | OUTPUT_FILE = "../../web_tests/outline_tests.json" 7 | 8 | all_data = [] 9 | 10 | # Iterate through all JSON files in the folder 11 | for filename in sorted(os.listdir(DIRECTORY)): 12 | if filename.endswith(".json"): 13 | file_path = os.path.join(DIRECTORY, filename) 14 | 15 | with open(file_path, 'r', encoding='utf-8') as f: 16 | try: 17 | data = json.load(f) 18 | if "outline" in data: 19 | new_data = { 20 | "outline": data.get("outline"), 21 | "scale": data.get("scale") 22 | } 23 | all_data.append(new_data) 24 | else: 25 | print(f"Skipping incomplete file: {filename}") 26 | except json.JSONDecodeError as e: 27 | print(f"Skipping invalid JSON: {filename} ({e})") 28 | 29 | # Write the aggregated result to a new JSON file 30 | with open(os.path.join(DIRECTORY, OUTPUT_FILE), 'w', encoding='utf-8') as f: 31 | json.dump(all_data, f, indent=4) 32 | 33 | print(f"Aggregated {len(all_data)} JSON files into {OUTPUT_FILE}") 34 | -------------------------------------------------------------------------------- /examples/tests/sc_string_to_web.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | 4 | # Path to the folder with cleaned JSON files 5 | DIRECTORY = "./string" 6 | OUTPUT_FILE = "../../web_tests/string_tests.json" 7 | 8 | all_data = [] 9 | 10 | # Iterate through all JSON files in the folder 11 | for filename in sorted(os.listdir(DIRECTORY)): 12 | if filename.endswith(".json"): 13 | file_path = os.path.join(DIRECTORY, filename) 14 | 15 | with open(file_path, 'r', encoding='utf-8') as f: 16 | try: 17 | data = json.load(f) 18 | if "string" in data: 19 | new_data = { 20 | "string": data.get("string"), 21 | "body": data.get("body") 22 | } 23 | all_data.append(new_data) 24 | else: 25 | print(f"Skipping incomplete file: {filename}") 26 | except json.JSONDecodeError as e: 27 | print(f"Skipping invalid JSON: {filename} ({e})") 28 | 29 | # Write the aggregated result to a new JSON file 30 | with open(os.path.join(DIRECTORY, OUTPUT_FILE), 'w', encoding='utf-8') as f: 31 | json.dump(all_data, f, indent=4) 32 | 33 | print(f"Aggregated {len(all_data)} JSON files into {OUTPUT_FILE}") 34 | -------------------------------------------------------------------------------- /examples/tests/sc_stroke_to_web.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | 4 | # Path to the folder with cleaned JSON files 5 | DIRECTORY = "./stroke" 6 | OUTPUT_FILE = "../../web_tests/stroke_tests.json" 7 | 8 | all_data = [] 9 | 10 | # Iterate through all JSON files in the folder 11 | for filename in sorted(os.listdir(DIRECTORY)): 12 | if filename.endswith(".json"): 13 | file_path = os.path.join(DIRECTORY, filename) 14 | 15 | with open(file_path, 'r', encoding='utf-8') as f: 16 | try: 17 | data = json.load(f) 18 | if "stroke" in data: 19 | new_data = { 20 | "stroke": data.get("stroke"), 21 | "scale": data.get("scale") 22 | } 23 | all_data.append(new_data) 24 | else: 25 | print(f"Skipping incomplete file: {filename}") 26 | except json.JSONDecodeError as e: 27 | print(f"Skipping invalid JSON: {filename} ({e})") 28 | 29 | # Write the aggregated result to a new JSON file 30 | with open(os.path.join(DIRECTORY, OUTPUT_FILE), 'w', encoding='utf-8') as f: 31 | json.dump(all_data, f, indent=4) 32 | 33 | print(f"Aggregated {len(all_data)} JSON files into {OUTPUT_FILE}") 34 | -------------------------------------------------------------------------------- /examples/tests/string/test_0.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 0, 3 | "body": [ 4 | [ 5 | [ 6 | -1000, 7 | -1000 8 | ], 9 | [ 10 | -1000, 11 | 1000 12 | ], 13 | [ 14 | 1000, 15 | 1000 16 | ], 17 | [ 18 | 1000, 19 | -1000 20 | ] 21 | ] 22 | ], 23 | "string": [ 24 | [ 25 | [ 26 | 500, 27 | 2000 28 | ], 29 | [ 30 | -500, 31 | -500 32 | ], 33 | [ 34 | 500, 35 | 0 36 | ], 37 | [ 38 | -500, 39 | -2500 40 | ] 41 | ] 42 | ], 43 | "slice": [ 44 | [[[[100, -1000], [500, 0], [-500, -500], [100, 1000], [-1000, 1000], [-1000, -1000]]], [[[500, 0], [100, -1000], [1000, -1000], [1000, 1000], [100, 1000], [-500, -500]]]] 45 | ], 46 | "clip_direct": [ 47 | [[[100, 1000], [-500, -500]], [[-500, -500], [500, 0], [100, -1000]]] 48 | ], 49 | "clip_invert": [ 50 | [[[100, -1000], [-500, -2500]], [[500, 2000], [100, 1000]]] 51 | ] 52 | } -------------------------------------------------------------------------------- /examples/tests/string/test_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 0, 3 | "body": [ 4 | [ 5 | [ 6 | -1000, 7 | 0 8 | ], 9 | [ 10 | 0, 11 | 1000 12 | ], 13 | [ 14 | 1000, 15 | 0 16 | ], 17 | [ 18 | 0, 19 | -1000 20 | ] 21 | ] 22 | ], 23 | "string": [ 24 | [ 25 | [ 26 | -1000, 27 | 1000 28 | ], 29 | [ 30 | 0, 31 | 0 32 | ], 33 | [ 34 | 1000, 35 | 1000 36 | ] 37 | ], 38 | [ 39 | [ 40 | -1000, 41 | -1000 42 | ], 43 | [ 44 | 0, 45 | 0 46 | ], 47 | [ 48 | 1000, 49 | -1000 50 | ] 51 | ] 52 | ], 53 | "slice": [ 54 | [[[[-1000, 0], [-500, -500], [0, 0], [-500, 500]]], [[[-500, -500], [0, -1000], [500, -500], [0, 0]]], [[[-500, 500], [0, 0], [500, 500], [0, 1000]]], [[[0, 0], [500, -500], [1000, 0], [500, 500]]]] 55 | ], 56 | "clip_direct": [ 57 | [[[-500, -500], [0, 0], [500, -500]], [[-500, 500], [0, 0], [500, 500]]] 58 | ], 59 | "clip_invert": [ 60 | [[[-1000, -1000], [-500, -500]], [[-1000, 1000], [-500, 500]], [[500, -500], [1000, -1000]], [[500, 500], [1000, 1000]]] 61 | ] 62 | } -------------------------------------------------------------------------------- /examples/tests/string/test_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 0, 3 | "body": [ 4 | [ 5 | [ 6 | -2000, 7 | -2000 8 | ], 9 | [ 10 | -2000, 11 | 2000 12 | ], 13 | [ 14 | 2000, 15 | 2000 16 | ], 17 | [ 18 | 2000, 19 | -2000 20 | ] 21 | ], 22 | [ 23 | [ 24 | -1000, 25 | -1000 26 | ], 27 | [ 28 | 1000, 29 | -1000 30 | ], 31 | [ 32 | 1000, 33 | 1000 34 | ], 35 | [ 36 | -1000, 37 | 1000 38 | ] 39 | ] 40 | ], 41 | "string": [ 42 | [ 43 | [ 44 | -1000, 45 | 2500 46 | ], 47 | [ 48 | -1000, 49 | -2500 50 | ], 51 | [ 52 | 0, 53 | -2500 54 | ], 55 | [ 56 | 0, 57 | 2500 58 | ], 59 | [ 60 | 1000, 61 | 2500 62 | ], 63 | [ 64 | 1000, 65 | -2500 66 | ] 67 | ], 68 | [ 69 | [ 70 | -2500, 71 | 1000 72 | ], 73 | [ 74 | 2500, 75 | 1000 76 | ], 77 | [ 78 | 2500, 79 | 0 80 | ], 81 | [ 82 | -2500, 83 | 0 84 | ], 85 | [ 86 | -2500, 87 | -1000 88 | ], 89 | [ 90 | 2500, 91 | -1000 92 | ] 93 | ] 94 | ], 95 | "slice": [ 96 | [[[[-1000, -2000], [-1000, -1000], [-2000, -1000], [-2000, -2000]]], [[[-1000, -1000], [-1000, 0], [-2000, 0], [-2000, -1000]]], [[[-1000, 0], [-1000, 1000], [-2000, 1000], [-2000, 0]]], [[[-1000, 1000], [-1000, 2000], [-2000, 2000], [-2000, 1000]]], [[[0, -2000], [0, -1000], [-1000, -1000], [-1000, -2000]]], [[[0, 1000], [0, 2000], [-1000, 2000], [-1000, 1000]]], [[[1000, -2000], [1000, -1000], [0, -1000], [0, -2000]]], [[[1000, 1000], [1000, 2000], [0, 2000], [0, 1000]]], [[[2000, -2000], [2000, -1000], [1000, -1000], [1000, -2000]]], [[[2000, -1000], [2000, 0], [1000, 0], [1000, -1000]]], [[[2000, 0], [2000, 1000], [1000, 1000], [1000, 0]]], [[[2000, 1000], [2000, 2000], [1000, 2000], [1000, 1000]]]] 97 | ], 98 | "clip_direct": [ 99 | [[[-2000, -1000], [-1000, -1000], [-1000, -2000]], [[-1000, 0], [-2000, 0]], [[-2000, 1000], [-1000, 1000]], [[-1000, 2000], [-1000, 1000]], [[0, -2000], [0, -1000]], [[0, 1000], [0, 2000]], [[1000, -1000], [1000, -2000]], [[1000, -1000], [2000, -1000]], [[2000, 0], [1000, 0]], [[1000, 2000], [1000, 1000]], [[1000, 1000], [2000, 1000]]] 100 | ], 101 | "clip_invert": [ 102 | [[[-2000, 0], [-2500, 0], [-2500, -1000]], [[-2500, -1000], [-2000, -1000]], [[-2500, 1000], [-2000, 1000]], [[-1000, -2000], [-1000, -2500]], [[-1000, -2500], [0, -2500], [0, -2000]], [[-1000, 1000], [-1000, 0], [-1000, -1000]], [[-1000, -1000], [0, -1000], [0, 0], [0, 1000], [1000, 1000], [1000, 0], [0, 0], [-1000, 0]], [[-1000, 1000], [0, 1000]], [[-1000, 2500], [-1000, 2000]], [[0, -1000], [1000, -1000]], [[0, 2000], [0, 2500], [1000, 2500], [1000, 2000]], [[1000, -2000], [1000, -2500]], [[1000, 0], [1000, -1000]], [[2000, -1000], [2500, -1000]], [[2000, 1000], [2500, 1000], [2500, 0], [2000, 0]]] 103 | ] 104 | } -------------------------------------------------------------------------------- /examples/tests/stroke/test_0.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "stroke": [ 4 | [ 5 | [-10.0, 0.0], 6 | [ 0.0, 0.0], 7 | [ 0.0, 10.0] 8 | ] 9 | ] 10 | } -------------------------------------------------------------------------------- /examples/tests/stroke/test_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "stroke": [ 4 | [ 5 | [-5.0, -5.0], 6 | [-5.0, 5.0], 7 | [ 5.0, 5.0], 8 | [ 5.0, -5.0] 9 | ] 10 | ] 11 | } -------------------------------------------------------------------------------- /examples/tests/stroke/test_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "stroke": [ 4 | [ 5 | [-10.0, -5.0], 6 | [ 0.0, -5.0], 7 | [ 0.0, 5.0], 8 | [ 10.0, 5.0] 9 | ] 10 | ] 11 | } -------------------------------------------------------------------------------- /examples/tests/stroke/test_3.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "stroke": [ 4 | [ 5 | [ 10.0, -10.0], 6 | [-10.0, -10.0], 7 | [-10.0, -6.0], 8 | [ 10.0, -6.0], 9 | [ 10.0, -2.0], 10 | [-10.0, -2.0], 11 | [-10.0, 2.0], 12 | [ 10.0, 2.0], 13 | [ 10.0, 6.0], 14 | [-10.0, 6.0], 15 | [-10.0, 10.0], 16 | [ 10.0, 10.0] 17 | ] 18 | ] 19 | } -------------------------------------------------------------------------------- /examples/tests/stroke/test_4.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "stroke": [ 4 | [ 5 | [ 2.0, 1.0], 6 | [ 5.0, 1.0], 7 | [ 8.0, 4.0], 8 | [11.0, 4.0], 9 | [11.0, 1.0], 10 | [ 8.0, 1.0], 11 | [ 5.0, 4.0], 12 | [ 2.0, 4.0] 13 | ] 14 | ] 15 | } -------------------------------------------------------------------------------- /iOverlay/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [1.10.0] - 2025-02-02 2 | ### Changed 3 | - snap by radius can now grow without limit. 4 | - enum Precision converted to struct 5 | ### Added 6 | - New SimplifyShape API `simplify_shape_with_solver` which allow to set Solver. 7 | 8 | ## [1.9.4] - 2025-01-10 9 | ### Fixed 10 | - hole path builder uses clockwise edge priority, which is more topologically natural. 11 | - holes-builder now uses edge orientation and not only its position. 12 | 13 | ## [1.9.0] - 2024-11-20 14 | ### Changed 15 | - new fragment solver for splitting big data set 16 | - multithreading splitting 17 | ### Removed 18 | - f32/f64 deprecated api removed 19 | ## [1.8.2] - 2024-11-13 20 | ### Fixed 21 | - Small fix hole bind for degenerate contours. 22 | ## [1.8.1] - 2024-11-12 23 | ### Fixed 24 | - Fixed bug bind holes not correct shape index. 25 | ## [1.8.0] - 2024-11-11 26 | ### Added 27 | - New Float API. A new template-based Float API that uses an iterator, eliminating data cloning. This API can work directly with user-defined Point structures. The previous F32/F64 API is now deprecated. 28 | - new Single Boolean Operation `overlay`, which work without creating `OverlayGraph`, and can be 10-20% faster in some cases. 29 | ### Changed 30 | - The String Line API is now officially supported 31 | - The clip operation now keep the original path order. 32 | ### Fixed 33 | - Hole Solver is reworked and connect holes more carefully 34 | 35 | ## [1.7.4] - 2024-11-06 36 | ### Fixed 37 | - Fixed bug bind holes same hole point and contour x_segment.a. 38 | 39 | ## [1.7.3] - 2024-11-05 40 | ### Fixed 41 | - Fixed bug min_area filter not work. (thx Azorlogh) 42 | 43 | ## [1.7.2] - 2024-10-24 44 | ### Fixed 45 | - Fixed bug joining holes to shapes when holes were unsorted. 46 | 47 | ## [1.7.1] - 2024-10-14 48 | ### Changed 49 | - Updated `clip_string_lines` methods to output `Vec` instead of `Vec`. 50 | 51 | ## [1.7.0] - 2024-10-07 52 | ### Added 53 | - New `FillRule` options: `Positive` and `Negative`. 54 | - Experimental Line String API: 55 | - `StringOverlay`, `StringGraph`, `F32StringOverlay`, `F32OverlayGraph`, `F64StringOverlay`,`F64OverlayGraph` 56 | - `slice` API for slicing polygons and line strings. 57 | - `clip` API for clipping line strings against shapes. 58 | -------------------------------------------------------------------------------- /iOverlay/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "i_overlay" 3 | version = "4.0.2" 4 | authors = ["Nail Sharipov "] 5 | edition = "2024" 6 | description = "Boolean Operations for 2D Polygons: Supports intersection, union, difference, xor, and self-intersections for all polygon varieties." 7 | license = "MIT" 8 | repository = "https://github.com/iShape-Rust/iOverlay" 9 | readme = "README.md" 10 | 11 | keywords = ["polygon-boolean", "clipping", "intersection", "union", "buffering"] 12 | categories = ["algorithms", "graphics", "science::geo", "mathematics", "no-std"] 13 | 14 | [dependencies] 15 | i_float = { version = "~1.15.0" } 16 | i_shape = { version = "~1.14.0" } 17 | i_tree = { version = "~0.16.0" } 18 | i_key_sort = { version = "~0.6.0" } 19 | 20 | #i_float = { path = "../../iFloat"} 21 | #i_shape = { path = "../../iShape"} 22 | #i_tree = { path = "../../iTree" } 23 | #i_key_sort = { path = "../../iKeySort" } 24 | 25 | rayon = { optional = true, version = "^1.10" } 26 | 27 | 28 | [features] 29 | default = [] 30 | 31 | glam = ["i_float/glam"] 32 | serde = ["i_float/serde", "i_shape/serde"] 33 | allow_multithreading = ["dep:rayon"] 34 | 35 | [dev-dependencies] 36 | serde = { version = "^1.0", features = ["derive"] } 37 | serde_json = "^1.0" 38 | rand = { version = "~0.9", features = ["alloc"] } 39 | #i_float = { path = "../../iFloat", features = ["serde"] } 40 | #i_shape = { path = "../../iShape", features = ["serde"] } 41 | i_float = { version = "~1.15.0", features = ["serde"] } 42 | i_shape = { version = "~1.14.0", features = ["serde"] } -------------------------------------------------------------------------------- /iOverlay/readme/overlay_rules.md: -------------------------------------------------------------------------------- 1 | # Overlay Rules 2 | 3 | AB 4 | 5 | ## Union, A or B 6 | Union 7 | 8 | ## Intersection, A and B 9 | Intersection 10 | 11 | ## Difference, A - B 12 | Difference 13 | 14 | ## Inverse Difference, B - A 15 | Inverse Difference 16 | 17 | ## Exclusion, A xor B 18 | Exclusion 19 | -------------------------------------------------------------------------------- /iOverlay/src/bind/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod solver; 2 | pub(crate) mod segment; -------------------------------------------------------------------------------- /iOverlay/src/build/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod builder; 2 | pub(crate) mod boolean; 3 | pub(crate) mod string; 4 | mod graph; 5 | mod offset; 6 | mod util; -------------------------------------------------------------------------------- /iOverlay/src/build/offset.rs: -------------------------------------------------------------------------------- 1 | use crate::build::builder::{FillStrategy, GraphBuilder}; 2 | use crate::core::graph::OverlayNode; 3 | use crate::core::link::OverlayLink; 4 | use crate::core::solver::Solver; 5 | use crate::mesh::graph::OffsetGraph; 6 | use crate::segm::offset::ShapeCountOffset; 7 | use crate::segm::segment::{Segment, SegmentFill}; 8 | 9 | impl GraphBuilder { 10 | #[inline] 11 | pub(crate) fn build_offset(&mut self, 12 | solver: &Solver, 13 | segments: &[Segment], 14 | ) -> OffsetGraph { 15 | self.build_fills_with_strategy::(solver, segments); 16 | self.build_links_all(segments); 17 | self.offset_graph(solver) 18 | } 19 | 20 | #[inline] 21 | fn offset_graph(&mut self, solver: &Solver) -> OffsetGraph { 22 | self.build_nodes_and_connect_links(solver); 23 | OffsetGraph { 24 | nodes: &self.nodes, 25 | links: &self.links, 26 | } 27 | } 28 | } 29 | 30 | struct SubjectOffsetStrategy; 31 | const BOLD_BIT: usize = 2; 32 | 33 | impl FillStrategy for SubjectOffsetStrategy { 34 | 35 | #[inline(always)] 36 | fn add_and_fill(this: ShapeCountOffset, bot: ShapeCountOffset) -> (ShapeCountOffset, SegmentFill) { 37 | let top_subj = bot.subj + this.subj; 38 | let bot_subj = bot.subj; 39 | 40 | let subj_top = (top_subj > 0) as SegmentFill; 41 | let subj_bot = (bot_subj > 0) as SegmentFill; 42 | 43 | let bold = this.bold as SegmentFill; 44 | 45 | let fill = subj_top | (subj_bot << 1) | (bold << BOLD_BIT); 46 | let top = ShapeCountOffset { subj: top_subj, bold: false }; // bold not need 47 | 48 | (top, fill) 49 | } 50 | } 51 | 52 | 53 | impl OverlayLink { 54 | #[inline(always)] 55 | pub(crate) fn is_bold(&self) -> bool { 56 | self.fill & (1 << BOLD_BIT) != 0 57 | } 58 | } -------------------------------------------------------------------------------- /iOverlay/src/build/util.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use i_float::int::point::IntPoint; 3 | use i_key_sort::bin_key::index::BinLayout; 4 | use crate::build::builder::{GraphBuilder, GraphNode}; 5 | use crate::segm::winding::WindingCount; 6 | 7 | impl GraphBuilder { 8 | 9 | pub(crate) fn test_contour_for_loops(&mut self, min: i32, contour: &[IntPoint], buffer: &mut Vec) -> bool { 10 | if let Some(layout) = Self::contour_bin_layout(min, contour) { 11 | self.bin_store.init(layout); 12 | self.bin_store.reserve_bins_space(contour.iter().map(|p|&p.x)); 13 | 14 | let count = self.bin_store.prepare_bins(); 15 | buffer.resize(count, IntPoint::default()); 16 | 17 | for p in contour.iter() { 18 | let index = self.bin_store.layout.index(p.x); 19 | unsafe { 20 | let bin = self.bin_store.bins.get_unchecked_mut(index); 21 | let item_index = bin.data; 22 | bin.data += 1; 23 | *buffer.get_unchecked_mut(item_index) = *p; 24 | } 25 | } 26 | 27 | for bin in self.bin_store.bins.iter() { 28 | let start = bin.offset; 29 | let end = bin.data; 30 | if end > start + 1 { 31 | for i in start..end - 1 { 32 | let a = unsafe { buffer.get_unchecked(i) }; 33 | for j in i + 1..end { 34 | let b = unsafe { buffer.get_unchecked(j) }; 35 | if a == b { 36 | return true; 37 | } 38 | } 39 | } 40 | } 41 | } 42 | } else if contour.len() < 64 { 43 | let n = contour.len(); 44 | if n > 1 { 45 | for i in 0..n - 1 { 46 | let a = unsafe { contour.get_unchecked(i) }; 47 | for j in i + 1..n { 48 | let b = unsafe { contour.get_unchecked(j) }; 49 | if a == b { 50 | return true; 51 | } 52 | } 53 | } 54 | } 55 | } else { 56 | buffer.clear(); 57 | buffer.extend_from_slice(contour); 58 | buffer.sort_unstable(); 59 | for w in buffer.windows(2) { 60 | if w[0] == w[1] { 61 | return true; 62 | } 63 | } 64 | } 65 | false 66 | } 67 | 68 | #[inline] 69 | fn contour_bin_layout(min: i32, contour: &[IntPoint]) -> Option> { 70 | let count = contour.len(); 71 | 72 | if !(64..=1_000_000).contains(&count) { 73 | // direct approach work better for small and large data 74 | return None 75 | } 76 | 77 | let mut max = i32::MIN; 78 | 79 | for p in contour.iter() { 80 | max = max.max(p.x); 81 | } 82 | 83 | BinLayout::new(min..max, count) 84 | } 85 | } -------------------------------------------------------------------------------- /iOverlay/src/core/fill_rule.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | 3 | /// Represents the rule used to determine the "bind" of a shape, affecting how shapes are filled. For a visual description, see [Fill Rules](https://ishape-rust.github.io/iShape-js/overlay/filling_rules/filling_rules.html). 4 | /// - `EvenOdd`: Only odd-numbered sub-regions are filled. 5 | /// - `NonZero`: Only non-zero sub-regions are filled. 6 | /// - `Positive`: Fills regions where the winding number is positive. 7 | /// - `Negative`: Fills regions where the winding number is negative. 8 | #[derive(Debug, Clone, Copy, PartialEq, Default)] 9 | pub enum FillRule { 10 | EvenOdd, 11 | #[default] 12 | NonZero, 13 | Positive, 14 | Negative 15 | } 16 | 17 | impl fmt::Display for FillRule { 18 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 19 | let text = match self { 20 | FillRule::EvenOdd => "EvenOdd", 21 | FillRule::NonZero => "NonZero", 22 | FillRule::Positive => "Positive", 23 | FillRule::Negative => "Negative", 24 | }; 25 | 26 | write!(f, "{}", text) 27 | } 28 | } -------------------------------------------------------------------------------- /iOverlay/src/core/graph.rs: -------------------------------------------------------------------------------- 1 | //! This module defines the graph structure that represents the relationships between the paths in 2 | //! subject and clip polygons after boolean operations. The graph helps in extracting final shapes 3 | //! based on the overlay rule applied. 4 | 5 | use alloc::vec::Vec; 6 | use super::link::OverlayLink; 7 | use crate::build::builder::GraphNode; 8 | use crate::core::overlay::IntOverlayOptions; 9 | 10 | /// A representation of geometric shapes organized for efficient boolean operations. 11 | /// 12 | /// `OverlayGraph` is a core structure designed to facilitate the execution of boolean operations on shapes, such as union, intersection, and difference. It organizes and preprocesses geometric data, making it optimized for these operations. This struct is the result of compiling shape data into a form where boolean operations can be applied directly, efficiently managing the complex relationships between different geometric entities. 13 | /// 14 | /// Use `OverlayGraph` to perform boolean operations on the geometric shapes you've added to an `Overlay`, after it has processed the shapes according to the specified build and overlay rules. 15 | /// [More information](https://ishape-rust.github.io/iShape-js/overlay/overlay_graph/overlay_graph.html) about Overlay Graph. 16 | pub struct OverlayGraph<'a> { 17 | pub(crate) options: IntOverlayOptions, 18 | pub(crate) nodes: &'a [OverlayNode], 19 | pub(crate) links: &'a [OverlayLink], 20 | } 21 | 22 | #[derive(Debug)] 23 | pub(crate) enum OverlayNode { 24 | Bridge([usize; 2]), 25 | Cross(Vec), 26 | } 27 | 28 | impl GraphNode for OverlayNode { 29 | #[inline] 30 | fn with_indices(indices: &[usize]) -> Self { 31 | if indices.len() == 2 { 32 | Self::Bridge(unsafe { [*indices.get_unchecked(0), *indices.get_unchecked(1)] }) 33 | } else { 34 | Self::Cross(indices.to_vec()) 35 | } 36 | } 37 | } 38 | 39 | impl OverlayGraph<'_> { 40 | pub fn validate(&self) { 41 | for node in self.nodes.iter() { 42 | if let OverlayNode::Cross(indices) = node { 43 | debug_assert!(indices.len() > 1, "indices: {}", indices.len()); 44 | debug_assert!( 45 | self.nodes.len() <= self.links.len(), 46 | "nodes is more then links" 47 | ); 48 | } 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /iOverlay/src/core/link.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use crate::core::overlay_rule::OverlayRule; 3 | use crate::geom::id_point::IdPoint; 4 | use crate::segm::segment::SegmentFill; 5 | 6 | #[derive(Debug, Clone, Copy)] 7 | pub(crate) struct OverlayLink { 8 | pub(crate) a: IdPoint, 9 | pub(crate) b: IdPoint, 10 | pub(crate) fill: SegmentFill, 11 | } 12 | 13 | impl OverlayLink { 14 | #[inline(always)] 15 | pub(crate) fn new(a: IdPoint, b: IdPoint, fill: SegmentFill) -> OverlayLink { 16 | OverlayLink { a, b, fill } 17 | } 18 | 19 | #[inline(always)] 20 | pub(crate) fn other(&self, node_id: usize) -> IdPoint { 21 | if self.a.id == node_id { self.b } else { self.a } 22 | } 23 | 24 | #[inline(always)] 25 | pub(crate) fn is_direct(&self) -> bool { 26 | self.a.point < self.b.point 27 | } 28 | } 29 | 30 | pub(crate) trait OverlayLinkFilter { 31 | fn filter_by_overlay(&self, fill_rule: OverlayRule) -> Vec; 32 | fn filter_by_overlay_into(&self, overlay_rule: OverlayRule, buffer: &mut Vec); 33 | } -------------------------------------------------------------------------------- /iOverlay/src/core/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod overlay; 2 | pub mod solver; 3 | pub mod graph; 4 | pub mod overlay_rule; 5 | pub mod extract; 6 | pub mod fill_rule; 7 | pub mod simplify; 8 | pub(crate) mod link; 9 | pub(crate) mod nearest_vector; 10 | pub mod divide; -------------------------------------------------------------------------------- /iOverlay/src/core/overlay_rule.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use crate::segm::segment::{SegmentFill, BOTH_BOTTOM, BOTH_TOP, CLIP_TOP, NONE, SUBJ_TOP}; 3 | 4 | /// Defines the types of overlay/boolean operations that can be applied to shapes. For a visual description, see [Overlay Rules](https://ishape-rust.github.io/iShape-js/overlay/overlay_rules/overlay_rules.html). 5 | /// - `Subject`: Processes the subject shape, useful for resolving self-intersections and degenerate cases within the subject itself. 6 | /// - `Clip`: Similar to `Subject`, but for Clip shapes. 7 | /// - `Intersect`: Finds the common area between the subject and clip shapes, effectively identifying where they overlap. 8 | /// - `Union`: Combines the area of both subject and clip shapes into a single unified shape. 9 | /// - `Difference`: Subtracts the area of the clip shape from the subject shape, removing the clip shape's area from the subject. 10 | /// - `InverseDifference`: Subtracts the area of the subject shape from the clip shape, removing the subject shape's area from the clip. 11 | /// - `Xor`: Produces a shape consisting of areas unique to each shape, excluding any parts where the subject and clip overlap. 12 | #[derive(Debug, Clone, Copy, PartialEq)] 13 | pub enum OverlayRule { 14 | Subject, 15 | Clip, 16 | Intersect, 17 | Union, 18 | Difference, 19 | InverseDifference, 20 | Xor, 21 | } 22 | 23 | impl OverlayRule { 24 | #[inline(always)] 25 | pub(crate) fn is_fill_top(&self, fill: SegmentFill) -> bool { 26 | match self { 27 | OverlayRule::Subject => fill & SUBJ_TOP == SUBJ_TOP, 28 | OverlayRule::Clip => fill & CLIP_TOP == CLIP_TOP, 29 | OverlayRule::Intersect => fill & BOTH_TOP == BOTH_TOP, 30 | OverlayRule::Union => fill & BOTH_BOTTOM == NONE, 31 | OverlayRule::Difference => fill & BOTH_TOP == SUBJ_TOP, 32 | OverlayRule::InverseDifference => fill & BOTH_TOP == CLIP_TOP, 33 | OverlayRule::Xor => { 34 | let is_subject = fill & BOTH_TOP == SUBJ_TOP; 35 | let is_clip = fill & BOTH_TOP == CLIP_TOP; 36 | is_subject || is_clip 37 | } 38 | } 39 | } 40 | } 41 | 42 | impl fmt::Display for OverlayRule { 43 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 44 | let text = match self { 45 | OverlayRule::Subject => "Subject", 46 | OverlayRule::Clip => "Clip", 47 | OverlayRule::Intersect => "Intersect", 48 | OverlayRule::Union => "Union", 49 | OverlayRule::Difference => "Difference", 50 | OverlayRule::InverseDifference => "InverseDifference", 51 | OverlayRule::Xor => "Xor", 52 | }; 53 | 54 | write!(f, "{}", text) 55 | } 56 | } -------------------------------------------------------------------------------- /iOverlay/src/float/clip.rs: -------------------------------------------------------------------------------- 1 | use i_float::float::compatible::FloatPointCompatible; 2 | use i_float::float::number::FloatNumber; 3 | use i_shape::base::data::Paths; 4 | use i_shape::source::resource::ShapeResource; 5 | use crate::core::fill_rule::FillRule; 6 | use crate::core::solver::Solver; 7 | use crate::float::string_overlay::FloatStringOverlay; 8 | use crate::string::clip::ClipRule; 9 | 10 | pub trait FloatClip 11 | where 12 | R: ShapeResource, 13 | P: FloatPointCompatible, 14 | T: FloatNumber, 15 | { 16 | /// Clips paths according to the specified build and clip rules. 17 | /// - `resource`: A clipping shape. 18 | /// `ShapeResource` can be one of the following: 19 | /// - `Contour`: A contour representing a closed path. This path is interpreted as closed, so it doesn’t require the start and endpoint to be the same for processing. 20 | /// - `Contours`: A collection of contours, each representing a closed path. 21 | /// - `Shapes`: A collection of shapes, where each shape may consist of multiple contours. 22 | /// - `fill_rule`: Fill rule to determine filled areas (non-zero, even-odd, positive, negative). 23 | /// - `clip_rule`: Clip rule to determine how boundary and inversion settings affect the result. 24 | /// 25 | /// # Returns 26 | /// A `Paths

` collection of string lines that meet the clipping conditions. 27 | fn clip_by(&self, source: &R, fill_rule: FillRule, clip_rule: ClipRule) -> Paths

; 28 | 29 | /// Clips paths according to the specified build and clip rules. 30 | /// - `resource`: A clipping shape. 31 | /// `ShapeResource` can be one of the following: 32 | /// - `Contour`: A contour representing a closed path. This path is interpreted as closed, so it doesn’t require the start and endpoint to be the same for processing. 33 | /// - `Contours`: A collection of contours, each representing a closed path. 34 | /// - `Shapes`: A collection of shapes, where each shape may consist of multiple contours. 35 | /// - `fill_rule`: Fill rule to determine filled areas (non-zero, even-odd, positive, negative). 36 | /// - `clip_rule`: Clip rule to determine how boundary and inversion settings affect the result. 37 | /// - `solver`: Type of solver to use. 38 | /// 39 | /// # Returns 40 | /// A `Paths

` collection of string lines that meet the clipping conditions. 41 | fn clip_by_with_solver(&self, source: &R, fill_rule: FillRule, clip_rule: ClipRule, solver: Solver) -> Paths

; 42 | } 43 | 44 | impl FloatClip for R1 45 | where 46 | R0: ShapeResource, 47 | R1: ShapeResource, 48 | P: FloatPointCompatible, 49 | T: FloatNumber, 50 | { 51 | #[inline] 52 | fn clip_by(&self, resource: &R0, fill_rule: FillRule, clip_rule: ClipRule) -> Paths

{ 53 | self.clip_by_with_solver(resource, fill_rule, clip_rule, Default::default()) 54 | } 55 | 56 | #[inline] 57 | fn clip_by_with_solver(&self, resource: &R0, fill_rule: FillRule, clip_rule: ClipRule, solver: Solver) -> Paths

{ 58 | FloatStringOverlay::with_shape_and_string(resource, self) 59 | .clip_string_lines_with_solver(fill_rule, clip_rule, solver) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /iOverlay/src/float/graph.rs: -------------------------------------------------------------------------------- 1 | //! This module defines the graph structure that represents the relationships between the paths in 2 | //! subject and clip polygons after boolean operations. The graph helps in extracting final shapes 3 | //! based on the overlay rule applied. 4 | 5 | use crate::core::graph::OverlayGraph; 6 | use crate::core::overlay_rule::OverlayRule; 7 | use i_float::adapter::FloatPointAdapter; 8 | use i_float::float::compatible::FloatPointCompatible; 9 | use i_float::float::number::FloatNumber; 10 | use i_shape::base::data::Shapes; 11 | use i_shape::float::adapter::ShapesToFloat; 12 | use i_shape::float::despike::DeSpikeContour; 13 | use i_shape::float::simple::SimplifyContour; 14 | use crate::core::extract::BooleanExtractionBuffer; 15 | 16 | /// The `FloatOverlayGraph` struct represents an overlay graph with floating point precision, 17 | /// providing methods to extract geometric shapes from the graph after applying boolean operations. 18 | /// [More information](https://ishape-rust.github.io/iShape-js/overlay/overlay_graph/overlay_graph.html) about Overlay Graph. 19 | pub struct FloatOverlayGraph<'a, P: FloatPointCompatible, T: FloatNumber> { 20 | pub graph: OverlayGraph<'a>, 21 | pub adapter: FloatPointAdapter, 22 | clean_result: bool 23 | } 24 | 25 | impl<'a, P: FloatPointCompatible, T: FloatNumber> FloatOverlayGraph<'a, P, T> { 26 | #[inline] 27 | pub(crate) fn new(graph: OverlayGraph<'a>, adapter: FloatPointAdapter, clean_result: bool) -> Self { 28 | Self { graph, adapter, clean_result } 29 | } 30 | 31 | /// Extracts shapes from the overlay graph based on the specified overlay rule. 32 | /// This method is used to retrieve the final geometric shapes after boolean operations have been applied. 33 | /// It's suitable for most use cases where the minimum area of shapes is not a concern. 34 | /// 35 | /// # Parameters 36 | /// - `overlay_rule`: The boolean operation rule to apply when extracting shapes from the graph, such as union or intersection. 37 | /// 38 | /// # Returns 39 | /// A `Shapes

` collection, representing the geometric result of the applied overlay rule. 40 | /// 41 | /// # Shape Representation 42 | /// The output is a `Shapes

`, where: 43 | /// - The outer `Vec>` represents a set of shapes. 44 | /// - Each shape `Vec>` represents a collection of paths, where the first path is the outer boundary, and all subsequent paths are holes in this boundary. 45 | /// - Each path `Vec

` is a sequence of points, forming a closed path. 46 | /// 47 | /// Note: Outer boundary paths have a counterclockwise order, and holes have a clockwise order. 48 | #[inline] 49 | pub fn extract_shapes(&self, overlay_rule: OverlayRule, buffer: &mut BooleanExtractionBuffer) -> Shapes

{ 50 | let shapes = self 51 | .graph 52 | .extract_shapes(overlay_rule, buffer); 53 | let mut float = shapes.to_float(&self.adapter); 54 | 55 | if self.clean_result { 56 | if self.graph.options.preserve_output_collinear { 57 | float.despike_contour(&self.adapter); 58 | } else { 59 | float.simplify_contour(&self.adapter); 60 | } 61 | } 62 | 63 | float 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /iOverlay/src/float/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod overlay; 2 | pub mod graph; 3 | pub mod simplify; 4 | pub mod string_overlay; 5 | pub mod string_graph; 6 | pub mod slice; 7 | pub mod clip; 8 | pub mod single; -------------------------------------------------------------------------------- /iOverlay/src/float/simplify.rs: -------------------------------------------------------------------------------- 1 | use i_float::float::compatible::FloatPointCompatible; 2 | use i_float::float::number::FloatNumber; 3 | use i_shape::base::data::Shapes; 4 | use i_shape::source::resource::ShapeResource; 5 | use crate::core::fill_rule::FillRule; 6 | use crate::core::overlay_rule::OverlayRule; 7 | use crate::core::solver::Solver; 8 | use crate::float::overlay::{FloatOverlay, OverlayOptions}; 9 | 10 | /// Trait `Simplify` provides a method to simplify geometric shapes by reducing the number of points in contours or shapes 11 | /// while preserving overall shape and topology. The method applies a minimum area threshold and a build rule to 12 | /// determine which areas should be retained or excluded. 13 | pub trait SimplifyShape { 14 | /// Simplifies the shape or collection of points, contours, or shapes, based on a specified minimum area threshold. 15 | /// 16 | /// - Returns: A collection of `Shapes

` that represents the simplified geometry. 17 | /// 18 | /// Note: Outer boundary paths have a **main_direction** order, and holes have an opposite to **main_direction** order. 19 | fn simplify_shape(&self, fill_rule: FillRule) -> Shapes

; 20 | 21 | /// Simplifies the shape or collection of points, contours, or shapes, based on a specified minimum area threshold. 22 | /// - `options`: Adjust custom behavior. 23 | /// - `solver`: Type of solver to use. 24 | /// - Returns: A collection of Shapes

that represents the simplified geometry. 25 | /// 26 | /// Note: Outer boundary paths have a **main_direction** order, and holes have an opposite to **main_direction** order. 27 | fn simplify_shape_custom(&self, fill_rule: FillRule, options: OverlayOptions, solver: Solver) -> Shapes

; 28 | } 29 | 30 | impl SimplifyShape for S 31 | where 32 | S: ShapeResource, 33 | P: FloatPointCompatible, 34 | T: FloatNumber, 35 | { 36 | #[inline] 37 | fn simplify_shape(&self, fill_rule: FillRule) -> Shapes

{ 38 | FloatOverlay::with_subj_custom(self, Default::default(), Default::default()) 39 | .overlay(OverlayRule::Subject, fill_rule) 40 | } 41 | 42 | #[inline] 43 | fn simplify_shape_custom(&self, fill_rule: FillRule, options: OverlayOptions, solver: Solver) -> Shapes

{ 44 | FloatOverlay::with_subj_custom(self, options, solver) 45 | .overlay(OverlayRule::Subject, fill_rule) 46 | } 47 | } 48 | 49 | #[cfg(test)] 50 | mod tests { 51 | use alloc::vec; 52 | use crate::core::fill_rule::FillRule; 53 | use crate::float::simplify::SimplifyShape; 54 | 55 | #[test] 56 | fn test_contour_slice() { 57 | let rect = [[0.0, 0.0], [0.0, 0.5], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]]; 58 | 59 | let shapes = rect.as_slice().simplify_shape(FillRule::NonZero); 60 | 61 | assert_eq!(shapes.len(), 1); 62 | assert_eq!(shapes[0].len(), 1); 63 | assert_eq!(shapes[0][0].len(), 4); 64 | } 65 | 66 | #[test] 67 | fn test_contour_vec() { 68 | let rect = vec![[0.0, 0.0], [0.0, 0.5], [0.0, 1.0], [1.0, 1.0], [1.0, 0.0]]; 69 | 70 | let shapes = rect.simplify_shape(FillRule::NonZero); 71 | 72 | assert_eq!(shapes.len(), 1); 73 | assert_eq!(shapes[0].len(), 1); 74 | assert_eq!(shapes[0][0].len(), 4); 75 | } 76 | } -------------------------------------------------------------------------------- /iOverlay/src/geom/end.rs: -------------------------------------------------------------------------------- 1 | use i_float::int::point::IntPoint; 2 | use i_key_sort::bin_key::index::{BinKey, BinLayout}; 3 | 4 | #[derive(Clone, Copy)] 5 | pub(crate) struct End { 6 | pub(crate) index: usize, 7 | pub(crate) point: IntPoint, 8 | } 9 | 10 | impl Default for End { 11 | #[inline(always)] 12 | fn default() -> Self { 13 | Self { 14 | index: 0, 15 | point: IntPoint::ZERO, 16 | } 17 | } 18 | } 19 | 20 | impl BinKey for End { 21 | #[inline(always)] 22 | fn bin_key(&self) -> i32 { 23 | self.point.x 24 | } 25 | 26 | #[inline(always)] 27 | fn bin_index(&self, layout: &BinLayout) -> usize { 28 | layout.index(self.point.x) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /iOverlay/src/geom/id_point.rs: -------------------------------------------------------------------------------- 1 | use i_float::int::point::IntPoint; 2 | use i_key_sort::bin_key::index::{BinKey, BinLayout}; 3 | 4 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 5 | pub(crate) struct IdPoint { 6 | pub(crate) id: usize, 7 | pub(crate) point: IntPoint, 8 | } 9 | 10 | impl IdPoint { 11 | pub(crate) fn new(id: usize, point: IntPoint) -> Self { 12 | Self { id, point } 13 | } 14 | } 15 | 16 | impl BinKey for IdPoint { 17 | #[inline(always)] 18 | fn bin_key(&self) -> i32 { 19 | self.point.x 20 | } 21 | 22 | #[inline(always)] 23 | fn bin_index(&self, layout: &BinLayout) -> usize { 24 | layout.index(self.point.x) 25 | } 26 | } -------------------------------------------------------------------------------- /iOverlay/src/geom/line_range.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 2 | pub(crate) struct LineRange { 3 | pub(crate) min: i32, 4 | pub(crate) max: i32, 5 | } -------------------------------------------------------------------------------- /iOverlay/src/geom/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod x_segment; 2 | pub(crate) mod v_segment; 3 | pub(crate) mod end; 4 | pub(crate) mod line_range; 5 | pub(crate) mod id_point; -------------------------------------------------------------------------------- /iOverlay/src/geom/v_segment.rs: -------------------------------------------------------------------------------- 1 | use core::cmp::Ordering; 2 | use core::mem; 3 | use i_tree::ExpiredKey; 4 | use i_float::int::point::IntPoint; 5 | use i_float::triangle::Triangle; 6 | use crate::geom::x_segment::XSegment; 7 | 8 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 9 | pub(crate) struct VSegment { 10 | pub(crate) a: IntPoint, 11 | pub(crate) b: IntPoint, 12 | } 13 | 14 | impl VSegment { 15 | #[inline(always)] 16 | fn is_under_segment_order(&self, other: &VSegment) -> Ordering { 17 | match self.a.cmp(&other.a) { 18 | Ordering::Less => Triangle::clock_order_point(self.a, other.a, self.b), 19 | Ordering::Equal => Triangle::clock_order_point(self.a, other.b, self.b), 20 | Ordering::Greater => Triangle::clock_order_point(other.a, other.b, self.a), 21 | } 22 | } 23 | 24 | #[inline(always)] 25 | pub(crate) fn is_under_point_order(&self, p: IntPoint) -> Ordering { 26 | debug_assert!(self.a.x <= p.x && p.x <= self.b.x); 27 | debug_assert!(p != self.a && p != self.b); 28 | 29 | Triangle::clock_order_point(self.a, p, self.b) 30 | } 31 | 32 | #[inline(always)] 33 | pub(crate) fn is_under_segment(&self, other: &VSegment) -> bool { 34 | match self.a.cmp(&other.a) { 35 | Ordering::Less => { 36 | Triangle::is_clockwise_point(self.a, other.a, self.b) 37 | } 38 | Ordering::Equal => { 39 | Triangle::is_clockwise_point(self.a, other.b, self.b) 40 | } 41 | Ordering::Greater => { 42 | Triangle::is_clockwise_point(other.a, other.b, self.a) 43 | } 44 | } 45 | } 46 | 47 | #[inline(always)] 48 | pub(crate) fn cmp_by_angle(&self, other: &Self) -> Ordering { 49 | // sort angles counterclockwise 50 | debug_assert!(self.a == other.a); 51 | let v0 = self.b.subtract(self.a); 52 | let v1 = other.b.subtract(other.a); 53 | let cross = v0.cross_product(v1); 54 | 0.cmp(&cross) 55 | } 56 | } 57 | 58 | impl From for XSegment { 59 | #[inline(always)] 60 | fn from(seg: VSegment) -> Self { 61 | unsafe { mem::transmute(seg) } 62 | } 63 | } 64 | 65 | impl From for VSegment { 66 | #[inline(always)] 67 | fn from(seg: XSegment) -> Self { 68 | unsafe { mem::transmute(seg) } 69 | } 70 | } 71 | 72 | impl PartialOrd for VSegment { 73 | #[inline(always)] 74 | fn partial_cmp(&self, other: &Self) -> Option { 75 | Some(self.cmp(other)) 76 | } 77 | } 78 | 79 | impl Ord for VSegment { 80 | #[inline(always)] 81 | fn cmp(&self, other: &Self) -> Ordering { 82 | self.is_under_segment_order(other) 83 | } 84 | } 85 | 86 | impl ExpiredKey for VSegment { 87 | #[inline] 88 | fn expiration(&self) -> i32 { 89 | self.b.x 90 | } 91 | } 92 | 93 | #[cfg(test)] 94 | mod tests { 95 | use core::cmp::Ordering; 96 | use i_float::int::point::IntPoint; 97 | use crate::geom::v_segment::VSegment; 98 | 99 | #[test] 100 | fn test_00() { 101 | let p = IntPoint::new(-10, 10); 102 | let s = VSegment { a: IntPoint::new(-10, -10), b: IntPoint::new(10, -10) }; 103 | let order = s.is_under_point_order(p); 104 | assert_eq!(order, Ordering::Less); 105 | } 106 | } -------------------------------------------------------------------------------- /iOverlay/src/geom/x_segment.rs: -------------------------------------------------------------------------------- 1 | use core::cmp::Ordering; 2 | use i_float::int::point::IntPoint; 3 | use i_key_sort::bin_key::index::{BinKey, BinLayout}; 4 | use crate::geom::line_range::LineRange; 5 | 6 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 7 | pub(crate) struct XSegment { 8 | pub(crate) a: IntPoint, 9 | pub(crate) b: IntPoint, 10 | } 11 | 12 | impl XSegment { 13 | #[inline(always)] 14 | pub(crate) fn y_range(&self) -> LineRange { 15 | if self.a.y < self.b.y { 16 | LineRange { min: self.a.y, max: self.b.y } 17 | } else { 18 | LineRange { min: self.b.y, max: self.a.y } 19 | } 20 | } 21 | 22 | #[inline(always)] 23 | pub(crate) fn is_not_vertical(&self) -> bool { 24 | self.a.x != self.b.x 25 | } 26 | 27 | 28 | #[inline(always)] 29 | pub(crate) fn is_not_intersect_y_range(&self, range: &LineRange) -> bool { 30 | range.min > self.a.y && range.min > self.b.y || range.max < self.a.y && range.max < self.b.y 31 | } 32 | } 33 | 34 | impl PartialOrd for XSegment { 35 | #[inline(always)] 36 | fn partial_cmp(&self, other: &Self) -> Option { 37 | Some(self.cmp(other)) 38 | } 39 | } 40 | 41 | impl Ord for XSegment { 42 | #[inline(always)] 43 | fn cmp(&self, other: &Self) -> Ordering { 44 | let a = self.a.cmp(&other.a); 45 | if a == Ordering::Equal { 46 | self.b.cmp(&other.b) 47 | } else { 48 | a 49 | } 50 | } 51 | } 52 | 53 | impl BinKey for XSegment { 54 | #[inline(always)] 55 | fn bin_key(&self) -> i32 { 56 | self.a.x 57 | } 58 | 59 | #[inline(always)] 60 | fn bin_index(&self, layout: &BinLayout) -> usize { 61 | layout.index(self.a.x) 62 | } 63 | } -------------------------------------------------------------------------------- /iOverlay/src/mesh/graph.rs: -------------------------------------------------------------------------------- 1 | use crate::core::graph::OverlayNode; 2 | use crate::core::link::OverlayLink; 3 | 4 | pub struct OffsetGraph<'a> { 5 | pub(crate) nodes: &'a [OverlayNode], 6 | pub(crate) links: &'a [OverlayLink], 7 | } -------------------------------------------------------------------------------- /iOverlay/src/mesh/math.rs: -------------------------------------------------------------------------------- 1 | use i_float::float::compatible::FloatPointCompatible; 2 | use i_float::float::number::FloatNumber; 3 | use i_float::float::vector::FloatPointMath; 4 | 5 | pub(crate) struct Math { 6 | _phantom: core::marker::PhantomData<(T, P)>, 7 | } 8 | impl> Math { 9 | #[inline(always)] 10 | pub(crate) fn normal(a: &P, b: &P) -> P { 11 | let c = FloatPointMath::sub(a, b); 12 | FloatPointMath::normalize(&c) 13 | } 14 | 15 | #[inline(always)] 16 | pub(crate) fn ortho_and_scale(p: &P, s: T) -> P { 17 | let t = P::from_xy(-p.y(), p.x()); 18 | FloatPointMath::scale(&t, s) 19 | } 20 | } -------------------------------------------------------------------------------- /iOverlay/src/mesh/miter.rs: -------------------------------------------------------------------------------- 1 | use i_float::adapter::FloatPointAdapter; 2 | use i_float::float::compatible::FloatPointCompatible; 3 | use i_float::float::number::FloatNumber; 4 | use i_float::int::point::IntPoint; 5 | 6 | pub(super) struct Miter; 7 | 8 | pub(super) enum SharpMiter { 9 | Degenerate, 10 | AB(IntPoint, IntPoint), 11 | AcB(IntPoint, IntPoint, IntPoint), 12 | } 13 | 14 | impl Miter { 15 | #[inline] 16 | pub(super) fn sharp>( 17 | pa: P, 18 | pb: P, 19 | va: P, 20 | vb: P, 21 | adapter: &FloatPointAdapter, 22 | ) -> SharpMiter { 23 | let ia = adapter.float_to_int(&pa); 24 | let ib = adapter.float_to_int(&pb); 25 | 26 | if ia == ib { 27 | return SharpMiter::Degenerate; 28 | } 29 | 30 | let pax = pa.x(); 31 | let pay = pa.y(); 32 | let pbx = pb.x(); 33 | let pby = pb.y(); 34 | let vax = va.x(); 35 | let vay = va.y(); 36 | let vbx = vb.x(); 37 | let vby = vb.y(); 38 | 39 | let xx = vax + vbx; 40 | let yy = vay + vby; 41 | 42 | let k = if xx.abs() > yy.abs() { 43 | (pbx - pax) / xx 44 | } else { 45 | (pby - pay) / yy 46 | }; 47 | 48 | let x = pax + k * vax; 49 | let y = pay + k * vay; 50 | let c = P::from_xy(x, y); 51 | 52 | let ic = adapter.float_to_int(&c); 53 | 54 | if ia == ic || ib == ic { 55 | SharpMiter::AB(ia, ib) 56 | } else { 57 | SharpMiter::AcB(ia, ic, ib) 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /iOverlay/src/mesh/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod stroke; 2 | pub mod outline; 3 | pub(crate) mod math; 4 | pub(crate) mod graph; 5 | mod rotator; 6 | mod subject; 7 | pub mod style; 8 | mod miter; 9 | mod overlay; 10 | mod extract; -------------------------------------------------------------------------------- /iOverlay/src/mesh/outline/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod offset; 2 | mod builder; 3 | mod builder_join; 4 | mod section; 5 | -------------------------------------------------------------------------------- /iOverlay/src/mesh/outline/section.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use core::marker::PhantomData; 3 | use i_float::adapter::FloatPointAdapter; 4 | use i_float::float::compatible::FloatPointCompatible; 5 | use i_float::float::number::FloatNumber; 6 | use i_float::float::vector::FloatPointMath; 7 | use crate::mesh::math::Math; 8 | use crate::segm::offset::ShapeCountOffset; 9 | use crate::segm::segment::Segment; 10 | 11 | #[derive(Debug, Clone)] 12 | pub(super) struct Section, T: FloatNumber> { 13 | pub(super) b: P, 14 | pub(super) a_top: P, 15 | pub(super) b_top: P, 16 | pub(super) dir: P, 17 | _phantom: PhantomData, 18 | } 19 | 20 | impl> Section { 21 | pub(crate) fn new(radius: T, a: &P, b: &P) -> Self { 22 | let dir = Math::normal(b, a); 23 | let t = Math::ortho_and_scale(&dir, radius); 24 | 25 | let a_top = FloatPointMath::add(a, &t); 26 | let b_top = FloatPointMath::add(b, &t); 27 | 28 | Section { 29 | b: *b, 30 | a_top, 31 | b_top, 32 | dir, 33 | _phantom: Default::default(), 34 | } 35 | } 36 | } 37 | 38 | pub(crate) trait SectionToSegment> { 39 | fn add_section(&mut self, section: &Section, adapter: &FloatPointAdapter); 40 | } 41 | 42 | impl> SectionToSegment for Vec> { 43 | fn add_section(&mut self, section: &Section, adapter: &FloatPointAdapter) { 44 | let a_top = adapter.float_to_int(§ion.a_top); 45 | let b_top = adapter.float_to_int(§ion.b_top); 46 | self.push(Segment::bold_subject_ab(a_top, b_top)); 47 | } 48 | } -------------------------------------------------------------------------------- /iOverlay/src/mesh/overlay.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use crate::build::builder::GraphBuilder; 3 | use crate::core::graph::OverlayNode; 4 | use crate::core::solver::Solver; 5 | use crate::mesh::graph::OffsetGraph; 6 | use crate::segm::offset::ShapeCountOffset; 7 | use crate::segm::segment::Segment; 8 | use crate::split::solver::SplitSolver; 9 | 10 | pub struct OffsetOverlay { 11 | pub(super) segments: Vec>, 12 | pub(crate) split_solver: SplitSolver, 13 | pub(crate) graph_builder: GraphBuilder 14 | } 15 | 16 | impl OffsetOverlay { 17 | #[inline] 18 | pub fn new(capacity: usize) -> Self { 19 | Self { 20 | segments: Vec::with_capacity(capacity), 21 | split_solver: SplitSolver::new(), 22 | graph_builder: GraphBuilder::::new() 23 | } 24 | } 25 | 26 | #[inline] 27 | pub fn clear(&mut self) { 28 | self.segments.clear(); 29 | } 30 | 31 | 32 | #[inline] 33 | pub fn add_segments(&mut self, segments: &[Segment]) { 34 | self.segments.extend_from_slice(segments); 35 | } 36 | 37 | #[inline] 38 | pub fn with_segments(segments: Vec>) -> Self { 39 | Self { 40 | segments, 41 | split_solver: SplitSolver::new(), 42 | graph_builder: GraphBuilder::::new() 43 | } 44 | } 45 | 46 | #[inline] 47 | pub fn build_graph_view_with_solver(&mut self, solver: Solver) -> Option { 48 | self.split_solver.split_segments(&mut self.segments, &solver); 49 | if self.segments.is_empty() { 50 | return None; 51 | } 52 | let graph = self 53 | .graph_builder 54 | .build_offset(&solver, &self.segments); 55 | 56 | Some(graph) 57 | } 58 | } -------------------------------------------------------------------------------- /iOverlay/src/mesh/rotator.rs: -------------------------------------------------------------------------------- 1 | use i_float::float::compatible::FloatPointCompatible; 2 | use i_float::float::number::FloatNumber; 3 | 4 | pub(crate) struct Rotator { 5 | a_x: T, 6 | a_y: T, 7 | b_x: T, 8 | b_y: T, 9 | } 10 | 11 | impl Rotator { 12 | 13 | #[inline] 14 | pub(crate) fn new(cs: T, sn: T) -> Self { 15 | let a_x = cs; 16 | let a_y = sn; 17 | let b_x = -a_y; 18 | let b_y = a_x; 19 | 20 | Self { 21 | a_x, 22 | a_y, 23 | b_x, 24 | b_y, 25 | } 26 | } 27 | 28 | #[inline] 29 | pub(crate) fn with_angle(angle: T) -> Self { 30 | let (sin, cos) = angle.sin_cos(); 31 | Self::new(cos, sin) 32 | } 33 | 34 | #[inline] 35 | pub(crate) fn with_vector>(v: &P) -> Self { 36 | Self::new(v.x(), v.y()) 37 | } 38 | 39 | #[inline] 40 | pub(crate) fn rotate>(&self, v: &P) -> P { 41 | let v_x = v.x(); 42 | let v_y = v.y(); 43 | let x = self.a_x * v_x + self.b_x * v_y; 44 | let y = self.a_y * v_x + self.b_y * v_y; 45 | P::from_xy(x, y) 46 | } 47 | } 48 | 49 | #[cfg(test)] 50 | mod tests { 51 | use core::f64::consts::PI; 52 | use crate::mesh::rotator::Rotator; 53 | 54 | 55 | #[test] 56 | fn test_ccw_rotate() { 57 | let deg_45 = 0.25 * PI; 58 | let rotator = Rotator::with_angle(deg_45); 59 | let v0 = [1.0, 0.0]; 60 | let v1 = rotator.rotate(&v0); 61 | let v2 = rotator.rotate(&v1); 62 | let v3 = rotator.rotate(&v2); 63 | 64 | let i_sqrt2 = 1.0 / 2.0f64.sqrt(); 65 | 66 | compare_vecs(v1, [i_sqrt2, i_sqrt2]); 67 | compare_vecs(v2, [0.0, 1.0]); 68 | compare_vecs(v3, [-i_sqrt2, i_sqrt2]); 69 | } 70 | 71 | #[test] 72 | fn test_cw_rotate() { 73 | let deg_45 = -0.25 * PI; 74 | let rotator = Rotator::with_angle(deg_45); 75 | let v0 = [1.0, 0.0]; 76 | let v1 = rotator.rotate(&v0); 77 | let v2 = rotator.rotate(&v1); 78 | let v3 = rotator.rotate(&v2); 79 | 80 | let i_sqrt2 = 1.0 / 2.0f64.sqrt(); 81 | 82 | compare_vecs(v1, [i_sqrt2, -i_sqrt2]); 83 | compare_vecs(v2, [0.0, -1.0]); 84 | compare_vecs(v3, [-i_sqrt2, -i_sqrt2]); 85 | } 86 | 87 | fn compare_vecs(v0: [f64; 2], v1: [f64; 2]) { 88 | assert!((v0[0] - v1[0]).abs() < 0.0001); 89 | assert!((v0[1] - v1[1]).abs() < 0.0001); 90 | } 91 | } -------------------------------------------------------------------------------- /iOverlay/src/mesh/stroke/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod offset; 2 | mod builder_join; 3 | mod section; 4 | mod builder; 5 | mod builder_cap; -------------------------------------------------------------------------------- /iOverlay/src/mesh/stroke/section.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use core::marker::PhantomData; 3 | use i_float::adapter::FloatPointAdapter; 4 | use i_float::float::compatible::FloatPointCompatible; 5 | use i_float::float::number::FloatNumber; 6 | use i_float::float::vector::FloatPointMath; 7 | use crate::mesh::math::Math; 8 | use crate::segm::offset::ShapeCountOffset; 9 | use crate::segm::segment::Segment; 10 | 11 | #[derive(Debug, Clone)] 12 | pub(super) struct Section, T: FloatNumber> { 13 | pub(super) a: P, 14 | pub(super) b: P, 15 | pub(super) a_top: P, 16 | pub(super) b_top: P, 17 | pub(super) a_bot: P, 18 | pub(super) b_bot: P, 19 | pub(super) dir: P, 20 | _phantom: PhantomData, 21 | } 22 | 23 | impl> Section { 24 | pub(crate) fn new(radius: T, a: &P, b: &P) -> Self { 25 | let dir = Math::normal(b, a); 26 | let t = Math::ortho_and_scale(&dir, radius); 27 | 28 | let a_top = FloatPointMath::add(a, &t); 29 | let a_bot = FloatPointMath::sub(a, &t); 30 | 31 | let b_top = FloatPointMath::add(b, &t); 32 | let b_bot = FloatPointMath::sub(b, &t); 33 | 34 | Section { 35 | a: *a, 36 | b: *b, 37 | a_top, 38 | b_top, 39 | a_bot, 40 | b_bot, 41 | dir, 42 | _phantom: Default::default(), 43 | } 44 | } 45 | } 46 | 47 | pub(crate) trait SectionToSegment> { 48 | fn add_section(&mut self, section: &Section, adapter: &FloatPointAdapter); 49 | } 50 | 51 | impl> SectionToSegment for Vec> { 52 | fn add_section(&mut self, section: &Section, adapter: &FloatPointAdapter) { 53 | let a_top = adapter.float_to_int(§ion.a_top); 54 | let b_top = adapter.float_to_int(§ion.b_top); 55 | let a_bot = adapter.float_to_int(§ion.a_bot); 56 | let b_bot = adapter.float_to_int(§ion.b_bot); 57 | 58 | self.push(Segment::bold_subject_ab(b_top, a_top)); 59 | self.push(Segment::bold_subject_ab(a_bot, b_bot)); 60 | } 61 | } -------------------------------------------------------------------------------- /iOverlay/src/mesh/subject.rs: -------------------------------------------------------------------------------- 1 | use crate::geom::x_segment::XSegment; 2 | use crate::segm::segment::Segment; 3 | use i_float::int::point::IntPoint; 4 | use crate::segm::offset::ShapeCountOffset; 5 | 6 | impl Segment { 7 | 8 | #[inline] 9 | pub(crate) fn bold_subject_ab(p0: IntPoint, p1: IntPoint) -> Self { 10 | if p0 < p1 { 11 | Self { 12 | x_segment: XSegment { a: p0, b: p1 }, 13 | count: ShapeCountOffset { subj: 1, bold: true }, 14 | } 15 | } else { 16 | Self { 17 | x_segment: XSegment { a: p1, b: p0 }, 18 | count: ShapeCountOffset { subj: -1, bold: true }, 19 | } 20 | } 21 | } 22 | 23 | #[inline] 24 | pub(crate) fn weak_subject_ab(p0: IntPoint, p1: IntPoint) -> Self { 25 | if p0 < p1 { 26 | Self { 27 | x_segment: XSegment { a: p0, b: p1 }, 28 | count: ShapeCountOffset { subj: 1, bold: false }, 29 | } 30 | } else { 31 | Self { 32 | x_segment: XSegment { a: p1, b: p0 }, 33 | count: ShapeCountOffset { subj: -1, bold: false }, 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /iOverlay/src/segm/boolean.rs: -------------------------------------------------------------------------------- 1 | use crate::core::overlay::ShapeType; 2 | use crate::segm::winding::WindingCount; 3 | 4 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 5 | pub struct ShapeCountBoolean { 6 | pub subj: i32, 7 | pub clip: i32, 8 | } 9 | 10 | impl ShapeCountBoolean { 11 | pub(crate) const SUBJ_DIRECT: ShapeCountBoolean = ShapeCountBoolean { subj: 1, clip: 0 }; 12 | pub(crate) const SUBJ_INVERT: ShapeCountBoolean = ShapeCountBoolean { subj: -1, clip: 0 }; 13 | pub(crate) const CLIP_DIRECT: ShapeCountBoolean = ShapeCountBoolean { subj: 0, clip: 1 }; 14 | pub(crate) const CLIP_INVERT: ShapeCountBoolean = ShapeCountBoolean { subj: 0, clip: -1 }; 15 | } 16 | 17 | impl WindingCount for ShapeCountBoolean { 18 | #[inline(always)] 19 | fn is_not_empty(&self) -> bool { self.subj != 0 || self.clip != 0 } 20 | 21 | #[inline(always)] 22 | fn new(subj: i32, clip: i32) -> Self { Self { subj, clip } } 23 | 24 | #[inline(always)] 25 | fn with_shape_type(shape_type: ShapeType) -> (Self, Self) { 26 | match shape_type { 27 | ShapeType::Subject => (ShapeCountBoolean::SUBJ_DIRECT, ShapeCountBoolean::SUBJ_INVERT), 28 | ShapeType::Clip => (ShapeCountBoolean::CLIP_DIRECT, ShapeCountBoolean::CLIP_INVERT) 29 | } 30 | } 31 | 32 | #[inline(always)] 33 | fn add(self, count: Self) -> Self { 34 | let subj = self.subj + count.subj; 35 | let clip = self.clip + count.clip; 36 | 37 | Self { subj, clip } 38 | } 39 | 40 | #[inline(always)] 41 | fn apply(&mut self, count: Self) { 42 | self.subj += count.subj; 43 | self.clip += count.clip; 44 | } 45 | 46 | #[inline(always)] 47 | fn invert(self) -> Self { 48 | Self { subj: -self.subj, clip: -self.clip } 49 | } 50 | } -------------------------------------------------------------------------------- /iOverlay/src/segm/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod winding; 2 | pub mod boolean; 3 | pub mod string; 4 | pub mod offset; 5 | pub(crate) mod segment; 6 | pub(crate) mod merge; 7 | pub(crate) mod build; -------------------------------------------------------------------------------- /iOverlay/src/segm/offset.rs: -------------------------------------------------------------------------------- 1 | use crate::core::overlay::ShapeType; 2 | use crate::segm::winding::WindingCount; 3 | 4 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 5 | pub struct ShapeCountOffset { 6 | pub(crate) subj: i32, 7 | pub(crate) bold: bool, 8 | } 9 | 10 | impl WindingCount for ShapeCountOffset { 11 | #[inline(always)] 12 | fn is_not_empty(&self) -> bool { self.subj != 0 } 13 | 14 | #[inline(always)] 15 | fn new(subj: i32, _: i32) -> Self { 16 | Self {subj, bold: true} 17 | } 18 | 19 | #[inline(always)] 20 | fn with_shape_type(shape_type: ShapeType) -> (Self, Self) { 21 | match shape_type { 22 | ShapeType::Subject => (Self {subj: 1, bold: true}, Self {subj: -1, bold: true}), 23 | ShapeType::Clip => (Self {subj: 0, bold: true}, Self {subj: 0, bold: true}), 24 | } 25 | } 26 | 27 | #[inline(always)] 28 | fn add(self, count: Self) -> Self { 29 | let subj = self.subj + count.subj; 30 | let bold = self.bold || count.bold; 31 | Self {subj, bold} 32 | } 33 | 34 | #[inline(always)] 35 | fn apply(&mut self, count: Self) { 36 | self.subj += count.subj; 37 | self.bold = self.bold || count.bold; 38 | } 39 | 40 | #[inline(always)] 41 | fn invert(self) -> Self { 42 | let subj = -self.subj; 43 | Self {subj, bold: self.bold} 44 | } 45 | } -------------------------------------------------------------------------------- /iOverlay/src/segm/segment.rs: -------------------------------------------------------------------------------- 1 | use core::cmp::Ordering; 2 | use i_float::int::point::IntPoint; 3 | use i_key_sort::bin_key::index::{BinKey, BinLayout}; 4 | use crate::geom::x_segment::XSegment; 5 | use crate::segm::winding::WindingCount; 6 | 7 | pub type SegmentFill = u8; 8 | 9 | pub const NONE: SegmentFill = 0; 10 | 11 | pub const SUBJ_TOP: SegmentFill = 0b0001; 12 | pub const SUBJ_BOTTOM: SegmentFill = 0b0010; 13 | pub const CLIP_TOP: SegmentFill = 0b0100; 14 | pub const CLIP_BOTTOM: SegmentFill = 0b1000; 15 | 16 | pub const SUBJ_BOTH: SegmentFill = SUBJ_TOP | SUBJ_BOTTOM; 17 | pub const CLIP_BOTH: SegmentFill = CLIP_TOP | CLIP_BOTTOM; 18 | pub const BOTH_TOP: SegmentFill = SUBJ_TOP | CLIP_TOP; 19 | pub const BOTH_BOTTOM: SegmentFill = SUBJ_BOTTOM | CLIP_BOTTOM; 20 | 21 | pub const ALL: SegmentFill = SUBJ_BOTH | CLIP_BOTH; 22 | 23 | #[derive(Debug, Clone, Copy)] 24 | pub(crate) struct Segment { 25 | pub(crate) x_segment: XSegment, 26 | pub(crate) count: C, 27 | } 28 | 29 | impl Segment { 30 | #[inline(always)] 31 | pub(crate) fn create_and_validate(a: IntPoint, b: IntPoint, count: C) -> Self { 32 | if a < b { 33 | Self { x_segment: XSegment { a, b }, count } 34 | } else { 35 | Self { x_segment: XSegment { a: b, b: a }, count: count.invert() } 36 | } 37 | } 38 | } 39 | 40 | impl PartialEq for Segment { 41 | #[inline(always)] 42 | fn eq(&self, other: &Self) -> bool { 43 | self.x_segment == other.x_segment 44 | } 45 | } 46 | 47 | impl Eq for Segment {} 48 | 49 | impl PartialOrd for Segment { 50 | #[inline(always)] 51 | fn partial_cmp(&self, other: &Self) -> Option { 52 | Some(self.cmp(other)) 53 | } 54 | } 55 | 56 | impl Ord for Segment { 57 | #[inline(always)] 58 | fn cmp(&self, other: &Self) -> Ordering { 59 | self.x_segment.cmp(&other.x_segment) 60 | } 61 | } 62 | 63 | impl BinKey for Segment { 64 | #[inline(always)] 65 | fn bin_key(&self) -> i32 { 66 | self.x_segment.bin_key() 67 | } 68 | 69 | #[inline(always)] 70 | fn bin_index(&self, layout: &BinLayout) -> usize { 71 | self.x_segment.bin_index(layout) 72 | } 73 | } -------------------------------------------------------------------------------- /iOverlay/src/segm/string.rs: -------------------------------------------------------------------------------- 1 | use core::cmp::Ordering; 2 | use crate::core::overlay::ShapeType; 3 | use crate::segm::winding::WindingCount; 4 | 5 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 6 | pub struct ShapeCountString { 7 | pub subj: i32, 8 | pub clip: u8, 9 | } 10 | 11 | pub(crate) const STRING_FORWARD_CLIP: u8 = 0b10; 12 | pub(crate) const STRING_BACK_CLIP: u8 = 0b1; 13 | 14 | impl WindingCount for ShapeCountString { 15 | #[inline(always)] 16 | fn is_not_empty(&self) -> bool { self.subj != 0 || self.clip != 0 } 17 | 18 | #[inline(always)] 19 | fn new(subj: i32, clip: i32) -> Self { 20 | // 0 - bit - back 21 | // 1 - bit - forward 22 | let mask = match clip.cmp(&0) { 23 | Ordering::Greater => STRING_FORWARD_CLIP, 24 | Ordering::Less => STRING_BACK_CLIP, 25 | Ordering::Equal => 0, 26 | }; 27 | Self { subj, clip: mask } 28 | } 29 | 30 | #[inline(always)] 31 | fn with_shape_type(shape_type: ShapeType) -> (Self, Self) { 32 | match shape_type { 33 | ShapeType::Subject => (Self { subj: 1, clip: 0 }, Self { subj: -1, clip: 0 }), 34 | ShapeType::Clip => (Self { subj: 0, clip: STRING_FORWARD_CLIP }, Self { subj: 0, clip: STRING_BACK_CLIP }) 35 | } 36 | } 37 | 38 | #[inline(always)] 39 | fn add(self, count: Self) -> Self { 40 | let subj = self.subj + count.subj; 41 | let clip = self.clip | count.clip; 42 | 43 | Self { subj, clip } 44 | } 45 | 46 | #[inline(always)] 47 | fn apply(&mut self, count: Self) { 48 | self.subj += count.subj; 49 | self.clip |= count.clip; 50 | } 51 | 52 | #[inline(always)] 53 | fn invert(self) -> Self { 54 | let b0 = self.clip & 0b01; 55 | let b1 = self.clip & 0b10; 56 | let clip = (b0 << 1) | (b1 >> 1); 57 | 58 | Self { subj: -self.subj, clip } 59 | } 60 | } -------------------------------------------------------------------------------- /iOverlay/src/segm/winding.rs: -------------------------------------------------------------------------------- 1 | use crate::core::overlay::ShapeType; 2 | 3 | pub(crate) trait WindingCount 4 | where 5 | Self: Clone + Copy + Send, 6 | { 7 | fn is_not_empty(&self) -> bool; 8 | fn new(subj: i32, clip: i32) -> Self; 9 | fn with_shape_type(shape_type: ShapeType) -> (Self, Self); 10 | fn add(self, count: Self) -> Self; 11 | fn apply(&mut self, count: Self); 12 | fn invert(self) -> Self; 13 | } -------------------------------------------------------------------------------- /iOverlay/src/split/fragment.rs: -------------------------------------------------------------------------------- 1 | use i_float::int::rect::IntRect; 2 | use crate::geom::x_segment::XSegment; 3 | 4 | #[derive(Debug, Clone)] 5 | pub(super) struct Fragment { 6 | pub(super) index: usize, 7 | pub(super) rect: IntRect, 8 | pub(super) x_segment: XSegment, 9 | } 10 | 11 | impl Fragment { 12 | 13 | #[inline] 14 | pub(super) fn with_index_and_segment(index: usize, x_segment: XSegment) -> Self { 15 | let (min_y, max_y) = if x_segment.a.y < x_segment.b.y { 16 | (x_segment.a.y, x_segment.b.y) 17 | } else { 18 | (x_segment.b.y, x_segment.a.y) 19 | }; 20 | 21 | let rect = IntRect { 22 | min_x: x_segment.a.x, 23 | max_x: x_segment.b.x, 24 | min_y, 25 | max_y, 26 | }; 27 | 28 | Self { 29 | index, 30 | rect, 31 | x_segment, 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /iOverlay/src/split/line_mark.rs: -------------------------------------------------------------------------------- 1 | use i_float::int::point::IntPoint; 2 | use i_key_sort::bin_key::index::{BinKey, BinLayout}; 3 | 4 | #[derive(Clone, Copy, PartialEq)] 5 | pub(super) struct LineMark { 6 | pub(super) index: usize, 7 | pub(super) point: IntPoint, 8 | } 9 | 10 | impl BinKey for LineMark { 11 | #[inline(always)] 12 | fn bin_key(&self) -> usize { 13 | self.index 14 | } 15 | 16 | #[inline(always)] 17 | fn bin_index(&self, layout: &BinLayout) -> usize { 18 | layout.index(self.index) 19 | } 20 | } -------------------------------------------------------------------------------- /iOverlay/src/split/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod solver; 2 | 3 | mod cross_solver; 4 | mod fragment; 5 | mod solver_list; 6 | mod solver_tree; 7 | mod snap_radius; 8 | mod line_mark; 9 | mod grid_layout; 10 | mod solver_fragment; -------------------------------------------------------------------------------- /iOverlay/src/split/snap_radius.rs: -------------------------------------------------------------------------------- 1 | use crate::core::solver::Solver; 2 | 3 | pub(super) struct SnapRadius { 4 | current: usize, 5 | step: usize, 6 | } 7 | 8 | impl SnapRadius { 9 | pub(super) fn increment(&mut self) { 10 | self.current = 60.min(self.current + self.step); 11 | } 12 | 13 | pub(super) fn radius(&self) -> i64 { 14 | 1 << self.current 15 | } 16 | } 17 | 18 | impl Solver { 19 | pub(super) fn snap_radius(&self) -> SnapRadius { 20 | SnapRadius { 21 | current: self.precision.start, 22 | step: self.precision.progression, 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /iOverlay/src/split/solver_list.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use crate::core::solver::Solver; 3 | use crate::segm::segment::Segment; 4 | use crate::segm::winding::WindingCount; 5 | use crate::split::snap_radius::SnapRadius; 6 | use crate::split::solver::SplitSolver; 7 | 8 | impl SplitSolver { 9 | pub(super) fn list_split( 10 | &mut self, 11 | snap_radius: SnapRadius, 12 | segments: &mut Vec>, 13 | solver: &Solver 14 | ) -> bool { 15 | let mut need_to_fix = true; 16 | 17 | let mut snap_radius = snap_radius; 18 | let mut any_intersection = false; 19 | 20 | while need_to_fix && segments.len() > 1 { 21 | need_to_fix = false; 22 | self.marks.clear(); 23 | 24 | let radius: i64 = snap_radius.radius(); 25 | 26 | for i in 0..segments.len() - 1 { 27 | let ei = &segments[i].x_segment; 28 | let ri = ei.y_range(); 29 | for (j, s) in segments.iter().enumerate().skip(i + 1) { 30 | let ej = &s.x_segment; 31 | if ei.b.x < ej.a.x { 32 | break; 33 | } 34 | 35 | if ej.is_not_intersect_y_range(&ri) { 36 | continue; 37 | } 38 | 39 | let is_round = SplitSolver::cross(i, j, ei, ej, &mut self.marks, radius); 40 | need_to_fix = need_to_fix || is_round 41 | } 42 | } 43 | 44 | if self.marks.is_empty() { 45 | return any_intersection; 46 | } 47 | any_intersection = true; 48 | self.apply(segments, need_to_fix, solver); 49 | 50 | snap_radius.increment(); 51 | 52 | if need_to_fix && !solver.is_list_split(segments) { 53 | // finish with tree solver if edges is become large 54 | self.tree_split(snap_radius, segments, solver); 55 | return true; 56 | } 57 | } 58 | 59 | any_intersection 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /iOverlay/src/string/filter.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use crate::core::link::OverlayLink; 3 | use crate::segm::segment::{SUBJ_BOTH, SUBJ_BOTTOM, SUBJ_TOP}; 4 | use crate::string::graph::StringGraph; 5 | use crate::string::rule::StringRule; 6 | 7 | impl OverlayLink { 8 | #[inline] 9 | pub(super) fn visit_fill(&self, fill: u8, node_id: usize, clockwise: bool) -> u8 { 10 | let is_a = self.a.id == node_id; 11 | let direct = self.a.point < self.b.point; 12 | let same = clockwise == direct; 13 | 14 | let mask = if is_a { 15 | if same { SUBJ_TOP } else { SUBJ_BOTTOM } 16 | } else if same { 17 | SUBJ_BOTTOM 18 | } else { 19 | SUBJ_TOP 20 | }; 21 | 22 | fill & !mask 23 | } 24 | 25 | #[inline] 26 | pub(super) fn is_move_possible(&self, fill: u8, node_id: usize, clockwise: bool) -> bool { 27 | match fill { 28 | SUBJ_BOTH => return true, 29 | 0 => return false, 30 | _ => {} 31 | } 32 | 33 | let is_a = self.a.id == node_id; 34 | let direct = self.a.point < self.b.point; 35 | let left = if direct { 36 | fill & SUBJ_TOP != 0 37 | } else { 38 | fill & SUBJ_BOTTOM != 0 39 | }; 40 | 41 | is_a == (clockwise == left) 42 | } 43 | } 44 | 45 | impl StringGraph<'_> { 46 | #[inline(always)] 47 | pub(super) fn filter(&self, ext_rule: StringRule) -> Vec { 48 | match ext_rule { 49 | StringRule::Slice => self.filter_slice(), 50 | } 51 | } 52 | 53 | #[inline] 54 | fn filter_slice(&self) -> Vec { 55 | self.links 56 | .iter() 57 | .map(|link| link.fill & SUBJ_BOTH) 58 | .collect() 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /iOverlay/src/string/graph.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use crate::build::builder::GraphNode; 3 | use crate::core::link::OverlayLink; 4 | 5 | pub struct StringGraph<'a> { 6 | pub(crate) nodes: &'a [Vec], 7 | pub(crate) links: &'a mut [OverlayLink], 8 | } 9 | impl StringGraph<'_> { 10 | #[inline(always)] 11 | pub(super) fn node(&self, index: usize) -> &[usize] { 12 | unsafe { self.nodes.get_unchecked(index) } 13 | } 14 | 15 | #[inline(always)] 16 | pub(super) fn link(&self, index: usize) -> &OverlayLink { 17 | unsafe { self.links.get_unchecked(index) } 18 | } 19 | } 20 | 21 | impl GraphNode for Vec { 22 | #[inline(always)] 23 | fn with_indices(indices: &[usize]) -> Self { 24 | indices.to_vec() 25 | } 26 | } -------------------------------------------------------------------------------- /iOverlay/src/string/line.rs: -------------------------------------------------------------------------------- 1 | use i_float::int::point::IntPoint; 2 | 3 | pub type IntLine = [IntPoint; 2]; 4 | -------------------------------------------------------------------------------- /iOverlay/src/string/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod slice; 2 | pub mod line; 3 | pub mod rule; 4 | pub mod overlay; 5 | pub mod graph; 6 | pub mod split; 7 | pub mod clip; 8 | pub mod extract; 9 | mod filter; -------------------------------------------------------------------------------- /iOverlay/src/string/rule.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone, Copy, PartialEq)] 2 | pub enum StringRule { 3 | Slice 4 | } -------------------------------------------------------------------------------- /iOverlay/src/util/log.rs: -------------------------------------------------------------------------------- 1 | 2 | pub(crate) trait Int { 3 | fn log2_sqrt(&self) -> usize; 4 | } 5 | 6 | impl Int for usize { 7 | 8 | #[inline] 9 | fn log2_sqrt(&self) -> usize { 10 | let z = self.leading_zeros(); 11 | let i = (usize::BITS - z) as usize; 12 | let n = (i + 1) >> 1; 13 | 1 << n 14 | } 15 | } 16 | 17 | #[cfg(test)] 18 | mod tests { 19 | use alloc::vec; 20 | use alloc::vec::Vec; 21 | use crate::util::log::Int; 22 | 23 | #[test] 24 | fn test_0() { 25 | let tests: Vec<[usize; 2]> = vec![ 26 | [0, 1], 27 | [1, 2], 28 | [3, 2], 29 | [15, 4], 30 | [16, 8], 31 | [255, 16], 32 | [256, 32], 33 | ]; 34 | 35 | for test in tests { 36 | let a = test[0].log2_sqrt(); 37 | let b = test[1]; 38 | assert_eq!(a, b); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /iOverlay/src/util/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod sort; 2 | pub(crate) mod log; -------------------------------------------------------------------------------- /iOverlay/src/util/sort.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "allow_multithreading")] 2 | use rayon::prelude::*; 3 | use core::cmp::Ordering; 4 | use i_key_sort::bin_key::index::{BinKey, BinLayoutOp}; 5 | use i_key_sort::sort::key_sort::KeyBinSort; 6 | use crate::core::solver::Solver; 7 | 8 | pub(crate) trait SmartBinSort { 9 | fn smart_bin_sort_by(&mut self, solver: &Solver, compare: F) 10 | where 11 | F: Fn(&Self::Item, &Self::Item) -> Ordering + Sync; 12 | 13 | type Item: Send; 14 | } 15 | 16 | impl SmartBinSort for [T] 17 | where 18 | T: BinKey + Clone + Send, 19 | U: Copy + Ord + BinLayoutOp, 20 | { 21 | fn smart_bin_sort_by(&mut self, _solver: &Solver, compare: F) 22 | where 23 | F: Fn(&T, &T) -> Ordering + Sync, 24 | { 25 | #[cfg(feature = "allow_multithreading")] 26 | { 27 | if let Some(multithreading) = _solver.multithreading { 28 | if self.len() > multithreading.par_sort_min_size { 29 | self.par_sort_unstable_by(compare); 30 | return; 31 | } 32 | } 33 | } 34 | 35 | // Fallback to standard sort if multithreading is not enabled 36 | self.sort_with_bins(compare) 37 | } 38 | 39 | type Item = T; 40 | } 41 | #[cfg(test)] 42 | mod tests { 43 | use alloc::vec; 44 | use i_key_sort::bin_key::index::BinLayout; 45 | use super::*; 46 | 47 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 48 | struct Point { 49 | x: i32, 50 | y: i32, 51 | } 52 | 53 | impl PartialOrd for Point { 54 | #[inline(always)] 55 | fn partial_cmp(&self, other: &Self) -> Option { 56 | Some(self.cmp(other)) 57 | } 58 | } 59 | 60 | impl Ord for Point { 61 | #[inline(always)] 62 | fn cmp(&self, other: &Self) -> Ordering { 63 | let x = self.x == other.x; 64 | if x && self.y == other.y { 65 | Ordering::Equal 66 | } else if self.x < other.x || x && self.y < other.y { 67 | Ordering::Less 68 | } else { 69 | Ordering::Greater 70 | } 71 | } 72 | } 73 | 74 | impl BinKey for Point { 75 | #[inline(always)] 76 | fn bin_key(&self) -> i32 { 77 | self.x 78 | } 79 | 80 | #[inline(always)] 81 | fn bin_index(&self, layout: &BinLayout) -> usize { 82 | layout.index(self.x.into()) 83 | } 84 | } 85 | 86 | #[test] 87 | fn test_sort_by() { 88 | let mut data = vec![ 89 | Point { x: 5, y: 1 }, 90 | Point { x: 3, y: 1 }, 91 | Point { x: 1, y: 1 }, 92 | Point { x: 4, y: 1 }, 93 | Point { x: 2, y: 1 }, 94 | ]; 95 | data.smart_bin_sort_by(&Solver::AUTO, |a, b| a.cmp(&b)); 96 | 97 | assert_eq!(data, vec![ 98 | Point { x: 1, y: 1 }, 99 | Point { x: 2, y: 1 }, 100 | Point { x: 3, y: 1 }, 101 | Point { x: 4, y: 1 }, 102 | Point { x: 5, y: 1 }, 103 | ]); 104 | } 105 | } -------------------------------------------------------------------------------- /iOverlay/src/vector/edge.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use i_float::int::point::IntPoint; 3 | use i_shape::int::path::IntPath; 4 | 5 | pub type SideFill = u8; 6 | pub type VectorPath = Vec; 7 | pub type VectorShape = Vec; 8 | 9 | pub const SUBJ_LEFT: u8 = 0b0001; 10 | pub const SUBJ_RIGHT: u8 = 0b0010; 11 | pub const CLIP_LEFT: u8 = 0b0100; 12 | pub const CLIP_RIGHT: u8 = 0b1000; 13 | 14 | pub trait Reverse { 15 | fn reverse(self) -> Self; 16 | } 17 | 18 | impl Reverse for SideFill { 19 | fn reverse(self) -> Self { 20 | let subj_left = self & SUBJ_LEFT; 21 | let subj_right = self & SUBJ_RIGHT; 22 | let clip_left = self & CLIP_LEFT; 23 | let clip_right = self & CLIP_RIGHT; 24 | 25 | (subj_left << 1) | (subj_right >> 1) | (clip_left << 1) | (clip_right >> 1) 26 | } 27 | } 28 | 29 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 30 | pub struct VectorEdge { 31 | pub a: IntPoint, 32 | pub b: IntPoint, 33 | pub fill: SideFill, 34 | } 35 | 36 | impl VectorEdge { 37 | pub(crate) fn new(fill: SideFill, a: IntPoint, b: IntPoint) -> Self { 38 | let fill = if a < b { 39 | fill 40 | } else { 41 | fill.reverse() 42 | }; 43 | 44 | Self { a, b, fill } 45 | } 46 | } 47 | 48 | pub trait ToPath { 49 | fn to_path(&self) -> IntPath; 50 | } 51 | 52 | impl ToPath for VectorPath { 53 | fn to_path(&self) -> IntPath { 54 | self.iter().map(|e| e.a).collect() 55 | } 56 | } -------------------------------------------------------------------------------- /iOverlay/src/vector/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod edge; 2 | pub mod extract_vectors; 3 | -------------------------------------------------------------------------------- /iOverlay/tests/board_tests.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use i_float::int::point::IntPoint; 4 | use i_overlay::core::fill_rule::FillRule; 5 | use i_overlay::core::overlay::{Overlay, ShapeType}; 6 | use i_overlay::core::overlay_rule::OverlayRule; 7 | use i_shape::int::path::IntPath; 8 | 9 | #[test] 10 | fn test_0() { 11 | assert_eq!(1, test(1, OverlayRule::Xor)); 12 | } 13 | 14 | #[test] 15 | fn test_1() { 16 | assert_eq!(5, test(2, OverlayRule::Xor)); 17 | } 18 | 19 | #[test] 20 | fn test_2() { 21 | assert_eq!(9 + 4, test(3, OverlayRule::Xor)); 22 | } 23 | 24 | #[test] 25 | fn test_12() { 26 | let s = 12 * 12 + 11 * 11; 27 | assert_eq!(s, test(12, OverlayRule::Xor)); 28 | } 29 | 30 | #[test] 31 | fn test_n() { 32 | for i in 1..20 { 33 | let s = i * i + (i - 1) * (i - 1); 34 | assert_eq!(s, test(i, OverlayRule::Xor)); 35 | } 36 | } 37 | 38 | fn test(n: usize, rule: OverlayRule) -> usize { 39 | let subj_paths = many_squares(IntPoint::new(0, 0), 20, 30, n); 40 | let clip_paths = many_squares(IntPoint::new(15, 15), 20, 30, n - 1); 41 | 42 | let mut overlay = Overlay::new(8 * n * n); 43 | overlay.add_contours(&subj_paths, ShapeType::Subject); 44 | overlay.add_contours(&clip_paths, ShapeType::Clip); 45 | 46 | let graph = overlay.build_graph_view(FillRule::NonZero).unwrap(); 47 | let result = graph.extract_shapes(rule, &mut Default::default()); 48 | 49 | result.len() 50 | } 51 | 52 | fn many_squares(start: IntPoint, size: i32, offset: i32, n: usize) -> Vec { 53 | let mut result = Vec::with_capacity(n * n); 54 | let mut y = start.y; 55 | for _ in 0..n { 56 | let mut x = start.x; 57 | for _ in 0..n { 58 | let path: IntPath = vec![ 59 | IntPoint::new(x, y), 60 | IntPoint::new(x, y + size), 61 | IntPoint::new(x + size, y + size), 62 | IntPoint::new(x + size, y), 63 | ]; 64 | result.push(path); 65 | x += offset; 66 | } 67 | y += offset; 68 | } 69 | 70 | result 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_125.json: -------------------------------------------------------------------------------- 1 | { 2 | "inverseDifference" : [ 3 | [ 4 | 5 | ] 6 | ], 7 | "fillRule" : 1, 8 | "clip" : [ 9 | [ 10 | 11 | ] 12 | ], 13 | "subject" : [ 14 | [ 15 | [ 16 | [ 17 | [ 18 | 0, 19 | 0 20 | ], 21 | [ 22 | 0, 23 | 20000 24 | ], 25 | [ 26 | 4000, 27 | 20000 28 | ], 29 | [ 30 | 4000, 31 | 0 32 | ] 33 | ] 34 | ] 35 | ] 36 | ], 37 | "intersect" : [ 38 | [ 39 | 40 | ] 41 | ], 42 | "subjPaths" : [ 43 | [ 44 | [ 45 | 0, 46 | 5000 47 | ], 48 | [ 49 | 0, 50 | 10000 51 | ], 52 | [ 53 | 4000, 54 | 10000 55 | ], 56 | [ 57 | 4000, 58 | 5000 59 | ] 60 | ], 61 | [ 62 | [ 63 | 0, 64 | 10000 65 | ], 66 | [ 67 | 0, 68 | 20000 69 | ], 70 | [ 71 | 4000, 72 | 20000 73 | ], 74 | [ 75 | 4000, 76 | 10000 77 | ] 78 | ], 79 | [ 80 | [ 81 | 0, 82 | 0 83 | ], 84 | [ 85 | 0, 86 | 15000 87 | ], 88 | [ 89 | 4000, 90 | 15000 91 | ], 92 | [ 93 | 4000, 94 | 0 95 | ] 96 | ] 97 | ], 98 | "difference" : [ 99 | [ 100 | [ 101 | [ 102 | [ 103 | 0, 104 | 0 105 | ], 106 | [ 107 | 0, 108 | 20000 109 | ], 110 | [ 111 | 4000, 112 | 20000 113 | ], 114 | [ 115 | 4000, 116 | 0 117 | ] 118 | ] 119 | ] 120 | ] 121 | ], 122 | "union" : [ 123 | [ 124 | [ 125 | [ 126 | [ 127 | 0, 128 | 0 129 | ], 130 | [ 131 | 0, 132 | 20000 133 | ], 134 | [ 135 | 4000, 136 | 20000 137 | ], 138 | [ 139 | 4000, 140 | 0 141 | ] 142 | ] 143 | ] 144 | ] 145 | ], 146 | "xor" : [ 147 | [ 148 | [ 149 | [ 150 | [ 151 | 0, 152 | 0 153 | ], 154 | [ 155 | 0, 156 | 20000 157 | ], 158 | [ 159 | 4000, 160 | 20000 161 | ], 162 | [ 163 | 4000, 164 | 0 165 | ] 166 | ] 167 | ] 168 | ] 169 | ], 170 | "clipPaths" : [ 171 | 172 | ] 173 | } 174 | -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_127.json: -------------------------------------------------------------------------------- 1 | { 2 | "subjPaths" : [ 3 | [ 4 | [ 5 | 0, 6 | 10000 7 | ], 8 | [ 9 | -10000, 10 | 0 11 | ], 12 | [ 13 | 0, 14 | -10000 15 | ], 16 | [ 17 | 10000, 18 | 0 19 | ], 20 | [ 21 | 0, 22 | 10000 23 | ], 24 | [ 25 | -10000, 26 | 0 27 | ], 28 | [ 29 | 0, 30 | -10000 31 | ], 32 | [ 33 | 10000, 34 | 0 35 | ] 36 | ] 37 | ], 38 | "clipPaths" : [ 39 | [ 40 | [ 41 | -10000, 42 | 0 43 | ], 44 | [ 45 | 0, 46 | -10000 47 | ], 48 | [ 49 | 10000, 50 | 0 51 | ], 52 | [ 53 | 0, 54 | 10000 55 | ], 56 | [ 57 | -10000, 58 | 0 59 | ], 60 | [ 61 | 0, 62 | -10000 63 | ], 64 | [ 65 | 10000, 66 | 0 67 | ], 68 | [ 69 | -10000, 70 | 0 71 | ] 72 | ] 73 | ], 74 | "clip" : [ 75 | [ 76 | [ 77 | [ 78 | [ 79 | -10000, 80 | 0 81 | ], 82 | [ 83 | 0, 84 | 10000 85 | ], 86 | [ 87 | 10000, 88 | 0 89 | ], 90 | [ 91 | 0, 92 | -10000 93 | ] 94 | ] 95 | ] 96 | ] 97 | ], 98 | "fillRule" : 1, 99 | "intersect" : [ 100 | [ 101 | [ 102 | [ 103 | [ 104 | -10000, 105 | 0 106 | ], 107 | [ 108 | 0, 109 | 10000 110 | ], 111 | [ 112 | 10000, 113 | 0 114 | ], 115 | [ 116 | 0, 117 | -10000 118 | ] 119 | ] 120 | ] 121 | ] 122 | ], 123 | "inverseDifference" : [ 124 | [ 125 | 126 | ] 127 | ], 128 | "union" : [ 129 | [ 130 | [ 131 | [ 132 | [ 133 | -10000, 134 | 0 135 | ], 136 | [ 137 | 0, 138 | 10000 139 | ], 140 | [ 141 | 10000, 142 | 0 143 | ], 144 | [ 145 | 0, 146 | -10000 147 | ] 148 | ] 149 | ] 150 | ] 151 | ], 152 | "xor" : [ 153 | [ 154 | 155 | ] 156 | ], 157 | "subject" : [ 158 | [ 159 | [ 160 | [ 161 | [ 162 | -10000, 163 | 0 164 | ], 165 | [ 166 | 0, 167 | 10000 168 | ], 169 | [ 170 | 10000, 171 | 0 172 | ], 173 | [ 174 | 0, 175 | -10000 176 | ] 177 | ] 178 | ] 179 | ] 180 | ], 181 | "difference" : [ 182 | [ 183 | 184 | ] 185 | ] 186 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_134.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 1, 3 | "subjPaths": [[[-10240, -10240], [-10240, 10240], [10240, 10240], [10240, -10240]], [[-10240, -12800], [-5120, 5120], [5120, 5120], [5120, -5120]]], 4 | "clipPaths": [[[-15360, -15360], [-15360, 15360], [15360, 15360], [15360, -15360]]], 5 | "subject": [[[[[-10240, -12800], [-9509, -10240], [-10240, -10240], [-10240, 10240], [10240, 10240], [10240, -10240], [-5120, -10240]]]]], 6 | "clip": [[[[[-15360, -15360], [-15360, 15360], [15360, 15360], [15360, -15360]]]]], 7 | "union": [[[[[-15360, -15360], [-15360, 15360], [15360, 15360], [15360, -15360]]]]], 8 | "intersect": [[[[[-10240, -12800], [-9509, -10240], [-10240, -10240], [-10240, 10240], [10240, 10240], [10240, -10240], [-5120, -10240]]]]], 9 | "difference": [[]], 10 | "inverseDifference": [[[[[-15360, -15360], [-15360, 15360], [15360, 15360], [15360, -15360]], [[-9509, -10240], [-10240, -12800], [-5120, -10240], [10240, -10240], [10240, 10240], [-10240, 10240], [-10240, -10240]]]]], 11 | "xor": [[[[[-15360, -15360], [-15360, 15360], [15360, 15360], [15360, -15360]], [[-9509, -10240], [-10240, -12800], [-5120, -10240], [10240, -10240], [10240, 10240], [-10240, 10240], [-10240, -10240]]]]] 12 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_138.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 1, 3 | "subjPaths": [[[0, 0], [0, 80], [90, 80], [90, 0]], [[10, 10], [80, 10], [80, 50], [10, 50], [70, 20], [70, 40]], [[50, 60], [60, 60], [60, 70], [50, 70]]], 4 | "clipPaths": [], 5 | "subject": [[[[[0, 0], [0, 80], [90, 80], [90, 0]], [[50, 30], [10, 10], [80, 10], [80, 50], [10, 50]], [[50, 70], [50, 60], [60, 60], [60, 70]]], [[[50, 30], [70, 40], [70, 20]]]]], 6 | "clip": [[]], 7 | "union": [[[[[0, 0], [0, 80], [90, 80], [90, 0]], [[50, 30], [10, 10], [80, 10], [80, 50], [10, 50]], [[50, 70], [50, 60], [60, 60], [60, 70]]], [[[50, 30], [70, 40], [70, 20]]]]], 8 | "intersect": [[]], 9 | "difference": [[[[[0, 0], [0, 80], [90, 80], [90, 0]], [[50, 30], [10, 10], [80, 10], [80, 50], [10, 50]], [[50, 70], [50, 60], [60, 60], [60, 70]]], [[[50, 30], [70, 40], [70, 20]]]]], 10 | "inverseDifference": [[]], 11 | "xor": [[[[[0, 0], [0, 80], [90, 80], [90, 0]], [[50, 30], [10, 10], [80, 10], [80, 50], [10, 50]], [[50, 70], [50, 60], [60, 60], [60, 70]]], [[[50, 30], [70, 40], [70, 20]]]]] 12 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_139.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 1, 3 | "subjPaths": [[[0, 0], [0, 80], [90, 80], [90, 0]], [[10, 10], [80, 10], [80, 50], [10, 50], [70, 20], [70, 40]], [[20, 60], [30, 60], [30, 70], [20, 70]], [[50, 60], [60, 60], [60, 70], [50, 70]]], 4 | "clipPaths": [], 5 | "subject": [[[[[0, 0], [0, 80], [90, 80], [90, 0]], [[50, 30], [10, 10], [80, 10], [80, 50], [10, 50]], [[20, 70], [20, 60], [30, 60], [30, 70]], [[50, 70], [50, 60], [60, 60], [60, 70]]], [[[50, 30], [70, 40], [70, 20]]]]], 6 | "clip": [[]], 7 | "union": [[[[[0, 0], [0, 80], [90, 80], [90, 0]], [[50, 30], [10, 10], [80, 10], [80, 50], [10, 50]], [[20, 70], [20, 60], [30, 60], [30, 70]], [[50, 70], [50, 60], [60, 60], [60, 70]]], [[[50, 30], [70, 40], [70, 20]]]]], 8 | "intersect": [[]], 9 | "difference": [[[[[0, 0], [0, 80], [90, 80], [90, 0]], [[50, 30], [10, 10], [80, 10], [80, 50], [10, 50]], [[20, 70], [20, 60], [30, 60], [30, 70]], [[50, 70], [50, 60], [60, 60], [60, 70]]], [[[50, 30], [70, 40], [70, 20]]]]], 10 | "inverseDifference": [[]], 11 | "xor": [[[[[0, 0], [0, 80], [90, 80], [90, 0]], [[50, 30], [10, 10], [80, 10], [80, 50], [10, 50]], [[20, 70], [20, 60], [30, 60], [30, 70]], [[50, 70], [50, 60], [60, 60], [60, 70]]], [[[50, 30], [70, 40], [70, 20]]]]] 12 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_140.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 1, 3 | "subjPaths": [[[0, 0], [0, 7000], [7000, 7000], [7000, 0]], [[1000, 1000], [6000, 1000], [6000, 4000], [1000, 4000]], [[3000, 5000], [4000, 5000], [4000, 6000], [3000, 6000]], [[2000, 2000], [5000, 2000], [5000, 3000], [2000, 3000]]], 4 | "clipPaths": [], 5 | "subject": [[[[[0, 0], [0, 7000], [7000, 7000], [7000, 0]], [[1000, 4000], [1000, 1000], [6000, 1000], [6000, 4000]], [[3000, 6000], [3000, 5000], [4000, 5000], [4000, 6000]]], [[[2000, 2000], [2000, 3000], [5000, 3000], [5000, 2000]]]]], 6 | "clip": [[]], 7 | "union": [[[[[0, 0], [0, 7000], [7000, 7000], [7000, 0]], [[1000, 4000], [1000, 1000], [6000, 1000], [6000, 4000]], [[3000, 6000], [3000, 5000], [4000, 5000], [4000, 6000]]], [[[2000, 2000], [2000, 3000], [5000, 3000], [5000, 2000]]]]], 8 | "intersect": [[]], 9 | "difference": [[[[[0, 0], [0, 7000], [7000, 7000], [7000, 0]], [[1000, 4000], [1000, 1000], [6000, 1000], [6000, 4000]], [[3000, 6000], [3000, 5000], [4000, 5000], [4000, 6000]]], [[[2000, 2000], [2000, 3000], [5000, 3000], [5000, 2000]]]]], 10 | "inverseDifference": [[]], 11 | "xor": [[[[[0, 0], [0, 7000], [7000, 7000], [7000, 0]], [[1000, 4000], [1000, 1000], [6000, 1000], [6000, 4000]], [[3000, 6000], [3000, 5000], [4000, 5000], [4000, 6000]]], [[[2000, 2000], [2000, 3000], [5000, 3000], [5000, 2000]]]]] 12 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_141.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 1, 3 | "subjPaths": [[[0, 0], [0, 7000], [7000, 7000], [7000, 0]], [[-2000, 0], [-2000, 7000], [-1000, 7000], [-1000, 0]], [[1000, 1000], [6000, 1000], [6000, 4000], [1000, 4000]], [[3000, 5000], [4000, 6000], [3000, 6000]], [[3000, 5000], [5000, 5000], [5000, 6000]], [[2000, 2000], [5000, 2000], [5000, 3000], [2000, 3000]]], 4 | "clipPaths": [], 5 | "subject": [[[[[-2000, 0], [-2000, 7000], [-1000, 7000], [-1000, 0]]], [[[0, 0], [0, 7000], [7000, 7000], [7000, 0]], [[1000, 4000], [1000, 1000], [6000, 1000], [6000, 4000]], [[3000, 6000], [3000, 5000], [4000, 6000]], [[5000, 6000], [3000, 5000], [5000, 5000]]], [[[2000, 2000], [2000, 3000], [5000, 3000], [5000, 2000]]]]], 6 | "clip": [[]], 7 | "union": [[[[[-2000, 0], [-2000, 7000], [-1000, 7000], [-1000, 0]]], [[[0, 0], [0, 7000], [7000, 7000], [7000, 0]], [[1000, 4000], [1000, 1000], [6000, 1000], [6000, 4000]], [[3000, 6000], [3000, 5000], [4000, 6000]], [[5000, 6000], [3000, 5000], [5000, 5000]]], [[[2000, 2000], [2000, 3000], [5000, 3000], [5000, 2000]]]]], 8 | "intersect": [[]], 9 | "difference": [[[[[-2000, 0], [-2000, 7000], [-1000, 7000], [-1000, 0]]], [[[0, 0], [0, 7000], [7000, 7000], [7000, 0]], [[1000, 4000], [1000, 1000], [6000, 1000], [6000, 4000]], [[3000, 6000], [3000, 5000], [4000, 6000]], [[5000, 6000], [3000, 5000], [5000, 5000]]], [[[2000, 2000], [2000, 3000], [5000, 3000], [5000, 2000]]]]], 10 | "inverseDifference": [[]], 11 | "xor": [[[[[-2000, 0], [-2000, 7000], [-1000, 7000], [-1000, 0]]], [[[0, 0], [0, 7000], [7000, 7000], [7000, 0]], [[1000, 4000], [1000, 1000], [6000, 1000], [6000, 4000]], [[3000, 6000], [3000, 5000], [4000, 6000]], [[5000, 6000], [3000, 5000], [5000, 5000]]], [[[2000, 2000], [2000, 3000], [5000, 3000], [5000, 2000]]]]] 12 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_142.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 1, 3 | "subjPaths": [[[0, 0], [0, 9000], [7000, 9000], [7000, 0]], [[-2000, 0], [-2000, 7000], [-1000, 7000], [-1000, 0]], [[1000, 1000], [6000, 1000], [6000, 4000], [1000, 4000]], [[3000, 5000], [4000, 5000], [4000, 6000]], [[3000, 5000], [5000, 8000], [3000, 8000]], [[2000, 2000], [5000, 2000], [5000, 3000], [2000, 3000]]], 4 | "clipPaths": [], 5 | "subject": [[[[[-2000, 0], [-2000, 7000], [-1000, 7000], [-1000, 0]]], [[[0, 0], [0, 9000], [7000, 9000], [7000, 0]], [[1000, 4000], [1000, 1000], [6000, 1000], [6000, 4000]], [[3000, 8000], [3000, 5000], [5000, 8000]], [[4000, 6000], [3000, 5000], [4000, 5000]]], [[[2000, 2000], [2000, 3000], [5000, 3000], [5000, 2000]]]]], 6 | "clip": [[]], 7 | "union": [[[[[-2000, 0], [-2000, 7000], [-1000, 7000], [-1000, 0]]], [[[0, 0], [0, 9000], [7000, 9000], [7000, 0]], [[1000, 4000], [1000, 1000], [6000, 1000], [6000, 4000]], [[3000, 8000], [3000, 5000], [5000, 8000]], [[4000, 6000], [3000, 5000], [4000, 5000]]], [[[2000, 2000], [2000, 3000], [5000, 3000], [5000, 2000]]]]], 8 | "intersect": [[]], 9 | "difference": [[[[[-2000, 0], [-2000, 7000], [-1000, 7000], [-1000, 0]]], [[[0, 0], [0, 9000], [7000, 9000], [7000, 0]], [[1000, 4000], [1000, 1000], [6000, 1000], [6000, 4000]], [[3000, 8000], [3000, 5000], [5000, 8000]], [[4000, 6000], [3000, 5000], [4000, 5000]]], [[[2000, 2000], [2000, 3000], [5000, 3000], [5000, 2000]]]]], 10 | "inverseDifference": [[]], 11 | "xor": [[[[[-2000, 0], [-2000, 7000], [-1000, 7000], [-1000, 0]]], [[[0, 0], [0, 9000], [7000, 9000], [7000, 0]], [[1000, 4000], [1000, 1000], [6000, 1000], [6000, 4000]], [[3000, 8000], [3000, 5000], [5000, 8000]], [[4000, 6000], [3000, 5000], [4000, 5000]]], [[[2000, 2000], [2000, 3000], [5000, 3000], [5000, 2000]]]]] 12 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_143.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 1, 3 | "subjPaths": [[[0, -100], [0, 250], [230, 250], [230, 170], [300, 170], [180, 100], [300, 100]], [[200, 200], [20, 200], [330, -70], [20, -70]]], 4 | "clipPaths": [], 5 | "subject": [[[[[0, -100], [0, 250], [230, 250], [230, 170], [300, 170], [180, 100], [300, 100], [206, 38], [330, -70], [45, -70]], [[200, 200], [20, 200], [134, 101]]]]], 6 | "clip": [[]], 7 | "union": [[[[[0, -100], [0, 250], [230, 250], [230, 170], [300, 170], [180, 100], [300, 100], [206, 38], [330, -70], [45, -70]], [[200, 200], [20, 200], [134, 101]]]]], 8 | "intersect": [[]], 9 | "difference": [[[[[0, -100], [0, 250], [230, 250], [230, 170], [300, 170], [180, 100], [300, 100], [206, 38], [330, -70], [45, -70]], [[200, 200], [20, 200], [134, 101]]]]], 10 | "inverseDifference": [[]], 11 | "xor": [[[[[0, -100], [0, 250], [230, 250], [230, 170], [300, 170], [180, 100], [300, 100], [206, 38], [330, -70], [45, -70]], [[200, 200], [20, 200], [134, 101]]]]] 12 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_149.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 1, 3 | "subjPaths": [ 4 | [[0, 0], [1, 7], [7, 1]], 5 | [[0, 0], [5, 1], [1, 5]], 6 | [[0, 0], [1, 3], [3, 1]], 7 | [[0, 0], [2, 1], [1, 2]] 8 | ], 9 | "clipPaths": [], 10 | "subject": [[ 11 | [ 12 | [[0, 0], [1, 7], [7, 1]], 13 | [[0, 0], [5, 1], [1, 5]] 14 | ], 15 | [ 16 | [[0, 0], [1, 3], [3, 1]], 17 | [[0, 0], [2, 1], [1, 2]] 18 | ] 19 | ]], 20 | "clip": [[]], 21 | "union": [[ 22 | [ 23 | [[0, 0], [1, 7], [7, 1]], 24 | [[0, 0], [5, 1], [1, 5]] 25 | ], 26 | [ 27 | [[0, 0], [1, 3], [3, 1]], 28 | [[0, 0], [2, 1], [1, 2]] 29 | ] 30 | ]], 31 | "intersect": [[]], 32 | "difference": [[ 33 | [ 34 | [[0, 0], [1, 7], [7, 1]], 35 | [[0, 0], [5, 1], [1, 5]] 36 | ], 37 | [ 38 | [[0, 0], [1, 3], [3, 1]], 39 | [[0, 0], [2, 1], [1, 2]] 40 | ] 41 | ]], 42 | "inverseDifference": [[]], 43 | "xor": [[ 44 | [ 45 | [[0, 0], [1, 7], [7, 1]], 46 | [[0, 0], [5, 1], [1, 5]] 47 | ], 48 | [ 49 | [[0, 0], [1, 3], [3, 1]], 50 | [[0, 0], [2, 1], [1, 2]] 51 | ] 52 | ]] 53 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_15.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 1, 3 | "subjPaths": [[[-20480, -20480], [-20480, 20480], [20480, 20480], [20480, -20480]], [[-10240, -10240], [-10240, 0], [0, 0], [0, -10240]], [[0, 0], [0, 10240], [10240, 10240], [10240, 0]]], 4 | "clipPaths": [[[-5120, -5120], [-5120, 5120], [5120, 5120], [5120, -5120]]], 5 | "subject": [[[[[-20480, -20480], [-20480, 20480], [20480, 20480], [20480, -20480]]]]], 6 | "clip": [[[[[-5120, -5120], [-5120, 5120], [5120, 5120], [5120, -5120]]]]], 7 | "union": [[[[[-20480, -20480], [-20480, 20480], [20480, 20480], [20480, -20480]]]]], 8 | "intersect": [[[[[-5120, -5120], [-5120, 5120], [5120, 5120], [5120, -5120]]]]], 9 | "difference": [[[[[-20480, -20480], [-20480, 20480], [20480, 20480], [20480, -20480]], [[-5120, -5120], [5120, -5120], [5120, 5120], [-5120, 5120]]]]], 10 | "inverseDifference": [[]], 11 | "xor": [[[[[-20480, -20480], [-20480, 20480], [20480, 20480], [20480, -20480]], [[-5120, -5120], [5120, -5120], [5120, 5120], [-5120, 5120]]]]] 12 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_150.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 1, 3 | "subjPaths": [[[0, 0], [4, -1], [4, 1]], [[0, 0], [5, 2], [5, -2]], [[0, 0], [6, -3], [6, 3]], [[0, 0], [7, 4], [7, -4]]], 4 | "clipPaths": [], 5 | "subject": [[[[[0, 0], [7, 4], [7, -4]], [[6, 3], [0, 0], [5, 2], [5, -2], [0, 0], [6, -3]], [[4, 1], [0, 0], [4, -1]]]]], 6 | "clip": [[]], 7 | "union": [[[[[0, 0], [7, 4], [7, -4]], [[6, 3], [0, 0], [5, 2], [5, -2], [0, 0], [6, -3]], [[4, 1], [0, 0], [4, -1]]]]], 8 | "intersect": [[]], 9 | "difference": [[[[[0, 0], [7, 4], [7, -4]], [[6, 3], [0, 0], [5, 2], [5, -2], [0, 0], [6, -3]], [[4, 1], [0, 0], [4, -1]]]]], 10 | "inverseDifference": [[]], 11 | "xor": [[[[[0, 0], [7, 4], [7, -4]], [[6, 3], [0, 0], [5, 2], [5, -2], [0, 0], [6, -3]], [[4, 1], [0, 0], [4, -1]]]]] 12 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_151.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 1, 3 | "subjPaths": [ 4 | [[0, 0], [3, 3], [6, 0]], 5 | [[0, 0], [3,-3], [0,-6]], 6 | [[3,-3], [6, 0], [6,-6]], 7 | [[3,-3], [5,-4], [5,-2]] 8 | ], 9 | "clipPaths": [], 10 | "subject": [[ 11 | [ 12 | [[0, -6], [0, 0], [3, -3]] 13 | ], 14 | [ 15 | [[0, 0], [3, 3], [6, 0]] 16 | ], 17 | [ 18 | [[3, -3], [6, 0], [6, -6]], 19 | [[5, -2], [3, -3], [5, -4]] 20 | ] 21 | ]], 22 | "clip": [[]], 23 | "union": [[ 24 | [ 25 | [[0, -6], [0, 0], [3, -3]] 26 | ], 27 | [ 28 | [[0, 0], [3, 3], [6, 0]] 29 | ], 30 | [ 31 | [[3, -3], [6, 0], [6, -6]], 32 | [[5, -2], [3, -3], [5, -4]] 33 | ] 34 | ]], 35 | "intersect": [[]], 36 | "difference": [[ 37 | [ 38 | [[0, -6], [0, 0], [3, -3]] 39 | ], 40 | [ 41 | [[0, 0], [3, 3], [6, 0]] 42 | ], 43 | [ 44 | [[3, -3], [6, 0], [6, -6]], 45 | [[5, -2], [3, -3], [5, -4]] 46 | ] 47 | ]], 48 | "inverseDifference": [[]], 49 | "xor": [[ 50 | [ 51 | [[0, -6], [0, 0], [3, -3]] 52 | ], 53 | [ 54 | [[0, 0], [3, 3], [6, 0]] 55 | ], 56 | [ 57 | [[3, -3], [6, 0], [6, -6]], 58 | [[5, -2], [3, -3], [5, -4]] 59 | ] 60 | ]] 61 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_152.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 1, 3 | "subjPaths": [[[0, 0], [3, 4], [3, 1]], [[0, 0], [2, 1], [2, 2]], [[0, 0], [3, -1], [3, -4]], [[0, 0], [2, -2], [2, -1]]], 4 | "clipPaths": [], 5 | "subject": [[[[[0, 0], [3, 4], [3, 1]], [[2, 2], [0, 0], [2, 1]]], [[[0, 0], [3, -1], [3, -4]], [[2, -1], [0, 0], [2, -2]]]]], 6 | "clip": [[]], 7 | "union": [[[[[0, 0], [3, 4], [3, 1]], [[2, 2], [0, 0], [2, 1]]], [[[0, 0], [3, -1], [3, -4]], [[2, -1], [0, 0], [2, -2]]]]], 8 | "intersect": [[]], 9 | "difference": [[[[[0, 0], [3, 4], [3, 1]], [[2, 2], [0, 0], [2, 1]]], [[[0, 0], [3, -1], [3, -4]], [[2, -1], [0, 0], [2, -2]]]]], 10 | "inverseDifference": [[]], 11 | "xor": [[[[[0, 0], [3, 4], [3, 1]], [[2, 2], [0, 0], [2, 1]]], [[[0, 0], [3, -1], [3, -4]], [[2, -1], [0, 0], [2, -2]]]]] 12 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_153.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 1, 3 | "subjPaths": [ 4 | [[0, 0], [3, 7], [3, 3]], 5 | [[0, 0], [3, 2], [3,-2]], 6 | [[0, 0], [3,-3], [3,-7]], 7 | 8 | [[0, 0], [2, 3], [2, 4]], 9 | [[0, 0], [2,-1], [2, 1]], 10 | [[0, 0], [2,-4], [2,-3]] 11 | ], 12 | "clipPaths": [], 13 | "subject": [[ 14 | [ 15 | [[0, 0], [5, 2], [5, -2]], 16 | [[0, 0], [4, -1], [4, 1]] 17 | ], 18 | [ 19 | [[0, 0], [7, 4], [7, -4]], 20 | [[0, 0], [6, -3], [6, 3]] 21 | ], 22 | [ 23 | [[0, 0], [9, 6], [9, -6]], 24 | [[0, 0], [8, -5], [8, 5]] 25 | ] 26 | ]], 27 | "clip": [[]], 28 | "union": [[ 29 | [ 30 | [[0, 0], [5, 2], [5, -2]], 31 | [[0, 0], [4, -1], [4, 1]] 32 | ], 33 | [ 34 | [[0, 0], [7, 4], [7, -4]], 35 | [[0, 0], [6, -3], [6, 3]] 36 | ], 37 | [ 38 | [[0, 0], [9, 6], [9, -6]], 39 | [[0, 0], [8, -5], [8, 5]] 40 | ] 41 | ]], 42 | "intersect": [[]], 43 | "difference": [[ 44 | [ 45 | [[0, 0], [5, 2], [5, -2]], 46 | [[0, 0], [4, -1], [4, 1]] 47 | ], 48 | [ 49 | [[0, 0], [7, 4], [7, -4]], 50 | [[0, 0], [6, -3], [6, 3]] 51 | ], 52 | [ 53 | [[0, 0], [9, 6], [9, -6]], 54 | [[0, 0], [8, -5], [8, 5]] 55 | ] 56 | ]], 57 | "inverseDifference": [[]], 58 | "xor": [[ 59 | [ 60 | [[0, 0], [5, 2], [5, -2]], 61 | [[0, 0], [4, -1], [4, 1]] 62 | ], 63 | [ 64 | [[0, 0], [7, 4], [7, -4]], 65 | [[0, 0], [6, -3], [6, 3]] 66 | ], 67 | [ 68 | [[0, 0], [9, 6], [9, -6]], 69 | [[0, 0], [8, -5], [8, 5]] 70 | ] 71 | ]] 72 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_154.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 1, 3 | "subjPaths": [ 4 | [[0, 0], [6,-3], [4,-1]], 5 | [[0, 0], [5, 8], [7,-5]], 6 | [[0, 0], [0,-2], [6, 5]] 7 | ], 8 | "clipPaths": [], 9 | "subject": [[ 10 | [ 11 | [[0, 0], [5, 2], [5, -2]], 12 | [[0, 0], [4, -1], [4, 1]] 13 | ], 14 | [ 15 | [[0, 0], [7, 4], [7, -4]], 16 | [[0, 0], [6, -3], [6, 3]] 17 | ], 18 | [ 19 | [[0, 0], [9, 6], [9, -6]], 20 | [[0, 0], [8, -5], [8, 5]] 21 | ] 22 | ]], 23 | "clip": [[]], 24 | "union": [[ 25 | [ 26 | [[0, 0], [5, 2], [5, -2]], 27 | [[0, 0], [4, -1], [4, 1]] 28 | ], 29 | [ 30 | [[0, 0], [7, 4], [7, -4]], 31 | [[0, 0], [6, -3], [6, 3]] 32 | ], 33 | [ 34 | [[0, 0], [9, 6], [9, -6]], 35 | [[0, 0], [8, -5], [8, 5]] 36 | ] 37 | ]], 38 | "intersect": [[]], 39 | "difference": [[ 40 | [ 41 | [[0, 0], [5, 2], [5, -2]], 42 | [[0, 0], [4, -1], [4, 1]] 43 | ], 44 | [ 45 | [[0, 0], [7, 4], [7, -4]], 46 | [[0, 0], [6, -3], [6, 3]] 47 | ], 48 | [ 49 | [[0, 0], [9, 6], [9, -6]], 50 | [[0, 0], [8, -5], [8, 5]] 51 | ] 52 | ]], 53 | "inverseDifference": [[]], 54 | "xor": [[ 55 | [ 56 | [[0, 0], [5, 2], [5, -2]], 57 | [[0, 0], [4, -1], [4, 1]] 58 | ], 59 | [ 60 | [[0, 0], [7, 4], [7, -4]], 61 | [[0, 0], [6, -3], [6, 3]] 62 | ], 63 | [ 64 | [[0, 0], [9, 6], [9, -6]], 65 | [[0, 0], [8, -5], [8, 5]] 66 | ] 67 | ]] 68 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_155.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 1, 3 | "subjPaths": [ 4 | [[0, 0], [3, 3], [2, 1], [0, 0], [2, -1], [3, -3]] 5 | ], 6 | "clipPaths": [], 7 | "subject": [[ 8 | [ 9 | [[0, 0], [3, 3], [2, 1]] 10 | ], 11 | [ 12 | [[0, 0], [2,-1], [3,-3]] 13 | ] 14 | ]], 15 | "clip": [[]], 16 | "union": [[ 17 | [ 18 | [[0, 0], [3, 3], [2, 1]] 19 | ], 20 | [ 21 | [[0, 0], [2,-1], [3,-3]] 22 | ] 23 | ]], 24 | "intersect": [[]], 25 | "difference": [[ 26 | [ 27 | [[0, 0], [3, 3], [2, 1]] 28 | ], 29 | [ 30 | [[0, 0], [2,-1], [3,-3]] 31 | ] 32 | ]], 33 | "inverseDifference": [[]], 34 | "xor": [[ 35 | [ 36 | [[0, 0], [3, 3], [2, 1]] 37 | ], 38 | [ 39 | [[0, 0], [2,-1], [3,-3]] 40 | ] 41 | ]] 42 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_156.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 1, 3 | "subjPaths": [ 4 | [ 5 | [-248605787, -564314904], 6 | [-182310886, 174630394], 7 | [141078730, 146333787], 8 | [212224444, 139866009], 9 | [146738044, -599079275] 10 | ] 11 | ], 12 | "clipPaths": [ 13 | [ 14 | [-238095599, 180289705], 15 | [-200905813, 599079275], 16 | [248605787, 558655577], 17 | [212224444, 139866009], 18 | [141078730, 146333807], 19 | [-182310886, 174630394] 20 | ] 21 | ], 22 | "subject": [[ 23 | [ 24 | [[0, 0], [3, 3], [2, 1]] 25 | ], 26 | [ 27 | [[0, 0], [2,-1], [3,-3]] 28 | ] 29 | ]], 30 | "clip": [[]], 31 | "union": [[ 32 | [ 33 | [[0, 0], [3, 3], [2, 1]] 34 | ], 35 | [ 36 | [[0, 0], [2,-1], [3,-3]] 37 | ] 38 | ]], 39 | "intersect": [[]], 40 | "difference": [[ 41 | [ 42 | [[0, 0], [3, 3], [2, 1]] 43 | ], 44 | [ 45 | [[0, 0], [2,-1], [3,-3]] 46 | ] 47 | ]], 48 | "inverseDifference": [[]], 49 | "xor": [[ 50 | [ 51 | [[0, 0], [3, 3], [2, 1]] 52 | ], 53 | [ 54 | [[0, 0], [2,-1], [3,-3]] 55 | ] 56 | ]] 57 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_157.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 1, 3 | "subjPaths": [ 4 | [ 5 | [-15, -5], 6 | [ -5,-15], 7 | [ 5,-15], 8 | [ 15, -5], 9 | [ 15, 5], 10 | [ 5, 15], 11 | [ -5, 15], 12 | [-15, 5] 13 | ] 14 | ], 15 | "clipPaths": [], 16 | "subject": [[ 17 | [ 18 | [[0, 0], [3, 3], [2, 1]] 19 | ], 20 | [ 21 | [[0, 0], [2,-1], [3,-3]] 22 | ] 23 | ]], 24 | "clip": [[]], 25 | "union": [[ 26 | [ 27 | [[0, 0], [3, 3], [2, 1]] 28 | ], 29 | [ 30 | [[0, 0], [2,-1], [3,-3]] 31 | ] 32 | ]], 33 | "intersect": [[]], 34 | "difference": [[ 35 | [ 36 | [[0, 0], [3, 3], [2, 1]] 37 | ], 38 | [ 39 | [[0, 0], [2,-1], [3,-3]] 40 | ] 41 | ]], 42 | "inverseDifference": [[]], 43 | "xor": [[ 44 | [ 45 | [[0, 0], [3, 3], [2, 1]] 46 | ], 47 | [ 48 | [[0, 0], [2,-1], [3,-3]] 49 | ] 50 | ]] 51 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_158.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 1, 3 | "subjPaths": [ 4 | [ 5 | [ 10, -5], 6 | [-10, -5], 7 | [ 5, 10], 8 | [ 5,-10], 9 | [-10, 5], 10 | [ 10, 5], 11 | [ -5,-10], 12 | [ -5, 10] 13 | ] 14 | ], 15 | "clipPaths": [], 16 | "subject": [[ 17 | [ 18 | [[0, 0], [3, 3], [2, 1]] 19 | ], 20 | [ 21 | [[0, 0], [2,-1], [3,-3]] 22 | ] 23 | ]], 24 | "clip": [[]], 25 | "union": [[ 26 | [ 27 | [[0, 0], [3, 3], [2, 1]] 28 | ], 29 | [ 30 | [[0, 0], [2,-1], [3,-3]] 31 | ] 32 | ]], 33 | "intersect": [[]], 34 | "difference": [[ 35 | [ 36 | [[0, 0], [3, 3], [2, 1]] 37 | ], 38 | [ 39 | [[0, 0], [2,-1], [3,-3]] 40 | ] 41 | ]], 42 | "inverseDifference": [[]], 43 | "xor": [[ 44 | [ 45 | [[0, 0], [3, 3], [2, 1]] 46 | ], 47 | [ 48 | [[0, 0], [2,-1], [3,-3]] 49 | ] 50 | ]] 51 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_18.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 1, 3 | "subjPaths": [[[-20480, -20480], [-20480, 20480], [20480, 20480], [20480, -20480]], [[-10240, -10240], [-10240, 0], [0, 0], [0, -10240]], [[0, -10240], [0, 10240], [10240, 10240], [10240, -10240]]], 4 | "clipPaths": [[[-5120, -5120], [-5120, 5120], [5120, 5120], [5120, -5120]]], 5 | "subject": [[[[[-20480, -20480], [-20480, 20480], [20480, 20480], [20480, -20480]]]]], 6 | "clip": [[[[[-5120, -5120], [-5120, 5120], [5120, 5120], [5120, -5120]]]]], 7 | "union": [[[[[-20480, -20480], [-20480, 20480], [20480, 20480], [20480, -20480]]]]], 8 | "intersect": [[[[[-5120, -5120], [-5120, 5120], [5120, 5120], [5120, -5120]]]]], 9 | "difference": [[[[[-20480, -20480], [-20480, 20480], [20480, 20480], [20480, -20480]], [[-5120, -5120], [5120, -5120], [5120, 5120], [-5120, 5120]]]]], 10 | "inverseDifference": [[]], 11 | "xor": [[[[[-20480, -20480], [-20480, 20480], [20480, 20480], [20480, -20480]], [[-5120, -5120], [5120, -5120], [5120, 5120], [-5120, 5120]]]]] 12 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_19.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 1, 3 | "subjPaths": [[[-30720, -30720], [-30720, 30720], [30720, 30720], [30720, -30720]], [[-15360, -15360], [-15360, -5120], [-5120, -5120], [-5120, -15360]], [[5120, 5120], [5120, 15360], [15360, 15360], [15360, 5120]]], 4 | "clipPaths": [[[-10240, -10240], [-10240, 10240], [10240, 10240], [10240, -10240]]], 5 | "subject": [[[[[-30720, -30720], [-30720, 30720], [30720, 30720], [30720, -30720]]]]], 6 | "clip": [[[[[-10240, -10240], [-10240, 10240], [10240, 10240], [10240, -10240]]]]], 7 | "union": [[[[[-30720, -30720], [-30720, 30720], [30720, 30720], [30720, -30720]]]]], 8 | "intersect": [[[[[-10240, -10240], [-10240, 10240], [10240, 10240], [10240, -10240]]]]], 9 | "difference": [[[[[-30720, -30720], [-30720, 30720], [30720, 30720], [30720, -30720]], [[-10240, -10240], [10240, -10240], [10240, 10240], [-10240, 10240]]]]], 10 | "inverseDifference": [[]], 11 | "xor": [[[[[-30720, -30720], [-30720, 30720], [30720, 30720], [30720, -30720]], [[-10240, -10240], [10240, -10240], [10240, 10240], [-10240, 10240]]]]] 12 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_20.json: -------------------------------------------------------------------------------- 1 | { 2 | "subjPaths" : [ 3 | [ 4 | [ 5 | -10240, 6 | -10240 7 | ], 8 | [ 9 | 10240, 10 | 10240 11 | ] 12 | ] 13 | ], 14 | "union" : [ 15 | [ 16 | 17 | ] 18 | ], 19 | "subject" : [ 20 | [ 21 | 22 | ] 23 | ], 24 | "fillRule" : 0, 25 | "xor" : [ 26 | [ 27 | 28 | ] 29 | ], 30 | "difference" : [ 31 | [ 32 | 33 | ] 34 | ], 35 | "intersect" : [ 36 | [ 37 | 38 | ] 39 | ], 40 | "clipPaths" : [ 41 | [ 42 | [ 43 | -10240, 44 | 10240 45 | ], 46 | [ 47 | 10240, 48 | -10240 49 | ] 50 | ] 51 | ], 52 | "inverseDifference" : [ 53 | [ 54 | 55 | ] 56 | ], 57 | "clip" : [ 58 | [ 59 | 60 | ] 61 | ] 62 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_21.json: -------------------------------------------------------------------------------- 1 | { 2 | "xor" : [ 3 | [ 4 | 5 | ] 6 | ], 7 | "intersect" : [ 8 | [ 9 | 10 | ] 11 | ], 12 | "subject" : [ 13 | [ 14 | 15 | ] 16 | ], 17 | "clip" : [ 18 | [ 19 | 20 | ] 21 | ], 22 | "union" : [ 23 | [ 24 | 25 | ] 26 | ], 27 | "difference" : [ 28 | [ 29 | 30 | ] 31 | ], 32 | "fillRule" : 0, 33 | "inverseDifference" : [ 34 | [ 35 | 36 | ] 37 | ], 38 | "clipPaths" : [ 39 | [ 40 | [ 41 | -10240, 42 | 10240 43 | ], 44 | [ 45 | 10240, 46 | -10240 47 | ] 48 | ] 49 | ], 50 | "subjPaths" : [ 51 | [ 52 | [ 53 | -10240, 54 | -10240 55 | ], 56 | [ 57 | 10240, 58 | 10240 59 | ] 60 | ] 61 | ] 62 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_22.json: -------------------------------------------------------------------------------- 1 | { 2 | "difference" : [ 3 | [ 4 | 5 | ] 6 | ], 7 | "subject" : [ 8 | [ 9 | 10 | ] 11 | ], 12 | "xor" : [ 13 | [ 14 | 15 | ] 16 | ], 17 | "intersect" : [ 18 | [ 19 | 20 | ] 21 | ], 22 | "subjPaths" : [ 23 | [ 24 | [ 25 | -10240, 26 | -10240 27 | ], 28 | [ 29 | 10240, 30 | 10240 31 | ] 32 | ] 33 | ], 34 | "fillRule" : 0, 35 | "inverseDifference" : [ 36 | [ 37 | 38 | ] 39 | ], 40 | "clip" : [ 41 | [ 42 | 43 | ] 44 | ], 45 | "union" : [ 46 | [ 47 | 48 | ] 49 | ], 50 | "clipPaths" : [ 51 | [ 52 | [ 53 | -10240, 54 | -10238 55 | ], 56 | [ 57 | 10240, 58 | 10241 59 | ] 60 | ] 61 | ] 62 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_23.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule" : 0, 3 | "clip" : [ 4 | [ 5 | 6 | ] 7 | ], 8 | "clipPaths" : [ 9 | [ 10 | [ 11 | -10240, 12 | -10240 13 | ], 14 | [ 15 | 10240, 16 | 10240 17 | ] 18 | ] 19 | ], 20 | "subjPaths" : [ 21 | [ 22 | [ 23 | -10240, 24 | -10240 25 | ], 26 | [ 27 | 10240, 28 | 10240 29 | ] 30 | ] 31 | ], 32 | "intersect" : [ 33 | [ 34 | 35 | ] 36 | ], 37 | "subject" : [ 38 | [ 39 | 40 | ] 41 | ], 42 | "inverseDifference" : [ 43 | [ 44 | 45 | ] 46 | ], 47 | "xor" : [ 48 | [ 49 | 50 | ] 51 | ], 52 | "difference" : [ 53 | [ 54 | 55 | ] 56 | ], 57 | "union" : [ 58 | [ 59 | 60 | ] 61 | ] 62 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_24.json: -------------------------------------------------------------------------------- 1 | { 2 | "inverseDifference" : [ 3 | [ 4 | 5 | ] 6 | ], 7 | "intersect" : [ 8 | [ 9 | 10 | ] 11 | ], 12 | "union" : [ 13 | [ 14 | 15 | ] 16 | ], 17 | "xor" : [ 18 | [ 19 | 20 | ] 21 | ], 22 | "subjPaths" : [ 23 | [ 24 | [ 25 | -10240, 26 | -10240 27 | ], 28 | [ 29 | 10240, 30 | 10240 31 | ] 32 | ] 33 | ], 34 | "difference" : [ 35 | [ 36 | 37 | ] 38 | ], 39 | "subject" : [ 40 | [ 41 | 42 | ] 43 | ], 44 | "clipPaths" : [ 45 | [ 46 | [ 47 | 10240, 48 | 10240 49 | ], 50 | [ 51 | 20480, 52 | 20480 53 | ] 54 | ] 55 | ], 56 | "fillRule" : 0, 57 | "clip" : [ 58 | [ 59 | 60 | ] 61 | ] 62 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_29.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 1, 3 | "subjPaths": [[[-20480, -20480], [-20480, 20480], [20480, 20480], [20480, -20480]]], 4 | "clipPaths": [[[-10240, -10240], [-10240, 10240], [10240, 10240], [10240, -10240]], [[-5120, -5120], [-5120, 5120], [15360, 5120], [15360, -5120]]], 5 | "subject": [[[[[-20480, -20480], [-20480, 20480], [20480, 20480], [20480, -20480]]]]], 6 | "clip": [[[[[-10240, -10240], [-10240, 10240], [10240, 10240], [10240, 5120], [15360, 5120], [15360, -5120], [10240, -5120], [10240, -10240]]]]], 7 | "union": [[[[[-20480, -20480], [-20480, 20480], [20480, 20480], [20480, -20480]]]]], 8 | "intersect": [[[[[-10240, -10240], [-10240, 10240], [10240, 10240], [10240, 5120], [15360, 5120], [15360, -5120], [10240, -5120], [10240, -10240]]]]], 9 | "difference": [[[[[-20480, -20480], [-20480, 20480], [20480, 20480], [20480, -20480]], [[-10240, 10240], [-10240, -10240], [10240, -10240], [10240, -5120], [15360, -5120], [15360, 5120], [10240, 5120], [10240, 10240]]]]], 10 | "inverseDifference": [[]], 11 | "xor": [[[[[-20480, -20480], [-20480, 20480], [20480, 20480], [20480, -20480]], [[-10240, 10240], [-10240, -10240], [10240, -10240], [10240, -5120], [15360, -5120], [15360, 5120], [10240, 5120], [10240, 10240]]]]] 12 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_38.json: -------------------------------------------------------------------------------- 1 | { 2 | "difference" : [ 3 | [ 4 | 5 | ] 6 | ], 7 | "inverseDifference" : [ 8 | [ 9 | 10 | ] 11 | ], 12 | "union" : [ 13 | [ 14 | [ 15 | [ 16 | [ 17 | -10240, 18 | -10240 19 | ], 20 | [ 21 | -10240, 22 | 10240 23 | ], 24 | [ 25 | 10240, 26 | 10240 27 | ], 28 | [ 29 | 10240, 30 | -10240 31 | ] 32 | ] 33 | ] 34 | ] 35 | ], 36 | "subjPaths" : [ 37 | [ 38 | [ 39 | -10240, 40 | -10240 41 | ], 42 | [ 43 | -10240, 44 | 10240 45 | ], 46 | [ 47 | 10240, 48 | 10240 49 | ], 50 | [ 51 | 10240, 52 | -10240 53 | ] 54 | ] 55 | ], 56 | "intersect" : [ 57 | [ 58 | [ 59 | [ 60 | [ 61 | -10240, 62 | -10240 63 | ], 64 | [ 65 | -10240, 66 | 10240 67 | ], 68 | [ 69 | 10240, 70 | 10240 71 | ], 72 | [ 73 | 10240, 74 | -10240 75 | ] 76 | ] 77 | ] 78 | ] 79 | ], 80 | "xor" : [ 81 | [ 82 | 83 | ] 84 | ], 85 | "fillRule" : 0, 86 | "subject" : [ 87 | [ 88 | [ 89 | [ 90 | [ 91 | -10240, 92 | -10240 93 | ], 94 | [ 95 | -10240, 96 | 10240 97 | ], 98 | [ 99 | 10240, 100 | 10240 101 | ], 102 | [ 103 | 10240, 104 | -10240 105 | ] 106 | ] 107 | ] 108 | ] 109 | ], 110 | "clip" : [ 111 | [ 112 | [ 113 | [ 114 | [ 115 | -10240, 116 | -10240 117 | ], 118 | [ 119 | -10240, 120 | 10240 121 | ], 122 | [ 123 | 10240, 124 | 10240 125 | ], 126 | [ 127 | 10240, 128 | -10240 129 | ] 130 | ] 131 | ] 132 | ] 133 | ], 134 | "clipPaths" : [ 135 | [ 136 | [ 137 | -10240, 138 | -10240 139 | ], 140 | [ 141 | -10240, 142 | 10240 143 | ], 144 | [ 145 | 10240, 146 | 10240 147 | ], 148 | [ 149 | 10240, 150 | -10240 151 | ] 152 | ] 153 | ] 154 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_51.json: -------------------------------------------------------------------------------- 1 | { 2 | "union" : [ 3 | [ 4 | [ 5 | [ 6 | [ 7 | -10240, 8 | -10240 9 | ], 10 | [ 11 | -10240, 12 | 10240 13 | ], 14 | [ 15 | 10240, 16 | 10240 17 | ], 18 | [ 19 | 10240, 20 | -10240 21 | ] 22 | ] 23 | ] 24 | ] 25 | ], 26 | "xor" : [ 27 | [ 28 | [ 29 | [ 30 | [ 31 | -10240, 32 | -10240 33 | ], 34 | [ 35 | -10240, 36 | 10240 37 | ], 38 | [ 39 | 0, 40 | 0 41 | ], 42 | [ 43 | 10240, 44 | 10240 45 | ], 46 | [ 47 | 10240, 48 | -10240 49 | ] 50 | ] 51 | ] 52 | ] 53 | ], 54 | "fillRule" : 0, 55 | "clipPaths" : [ 56 | [ 57 | [ 58 | 0, 59 | 0 60 | ], 61 | [ 62 | -10240, 63 | 10240 64 | ], 65 | [ 66 | 10240, 67 | 10240 68 | ] 69 | ] 70 | ], 71 | "clip" : [ 72 | [ 73 | [ 74 | [ 75 | [ 76 | -10240, 77 | 10240 78 | ], 79 | [ 80 | 10240, 81 | 10240 82 | ], 83 | [ 84 | 0, 85 | 0 86 | ] 87 | ] 88 | ] 89 | ] 90 | ], 91 | "inverseDifference" : [ 92 | [ 93 | 94 | ] 95 | ], 96 | "subjPaths" : [ 97 | [ 98 | [ 99 | -10240, 100 | 10240 101 | ], 102 | [ 103 | 10240, 104 | 10240 105 | ], 106 | [ 107 | 10240, 108 | -10240 109 | ], 110 | [ 111 | -10240, 112 | -10240 113 | ] 114 | ] 115 | ], 116 | "subject" : [ 117 | [ 118 | [ 119 | [ 120 | [ 121 | -10240, 122 | -10240 123 | ], 124 | [ 125 | -10240, 126 | 10240 127 | ], 128 | [ 129 | 10240, 130 | 10240 131 | ], 132 | [ 133 | 10240, 134 | -10240 135 | ] 136 | ] 137 | ] 138 | ] 139 | ], 140 | "difference" : [ 141 | [ 142 | [ 143 | [ 144 | [ 145 | -10240, 146 | -10240 147 | ], 148 | [ 149 | -10240, 150 | 10240 151 | ], 152 | [ 153 | 0, 154 | 0 155 | ], 156 | [ 157 | 10240, 158 | 10240 159 | ], 160 | [ 161 | 10240, 162 | -10240 163 | ] 164 | ] 165 | ] 166 | ] 167 | ], 168 | "intersect" : [ 169 | [ 170 | [ 171 | [ 172 | [ 173 | -10240, 174 | 10240 175 | ], 176 | [ 177 | 10240, 178 | 10240 179 | ], 180 | [ 181 | 0, 182 | 0 183 | ] 184 | ] 185 | ] 186 | ] 187 | ] 188 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_52.json: -------------------------------------------------------------------------------- 1 | { 2 | "inverseDifference" : [ 3 | [ 4 | 5 | ] 6 | ], 7 | "intersect" : [ 8 | [ 9 | [ 10 | [ 11 | [ 12 | -10240, 13 | -10240 14 | ], 15 | [ 16 | -10240, 17 | 10240 18 | ], 19 | [ 20 | 0, 21 | 0 22 | ] 23 | ] 24 | ] 25 | ] 26 | ], 27 | "union" : [ 28 | [ 29 | [ 30 | [ 31 | [ 32 | -10240, 33 | -10240 34 | ], 35 | [ 36 | -10240, 37 | 10240 38 | ], 39 | [ 40 | 10240, 41 | 10240 42 | ], 43 | [ 44 | 10240, 45 | -10240 46 | ] 47 | ] 48 | ] 49 | ] 50 | ], 51 | "xor" : [ 52 | [ 53 | [ 54 | [ 55 | [ 56 | -10240, 57 | -10240 58 | ], 59 | [ 60 | 0, 61 | 0 62 | ], 63 | [ 64 | -10240, 65 | 10240 66 | ], 67 | [ 68 | 10240, 69 | 10240 70 | ], 71 | [ 72 | 10240, 73 | -10240 74 | ] 75 | ] 76 | ] 77 | ] 78 | ], 79 | "subjPaths" : [ 80 | [ 81 | [ 82 | -10240, 83 | 10240 84 | ], 85 | [ 86 | 10240, 87 | 10240 88 | ], 89 | [ 90 | 10240, 91 | -10240 92 | ], 93 | [ 94 | -10240, 95 | -10240 96 | ] 97 | ] 98 | ], 99 | "difference" : [ 100 | [ 101 | [ 102 | [ 103 | [ 104 | -10240, 105 | -10240 106 | ], 107 | [ 108 | 0, 109 | 0 110 | ], 111 | [ 112 | -10240, 113 | 10240 114 | ], 115 | [ 116 | 10240, 117 | 10240 118 | ], 119 | [ 120 | 10240, 121 | -10240 122 | ] 123 | ] 124 | ] 125 | ] 126 | ], 127 | "subject" : [ 128 | [ 129 | [ 130 | [ 131 | [ 132 | -10240, 133 | -10240 134 | ], 135 | [ 136 | -10240, 137 | 10240 138 | ], 139 | [ 140 | 10240, 141 | 10240 142 | ], 143 | [ 144 | 10240, 145 | -10240 146 | ] 147 | ] 148 | ] 149 | ] 150 | ], 151 | "clipPaths" : [ 152 | [ 153 | [ 154 | 0, 155 | 0 156 | ], 157 | [ 158 | -10240, 159 | -10240 160 | ], 161 | [ 162 | -10240, 163 | 10240 164 | ] 165 | ] 166 | ], 167 | "fillRule" : 0, 168 | "clip" : [ 169 | [ 170 | [ 171 | [ 172 | [ 173 | -10240, 174 | -10240 175 | ], 176 | [ 177 | -10240, 178 | 10240 179 | ], 180 | [ 181 | 0, 182 | 0 183 | ] 184 | ] 185 | ] 186 | ] 187 | ] 188 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_53.json: -------------------------------------------------------------------------------- 1 | { 2 | "clipPaths" : [ 3 | [ 4 | [ 5 | 0, 6 | 0 7 | ], 8 | [ 9 | 10240, 10 | -10240 11 | ], 12 | [ 13 | -10240, 14 | -10240 15 | ] 16 | ] 17 | ], 18 | "inverseDifference" : [ 19 | [ 20 | 21 | ] 22 | ], 23 | "intersect" : [ 24 | [ 25 | [ 26 | [ 27 | [ 28 | -10240, 29 | -10240 30 | ], 31 | [ 32 | 0, 33 | 0 34 | ], 35 | [ 36 | 10240, 37 | -10240 38 | ] 39 | ] 40 | ] 41 | ] 42 | ], 43 | "subjPaths" : [ 44 | [ 45 | [ 46 | -10240, 47 | 10240 48 | ], 49 | [ 50 | 10240, 51 | 10240 52 | ], 53 | [ 54 | 10240, 55 | -10240 56 | ], 57 | [ 58 | -10240, 59 | -10240 60 | ] 61 | ] 62 | ], 63 | "xor" : [ 64 | [ 65 | [ 66 | [ 67 | [ 68 | -10240, 69 | -10240 70 | ], 71 | [ 72 | -10240, 73 | 10240 74 | ], 75 | [ 76 | 10240, 77 | 10240 78 | ], 79 | [ 80 | 10240, 81 | -10240 82 | ], 83 | [ 84 | 0, 85 | 0 86 | ] 87 | ] 88 | ] 89 | ] 90 | ], 91 | "union" : [ 92 | [ 93 | [ 94 | [ 95 | [ 96 | -10240, 97 | -10240 98 | ], 99 | [ 100 | -10240, 101 | 10240 102 | ], 103 | [ 104 | 10240, 105 | 10240 106 | ], 107 | [ 108 | 10240, 109 | -10240 110 | ] 111 | ] 112 | ] 113 | ] 114 | ], 115 | "subject" : [ 116 | [ 117 | [ 118 | [ 119 | [ 120 | -10240, 121 | -10240 122 | ], 123 | [ 124 | -10240, 125 | 10240 126 | ], 127 | [ 128 | 10240, 129 | 10240 130 | ], 131 | [ 132 | 10240, 133 | -10240 134 | ] 135 | ] 136 | ] 137 | ] 138 | ], 139 | "fillRule" : 0, 140 | "clip" : [ 141 | [ 142 | [ 143 | [ 144 | [ 145 | -10240, 146 | -10240 147 | ], 148 | [ 149 | 0, 150 | 0 151 | ], 152 | [ 153 | 10240, 154 | -10240 155 | ] 156 | ] 157 | ] 158 | ] 159 | ], 160 | "difference" : [ 161 | [ 162 | [ 163 | [ 164 | [ 165 | -10240, 166 | -10240 167 | ], 168 | [ 169 | -10240, 170 | 10240 171 | ], 172 | [ 173 | 10240, 174 | 10240 175 | ], 176 | [ 177 | 10240, 178 | -10240 179 | ], 180 | [ 181 | 0, 182 | 0 183 | ] 184 | ] 185 | ] 186 | ] 187 | ] 188 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_54.json: -------------------------------------------------------------------------------- 1 | { 2 | "inverseDifference" : [ 3 | [ 4 | 5 | ] 6 | ], 7 | "subjPaths" : [ 8 | [ 9 | [ 10 | -10240, 11 | 10240 12 | ], 13 | [ 14 | 10240, 15 | 10240 16 | ], 17 | [ 18 | 10240, 19 | -10240 20 | ], 21 | [ 22 | -10240, 23 | -10240 24 | ] 25 | ] 26 | ], 27 | "difference" : [ 28 | [ 29 | [ 30 | [ 31 | [ 32 | -10240, 33 | -10240 34 | ], 35 | [ 36 | -10240, 37 | 10240 38 | ], 39 | [ 40 | 10240, 41 | 10240 42 | ], 43 | [ 44 | 0, 45 | 0 46 | ], 47 | [ 48 | 10240, 49 | -10240 50 | ] 51 | ] 52 | ] 53 | ] 54 | ], 55 | "clipPaths" : [ 56 | [ 57 | [ 58 | 0, 59 | 0 60 | ], 61 | [ 62 | 10240, 63 | 10240 64 | ], 65 | [ 66 | 10240, 67 | -10240 68 | ] 69 | ] 70 | ], 71 | "subject" : [ 72 | [ 73 | [ 74 | [ 75 | [ 76 | -10240, 77 | -10240 78 | ], 79 | [ 80 | -10240, 81 | 10240 82 | ], 83 | [ 84 | 10240, 85 | 10240 86 | ], 87 | [ 88 | 10240, 89 | -10240 90 | ] 91 | ] 92 | ] 93 | ] 94 | ], 95 | "fillRule" : 0, 96 | "union" : [ 97 | [ 98 | [ 99 | [ 100 | [ 101 | -10240, 102 | -10240 103 | ], 104 | [ 105 | -10240, 106 | 10240 107 | ], 108 | [ 109 | 10240, 110 | 10240 111 | ], 112 | [ 113 | 10240, 114 | -10240 115 | ] 116 | ] 117 | ] 118 | ] 119 | ], 120 | "intersect" : [ 121 | [ 122 | [ 123 | [ 124 | [ 125 | 0, 126 | 0 127 | ], 128 | [ 129 | 10240, 130 | 10240 131 | ], 132 | [ 133 | 10240, 134 | -10240 135 | ] 136 | ] 137 | ] 138 | ] 139 | ], 140 | "xor" : [ 141 | [ 142 | [ 143 | [ 144 | [ 145 | -10240, 146 | -10240 147 | ], 148 | [ 149 | -10240, 150 | 10240 151 | ], 152 | [ 153 | 10240, 154 | 10240 155 | ], 156 | [ 157 | 0, 158 | 0 159 | ], 160 | [ 161 | 10240, 162 | -10240 163 | ] 164 | ] 165 | ] 166 | ] 167 | ], 168 | "clip" : [ 169 | [ 170 | [ 171 | [ 172 | [ 173 | 0, 174 | 0 175 | ], 176 | [ 177 | 10240, 178 | 10240 179 | ], 180 | [ 181 | 10240, 182 | -10240 183 | ] 184 | ] 185 | ] 186 | ] 187 | ] 188 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_59.json: -------------------------------------------------------------------------------- 1 | { 2 | "clipPaths" : [ 3 | [ 4 | [ 5 | 10240, 6 | 0 7 | ], 8 | [ 9 | 0, 10 | 10240 11 | ], 12 | [ 13 | 10240, 14 | 10240 15 | ] 16 | ] 17 | ], 18 | "subject" : [ 19 | [ 20 | [ 21 | [ 22 | [ 23 | -10240, 24 | -10240 25 | ], 26 | [ 27 | -10240, 28 | 10240 29 | ], 30 | [ 31 | 10240, 32 | 10240 33 | ], 34 | [ 35 | 10240, 36 | -10240 37 | ] 38 | ] 39 | ] 40 | ] 41 | ], 42 | "intersect" : [ 43 | [ 44 | [ 45 | [ 46 | [ 47 | 0, 48 | 10240 49 | ], 50 | [ 51 | 10240, 52 | 10240 53 | ], 54 | [ 55 | 10240, 56 | 0 57 | ] 58 | ] 59 | ] 60 | ] 61 | ], 62 | "xor" : [ 63 | [ 64 | [ 65 | [ 66 | [ 67 | -10240, 68 | -10240 69 | ], 70 | [ 71 | -10240, 72 | 10240 73 | ], 74 | [ 75 | 0, 76 | 10240 77 | ], 78 | [ 79 | 10240, 80 | 0 81 | ], 82 | [ 83 | 10240, 84 | -10240 85 | ] 86 | ] 87 | ] 88 | ] 89 | ], 90 | "subjPaths" : [ 91 | [ 92 | [ 93 | -10240, 94 | 10240 95 | ], 96 | [ 97 | 10240, 98 | 10240 99 | ], 100 | [ 101 | 10240, 102 | -10240 103 | ], 104 | [ 105 | -10240, 106 | -10240 107 | ] 108 | ] 109 | ], 110 | "union" : [ 111 | [ 112 | [ 113 | [ 114 | [ 115 | -10240, 116 | -10240 117 | ], 118 | [ 119 | -10240, 120 | 10240 121 | ], 122 | [ 123 | 10240, 124 | 10240 125 | ], 126 | [ 127 | 10240, 128 | -10240 129 | ] 130 | ] 131 | ] 132 | ] 133 | ], 134 | "inverseDifference" : [ 135 | [ 136 | 137 | ] 138 | ], 139 | "difference" : [ 140 | [ 141 | [ 142 | [ 143 | [ 144 | -10240, 145 | -10240 146 | ], 147 | [ 148 | -10240, 149 | 10240 150 | ], 151 | [ 152 | 0, 153 | 10240 154 | ], 155 | [ 156 | 10240, 157 | 0 158 | ], 159 | [ 160 | 10240, 161 | -10240 162 | ] 163 | ] 164 | ] 165 | ] 166 | ], 167 | "clip" : [ 168 | [ 169 | [ 170 | [ 171 | [ 172 | 0, 173 | 10240 174 | ], 175 | [ 176 | 10240, 177 | 10240 178 | ], 179 | [ 180 | 10240, 181 | 0 182 | ] 183 | ] 184 | ] 185 | ] 186 | ], 187 | "fillRule" : 0 188 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_60.json: -------------------------------------------------------------------------------- 1 | { 2 | "subjPaths" : [ 3 | [ 4 | [ 5 | -10240, 6 | 10240 7 | ], 8 | [ 9 | 10240, 10 | 10240 11 | ], 12 | [ 13 | 10240, 14 | -10240 15 | ], 16 | [ 17 | -10240, 18 | -10240 19 | ] 20 | ] 21 | ], 22 | "difference" : [ 23 | [ 24 | [ 25 | [ 26 | [ 27 | -10240, 28 | -10240 29 | ], 30 | [ 31 | -10240, 32 | 0 33 | ], 34 | [ 35 | 0, 36 | 10240 37 | ], 38 | [ 39 | 10240, 40 | 10240 41 | ], 42 | [ 43 | 10240, 44 | -10240 45 | ] 46 | ] 47 | ] 48 | ] 49 | ], 50 | "clipPaths" : [ 51 | [ 52 | [ 53 | 0, 54 | 10240 55 | ], 56 | [ 57 | -10240, 58 | 0 59 | ], 60 | [ 61 | -10240, 62 | 10240 63 | ] 64 | ] 65 | ], 66 | "fillRule" : 0, 67 | "clip" : [ 68 | [ 69 | [ 70 | [ 71 | [ 72 | -10240, 73 | 0 74 | ], 75 | [ 76 | -10240, 77 | 10240 78 | ], 79 | [ 80 | 0, 81 | 10240 82 | ] 83 | ] 84 | ] 85 | ] 86 | ], 87 | "subject" : [ 88 | [ 89 | [ 90 | [ 91 | [ 92 | -10240, 93 | -10240 94 | ], 95 | [ 96 | -10240, 97 | 10240 98 | ], 99 | [ 100 | 10240, 101 | 10240 102 | ], 103 | [ 104 | 10240, 105 | -10240 106 | ] 107 | ] 108 | ] 109 | ] 110 | ], 111 | "xor" : [ 112 | [ 113 | [ 114 | [ 115 | [ 116 | -10240, 117 | -10240 118 | ], 119 | [ 120 | -10240, 121 | 0 122 | ], 123 | [ 124 | 0, 125 | 10240 126 | ], 127 | [ 128 | 10240, 129 | 10240 130 | ], 131 | [ 132 | 10240, 133 | -10240 134 | ] 135 | ] 136 | ] 137 | ] 138 | ], 139 | "union" : [ 140 | [ 141 | [ 142 | [ 143 | [ 144 | -10240, 145 | -10240 146 | ], 147 | [ 148 | -10240, 149 | 10240 150 | ], 151 | [ 152 | 10240, 153 | 10240 154 | ], 155 | [ 156 | 10240, 157 | -10240 158 | ] 159 | ] 160 | ] 161 | ] 162 | ], 163 | "intersect" : [ 164 | [ 165 | [ 166 | [ 167 | [ 168 | -10240, 169 | 0 170 | ], 171 | [ 172 | -10240, 173 | 10240 174 | ], 175 | [ 176 | 0, 177 | 10240 178 | ] 179 | ] 180 | ] 181 | ] 182 | ], 183 | "inverseDifference" : [ 184 | [ 185 | 186 | ] 187 | ] 188 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_61.json: -------------------------------------------------------------------------------- 1 | { 2 | "difference" : [ 3 | [ 4 | [ 5 | [ 6 | [ 7 | -10240, 8 | 0 9 | ], 10 | [ 11 | -10240, 12 | 10240 13 | ], 14 | [ 15 | 10240, 16 | 10240 17 | ], 18 | [ 19 | 10240, 20 | -10240 21 | ], 22 | [ 23 | 0, 24 | -10240 25 | ] 26 | ] 27 | ] 28 | ] 29 | ], 30 | "xor" : [ 31 | [ 32 | [ 33 | [ 34 | [ 35 | -10240, 36 | 0 37 | ], 38 | [ 39 | -10240, 40 | 10240 41 | ], 42 | [ 43 | 10240, 44 | 10240 45 | ], 46 | [ 47 | 10240, 48 | -10240 49 | ], 50 | [ 51 | 0, 52 | -10240 53 | ] 54 | ] 55 | ] 56 | ] 57 | ], 58 | "clipPaths" : [ 59 | [ 60 | [ 61 | -10240, 62 | 0 63 | ], 64 | [ 65 | 0, 66 | -10240 67 | ], 68 | [ 69 | -10240, 70 | -10240 71 | ] 72 | ] 73 | ], 74 | "fillRule" : 0, 75 | "clip" : [ 76 | [ 77 | [ 78 | [ 79 | [ 80 | -10240, 81 | -10240 82 | ], 83 | [ 84 | -10240, 85 | 0 86 | ], 87 | [ 88 | 0, 89 | -10240 90 | ] 91 | ] 92 | ] 93 | ] 94 | ], 95 | "subjPaths" : [ 96 | [ 97 | [ 98 | -10240, 99 | 10240 100 | ], 101 | [ 102 | 10240, 103 | 10240 104 | ], 105 | [ 106 | 10240, 107 | -10240 108 | ], 109 | [ 110 | -10240, 111 | -10240 112 | ] 113 | ] 114 | ], 115 | "inverseDifference" : [ 116 | [ 117 | 118 | ] 119 | ], 120 | "subject" : [ 121 | [ 122 | [ 123 | [ 124 | [ 125 | -10240, 126 | -10240 127 | ], 128 | [ 129 | -10240, 130 | 10240 131 | ], 132 | [ 133 | 10240, 134 | 10240 135 | ], 136 | [ 137 | 10240, 138 | -10240 139 | ] 140 | ] 141 | ] 142 | ] 143 | ], 144 | "union" : [ 145 | [ 146 | [ 147 | [ 148 | [ 149 | -10240, 150 | -10240 151 | ], 152 | [ 153 | -10240, 154 | 10240 155 | ], 156 | [ 157 | 10240, 158 | 10240 159 | ], 160 | [ 161 | 10240, 162 | -10240 163 | ] 164 | ] 165 | ] 166 | ] 167 | ], 168 | "intersect" : [ 169 | [ 170 | [ 171 | [ 172 | [ 173 | -10240, 174 | -10240 175 | ], 176 | [ 177 | -10240, 178 | 0 179 | ], 180 | [ 181 | 0, 182 | -10240 183 | ] 184 | ] 185 | ] 186 | ] 187 | ] 188 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_62.json: -------------------------------------------------------------------------------- 1 | { 2 | "difference" : [ 3 | [ 4 | [ 5 | [ 6 | [ 7 | -10240, 8 | -10240 9 | ], 10 | [ 11 | -10240, 12 | 10240 13 | ], 14 | [ 15 | 10240, 16 | 10240 17 | ], 18 | [ 19 | 10240, 20 | 0 21 | ], 22 | [ 23 | 0, 24 | -10240 25 | ] 26 | ] 27 | ] 28 | ] 29 | ], 30 | "clipPaths" : [ 31 | [ 32 | [ 33 | 0, 34 | -10240 35 | ], 36 | [ 37 | 10240, 38 | 0 39 | ], 40 | [ 41 | 10240, 42 | -10240 43 | ] 44 | ] 45 | ], 46 | "subjPaths" : [ 47 | [ 48 | [ 49 | -10240, 50 | 10240 51 | ], 52 | [ 53 | 10240, 54 | 10240 55 | ], 56 | [ 57 | 10240, 58 | -10240 59 | ], 60 | [ 61 | -10240, 62 | -10240 63 | ] 64 | ] 65 | ], 66 | "subject" : [ 67 | [ 68 | [ 69 | [ 70 | [ 71 | -10240, 72 | -10240 73 | ], 74 | [ 75 | -10240, 76 | 10240 77 | ], 78 | [ 79 | 10240, 80 | 10240 81 | ], 82 | [ 83 | 10240, 84 | -10240 85 | ] 86 | ] 87 | ] 88 | ] 89 | ], 90 | "intersect" : [ 91 | [ 92 | [ 93 | [ 94 | [ 95 | 0, 96 | -10240 97 | ], 98 | [ 99 | 10240, 100 | 0 101 | ], 102 | [ 103 | 10240, 104 | -10240 105 | ] 106 | ] 107 | ] 108 | ] 109 | ], 110 | "fillRule" : 0, 111 | "union" : [ 112 | [ 113 | [ 114 | [ 115 | [ 116 | -10240, 117 | -10240 118 | ], 119 | [ 120 | -10240, 121 | 10240 122 | ], 123 | [ 124 | 10240, 125 | 10240 126 | ], 127 | [ 128 | 10240, 129 | -10240 130 | ] 131 | ] 132 | ] 133 | ] 134 | ], 135 | "clip" : [ 136 | [ 137 | [ 138 | [ 139 | [ 140 | 0, 141 | -10240 142 | ], 143 | [ 144 | 10240, 145 | 0 146 | ], 147 | [ 148 | 10240, 149 | -10240 150 | ] 151 | ] 152 | ] 153 | ] 154 | ], 155 | "xor" : [ 156 | [ 157 | [ 158 | [ 159 | [ 160 | -10240, 161 | -10240 162 | ], 163 | [ 164 | -10240, 165 | 10240 166 | ], 167 | [ 168 | 10240, 169 | 10240 170 | ], 171 | [ 172 | 10240, 173 | 0 174 | ], 175 | [ 176 | 0, 177 | -10240 178 | ] 179 | ] 180 | ] 181 | ] 182 | ], 183 | "inverseDifference" : [ 184 | [ 185 | 186 | ] 187 | ] 188 | } -------------------------------------------------------------------------------- /iOverlay/tests/boolean/test_83.json: -------------------------------------------------------------------------------- 1 | { 2 | "subject" : [ 3 | [ 4 | [ 5 | [ 6 | [ 7 | -20480, 8 | 0 9 | ], 10 | [ 11 | 0, 12 | 0 13 | ], 14 | [ 15 | 7759, 16 | -15582 17 | ] 18 | ] 19 | ] 20 | ] 21 | ], 22 | "fillRule" : 0, 23 | "inverseDifference" : [ 24 | [ 25 | [ 26 | [ 27 | [ 28 | 0, 29 | 0 30 | ], 31 | [ 32 | 5120, 33 | 5120 34 | ], 35 | [ 36 | 7759, 37 | -15582 38 | ] 39 | ] 40 | ] 41 | ] 42 | ], 43 | "subjPaths" : [ 44 | [ 45 | [ 46 | 0, 47 | 0 48 | ], 49 | [ 50 | -20480, 51 | 0 52 | ], 53 | [ 54 | 7759, 55 | -15582 56 | ] 57 | ] 58 | ], 59 | "clipPaths" : [ 60 | [ 61 | [ 62 | 0, 63 | 0 64 | ], 65 | [ 66 | 7759, 67 | -15583 68 | ], 69 | [ 70 | 5120, 71 | 5120 72 | ] 73 | ] 74 | ], 75 | "clip" : [ 76 | [ 77 | [ 78 | [ 79 | [ 80 | 0, 81 | 0 82 | ], 83 | [ 84 | 5120, 85 | 5120 86 | ], 87 | [ 88 | 7759, 89 | -15582 90 | ] 91 | ] 92 | ] 93 | ] 94 | ], 95 | "difference" : [ 96 | [ 97 | [ 98 | [ 99 | [ 100 | -20480, 101 | 0 102 | ], 103 | [ 104 | 0, 105 | 0 106 | ], 107 | [ 108 | 7759, 109 | -15582 110 | ] 111 | ] 112 | ] 113 | ] 114 | ], 115 | "intersect" : [ 116 | [ 117 | 118 | ] 119 | ], 120 | "union" : [ 121 | [ 122 | [ 123 | [ 124 | [ 125 | -20480, 126 | 0 127 | ], 128 | [ 129 | 0, 130 | 0 131 | ], 132 | [ 133 | 5120, 134 | 5120 135 | ], 136 | [ 137 | 7759, 138 | -15582 139 | ] 140 | ] 141 | ] 142 | ] 143 | ], 144 | "xor" : [ 145 | [ 146 | [ 147 | [ 148 | [ 149 | -20480, 150 | 0 151 | ], 152 | [ 153 | 0, 154 | 0 155 | ], 156 | [ 157 | 5120, 158 | 5120 159 | ], 160 | [ 161 | 7759, 162 | -15582 163 | ] 164 | ] 165 | ] 166 | ] 167 | ] 168 | } -------------------------------------------------------------------------------- /iOverlay/tests/empty_tests.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use i_float::int::point::IntPoint; 4 | use i_overlay::core::fill_rule::FillRule; 5 | use i_overlay::core::overlay::{Overlay, ShapeType}; 6 | 7 | #[test] 8 | fn test_00() { 9 | let mut overlay = Overlay::new(1); 10 | overlay.add_contour(&[IntPoint::new(0, 0)], ShapeType::Subject); 11 | 12 | let graph = overlay.build_graph_view(FillRule::NonZero); 13 | assert!(graph.is_none()); 14 | } 15 | 16 | #[test] 17 | fn test_01() { 18 | let mut overlay = Overlay::new(1); 19 | overlay.add_contour(&[IntPoint::new(0, 0), IntPoint::new(1, 0)], ShapeType::Subject); 20 | 21 | let graph = overlay.build_graph_view(FillRule::NonZero); 22 | assert!(graph.is_none()); 23 | } 24 | } -------------------------------------------------------------------------------- /iOverlay/tests/outline/test_0.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "outline": [ 4 | [ 5 | [ 0.0, 0.0], 6 | [10.0, 0.0], 7 | [ 0.0, 10.0] 8 | ] 9 | ] 10 | } -------------------------------------------------------------------------------- /iOverlay/tests/outline/test_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "outline": [ 4 | [ 5 | [-5.0, -5.0], 6 | [ 5.0, -5.0], 7 | [ 5.0, 5.0], 8 | [-5.0, 5.0] 9 | ] 10 | ] 11 | } -------------------------------------------------------------------------------- /iOverlay/tests/outline/test_10.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "outline": [ 4 | [ 5 | [ 0.0, -10.0], 6 | [ 10.0, 0.0], 7 | [ 0.0, 10.0], 8 | [ -10.0, 0.0] 9 | ] 10 | ] 11 | } -------------------------------------------------------------------------------- /iOverlay/tests/outline/test_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "outline": [ 4 | [ 5 | [-15.0, -15.0], 6 | [ 15.0, -15.0], 7 | [ 15.0, 15.0], 8 | [-15.0, 15.0] 9 | ], 10 | [ 11 | [-5.0, -5.0], 12 | [-5.0, 5.0], 13 | [ 5.0, 5.0], 14 | [ 5.0, -5.0] 15 | ] 16 | ] 17 | } -------------------------------------------------------------------------------- /iOverlay/tests/outline/test_3.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "outline": [ 4 | [ 5 | [10.0, 0.0], 6 | [10.0, 5.0], 7 | [ 5.0, 5.0], 8 | [ 5.0, 10.0], 9 | [ 0.0, 10.0], 10 | [ 0.0, 0.0] 11 | ] 12 | ] 13 | } -------------------------------------------------------------------------------- /iOverlay/tests/outline/test_4.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "outline": [ 4 | [ 5 | [0.0, 0.0], 6 | [1.0, 0.0], 7 | [1.0, 1.0], 8 | [0.0, 1.0] 9 | ], 10 | [ 11 | [1.0, 2.0], 12 | [1.0, 3.0], 13 | [0.0, 3.0], 14 | [0.0, 2.0] 15 | ], 16 | [ 17 | [1.0, 4.0], 18 | [1.0, 5.0], 19 | [0.0, 5.0], 20 | [0.0, 4.0] 21 | ], 22 | [ 23 | [3.0, 0.0], 24 | [3.0, 1.0], 25 | [2.0, 1.0], 26 | [2.0, 0.0] 27 | ], 28 | [ 29 | [3.0, 2.0], 30 | [3.0, 3.0], 31 | [2.0, 3.0], 32 | [2.0, 2.0] 33 | ], 34 | [ 35 | [3.0, 4.0], 36 | [3.0, 5.0], 37 | [2.0, 5.0], 38 | [2.0, 4.0] 39 | ], 40 | [ 41 | [5.0, 0.0], 42 | [5.0, 1.0], 43 | [4.0, 1.0], 44 | [4.0, 0.0] 45 | ], 46 | [ 47 | [5.0, 2.0], 48 | [5.0, 3.0], 49 | [4.0, 3.0], 50 | [4.0, 2.0] 51 | ], 52 | [ 53 | [5.0, 4.0], 54 | [5.0, 5.0], 55 | [4.0, 5.0], 56 | [4.0, 4.0] 57 | ] 58 | ] 59 | } -------------------------------------------------------------------------------- /iOverlay/tests/outline/test_5.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "outline": [ 4 | [ 5 | [ 0.0, 0.0], 6 | [25.0, 0.0], 7 | [25.0, 25.0], 8 | [ 0.0, 25.0] 9 | ], 10 | [ 11 | [ 5.0, 10.0], 12 | [10.0, 10.0], 13 | [10.0, 5.0], 14 | [ 5.0, 5.0] 15 | ], 16 | [ 17 | [ 5.0, 20.0], 18 | [10.0, 20.0], 19 | [10.0, 15.0], 20 | [ 5.0, 15.0] 21 | 22 | ], 23 | [ 24 | [15.0, 10.0], 25 | [20.0, 10.0], 26 | [20.0, 5.0], 27 | [15.0, 5.0] 28 | ], 29 | [ 30 | [15.0, 20.0], 31 | [20.0, 20.0], 32 | [20.0, 15.0], 33 | [15.0, 15.0] 34 | ] 35 | ] 36 | } -------------------------------------------------------------------------------- /iOverlay/tests/outline/test_6.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "outline": [ 4 | [ 5 | [-10.0, -20.0], 6 | [ 10.0, -20.0], 7 | [ 10.0, -10.0], 8 | [ 20.0, -10.0], 9 | [ 20.0, 10.0], 10 | [ 10.0, 10.0], 11 | [ 10.0, 20.0], 12 | [-10.0, 20.0], 13 | [-10.0, 10.0], 14 | [-20.0, 10.0], 15 | [-20.0, -10.0], 16 | [-10.0, -10.0] 17 | ] 18 | ] 19 | } -------------------------------------------------------------------------------- /iOverlay/tests/outline/test_7.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "outline": [ 4 | [ 5 | [ 10.0, 10.0], 6 | [-10.0, 10.0], 7 | [-10.0, -10.0], 8 | [ 5.0, -10.0], 9 | [ 5.0, -15.0], 10 | [ 15.0, -15.0], 11 | [ 15.0, 15.0], 12 | [-15.0, 15.0], 13 | [-15.0, -10.0], 14 | [-10.0, -10.0] 15 | ] 16 | ] 17 | } -------------------------------------------------------------------------------- /iOverlay/tests/outline/test_8.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "outline": [ 4 | [ 5 | [2.0, 1.0], 6 | [4.0, 1.0], 7 | [5.0, 2.0], 8 | [13.0, 2.0], 9 | [13.0, 3.0], 10 | [12.0, 3.0], 11 | [12.0, 4.0], 12 | [11.0, 4.0], 13 | [11.0, 3.0], 14 | [10.0, 3.0], 15 | [9.0, 4.0], 16 | [8.0, 4.0], 17 | [8.0, 3.0], 18 | [5.0, 3.0], 19 | [5.0, 4.0], 20 | [4.0, 5.0], 21 | [2.0, 5.0], 22 | [1.0, 4.0], 23 | [1.0, 2.0] 24 | ], 25 | [ 26 | [2.0, 4.0], 27 | [4.0, 4.0], 28 | [4.0, 2.0], 29 | [2.0, 2.0] 30 | ] 31 | ] 32 | } -------------------------------------------------------------------------------- /iOverlay/tests/outline/test_9.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "outline": [ 4 | [ 5 | [ 0.0, 0.0], 6 | [ 5.0, 0.0], 7 | [ 1.0, 1.0], 8 | [ 0.0, 5.0] 9 | ] 10 | ] 11 | } -------------------------------------------------------------------------------- /iOverlay/tests/string/test_0.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 0, 3 | "body": [ 4 | [ 5 | [ 6 | -1000, 7 | -1000 8 | ], 9 | [ 10 | -1000, 11 | 1000 12 | ], 13 | [ 14 | 1000, 15 | 1000 16 | ], 17 | [ 18 | 1000, 19 | -1000 20 | ] 21 | ] 22 | ], 23 | "string": [ 24 | [ 25 | [ 26 | 1500, 27 | 0 28 | ], 29 | [ 30 | -1500, 31 | 0 32 | ] 33 | ] 34 | ], 35 | "slice": [ 36 | [[[[1000, -1000], [1000, 0], [-1000, 0], [-1000, -1000]]], [[[1000, 0], [1000, 1000], [-1000, 1000], [-1000, 0]]]] 37 | ], 38 | "clip_direct": [ 39 | [[[1000, 0], [-1000, 0]]] 40 | ], 41 | "clip_invert": [ 42 | [[[-1000, 0], [-1500, 0]], [[1500, 0], [1000, 0]]] 43 | ] 44 | } -------------------------------------------------------------------------------- /iOverlay/tests/string/test_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 0, 3 | "body": [ 4 | [ 5 | [ 6 | -1000, 7 | -1000 8 | ], 9 | [ 10 | -1000, 11 | 1000 12 | ], 13 | [ 14 | 1000, 15 | 1000 16 | ], 17 | [ 18 | 1000, 19 | -1000 20 | ] 21 | ] 22 | ], 23 | "string": [ 24 | [ 25 | [ 26 | -1500, 27 | 0 28 | ], 29 | [ 30 | 1500, 31 | 0 32 | ] 33 | ] 34 | ], 35 | "slice": [ 36 | [[[[1000, -1000], [1000, 0], [-1000, 0], [-1000, -1000]]], [[[1000, 0], [1000, 1000], [-1000, 1000], [-1000, 0]]]] 37 | ], 38 | "clip_direct": [ 39 | [[[-1000, 0], [1000, 0]]] 40 | ], 41 | "clip_invert": [ 42 | [[[-1500, 0], [-1000, 0]], [[1000, 0], [1500, 0]]] 43 | ] 44 | } -------------------------------------------------------------------------------- /iOverlay/tests/string/test_10.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 0, 3 | "body": [ 4 | [ 5 | [ 6 | -2000, 7 | -2000 8 | ], 9 | [ 10 | -2000, 11 | 2000 12 | ], 13 | [ 14 | 2000, 15 | 2000 16 | ], 17 | [ 18 | 2000, 19 | -2000 20 | ] 21 | ], 22 | [ 23 | [ 24 | -1000, 25 | -1000 26 | ], 27 | [ 28 | 1000, 29 | -1000 30 | ], 31 | [ 32 | 1000, 33 | 1000 34 | ], 35 | [ 36 | -1000, 37 | 1000 38 | ] 39 | ] 40 | ], 41 | "string": [ 42 | [ 43 | [ 44 | -1000, 45 | 2500 46 | ], 47 | [ 48 | -1000, 49 | -2500 50 | ], 51 | [ 52 | 0, 53 | -2500 54 | ], 55 | [ 56 | 0, 57 | 2500 58 | ], 59 | [ 60 | 1000, 61 | 2500 62 | ], 63 | [ 64 | 1000, 65 | -2500 66 | ] 67 | ], 68 | [ 69 | [ 70 | -2500, 71 | 1000 72 | ], 73 | [ 74 | 2500, 75 | 1000 76 | ], 77 | [ 78 | 2500, 79 | 0 80 | ], 81 | [ 82 | -2500, 83 | 0 84 | ], 85 | [ 86 | -2500, 87 | -1000 88 | ], 89 | [ 90 | 2500, 91 | -1000 92 | ] 93 | ] 94 | ], 95 | "slice": [ 96 | [[[[-1000, -2000], [-1000, -1000], [-2000, -1000], [-2000, -2000]]], [[[-1000, -1000], [-1000, 0], [-2000, 0], [-2000, -1000]]], [[[-1000, 0], [-1000, 1000], [-2000, 1000], [-2000, 0]]], [[[-1000, 1000], [-1000, 2000], [-2000, 2000], [-2000, 1000]]], [[[0, -2000], [0, -1000], [-1000, -1000], [-1000, -2000]]], [[[0, 1000], [0, 2000], [-1000, 2000], [-1000, 1000]]], [[[1000, -2000], [1000, -1000], [0, -1000], [0, -2000]]], [[[1000, 1000], [1000, 2000], [0, 2000], [0, 1000]]], [[[2000, -2000], [2000, -1000], [1000, -1000], [1000, -2000]]], [[[2000, -1000], [2000, 0], [1000, 0], [1000, -1000]]], [[[2000, 0], [2000, 1000], [1000, 1000], [1000, 0]]], [[[2000, 1000], [2000, 2000], [1000, 2000], [1000, 1000]]]] 97 | ], 98 | "clip_direct": [ 99 | [[[-2000, -1000], [-1000, -1000], [-1000, -2000]], [[-1000, 0], [-2000, 0]], [[-2000, 1000], [-1000, 1000]], [[-1000, 2000], [-1000, 1000]], [[0, -2000], [0, -1000]], [[0, 1000], [0, 2000]], [[1000, -1000], [1000, -2000]], [[1000, -1000], [2000, -1000]], [[2000, 0], [1000, 0]], [[1000, 2000], [1000, 1000]], [[1000, 1000], [2000, 1000]]] 100 | ], 101 | "clip_invert": [ 102 | [[[-2000, 0], [-2500, 0], [-2500, -1000]], [[-2500, -1000], [-2000, -1000]], [[-2500, 1000], [-2000, 1000]], [[-1000, -2000], [-1000, -2500]], [[-1000, -2500], [0, -2500], [0, -2000]], [[-1000, 1000], [-1000, 0], [-1000, -1000]], [[-1000, -1000], [0, -1000], [0, 0], [0, 1000], [1000, 1000], [1000, 0], [0, 0], [-1000, 0]], [[-1000, 1000], [0, 1000]], [[-1000, 2500], [-1000, 2000]], [[0, -1000], [1000, -1000]], [[0, 2000], [0, 2500], [1000, 2500], [1000, 2000]], [[1000, -2000], [1000, -2500]], [[1000, 0], [1000, -1000]], [[2000, -1000], [2500, -1000]], [[2000, 1000], [2500, 1000], [2500, 0], [2000, 0]]] 103 | ] 104 | } -------------------------------------------------------------------------------- /iOverlay/tests/string/test_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 0, 3 | "body": [ 4 | [ 5 | [ 6 | -1000, 7 | -1000 8 | ], 9 | [ 10 | -1000, 11 | 1000 12 | ], 13 | [ 14 | 1000, 15 | 1000 16 | ], 17 | [ 18 | 1000, 19 | -1000 20 | ] 21 | ] 22 | ], 23 | "string": [ 24 | [ 25 | [ 26 | 1500, 27 | 0 28 | ], 29 | [ 30 | -1500, 31 | 0 32 | ], 33 | [ 34 | 1500, 35 | 0 36 | ] 37 | ] 38 | ], 39 | "slice": [ 40 | [[[[1000, -1000], [1000, 0], [-1000, 0], [-1000, -1000]]], [[[1000, 0], [1000, 1000], [-1000, 1000], [-1000, 0]]]] 41 | ], 42 | "clip_direct": [ 43 | [[[-1000, 0], [1000, 0], [-1000, 0]]] 44 | ], 45 | "clip_invert": [ 46 | [[[-1500, 0], [-1000, 0], [-1500, 0]], [[1000, 0], [1500, 0], [1000, 0]]] 47 | ] 48 | } -------------------------------------------------------------------------------- /iOverlay/tests/string/test_3.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 0, 3 | "body": [ 4 | [ 5 | [ 6 | -1000, 7 | -1000 8 | ], 9 | [ 10 | -1000, 11 | 1000 12 | ], 13 | [ 14 | 1000, 15 | 1000 16 | ], 17 | [ 18 | 1000, 19 | -1000 20 | ] 21 | ] 22 | ], 23 | "string": [ 24 | [ 25 | [ 26 | -1500, 27 | 0 28 | ], 29 | [ 30 | 1500, 31 | 0 32 | ], 33 | [ 34 | -1500, 35 | 0 36 | ] 37 | ] 38 | ], 39 | "slice": [ 40 | [[[[1000, -1000], [1000, 0], [-1000, 0], [-1000, -1000]]], [[[1000, 0], [1000, 1000], [-1000, 1000], [-1000, 0]]]] 41 | ], 42 | "clip_direct": [ 43 | [[[-1000, 0], [1000, 0], [-1000, 0]]] 44 | ], 45 | "clip_invert": [ 46 | [[[-1500, 0], [-1000, 0], [-1500, 0]], [[1000, 0], [1500, 0], [1000, 0]]] 47 | ] 48 | } -------------------------------------------------------------------------------- /iOverlay/tests/string/test_4.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 0, 3 | "body": [ 4 | [ 5 | [ 6 | -1000, 7 | -1000 8 | ], 9 | [ 10 | -1000, 11 | 1000 12 | ], 13 | [ 14 | 1000, 15 | 1000 16 | ], 17 | [ 18 | 1000, 19 | -1000 20 | ] 21 | ] 22 | ], 23 | "string": [ 24 | [ 25 | [ 26 | 0, 27 | 1500 28 | ], 29 | [ 30 | 0, 31 | -1500 32 | ] 33 | ] 34 | ], 35 | "slice": [ 36 | [[[[0, -1000], [0, 1000], [-1000, 1000], [-1000, -1000]]], [[[1000, -1000], [1000, 1000], [0, 1000], [0, -1000]]]] 37 | ], 38 | "clip_direct": [ 39 | [[[0, 1000], [0, -1000]]] 40 | ], 41 | "clip_invert": [ 42 | [[[0, -1000], [0, -1500]], [[0, 1500], [0, 1000]]] 43 | ] 44 | } -------------------------------------------------------------------------------- /iOverlay/tests/string/test_5.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 0, 3 | "body": [ 4 | [ 5 | [ 6 | -1000, 7 | -1000 8 | ], 9 | [ 10 | -1000, 11 | 1000 12 | ], 13 | [ 14 | 1000, 15 | 1000 16 | ], 17 | [ 18 | 1000, 19 | -1000 20 | ] 21 | ] 22 | ], 23 | "string": [ 24 | [ 25 | [ 26 | 0, 27 | -1500 28 | ], 29 | [ 30 | 0, 31 | 1500 32 | ] 33 | ] 34 | ], 35 | "slice": [ 36 | [[[[0, -1000], [0, 1000], [-1000, 1000], [-1000, -1000]]], [[[1000, -1000], [1000, 1000], [0, 1000], [0, -1000]]]] 37 | ], 38 | "clip_direct": [ 39 | [[[0, -1000], [0, 1000]]] 40 | ], 41 | "clip_invert": [ 42 | [[[0, -1500], [0, -1000]], [[0, 1000], [0, 1500]]] 43 | ] 44 | } -------------------------------------------------------------------------------- /iOverlay/tests/string/test_6.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 0, 3 | "body": [ 4 | [ 5 | [ 6 | -1000, 7 | -1000 8 | ], 9 | [ 10 | -1000, 11 | 1000 12 | ], 13 | [ 14 | 1000, 15 | 1000 16 | ], 17 | [ 18 | 1000, 19 | -1000 20 | ] 21 | ] 22 | ], 23 | "string": [ 24 | [ 25 | [ 26 | 1500, 27 | 1500 28 | ], 29 | [ 30 | -1500, 31 | -1500 32 | ], 33 | [ 34 | 1500, 35 | 1500 36 | ] 37 | ] 38 | ], 39 | "slice": [ 40 | [[[[1000, 1000], [-1000, 1000], [-1000, -1000]]], [[[-1000, -1000], [1000, -1000], [1000, 1000]]]] 41 | ], 42 | "clip_direct": [ 43 | [[[-1000, -1000], [1000, 1000], [-1000, -1000]]] 44 | ], 45 | "clip_invert": [ 46 | [[[-1500, -1500], [-1000, -1000], [-1500, -1500]], [[1000, 1000], [1500, 1500], [1000, 1000]]] 47 | ] 48 | } -------------------------------------------------------------------------------- /iOverlay/tests/string/test_7.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 0, 3 | "body": [ 4 | [ 5 | [ 6 | -1000, 7 | -1000 8 | ], 9 | [ 10 | -1000, 11 | 1000 12 | ], 13 | [ 14 | 1000, 15 | 1000 16 | ], 17 | [ 18 | 1000, 19 | -1000 20 | ] 21 | ] 22 | ], 23 | "string": [ 24 | [ 25 | [ 26 | 500, 27 | 2000 28 | ], 29 | [ 30 | -500, 31 | -500 32 | ], 33 | [ 34 | 500, 35 | 0 36 | ], 37 | [ 38 | -500, 39 | -2500 40 | ] 41 | ] 42 | ], 43 | "slice": [ 44 | [[[[100, -1000], [500, 0], [-500, -500], [100, 1000], [-1000, 1000], [-1000, -1000]]], [[[500, 0], [100, -1000], [1000, -1000], [1000, 1000], [100, 1000], [-500, -500]]]] 45 | ], 46 | "clip_direct": [ 47 | [[[100, 1000], [-500, -500]], [[-500, -500], [500, 0], [100, -1000]]] 48 | ], 49 | "clip_invert": [ 50 | [[[100, -1000], [-500, -2500]], [[500, 2000], [100, 1000]]] 51 | ] 52 | } -------------------------------------------------------------------------------- /iOverlay/tests/string/test_8.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 0, 3 | "body": [ 4 | [ 5 | [ 6 | -1000, 7 | 0 8 | ], 9 | [ 10 | 0, 11 | 1000 12 | ], 13 | [ 14 | 1000, 15 | 0 16 | ], 17 | [ 18 | 0, 19 | -1000 20 | ] 21 | ] 22 | ], 23 | "string": [ 24 | [ 25 | [ 26 | -1000, 27 | 1000 28 | ], 29 | [ 30 | 0, 31 | 0 32 | ], 33 | [ 34 | 1000, 35 | 1000 36 | ] 37 | ], 38 | [ 39 | [ 40 | -1000, 41 | -1000 42 | ], 43 | [ 44 | 0, 45 | 0 46 | ], 47 | [ 48 | 1000, 49 | -1000 50 | ] 51 | ] 52 | ], 53 | "slice": [ 54 | [[[[-1000, 0], [-500, -500], [0, 0], [-500, 500]]], [[[-500, -500], [0, -1000], [500, -500], [0, 0]]], [[[-500, 500], [0, 0], [500, 500], [0, 1000]]], [[[0, 0], [500, -500], [1000, 0], [500, 500]]]] 55 | ], 56 | "clip_direct": [ 57 | [[[-500, -500], [0, 0], [500, -500]], [[-500, 500], [0, 0], [500, 500]]] 58 | ], 59 | "clip_invert": [ 60 | [[[-1000, -1000], [-500, -500]], [[-1000, 1000], [-500, 500]], [[500, -500], [1000, -1000]], [[500, 500], [1000, 1000]]] 61 | ] 62 | } -------------------------------------------------------------------------------- /iOverlay/tests/string/test_9.json: -------------------------------------------------------------------------------- 1 | { 2 | "fillRule": 0, 3 | "body": [ 4 | [ 5 | [ 6 | -1000, 7 | 0 8 | ], 9 | [ 10 | 0, 11 | 1000 12 | ], 13 | [ 14 | 1000, 15 | 0 16 | ], 17 | [ 18 | 0, 19 | -1000 20 | ] 21 | ] 22 | ], 23 | "string": [ 24 | [ 25 | [ 26 | 1000, 27 | 1000 28 | ], 29 | [ 30 | 0, 31 | 0 32 | ], 33 | [ 34 | -1000, 35 | 1000 36 | ] 37 | ], 38 | [ 39 | [ 40 | 1000, 41 | -1000 42 | ], 43 | [ 44 | 0, 45 | 0 46 | ], 47 | [ 48 | -1000, 49 | -1000 50 | ] 51 | ] 52 | ], 53 | "slice": [ 54 | [[[[-1000, 0], [-500, -500], [0, 0], [-500, 500]]], [[[-500, -500], [0, -1000], [500, -500], [0, 0]]], [[[-500, 500], [0, 0], [500, 500], [0, 1000]]], [[[0, 0], [500, -500], [1000, 0], [500, 500]]]] 55 | ], 56 | "clip_direct": [ 57 | [[[500, -500], [0, 0], [-500, -500]], [[500, 500], [0, 0], [-500, 500]]] 58 | ], 59 | "clip_invert": [ 60 | [[[-500, -500], [-1000, -1000]], [[-500, 500], [-1000, 1000]], [[1000, -1000], [500, -500]], [[1000, 1000], [500, 500]]] 61 | ] 62 | } -------------------------------------------------------------------------------- /iOverlay/tests/string_tests.rs: -------------------------------------------------------------------------------- 1 | mod data; 2 | mod util; 3 | 4 | #[cfg(test)] 5 | mod tests { 6 | use i_overlay::core::fill_rule::FillRule; 7 | use i_overlay::string::clip::{ClipRule, IntClip}; 8 | use i_overlay::string::slice::IntSlice; 9 | use crate::data::overlay::StringTest; 10 | use crate::util::overlay; 11 | use crate::util::overlay::JsonPrint; 12 | 13 | fn execute(index: usize) { 14 | let test = StringTest::load(index); 15 | let fill_rule = test.fill_rule.unwrap_or(FillRule::EvenOdd); 16 | 17 | let slice = test.body.slice_by_paths(&test.string, fill_rule); 18 | assert_eq!(true, overlay::is_group_of_shapes_one_of(&slice, &test.slice)); 19 | 20 | let clip_direct = test.body.clip_paths(&test.string, fill_rule, ClipRule { invert: false, boundary_included: false }); 21 | assert_eq!(true, overlay::is_paths_one_of(&clip_direct, &test.clip_direct)); 22 | 23 | let clip_invert = test.body.clip_paths(&test.string, fill_rule, ClipRule { invert: true, boundary_included: false }); 24 | assert_eq!(true, overlay::is_paths_one_of(&clip_invert, &test.clip_invert)); 25 | } 26 | 27 | fn debug_execute_slice(index: usize) { 28 | let test = StringTest::load(index); 29 | let fill_rule = test.fill_rule.unwrap_or(FillRule::EvenOdd); 30 | let slice = test.body.slice_by_paths(&test.string, fill_rule); 31 | 32 | println!("slice: {}", slice.json_print()); 33 | } 34 | 35 | fn debug_execute_clip(index: usize, invert: bool) { 36 | let test = StringTest::load(index); 37 | let fill_rule = test.fill_rule.unwrap_or(FillRule::EvenOdd); 38 | 39 | let clip = test.body.clip_paths(&test.string, fill_rule, ClipRule { invert, boundary_included: false }); 40 | 41 | println!("clip {}: {}", invert, clip.json_print()); 42 | } 43 | 44 | #[test] 45 | fn test_0() { 46 | execute(0); 47 | } 48 | 49 | #[test] 50 | fn test_1() { 51 | execute(1); 52 | } 53 | 54 | #[test] 55 | fn test_2() { 56 | execute(2); 57 | } 58 | 59 | #[test] 60 | fn test_3() { 61 | execute(3); 62 | } 63 | 64 | #[test] 65 | fn test_4() { 66 | execute(4); 67 | } 68 | 69 | #[test] 70 | fn test_5() { 71 | execute(5); 72 | } 73 | 74 | #[test] 75 | fn test_6() { 76 | execute(6); 77 | } 78 | 79 | #[test] 80 | fn test_7() { 81 | execute(7); 82 | } 83 | 84 | #[test] 85 | fn test_8() { 86 | execute(8); 87 | } 88 | 89 | #[test] 90 | fn test_9() { 91 | execute(9); 92 | } 93 | 94 | #[test] 95 | fn test_10() { 96 | execute(10); 97 | } 98 | 99 | #[test] 100 | fn test_11() { 101 | execute(11); 102 | } 103 | 104 | #[test] 105 | fn test_debug() { 106 | let index = 11; 107 | debug_execute_slice(index); 108 | debug_execute_clip(index, false); 109 | debug_execute_clip(index, true); 110 | } 111 | } -------------------------------------------------------------------------------- /iOverlay/tests/stroke/test_0.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "stroke": [ 4 | [ 5 | [-10.0, 0.0], 6 | [ 0.0, 0.0], 7 | [ 0.0, 10.0] 8 | ] 9 | ] 10 | } -------------------------------------------------------------------------------- /iOverlay/tests/stroke/test_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "stroke": [ 4 | [ 5 | [-5.0, -5.0], 6 | [-5.0, 5.0], 7 | [ 5.0, 5.0], 8 | [ 5.0, -5.0] 9 | ] 10 | ] 11 | } -------------------------------------------------------------------------------- /iOverlay/tests/stroke/test_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "stroke": [ 4 | [ 5 | [-10.0, -5.0], 6 | [ 0.0, -5.0], 7 | [ 0.0, 5.0], 8 | [ 10.0, 5.0] 9 | ] 10 | ] 11 | } -------------------------------------------------------------------------------- /iOverlay/tests/stroke/test_3.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "stroke": [ 4 | [ 5 | [ 10.0, -10.0], 6 | [-10.0, -10.0], 7 | [-10.0, -6.0], 8 | [ 10.0, -6.0], 9 | [ 10.0, -2.0], 10 | [-10.0, -2.0], 11 | [-10.0, 2.0], 12 | [ 10.0, 2.0], 13 | [ 10.0, 6.0], 14 | [-10.0, 6.0], 15 | [-10.0, 10.0], 16 | [ 10.0, 10.0] 17 | ] 18 | ] 19 | } -------------------------------------------------------------------------------- /iOverlay/tests/stroke/test_4.json: -------------------------------------------------------------------------------- 1 | { 2 | "scale": 1000.0, 3 | "stroke": [ 4 | [ 5 | [ 2.0, 1.0], 6 | [ 5.0, 1.0], 7 | [ 8.0, 4.0], 8 | [11.0, 4.0], 9 | [11.0, 1.0], 10 | [ 8.0, 1.0], 11 | [ 5.0, 4.0], 12 | [ 2.0, 4.0] 13 | ] 14 | ] 15 | } -------------------------------------------------------------------------------- /performance/rust_app/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust_app" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [profile.release] 7 | opt-level = 3 8 | lto = false 9 | codegen-units = 1 10 | 11 | [dependencies] 12 | 13 | #i_overlay = { path = "../../iOverlay", default-features = true } 14 | #i_overlay = { path = "../../iOverlay", default-features = false } 15 | i_overlay = "~3.4.0" -------------------------------------------------------------------------------- /performance/rust_app/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | target/release/rust_app --multithreading false --complex true --test 0 -------------------------------------------------------------------------------- /performance/rust_app/src/test/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod test_0_checkerboard; 2 | pub(crate) mod test_1_not_overlap; 3 | pub(crate) mod test_2_lines_net; 4 | pub(crate) mod test_3_spiral; 5 | pub(crate) mod test_4_windows; 6 | pub(crate) mod test_5_nested_squares; 7 | pub(crate) mod test_6_corrosion; 8 | pub(crate) mod test_7_concentric; 9 | pub(crate) mod test_8_wind_mill; 10 | mod util; -------------------------------------------------------------------------------- /performance/rust_app/src/test/test_1_not_overlap.rs: -------------------------------------------------------------------------------- 1 | use std::time::Instant; 2 | use i_overlay::core::fill_rule::FillRule; 3 | use i_overlay::core::overlay::Overlay; 4 | use i_overlay::core::overlay_rule::OverlayRule; 5 | use i_overlay::core::solver::Solver; 6 | use i_overlay::i_float::int::point::IntPoint; 7 | use crate::test::util::Util; 8 | 9 | pub(crate) struct NotOverlapTest; 10 | /* 11 | 12 | // 1 13 | // Union: 14 | 15 | multithreading on 16 | 17 | 5 - 0.000003 18 | 25 - 0.000012 19 | 113 - 0.000062 20 | 481 - 0.000344 21 | 1985 - 0.001668 22 | 8065 - 0.005425 23 | 32513 - 0.024718 24 | 130561 - 0.107485 25 | 523265 - 0.538060 26 | 2095105 - 2.470210 27 | 8384513 - 9.601191 28 | 29 | multithreading off 30 | 31 | 5 - 0.000003 32 | 25 - 0.000013 33 | 113 - 0.000065 34 | 481 - 0.000356 35 | 1985 - 0.001646 36 | 8065 - 0.005963 37 | 32513 - 0.028155 38 | 130561 - 0.125444 39 | 523265 - 0.640918 40 | 2095105 - 2.696089 41 | 8384513 - 12.902138 42 | 43 | geom multithreading off 44 | 45 | 5 - 0.000004 46 | 25 - 0.000014 47 | 113 - 0.000059 48 | 481 - 0.000267 49 | 1985 - 0.001084 50 | 8065 - 0.005110 51 | 32513 - 0.023544 52 | 130561 - 0.102948 53 | 523265 - 0.506193 54 | 2095105 - 2.137119 55 | 8384513 - 9.766767 56 | 57 | geom swipe line hash 58 | 59 | 5 - 0.000003 60 | 25 - 0.000011 61 | 113 - 0.000048 62 | 481 - 0.000214 63 | 1985 - 0.000965 64 | 8065 - 0.004601 65 | 32513 - 0.021743 66 | 130561 - 0.089794 67 | 523265 - 0.452441 68 | 2095105 - 1.907293 69 | 8384513 - 8.501941 70 | 71 | geom map 72 | 73 | 5 - 0.000003 74 | 25 - 0.000012 75 | 113 - 0.000049 76 | 481 - 0.000215 77 | 1985 - 0.000970 78 | 8065 - 0.004646 79 | 32513 - 0.021622 80 | 130561 - 0.092652 81 | 523265 - 0.462316 82 | 2095105 - 1.946025 83 | 8384513 - 8.754714 84 | 85 | 86 | */ 87 | 88 | // A grid of not overlapping squares. 89 | impl NotOverlapTest { 90 | pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64, simple_geometry: bool) { // 1000 91 | let subj_paths = Util::many_squares(IntPoint::new(0, 0), 10, 30, n); 92 | let clip_paths = Util::many_squares(IntPoint::new(15, 15), 10, 30, n - 1); 93 | 94 | let it_count = ((scale / (n as f64)) as usize).max(1); 95 | let sq_it_count= it_count * it_count; 96 | 97 | let start = Instant::now(); 98 | 99 | if simple_geometry { 100 | // for _i in 0..sq_it_count { 101 | // let _ = Overlay::with_contours(&subj_paths, &clip_paths) 102 | // .overlay_45geom_with_min_area_and_solver(rule, FillRule::NonZero, 0, solver); 103 | // } 104 | } else { 105 | for _i in 0..sq_it_count { 106 | let _ = Overlay::with_contours(&subj_paths, &clip_paths) 107 | .overlay_custom(rule, FillRule::NonZero, solver); 108 | } 109 | } 110 | 111 | let duration = start.elapsed(); 112 | let time = duration.as_secs_f64() / sq_it_count as f64; 113 | 114 | let polygons_count = n * n + (n - 1) * (n - 1); 115 | 116 | println!("{:.1} - {:.6}", polygons_count, time); 117 | } 118 | } -------------------------------------------------------------------------------- /performance/rust_app/src/test/test_2_lines_net.rs: -------------------------------------------------------------------------------- 1 | use std::time::Instant; 2 | use i_overlay::core::fill_rule::FillRule; 3 | use i_overlay::core::overlay::Overlay; 4 | use i_overlay::core::overlay_rule::OverlayRule; 5 | use i_overlay::core::solver::Solver; 6 | use crate::test::util::Util; 7 | 8 | pub(crate) struct LinesNetTest; 9 | 10 | /* 11 | 12 | // 2 13 | // Intersection: 14 | 15 | multithreading on 16 | 17 | 4 - 0.000004 18 | 8 - 0.000014 19 | 16 - 0.000050 20 | 32 - 0.000196 21 | 64 - 0.001016 22 | 128 - 0.003970 23 | 256 - 0.020870 24 | 512 - 0.096745 25 | 1024 - 0.397470 26 | 2048 - 1.537385 27 | 4096 - 7.696920 28 | 29 | multithreading off 30 | 31 | 4 - 0.000005 32 | 8 - 0.000015 33 | 16 - 0.000053 34 | 32 - 0.000216 35 | 64 - 0.001114 36 | 128 - 0.004175 37 | 256 - 0.018462 38 | 512 - 0.086984 39 | 1024 - 0.404892 40 | 2048 - 1.694361 41 | 4096 - 7.508013 42 | 43 | geom multithreading off 44 | 45 | 4 - 0.000006 46 | 8 - 0.000016 47 | 16 - 0.000050 48 | 32 - 0.000196 49 | 64 - 0.001032 50 | 128 - 0.003914 51 | 256 - 0.018113 52 | 512 - 0.088561 53 | 1024 - 0.371023 54 | 2048 - 1.676831 55 | 4096 - 7.055219 56 | 57 | // geom swipe line 58 | 59 | 4 - 0.000005 60 | 8 - 0.000014 61 | 16 - 0.000050 62 | 32 - 0.000191 63 | 64 - 0.000852 64 | 128 - 0.003730 65 | 256 - 0.017368 66 | 512 - 0.082651 67 | 1024 - 0.379062 68 | 2048 - 1.638863 69 | 4096 - 6.566427 70 | 71 | */ 72 | 73 | // A grid is formed by the intersection of a set of vertical and horizontal lines. 74 | impl LinesNetTest { 75 | pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64, simple_geometry: bool) { // 500 76 | let subj_paths = Util::many_lines_x(20, n); 77 | let clip_paths = Util::many_lines_y(20, n); 78 | 79 | let it_count = ((scale / (n as f64)) as usize).max(1); 80 | let sq_it_count= it_count * it_count; 81 | 82 | let start = Instant::now(); 83 | 84 | if simple_geometry { 85 | // for _ in 0..sq_it_count { 86 | // let _ = Overlay::with_contours(&subj_paths, &clip_paths) 87 | // .overlay_45geom_with_min_area_and_solver(rule, FillRule::NonZero, 0, solver); 88 | // } 89 | } else { 90 | for _ in 0..sq_it_count { 91 | let _ = Overlay::with_contours(&subj_paths, &clip_paths) 92 | .overlay_custom(rule, FillRule::NonZero, solver); 93 | } 94 | } 95 | 96 | let duration = start.elapsed(); 97 | let time = duration.as_secs_f64() / sq_it_count as f64; 98 | 99 | let polygons_count = 2 * n; 100 | 101 | println!("{} - {:.6}", polygons_count, time); 102 | } 103 | } -------------------------------------------------------------------------------- /performance/rust_app/src/test/test_3_spiral.rs: -------------------------------------------------------------------------------- 1 | use std::time::Instant; 2 | use i_overlay::core::fill_rule::FillRule; 3 | use i_overlay::float::simplify::SimplifyShape; 4 | use crate::test::util::Util; 5 | 6 | pub(crate) struct SpiralTest; 7 | 8 | /* 9 | 10 | // 3 11 | // Intersection: 12 | 13 | 14 | multithreading on 15 | 16 | 2 - 0.000002 17 | 4 - 0.000005 18 | 8 - 0.000009 19 | 16 - 0.000020 20 | 32 - 0.000048 21 | 64 - 0.000127 22 | 128 - 0.000305 23 | 256 - 0.000669 24 | 512 - 0.001606 25 | 1024 - 0.003560 26 | 2048 - 0.004930 27 | 4096 - 0.009528 28 | 8192 - 0.018779 29 | 16384 - 0.040263 30 | 32768 - 0.076609 31 | 65536 - 0.181387 32 | 131072 - 0.331046 33 | 262144 - 0.833816 34 | 524288 - 1.472624 35 | 1048576 - 3.232834 36 | 37 | multithreading off 38 | 39 | 2 - 0.000002 40 | 4 - 0.000005 41 | 8 - 0.000009 42 | 16 - 0.000020 43 | 32 - 0.000048 44 | 64 - 0.000129 45 | 128 - 0.000304 46 | 256 - 0.000668 47 | 512 - 0.001599 48 | 1024 - 0.003572 49 | 2048 - 0.005000 50 | 4096 - 0.009576 51 | 8192 - 0.017583 52 | 16384 - 0.040145 53 | 32768 - 0.076642 54 | 65536 - 0.181912 55 | 131072 - 0.343917 56 | 262144 - 0.781770 57 | 524288 - 1.417144 58 | 1048576 - 3.188509 59 | */ 60 | 61 | // Two irregular self-intersecting polygons are generated, the vertices of which are defined by a fixed radius and angle. 62 | impl SpiralTest { 63 | pub(crate) fn run(n: usize, scale: f64) { // 1000 64 | let subj_path = Util::spiral(n, 100.0); 65 | 66 | let it_count = ((scale / (n as f64)) as usize).max(1); 67 | let sq_it_count= it_count * it_count; 68 | 69 | let start = Instant::now(); 70 | 71 | for _ in 0..sq_it_count { 72 | let _ = subj_path.simplify_shape(FillRule::NonZero, Default::default()); 73 | } 74 | 75 | let duration = start.elapsed(); 76 | let time = duration.as_secs_f64() / sq_it_count as f64; 77 | 78 | let polygons_count = n; 79 | 80 | println!("{} - {:.6}", polygons_count, time); 81 | } 82 | } -------------------------------------------------------------------------------- /performance/rust_app/src/test/test_4_windows.rs: -------------------------------------------------------------------------------- 1 | use std::time::Instant; 2 | use i_overlay::core::fill_rule::FillRule; 3 | use i_overlay::core::overlay::Overlay; 4 | use i_overlay::core::overlay_rule::OverlayRule; 5 | use i_overlay::core::solver::Solver; 6 | use i_overlay::i_float::int::point::IntPoint; 7 | use crate::test::util::Util; 8 | 9 | pub(crate) struct WindowsTest; 10 | /* 11 | // 4 12 | // Difference: 13 | 14 | // multithreading on 15 | 8 - 0.000006 16 | 32 - 0.000021 17 | 128 - 0.000097 18 | 512 - 0.000516 19 | 2048 - 0.001548 20 | 8192 - 0.006780 21 | 32768 - 0.034149 22 | 131072 - 0.159685 23 | 524288 - 0.703147 24 | 2097152 - 3.182362 25 | 8388608 - 12.058687 26 | 27 | // multithreading off 28 | 8 - 0.000005 29 | 32 - 0.000021 30 | 128 - 0.000099 31 | 512 - 0.000541 32 | 2048 - 0.001745 33 | 8192 - 0.007748 34 | 32768 - 0.038207 35 | 131072 - 0.190786 36 | 524288 - 0.862933 37 | 2097152 - 3.832362 38 | 8388608 - 15.595299 39 | 40 | geom multithreading off 41 | 42 | 8 - 0.000006 43 | 32 - 0.000021 44 | 128 - 0.000080 45 | 512 - 0.000330 46 | 2048 - 0.001413 47 | 8192 - 0.006229 48 | 32768 - 0.030391 49 | 131072 - 0.146480 50 | 524288 - 0.632447 51 | 2097152 - 2.674580 52 | 8388608 - 11.443802 53 | 54 | */ 55 | 56 | // A grid of square frames, each with a smaller square cutout in the center. 57 | impl WindowsTest { 58 | pub(crate) fn run(n: usize, rule: OverlayRule, solver: Solver, scale: f64, simple_geometry: bool) { // 500 59 | let offset = 30; 60 | let x = (n as i32) * offset / 2; 61 | let origin = IntPoint::new(-x, -x); 62 | let (subj_paths, clip_paths) = Util::many_windows(origin, 20, 10, offset, n); 63 | 64 | let it_count = ((scale / (n as f64)) as usize).max(1); 65 | let sq_it_count= it_count * it_count; 66 | 67 | let start = Instant::now(); 68 | 69 | if simple_geometry { 70 | // for _ in 0..sq_it_count { 71 | // let _ = Overlay::with_contours(&subj_paths, &clip_paths) 72 | // .overlay_45geom_with_min_area_and_solver(rule, FillRule::NonZero, 0, solver); 73 | // } 74 | } else { 75 | for _ in 0..sq_it_count { 76 | let _ = Overlay::with_contours(&subj_paths, &clip_paths) 77 | .overlay_custom(rule, FillRule::NonZero, solver); 78 | } 79 | } 80 | 81 | let duration = start.elapsed(); 82 | let time = duration.as_secs_f64() / sq_it_count as f64; 83 | 84 | let polygons_count = 2 * n * n; 85 | 86 | println!("{} - {:.6}", polygons_count, time); 87 | } 88 | } -------------------------------------------------------------------------------- /readme/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iShape-Rust/iOverlay/b1f3e06f77ab7c3f2beb957130e482667774c666/readme/.DS_Store -------------------------------------------------------------------------------- /readme/overlay_rules.md: -------------------------------------------------------------------------------- 1 | # Overlay Rules 2 | 3 | AB 4 | 5 | ## Union, A or B 6 | Union 7 | 8 | ## Intersection, A and B 9 | Intersection 10 | 11 | ## Difference, A - B 12 | Difference 13 | 14 | ## Inverse Difference, B - A 15 | Inverse Difference 16 | 17 | ## Exclusion, A xor B 18 | Exclusion 19 | --------------------------------------------------------------------------------