GithubHelp home page GithubHelp logo

jbuckmccready / cavalier_contours Goto Github PK

View Code? Open in Web Editor NEW
130.0 130.0 11.0 505 KB

2D polyline/shape library for offsetting, combining, etc.

License: Apache License 2.0

Rust 100.00%
2d algorithm cad cam computation-geometry geometry

cavalier_contours's People

Contributors

hhemul avatar jbuckmccready avatar nevsden avatar svenlinden avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

cavalier_contours's Issues

Look into different offset join types

Currently segments are always joined with an arc when needed (to maintain constant offset distance from original input).

It's common in graphics libraries to offer other join types, e.g. see Microsoft documentation on the Shape.StrokeLineJoin Property here: https://docs.microsoft.com/en-us/dotnet/api/system.windows.shapes.shape.strokelinejoin?view=net-5.0

It may be possible add a miter and/or bevel join option to the parallel offset algorithm, need to investigate if it conflicts with the way offset slice pruning is performed.

Add characteristic polylines and more benchmarks

More benchmarks should be added for the core polyline functions:

  • Polyline path length
  • Polyline parallel offset
  • Polyline winding number
    These benchmarks can be added to cavalier_contours/benches/benches.rs.

More polylines (capturing different characteristics, e.g. polyline with lots of lines, polyline with lots of arcs, polyline when offset that has many self intersects, polyline when offset that has very little self intersects, etc.) should be added and available for the benchmarks as well. These can be added to cavalier_contours/benches/test_polylines.rs.

As things grow it may make sense to create another crate in the workspace that has all the characteristic polylines we want to add for testing so it may be accessed from the other crates (test_suite, etc.).

Failing test case from the web version

Feel free to close immediately, I didn't search if the bug was already submitted.

I just played with the web version of CavalierContours and found a failing test:

(80.0, 90.0, 0.374794619217547),
(231.5, -278.0, 0.0),
(230.0, 0.0, 1.0),
(320.0, 0.0, -0.5),
(280.0, 0.0, 0.5),
(390.0, 210.0, 0.0),
(280.0, 120.0, 0.5)], 3.0) =>
                    [PlineProperties::new(11, 0.0, 1563.689560505266, 66.99239238371496, -273.0909692941639, 401.41586988912127, 205.22199935960901)]

with mode Offset , Offset 3, Offset count 50, Handle Self Intersects activated.

Thanks for offering this project!

offset vs. boolean op inconsistency?

Given below (blue) polylines I create the offsets (green). In order to eliminate interferences the individual offsets are then combined with a boolean OR. The width of the slot in the center line is identical for the C shape and between the 2 squares.

offset

I would've expected the behaviour to be identical for the two cases, either create a segment through the slot or don't. I realize this an edge case, if the offset is slightly larger or smaller the resulting polylines show matching behaviour. Please let me know if my expectation is wrong or if this is an actual issue.

I've build the FFI library from the current master branch.

Offset failed, result not closed

{
  "isClosed": true, 
  "vertexes": [
    [0, 0, 0], 
    [0, 0, -0.001715931999999954], 
    [1.082450191001044, 50.871299999998655, 0.014190137999999974], 
    [6.351755575999277, 124.39169999999973, 0.13371957999999903], 
    [-15.339867047001007, 182.8941999999996, 0], 
    [-15.339867047000553, -20.589200000000005, 0], 
    [-33.35986704700099, -20.589200000000005, 0], 
    [-33.36040773300101, 183.4379, 0.21579646799999988], 
    [-52.47912682400147, 101.70770000000002, -0.013955579999999999], 
    [-47.65851889200076, -17.30449999999999, -0.0021751230000001134], 
    [-48.34986704700259, -128.5892000000058, -0.00033364200000000156], 
    [-45.9498670480034, -158.58919999999995, 0.08404565599999979], 
    [-55.54986704700127, -198.78920000000002, 0], 
    [-45.949867047001135, -198.7892, 0], 
    [-45.949867047001135, -207.7892, 0], 
    [-2.749867047000407, -207.7892, 0], 
    [-2.749867047000407, -198.7892, 0], 
    [9.029562464999799, -198.7892, 0.0924407319999996], 
    [19.402750884686043, -150.9626747345024, -0.0001571199999999693], 
    [20.47087665042227, -125.32843865163449, -0.00015608899999995686], 
    [22.073016405601493, -87.94516139964372, 0]
  ]
}

The offset algorithm in the WebCAD I implemented also has a similar error.
It is a precision problem that causes a small part of a line to be cropped.

API (and C FFI) for Multi Polyline Offset

Add C FFI wrapper function for the multi polyline offset algorithm (currently in Rust the Shape::parallel_offset method).

The way that the multi polyline offset is implemented requires spatial indexes for all the polylines and the bounding boxes of the polylines which will require a little more involved C FFI wrapper or a more simplistic wrapper which has to rebuild the indexes.

Current questions I'm mulling over:

  • Does it make sense to have a shape type? Maybe these composite algorithms should just stand on their own as free functions? Raises question for how the data can be elegantly passed in.
  • Rust API maybe needs to create traits to represent a shape type (or access to shape data) to avoid having to copy and allocate into an owned Shape type when polylines and indexes are already owned by other user structures?
  • The API in Rust maybe affects how the C FFI is formed?

[Bug]This polyline cannot be offset outside

09-49-57.mp4

This polyline cannot be offset outside


{"vertexes":[[0,0,2.9239855509708432],[422.7001801508977,-2181.678349165927,0.21373279590439873],[1217.850643134374,-1733.2038795014323,4.472986753391631],[961.0754444813574,-155.87051634718955,0.22947158823257166]],"isClosed":true}

Overlapping lines appear in the offset result

{"vertexes":[[0,0,0],[0,-882.3073569599583,0.19403531582953024],[30.99567883971031,-942.6919889602286,0.1940353158226031],[0,-1003.0766209573267,0],[0,-1880,0],[4,-1880,0],[4,-1003.2606547873729,-0.19502429444774674],[34.00003211540752,-945.3840053746535,-0.19502429171847763],[64,-1003.2606547872638,0],[64,-1880,0],[68,-1880,0],[68,-1003.0766209572321,0.1940353134888888],[37.00432115854346,-942.6919889585697,0.194035313489369],[68,-882.3073569597691,0],[68,0,0],[64,0,0],[64,-882.1233231297883,-0.19502431066879408],[33.999968577962136,-939.9999731353018,-0.19502429444816094],[4,-882.1233231296792,0],[4,0,0]],"isClosed":true}

If it can be improved better, it doesn't matter if it can't be improved

Problems with debug asserts

Hello!
I've continued investigations from topic #23

Got several new results.

Input vectors:

{
  "name": "intersected_initial",
  "isClosed": true,
  "Area": 4.155,
  "vertexes": [
    [68.969210062592879, 39.825009999999999, 0.227652821420176],
    [71.447072586049913, 41.014860011263892, 1.000000000000000],
    [70.455927413950079, 41.808919988736108, -0.227652821420176],
    [68.969209937407129, 41.095009999999995, 1.000000000000000]
  ]
}

{
  "name": "result_0",
  "isClosed": true,
  "Area": 11.3863,
  "vertexes": [
    [71.447351802797868, 41.015208780804905, 0.000000000000000],
    [75.004341802797867, 45.461448780804908, 1.000000000000000],
    [74.012638197202122, 46.254811219195098, 0.000000000000000],
    [70.455648197202123, 41.808571219195095, -0.227591152597540],
    [68.969209937407129, 41.095009999999995, 1.000000000000000],
    [68.969210062592879, 39.825009999999999, 0.227652821420176],
    [71.447072586049913, 41.014860011263892, 0.000175893156428]
  ]
}

In debug mode i see assert failure:

thread '<unnamed>' panicked at 'assertion failed: pline.at(0).pos().fuzzy_eq_eps(pline.last().unwrap().pos(), slice_join_eps)', C:\cavalier_contours-master\cavalier_contours\src\polyline\internal\pline_boolean.rs:605:9
stack backtrace:
   0:     0x7ffd5310f20f - std::backtrace_rs::backtrace::dbghelp::trace
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\..\..\backtrace\src\backtrace\dbghelp.rs:98
   1:     0x7ffd5310f20f - std::backtrace_rs::backtrace::trace_unsynchronized
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\..\..\backtrace\src\backtrace\mod.rs:66
   2:     0x7ffd5310f20f - std::sys_common::backtrace::_print_fmt
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\sys_common\backtrace.rs:66
   3:     0x7ffd5310f20f - std::sys_common::backtrace::_print::impl$0::fmt
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\sys_common\backtrace.rs:45
   4:     0x7ffd53121baa - core::fmt::write
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\core\src\fmt\mod.rs:1196
   5:     0x7ffd5310dac9 - std::io::Write::write_fmt<std::sys::windows::stdio::Stderr>
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\io\mod.rs:1654
   6:     0x7ffd53110e5b - std::sys_common::backtrace::_print
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\sys_common\backtrace.rs:48
   7:     0x7ffd53110e5b - std::sys_common::backtrace::print
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\sys_common\backtrace.rs:35
   8:     0x7ffd53110e5b - std::panicking::default_hook::closure$1
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\panicking.rs:295
   9:     0x7ffd53110a4e - std::panicking::default_hook
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\panicking.rs:314
  10:     0x7ffd53111451 - std::panicking::rust_panic_with_hook
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\panicking.rs:698
  11:     0x7ffd531112d2 - std::panicking::begin_panic_handler::closure$0
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\panicking.rs:586
  12:     0x7ffd5310fb17 - std::sys_common::backtrace::__rust_end_short_backtrace<std::panicking::begin_panic_handler::closure_env$0,never$>
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\sys_common\backtrace.rs:138
  13:     0x7ffd53110fe9 - std::panicking::begin_panic_handler
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\panicking.rs:584
  14:     0x7ffd53128225 - core::panicking::panic_fmt
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\core\src\panicking.rs:142
  15:     0x7ffd531280cc - core::panicking::panic
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\core\src\panicking.rs:48
  16:     0x7ffd5308fa01 - cavalier_contours::polyline::internal::pline_boolean::stitch_slices_into_closed_polylines::closure$0<cavalier_contours::polyline::pline::Polyline<f64>,cavalier_contours::polyline::pline::Polyline<f64>,f64,cavalier_contours::polyline::internal::pline_boole
                               at C:\cavalier_contours-master\cavalier_contours\src\polyline\internal\pline_boolean.rs:605
  17:     0x7ffd5308dfd4 - cavalier_contours::polyline::internal::pline_boolean::stitch_slices_into_closed_polylines<cavalier_contours::polyline::pline::Polyline<f64>,cavalier_contours::polyline::pline::Polyline<f64>,f64,cavalier_contours::polyline::internal::pline_boolean::OrAndSt
                               at C:\cavalier_contours-master\cavalier_contours\src\polyline\internal\pline_boolean.rs:693
  18:     0x7ffd53081602 - cavalier_contours::polyline::internal::pline_boolean::polyline_boolean<cavalier_contours::polyline::pline::Polyline<f64>,cavalier_contours::polyline::pline::Polyline<f64>,cavalier_contours::polyline::pline::Polyline<f64>,f64>
                               at C:\cavalier_contours-master\cavalier_contours\src\polyline\internal\pline_boolean.rs:870
  19:     0x7ffd530992ca - cavalier_contours::polyline::traits::PlineSource::boolean_opt<cavalier_contours::polyline::pline::Polyline<f64>,cavalier_contours::polyline::pline::Polyline<f64> >
                               at C:\cavalier_contours-master\cavalier_contours\src\polyline\traits.rs:1465
  20:     0x7ffd53096bbc - cavalier_contours_ffi::cavc_pline_boolean::closure$0
                               at C:\cavalier_contours-master\cavalier_contours_ffi\src\lib.rs:965
  21:     0x7ffd530dd120 - std::panicking::try::do_call<cavalier_contours_ffi::cavc_pline_boolean::closure_env$0,i32>
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3\library\std\src\panicking.rs:492
  22:     0x7ffd530df193 - std::panicking::try::do_catch<cavalier_contours_ffi::cavc_plinelist_pop::closure_env$0,i32>
  23:     0x7ffd530dc68c - std::panicking::try<i32,cavalier_contours_ffi::cavc_pline_boolean::closure_env$0>
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3\library\std\src\panicking.rs:456
  24:     0x7ffd53071d0d - std::panic::catch_unwind<cavalier_contours_ffi::cavc_pline_boolean::closure_env$0,i32>
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3\library\std\src\panic.rs:137
  25:     0x7ffd53101c11 - cavalier_contours_ffi::cavc_pline_boolean
                               at C:\cavalier_contours-master\cavalier_contours_ffi\src\lib.rs:949

C Code:

        cavc_pline_boolean_o combine_options;
        ret = cavc_pline_boolean_o_init(&combine_options);
        if (ret!=0) {qDebug()<<QString("cavc_pline_boolean_o_init returned error status %1").arg(ret);}

        try {
            int32_t ret =
            cavc_pline_boolean( candidate,
                                countur,
                                cavc_booleanOp_And,
                                &combine_options,
                                (const cavc_plinelist**) &pos_list,    /* +1 */
                                (const cavc_plinelist**) &neg_list);   /* +2 */

Assert fails in debug mode only.
Could you please check it.

There are several additional similar troubles. Could you please clarify, do you need input vectors data for this type of issues?
Im asking because i can copy-paste Rust backtrace in one minute, but grabbing faulty vectors takes time.

For example:

thread '<unnamed>' panicked at 'point does not lie on the line defined by p0 to p1 (based on distance)', C:\cavalier_contours-master\cavalier_contours\src\core\math\base_math.rs:272:5
stack backtrace:
   0:     0x7ffd532af20f - std::backtrace_rs::backtrace::dbghelp::trace
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\..\..\backtrace\src\backtrace\dbghelp.rs:98
   1:     0x7ffd532af20f - std::backtrace_rs::backtrace::trace_unsynchronized
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\..\..\backtrace\src\backtrace\mod.rs:66
   2:     0x7ffd532af20f - std::sys_common::backtrace::_print_fmt
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\sys_common\backtrace.rs:66
   3:     0x7ffd532af20f - std::sys_common::backtrace::_print::impl$0::fmt
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\sys_common\backtrace.rs:45
   4:     0x7ffd532c1baa - core::fmt::write
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\core\src\fmt\mod.rs:1196
   5:     0x7ffd532adac9 - std::io::Write::write_fmt<std::sys::windows::stdio::Stderr>
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\io\mod.rs:1654
   6:     0x7ffd532b0e5b - std::sys_common::backtrace::_print
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\sys_common\backtrace.rs:48
   7:     0x7ffd532b0e5b - std::sys_common::backtrace::print
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\sys_common\backtrace.rs:35
   8:     0x7ffd532b0e5b - std::panicking::default_hook::closure$1
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\panicking.rs:295
   9:     0x7ffd532b0a4e - std::panicking::default_hook
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\panicking.rs:314
  10:     0x7ffd532b1451 - std::panicking::rust_panic_with_hook
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\panicking.rs:698
  11:     0x7ffd532b12d2 - std::panicking::begin_panic_handler::closure$0
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\panicking.rs:586
  12:     0x7ffd532afb17 - std::sys_common::backtrace::__rust_end_short_backtrace<std::panicking::begin_panic_handler::closure_env$0,never$>
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\sys_common\backtrace.rs:138
  13:     0x7ffd532b0fe9 - std::panicking::begin_panic_handler
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\std\src\panicking.rs:584
  14:     0x7ffd532c8225 - core::panicking::panic_fmt
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3/library\core\src\panicking.rs:142
  15:     0x7ffd531f6598 - cavalier_contours::core::math::base_math::parametric_from_point<f64>
                               at C:\cavalier_contours-master\cavalier_contours\src\core\math\base_math.rs:272
  16:     0x7ffd531f5a3a - cavalier_contours::core::math::line_circle_intersect::line_circle_intr<f64>
                               at C:\cavalier_contours-master\cavalier_contours\src\core\math\line_circle_intersect.rs:156
  17:     0x7ffd531f0065 - cavalier_contours::polyline::pline_seg_intersect::pline_seg_intr::closure$0<f64>
                               at C:\cavalier_contours-master\cavalier_contours\src\polyline\pline_seg_intersect.rs:147
  18:     0x7ffd531ed80e - cavalier_contours::polyline::pline_seg_intersect::pline_seg_intr<f64>
                               at C:\cavalier_contours-master\cavalier_contours\src\polyline\pline_seg_intersect.rs:241
  19:     0x7ffd532419c9 - cavalier_contours::polyline::internal::pline_intersects::find_intersects::closure$0<cavalier_contours::polyline::pline::Polyline<f64>,cavalier_contours::polyline::pline::Polyline<f64>,f64>
                               at C:\cavalier_contours-master\cavalier_contours\src\polyline\internal\pline_intersects.rs:358
  20:     0x7ffd53245b1d - static_aabb2d_index::core::impl$8::visit<f64,tuple$<>,cavalier_contours::polyline::internal::pline_intersects::find_intersects::closure_env$0<cavalier_contours::polyline::pline::Polyline<f64>,cavalier_contours::polyline::pline::Polyline<f64>,f64> >
                               at C:\Users\KarakurT\.cargo\registry\src\github.com-1ecc6299db9ec823\static_aabb2d_index-1.0.0\src\core.rs:267
  21:     0x7ffd531e8484 - static_aabb2d_index::static_aabb2d_index::StaticAABB2DIndex<f64>::visit_query_with_stack_impl<f64,cavalier_contours::polyline::internal::pline_intersects::find_intersects::closure_env$0<cavalier_contours::polyline::pline::Polyline<f64>,cavalier_contours::
                               at C:\Users\KarakurT\.cargo\registry\src\github.com-1ecc6299db9ec823\static_aabb2d_index-1.0.0\src\static_aabb2d_index.rs:1050
  22:     0x7ffd531e6c61 - static_aabb2d_index::static_aabb2d_index::StaticAABB2DIndex<f64>::visit_query_with_stack<f64,cavalier_contours::polyline::internal::pline_intersects::find_intersects::closure_env$0<cavalier_contours::polyline::pline::Polyline<f64>,cavalier_contours::polyl
                               at C:\Users\KarakurT\.cargo\registry\src\github.com-1ecc6299db9ec823\static_aabb2d_index-1.0.0\src\static_aabb2d_index.rs:1012
  23:     0x7ffd5324116e - cavalier_contours::polyline::internal::pline_intersects::find_intersects<cavalier_contours::polyline::pline::Polyline<f64>,cavalier_contours::polyline::pline::Polyline<f64>,f64>
                               at C:\cavalier_contours-master\cavalier_contours\src\polyline\internal\pline_intersects.rs:402
  24:     0x7ffd53222bdb - cavalier_contours::polyline::internal::pline_boolean::process_for_boolean<cavalier_contours::polyline::pline::Polyline<f64>,cavalier_contours::polyline::pline::Polyline<f64>,f64>
                               at C:\cavalier_contours-master\cavalier_contours\src\polyline\internal\pline_boolean.rs:55
  25:     0x7ffd53220774 - cavalier_contours::polyline::internal::pline_boolean::polyline_boolean<cavalier_contours::polyline::pline::Polyline<f64>,cavalier_contours::polyline::pline::Polyline<f64>,cavalier_contours::polyline::pline::Polyline<f64>,f64>
                               at C:\cavalier_contours-master\cavalier_contours\src\polyline\internal\pline_boolean.rs:744
  26:     0x7ffd532392ca - cavalier_contours::polyline::traits::PlineSource::boolean_opt<cavalier_contours::polyline::pline::Polyline<f64>,cavalier_contours::polyline::pline::Polyline<f64> >
                               at C:\cavalier_contours-master\cavalier_contours\src\polyline\traits.rs:1465
  27:     0x7ffd53236bbc - cavalier_contours_ffi::cavc_pline_boolean::closure$0
                               at C:\cavalier_contours-master\cavalier_contours_ffi\src\lib.rs:965
  28:     0x7ffd5327d120 - std::panicking::try::do_call<cavalier_contours_ffi::cavc_pline_boolean::closure_env$0,i32>
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3\library\std\src\panicking.rs:492
  29:     0x7ffd5327f193 - std::panicking::try::do_catch<cavalier_contours_ffi::cavc_plinelist_pop::closure_env$0,i32>
  30:     0x7ffd5327c68c - std::panicking::try<i32,cavalier_contours_ffi::cavc_pline_boolean::closure_env$0>
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3\library\std\src\panicking.rs:456
  31:     0x7ffd53211d0d - std::panic::catch_unwind<cavalier_contours_ffi::cavc_pline_boolean::closure_env$0,i32>
                               at /rustc/e092d0b6b43f2de967af0887873151bb1c0b18d3\library\std\src\panic.rs:137
  32:     0x7ffd532a1c11 - cavalier_contours_ffi::cavc_pline_boolean
                               at C:\cavalier_contours-master\cavalier_contours_ffi\src\lib.rs:949

Best regards,
Andrei Rodionov

Look into supporting bulge values greater than 1

Currently it's assumed all bulge values are between -1 and 1 (this ensures the chord is always the line between the vertexes). If an arc is greater than a half circle it must be split into two segments.

The different polyline operations need to be reviewed for what the consequences would be to support larger bulge values. Original issue reporting bug: #13.

Hatching a polygon

I am trying to use this library also for hatching a polygon. With polygons without holes it works - for each hatching line I find intersections with a polygon and draw the segments. My initial code can be found at https://github.com/FreemapSlovakia/rustmap/blob/215c1c6c6929f16eef74b8aea5164c7cb6254ab7/src/draw.rs#L38 (disclaimer - it is work in progress and I am very new to Rust).

Unfortunately I don't know how to do it with polygons containing holes. I tried to search for Polygon struct that is capable to hold also holes but was not successful. Could you please help me with this?

Review intersect functions to minimize error and maximize consistency

The main intersect functions line_line_intr, line_circle_intr, circle_circle_intr, and pline_seg_intr all use a fuzzy epsilon value for float comparisons. These functions should be reviewed for improvement in quality (reducing error) and consistency (they should agree in as many cases as possible when given the same epsilon value).

Background:

The epsilon value is used to prevent pathological problems (e.g., when two lines are almost parallel they should just be treated as parallel due to limited precision of floats), and also makes results fuzzy/sticky (e.g., if the end of a line segment very nearly touches a circle it should be treated as an intersect).

This is required for geometric consistency in intersect detection within algorithms. E.g., if we parallel offset a line by x and then by -x and find intersects with the original input we expect the result to be overlapping (even the line may not be exactly the same due to error propagation through floating point arithmetic in the offset operation).

Some recent commits that attempt to patch some issues relating to intersect consistency and errors:
e6a598f
1a40c08
f712458

How should epsilon/tolerance values be organized?

How should epsilon/tolerance values be specified? There can be multiple epsilon values within an algorithm each of which may use different values (e.g. epsilon used or determining if two points overlap may be different than the epsilon used for fuzzy comparing in a distance check).

Currently in C++ they are just hard coded constants but it would be good to have them as part of the API with easy to use sensible defaults.

Currently I think all epsilon values should be explicitly passed to functions and have them be bundled inside of an options struct. The options struct then implements the Default trait with sensible default values, from the consuming side then specific epsilon values (or other option fields) could be changed from the default.

C FFI Design

This is a WIP for outlining the patterns to be followed for creating the C FFI. The content in this issue will be edited and evolve into the actual top level overview documentation for the C API. I don't have a lot of experience with good C APIs, so ideas are welcome.

Error Handling

  • All functions that can error return an i32 error code
  • If the function is successful then 0 is returned (no error)
  • If the function fails in a way that is caught explicitly then a specific positive error code associated with that function is returned (e.g. maybe 1 for null input), these specific error codes are documented
  • All panic unwinds are caught, if an unwind occurred then -1 is returned, these represent an "internal algorithm error" type of bug
  • All functions return values via output parameters as pointers (return value is always reserved for error code, parameters are documented as such)
  • After an error occurs an error message string and error report string are set and stored in thread local storage
  • cavc_last_error_msg and cavc_last_error_report can be used to retrieve the last error message and last error report as char* read only null terminated c strings, due to the thread local storage it must be accessed from the same thread which called the function that had an error (char* pointer will be invalid after next error occurs)
  • The error message is a human readable error message, the error report may hold serialized data with the name of the function and the inputs given for use in reproducing the issue
  • If the error message or error report is not set then a valid null terminated empty c string is returned

Naming and Type Conventions

  • All symbols (structs and functions) start with cavc_ for namespacing.
  • f64 (double in C) is used as the numeric type.
  • u8 (unsigned char in C) is used as a logical boolean (0 is false, otherwise true).
  • Conventional null terminated char* c strings are used for strings
  • Object creation functions end with the word create, e.g. cavc_pline_create.
  • Memory free functions end with _f, e.g. cavc_pline_f.
  • Property/data assessor functions use the word get, e.g. cavc_pline_get_is_closed.
  • Property/data setter functions use th workd set, e.g. cavc_pline_set_is_closed.
  • Computed value functions use eval or find, e.g. cavc_pline_eval_area.

Option Structs

There is a need to support option parameters for more complex functions. There could be an option struct associated with each function that supports optional parameters, for example:
The cavc_pline_eval_offset could have a cavc_pline_eval_offset_o struct as one of the parameters. The cavc_pline_eval_offset_o struct could hold epsilon values and other parameters to be used by the offset algorithm and could look something like this:

pub struct cavc_pline_eval_offset_o
{
    pub pos_equal_eps: f64,
    pub slice_join_eps: f64,
    pub offset_dist_eps: f64,
    pub handle_self_intersects: u8,
}

There could then also be a cavc_pline_eval_offset_o_i function that accepts a *mut cavc_pline_eval_offset_o for initializing sensible defaults.

The issue with this is API stability if we want to add new fields to the option struct in the future. Even if all the functions take the option struct by reference/pointer the functions wont know if a newer field is present or not. And this is really bad considering everything may "seem to work" when in fact the library is reading past the end of the struct memory because the calling side is assuming a different set of fields in the struct.

One solution is to make the option struct opaque and have the construction of the struct and all fields be modified through functions but this has several downsides: a create and free function must be created for the type and memory allocation managed by the caller, all fields must have a get/set function associated with them, and from a performance perspective heap memory allocations and frees must be performed around managing the option parameters (which could be very frequent).

Another solution is to not attempt to make the option structs be forward compatible and just create a new function and new struct type if new options are added (while keeping the old function and struct the same for continued compatibility), e.g. cavc_pline_eval_offset2 and cavc_pline_eval_offset_o2.

Current plan is to follow the pattern of just creating a new function and option struct, leaving the previous version present to not break backward compatibility. However this would all be done after a 1.0 release, before then there may be some breaking changes in iteration to arrive at a 1.0 API and ideally no second version functions are required. It may also be that there is just a 2.0 release if enough changes pile up of this nature.

Finish implementing polyline parallel offset

  • Finish adding functions to handle open polylines and self intersecting polylines with dual offset slicing.
  • Finalize option structure for passing in epsilon values and any other parameters.

Pass by T or &T for small structs implementing Copy?

For small structures that implement Copy such as Vector2 and PlineVertex, functions should be consistent in passing them by T (value) or by &T (reference), which do we choose? Currently they are always passed by value T. Compiled code is likely to be the same with fields being loaded into registers - question is mostly from an ergonomics/consistency consideration.

Advantages of pass by value:

  • Ownership is passed so it is easy to mutably update fields without having to first clone (never bump into ownership issues in general)
  • The structs implement the Copy trait so no calls to clone are required on the calling side
  • Avoid having to dereference using * in many cases where the owned value is required in some context (although may end up having to dereference on the calling side if starting with a reference...)

Disadvantage of pass by value:

  • Inconsistent with generic functions in the std library where everything is pass by reference by default
  • Have to use & to get reference to values often times for generic functions expecting a &T not a T
  • If more exotic larger numeric types are used it may be cheaper to pass by reference (considering the structs implement the Copy trait this may the least of our worries in attempting to support such a scenario...)

corner cases result

Hello!

I've checked some corner cases i guess that result for 2 vectors that share border (not actually invalid, but) could be more precise.

Vectors:

{
  "name": "A",
  "isClosed": true,
  "Area": 142.069,
  "vertexes": [
    [7.5000000000000000000000000, 12.0000000000000000000000000, 0.0000000000000000000000000],
    [52.5000000000000000000000000, 12.0000000000000000000000000, 1.0000000000000000000000000],
    [52.5000000000000000000000000, 15.0000000000000000000000000, 0.0000000000000000000000000],
    [7.5000000000000000000000000, 15.0000000000000000000000000, 1.0000000000000000000000000]
  ]
}
{
  "name": "B",
  "isClosed": true,
  "Area": 142.069,
  "vertexes": [
    [7.5000000000000000000000000, 9.0000000000000000000000000, 0.0000000000000000000000000],
    [52.5000000000000000000000000, 9.0000000000000000000000000, 1.0000000000000000000000000],
    [52.5000000000000000000000000, 12.0000000000000000000000000, 0.0000000000000000000000000],
    [7.5000000000000000000000000, 12.0000000000000000000000000, 1.0000000000000000000000000]
  ]
}

Test code:

(
    pline_closed![(7.5, 12.0, 0.0),
(52.5, 12.0, 1.0),
(52.5, 15.0, 0.0),
(7.5, 15.0, 1.0)],
    pline_closed![(7.5, 9.0, 0.0),
(52.5, 9.0, 1.0),
(52.5, 12.0, 0.0),
(7.5, 12.0, 1.0)]
)
=>
[
    (BooleanOp::Or, &[PlineProperties::new(6, 284.137166941154, 108.84955592153875, 6.0, 9.0, 54.0, 15.0), PlineProperties::new(2, 0.0, 90.0, 7.5, 12.0, 52.5, 12.0)], &[]),
    (BooleanOp::And, &[PlineProperties::new(2, 0.0, 90.0, 7.5, 12.0, 52.5, 12.0)], &[]),
    (BooleanOp::Not, &[PlineProperties::new(4, -142.06858347057704, 99.42477796076938, 6.0, 12.0, 54.0, 15.0)], &[]),
    (BooleanOp::Xor, &[PlineProperties::new(4, -142.06858347057704, 99.42477796076938, 6.0, 12.0, 54.0, 15.0), PlineProperties::new(4, 142.06858347057704, 99.42477796076938, 6.0, 9.0, 54.0, 12.0)], &[])
]

I would say it is valid result, but in some cases that not what user would expect.

Best regards,
Andrei.

Incorrect combining result (using C)

Hello!
Ive created this test vector:

"Vector, nodes 4, closed=1"
"    Node 0 : X=155.575   Y=113.665   B=0"
"    Node 1 : X=161.29   Y=113.665   B=1"
"    Node 2 : X=161.29   Y=114.935   B=0"
"    Node 3 : X=155.575   Y=114.935   B=1"
"Vector, nodes 4, closed=1"
"    Node 0 : X=161.29   Y=113.665   B=0"
"    Node 1 : X=167.64   Y=113.665   B=1"
"    Node 2 : X=167.64   Y=114.935   B=0"
"    Node 3 : X=161.29   Y=114.935   B=1"

Basically it is 2 horisontal lines: second one starts at same point, where first ends (X=161.290 Y=114.300)

I use this C function:

cavc_pline_boolean(scene[countur_in_scene_num], countur2, cavc_booleanOp_And, combine_options, &pos_list, &neg_list);

Could you please take a look on it?

C FFI: Shouldn't counts be size_t?

I'm very much NOT a Rust programmer - so, if for some reason, it's boneheaded, please enlighten me.

I noticed that the C FFI functions that take or return either the count of a vector or index of an item are declared as 32 bit integers, and there's explicit overflow checking for a 32 to 64 bit conversion.

Wouldn't it be better to use size_t and its Rust counterpart usize instead? This would eliminate the "impedance mismatch", to borrow a term from an unrelated field.

My particular application shouldn't ever need more than 4 billion-sum-odd vertexes, but wouldn't it be better in principle to support the capability?

If you agree, I'll change the types in the pull request I send for adding the Shape functionality to the C FFI.

Cut not worked

Here is the test code, I want to cut the polygon to make a rounded corner, although it doesn't fail, but the result is not correct.

(
    pline_closed![(233.5, 0.0, 0.0),
(222.0, 233.5, 0.0),
(-233.5, 233.5, 0.0),
(-233.5, 0.0, 0.0)],
    pline_closed![(222.0, 233.5, 0.0),
(232.934, 11.486, 0.4),
(-0.283, 233.5, 0.0)]
)
=>
[
    (BooleanOp::Or, &[PlineProperties::new(4, -107701.875, 1389.7830190582713, -233.5, 0.0, 233.5, 233.5)], &[]),
    (BooleanOp::And, &[PlineProperties::new(2, 0.0, 444.566, -0.283, 233.5, 222.0, 233.5)], &[]),
    (BooleanOp::Not, &[], &[]),
    (BooleanOp::Xor, &[], &[])
]

The point (222.0, 233.5, 0.0) is intend, it's not that rectangle.

004a5b09ef3fcafba04d0f589ee28ac c58ff5a7a321a3499f8ce9431380e32

Add more test coverage to pline_seg module

Currently all functions under cavalier_contours/polyline/pline_seg.rs module only have basic doc example tests which do not cover many edge cases. A test_pline_seg.rs module should be added to the tests directory (similar to the other test integration modules) with tests that cover more thoroughly each of the functions and edge cases that can arise.

Create abstracted Shape type

Create a Shape type which holds an indexed graph of closed polylines which represent positive and negative space. Then operations can be applied to the shape type similar to how they are done for single polylines for offsetting, boolean operations, area, etc.

There may be a way to generalize it to also support open polylines (not yet determined if feasible and how that may affect the algorithmic operations).

Improve intersect functions documentation

The functions in circle_circle_intersect.rs, line_circle_intersect.rs, line_line_intersect.rs, under cavalier_contours/core/math and cavalier_contours/polyline/pline_seg_intersect.rs need code examples added to their documentation. E.g. circle_circle_intr just has /// Finds the intersects between two circles.

Add more documentation about their parameters, and add a basic code example similar to what is done for the functions in the cavalier_contours/polyline/pline_seg.rs module.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.