jbuckmccready / cavalier_contours Goto Github PK
View Code? Open in Web Editor NEW2D polyline/shape library for offsetting, combining, etc.
License: Apache License 2.0
2D polyline/shape library for offsetting, combining, etc.
License: Apache License 2.0
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.
More benchmarks should be added for the core polyline functions:
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.).
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!
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.
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.
{
"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.
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:
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}
{"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
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
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.
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?
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 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.
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.
i32
error code0
is returned (no error)1
for null input), these specific error codes are documented-1
is returned, these represent an "internal algorithm error" type of bugcavc_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)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).char*
c strings are used for stringscreate
, e.g. cavc_pline_create
._f
, e.g. cavc_pline_f
.get
, e.g. cavc_pline_get_is_closed
.set
, e.g. cavc_pline_set_is_closed
.eval
or find
, e.g. cavc_pline_eval_area
.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.
Currently all overlapping results are kept (purposefully) by the polyline offset function. There could be an option to toggle this behavior to discard them rather than keep them.
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:
*
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:
&
to get reference to values often times for generic functions expecting a &T
not a T
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.
I posted this question at the C++ issue tracker-- I'm not sure whether this or that place is the right place to post. Anyway feel free to close the one that is not relevant.
The question is this: Is there any plan to support variable offsets? Something like NetTopologySuite VariableBuffer. Currently only uniform offset can be done throughout all the vertexes on the polylines.
Hi
Do you have the c# bindings for your rust FFI for Cavalier contours?
Chris
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?
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.
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.
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 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).
It is possible for arc overlap intersection to not be detected but other intersections are, this causes problems in boolean operation between polylines.
Original issue with test cases here:
jbuckmccready/CavalierContours#48
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.