1use std::{
2 ffi::{c_void, CString},
3 mem::MaybeUninit,
4 os::raw::c_char,
5 ptr::NonNull,
6};
7
8use num_traits::{FromPrimitive, ToPrimitive};
9use sfcgal_sys::{
10 initialize, sfcgal_alloc_handler_t, sfcgal_error_handler_t, sfcgal_free_handler_t,
11 sfcgal_geometry_alpha_shapes, sfcgal_geometry_approximate_medial_axis, sfcgal_geometry_area,
12 sfcgal_geometry_area_3d, sfcgal_geometry_as_text, sfcgal_geometry_as_text_decim,
13 sfcgal_geometry_clone, sfcgal_geometry_collection_add_geometry,
14 sfcgal_geometry_collection_create, sfcgal_geometry_collection_geometry_n,
15 sfcgal_geometry_collection_num_geometries, sfcgal_geometry_convexhull,
16 sfcgal_geometry_convexhull_3d, sfcgal_geometry_covers, sfcgal_geometry_covers_3d,
17 sfcgal_geometry_delete, sfcgal_geometry_difference, sfcgal_geometry_difference_3d,
18 sfcgal_geometry_distance, sfcgal_geometry_distance_3d, sfcgal_geometry_extrude,
19 sfcgal_geometry_extrude_polygon_straight_skeleton, sfcgal_geometry_extrude_straight_skeleton,
20 sfcgal_geometry_intersection, sfcgal_geometry_intersection_3d, sfcgal_geometry_intersects,
21 sfcgal_geometry_intersects_3d, sfcgal_geometry_is_3d, sfcgal_geometry_is_empty,
22 sfcgal_geometry_is_measured, sfcgal_geometry_is_planar, sfcgal_geometry_is_valid,
23 sfcgal_geometry_is_valid_detail, sfcgal_geometry_line_sub_string,
24 sfcgal_geometry_minkowski_sum, sfcgal_geometry_offset_polygon,
25 sfcgal_geometry_optimal_alpha_shapes, sfcgal_geometry_orientation,
26 sfcgal_geometry_straight_skeleton, sfcgal_geometry_straight_skeleton_distance_in_m,
27 sfcgal_geometry_t, sfcgal_geometry_tesselate, sfcgal_geometry_triangulate_2dz,
28 sfcgal_geometry_type_id, sfcgal_geometry_union, sfcgal_geometry_union_3d,
29 sfcgal_geometry_volume, sfcgal_io_read_wkt, sfcgal_multi_linestring_create,
30 sfcgal_multi_point_create, sfcgal_multi_polygon_create, sfcgal_prepared_geometry_t, srid_t,
31};
32use sfcgal_sys::{
33 sfcgal_approx_convex_partition_2, sfcgal_full_version, sfcgal_geometry_as_hexwkb,
35 sfcgal_geometry_as_obj, sfcgal_geometry_as_obj_file, sfcgal_geometry_as_vtk,
36 sfcgal_geometry_as_vtk_file, sfcgal_geometry_as_wkb, sfcgal_geometry_buffer3d,
37 sfcgal_geometry_force_lhr, sfcgal_geometry_force_rhr, sfcgal_geometry_force_valid,
38 sfcgal_geometry_has_validity_flag, sfcgal_geometry_make_solid, sfcgal_geometry_rotate,
39 sfcgal_geometry_rotate_2d, sfcgal_geometry_rotate_3d, sfcgal_geometry_rotate_3d_around_center,
40 sfcgal_geometry_rotate_x, sfcgal_geometry_rotate_y, sfcgal_geometry_rotate_z,
41 sfcgal_geometry_round, sfcgal_geometry_scale, sfcgal_geometry_scale_3d,
42 sfcgal_geometry_scale_3d_around_center, sfcgal_geometry_straight_skeleton_partition,
43 sfcgal_geometry_translate_2d, sfcgal_geometry_translate_3d, sfcgal_geometry_visibility_point,
44 sfcgal_geometry_visibility_segment, sfcgal_greene_approx_convex_partition_2, sfcgal_init,
45 sfcgal_io_read_binary_prepared, sfcgal_io_read_ewkt, sfcgal_io_read_wkb,
46 sfcgal_io_write_binary_prepared, sfcgal_linestring_add_point, sfcgal_linestring_create,
47 sfcgal_linestring_num_points, sfcgal_linestring_point_n, sfcgal_multi_solid_create,
48 sfcgal_optimal_convex_partition_2, sfcgal_point_create, sfcgal_point_create_from_xy,
49 sfcgal_point_create_from_xym, sfcgal_point_create_from_xyz, sfcgal_point_create_from_xyzm,
50 sfcgal_point_m, sfcgal_point_x, sfcgal_point_y, sfcgal_point_z,
51 sfcgal_polygon_add_interior_ring, sfcgal_polygon_create,
52 sfcgal_polygon_create_from_exterior_ring, sfcgal_polygon_exterior_ring,
53 sfcgal_polygon_interior_ring_n, sfcgal_polygon_num_interior_rings,
54 sfcgal_polyhedral_surface_add_polygon, sfcgal_polyhedral_surface_create,
55 sfcgal_polyhedral_surface_num_polygons, sfcgal_polyhedral_surface_polygon_n,
56 sfcgal_prepared_geometry_as_ewkt, sfcgal_prepared_geometry_create,
57 sfcgal_prepared_geometry_create_from_geometry, sfcgal_prepared_geometry_delete,
58 sfcgal_prepared_geometry_geometry, sfcgal_prepared_geometry_set_geometry,
59 sfcgal_prepared_geometry_set_srid, sfcgal_prepared_geometry_srid, sfcgal_set_alloc_handlers,
60 sfcgal_set_error_handlers, sfcgal_set_geometry_validation, sfcgal_solid_add_interior_shell,
61 sfcgal_solid_create, sfcgal_solid_create_from_exterior_shell, sfcgal_solid_num_shells,
62 sfcgal_solid_shell_n, sfcgal_triangle_create, sfcgal_triangle_create_from_points,
63 sfcgal_triangle_set_vertex, sfcgal_triangle_set_vertex_from_xy,
64 sfcgal_triangle_set_vertex_from_xyz, sfcgal_triangle_vertex,
65 sfcgal_triangulated_surface_add_triangle, sfcgal_triangulated_surface_create,
66 sfcgal_triangulated_surface_num_triangles, sfcgal_triangulated_surface_triangle_n,
67 sfcgal_version, sfcgal_y_monotone_partition_2,
68};
69
70use crate::{
71 conversion::{CoordSeq, CoordType, ToSFCGALGeom},
72 errors::get_last_error,
73 utils::{
74 _c_string_with_size, _string, check_computed_value, check_nan_value,
75 check_null_prepared_geom, check_predicate, get_raw_bytes,
76 },
77 Result, ToSFCGAL,
78};
79
80#[repr(C)]
82#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Primitive)]
83pub enum BufferType {
84 Round = 0,
85 CylSphere = 1,
86 Flat = 2,
87}
88
89#[repr(C)]
93#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Primitive)]
94pub enum GeomType {
95 Point = 1,
96 Linestring = 2,
97 Polygon = 3,
98 Multipoint = 4,
99 Multilinestring = 5,
100 Multipolygon = 6,
101 Geometrycollection = 7,
102 Polyhedralsurface = 15,
103 Triangulatedsurface = 16,
104 Triangle = 17,
105 Solid = 101,
106 Multisolid = 102,
107}
108
109impl GeomType {
110 fn is_collection_type(&self) -> bool {
111 matches!(
112 &self,
113 GeomType::Multipoint
114 | GeomType::Multilinestring
115 | GeomType::Multipolygon
116 | GeomType::Multisolid
117 | GeomType::Geometrycollection
118 )
119 }
120}
121
122#[derive(PartialEq, Eq, Debug, Primitive)]
124pub enum Orientation {
125 CounterClockWise = -1isize,
126 ClockWise = 1isize,
127 Undetermined = 0isize,
128}
129
130macro_rules! precondition_match_type {
131 ($value : expr, $input_type: expr) => {{
132 if $value._type()? != $input_type {
133 bail!("Wrong input Geometry type")
134 }
135 }};
136}
137
138macro_rules! precondition_match_type_other {
139 ($value : expr, $input_type: expr) => {{
140 if $value._type()? != $input_type {
141 bail!("Wrong input Geometry type for other")
142 }
143 }};
144}
145
146macro_rules! precondition_match_validity {
147 ($value : expr) => {{
148 if !$value.is_valid()? {
149 bail!("Input geometry is not valid")
150 }
151 }};
152}
153
154macro_rules! precondition_match_validity_other {
155 ($value : expr) => {{
156 if !$value.is_valid()? {
157 bail!("Input geometry is not valid for other")
158 }
159 }};
160}
161
162macro_rules! precondition_match_not_empty {
163 ($value : expr) => {{
164 if $value.is_empty()? {
165 bail!("Input geometry is empty")
166 }
167 }};
168}
169
170macro_rules! precondition_index_in_range {
171 ($index: expr, $range: expr) => {{
172 if !$range.contains(&$index) {
173 bail!("Index not in the expected range")
174 }
175 }};
176}
177
178macro_rules! precondition_index_in_result_value {
179 ($index: expr, $range_result: expr) => {{
180 match $range_result {
181 Ok(max_value) => {
182 if !(0..max_value).contains(&$index) {
183 bail!("Index not in the expected range")
184 }
185 }
186 Err(err) => {
187 bail!(err)
188 }
189 }
190 }};
191}
192
193#[repr(C)]
195pub struct SFCGeometry {
196 pub(crate) c_geom: NonNull<sfcgal_geometry_t>,
197 pub(crate) owned: bool,
198}
199
200impl Drop for SFCGeometry {
201 fn drop(&mut self) {
202 if self.owned {
203 unsafe { sfcgal_geometry_delete(self.c_geom.as_mut()) }
204 }
205 }
206}
207
208impl Clone for SFCGeometry {
209 fn clone(&self) -> SFCGeometry {
210 SFCGeometry {
211 c_geom: NonNull::new(unsafe { sfcgal_geometry_clone(self.c_geom.as_ref()) }).unwrap(),
212 owned: true,
213 }
214 }
215}
216
217impl std::fmt::Debug for SFCGeometry {
218 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
219 write!(f, "{}", self.to_wkt_decim(8).unwrap())
220 }
221}
222
223impl SFCGeometry {
224 pub fn new(wkt: &str) -> Result<SFCGeometry> {
236 initialize();
237
238 let c_str = CString::new(wkt)?;
239
240 let obj = unsafe { sfcgal_io_read_wkt(c_str.as_ptr(), wkt.len()) };
241
242 unsafe { SFCGeometry::new_from_raw(obj, true) }
243 }
244
245 pub(crate) fn _init() {
246 unsafe {
249 sfcgal_init();
250 }
251 }
252
253 pub(crate) unsafe fn new_from_raw(
254 g: *mut sfcgal_geometry_t,
255 owned: bool,
256 ) -> Result<SFCGeometry> {
257 Ok(SFCGeometry {
258 owned,
259 c_geom: NonNull::new(g).ok_or_else(|| {
260 format_err!(
261 "Obtained null pointer when creating geometry: {}",
262 get_last_error()
263 )
264 })?,
265 })
266 }
267
268 pub fn new_from_coordinates<T>(coords: &CoordSeq<T>) -> Result<SFCGeometry>
269 where
270 T: ToSFCGALGeom + CoordType,
271 {
272 coords.to_sfcgal()
273 }
274
275 pub fn to_wkt(&self) -> Result<String> {
278 let mut ptr = MaybeUninit::<*mut c_char>::uninit();
279
280 let mut length: usize = 0;
281
282 unsafe {
283 sfcgal_geometry_as_text(self.c_geom.as_ref(), ptr.as_mut_ptr(), &mut length);
284
285 Ok(_c_string_with_size(ptr.assume_init(), length))
286 }
287 }
288
289 pub fn to_wkt_decim(&self, nb_decim: i32) -> Result<String> {
293 let mut ptr = MaybeUninit::<*mut c_char>::uninit();
294
295 let mut length: usize = 0;
296
297 unsafe {
298 sfcgal_geometry_as_text_decim(
299 self.c_geom.as_ref(),
300 nb_decim,
301 ptr.as_mut_ptr(),
302 &mut length,
303 );
304
305 Ok(_c_string_with_size(ptr.assume_init(), length))
306 }
307 }
308
309 pub fn to_wkb_in_memory(&self) -> Result<Vec<u8>> {
311 let mut ptr = MaybeUninit::<*mut c_char>::uninit();
312
313 let mut length: usize = 0;
314
315 unsafe {
316 sfcgal_geometry_as_wkb(self.c_geom.as_ref(), ptr.as_mut_ptr(), &mut length);
317
318 Ok(get_raw_bytes(ptr.assume_init(), length))
319 }
320 }
321
322 pub unsafe fn to_ewkt_in_memory(
325 prepared: *const sfcgal_prepared_geometry_t,
326 num_decimals: i32,
327 ) -> Result<Vec<u8>> {
328 let mut ptr = MaybeUninit::<*mut c_char>::uninit();
329
330 let mut length: usize = 0;
331
332 unsafe {
333 sfcgal_prepared_geometry_as_ewkt(prepared, num_decimals, ptr.as_mut_ptr(), &mut length);
334
335 Ok(get_raw_bytes(ptr.assume_init(), length))
336 }
337 }
338
339 pub fn to_hexwkb_in_memory(&self) -> Result<Vec<u8>> {
341 let mut ptr = MaybeUninit::<*mut c_char>::uninit();
342
343 let mut length: usize = 0;
344
345 unsafe {
346 sfcgal_geometry_as_hexwkb(self.c_geom.as_ref(), ptr.as_mut_ptr(), &mut length);
347
348 Ok(get_raw_bytes(ptr.assume_init(), length))
349 }
350 }
351
352 pub fn io_read_binary_prepared(data: &[u8]) -> Result<*mut sfcgal_prepared_geometry_t> {
354 unsafe {
355 let ptr = data.as_ptr() as *const i8;
356
357 let length = data.len();
358
359 let result = sfcgal_io_read_binary_prepared(ptr, length);
360
361 check_null_prepared_geom(result)?;
362
363 Ok(result)
364 }
365 }
366
367 pub fn io_read_wkb(data: &[u8]) -> Result<*mut sfcgal_prepared_geometry_t> {
369 unsafe {
370 let ptr = data.as_ptr() as *const i8;
371
372 let length = data.len();
373
374 let result = sfcgal_io_read_wkb(ptr, length);
375
376 check_null_prepared_geom(result)?;
377
378 Ok(result)
379 }
380 }
381
382 pub fn io_read_ewkt(data: &[u8]) -> Result<*mut sfcgal_prepared_geometry_t> {
384 unsafe {
385 let ptr = data.as_ptr() as *const i8;
386
387 let length = data.len();
388
389 let result = sfcgal_io_read_ewkt(ptr, length);
390
391 check_null_prepared_geom(result)?;
392
393 Ok(result)
394 }
395 }
396
397 pub unsafe fn io_write_binary_prepared(
400 prepared_geometry: *mut sfcgal_prepared_geometry_t,
401 ) -> Result<Vec<u8>> {
402 unsafe {
403 let mut ptr = MaybeUninit::<*mut c_char>::uninit();
404
405 let mut length: usize = 0;
406
407 sfcgal_io_write_binary_prepared(prepared_geometry, ptr.as_mut_ptr(), &mut length);
408
409 Ok(get_raw_bytes(ptr.assume_init(), length))
410 }
411 }
412
413 pub fn to_obj_file(&self, filename: &str) -> Result<()> {
415 unsafe {
416 let c_string = CString::new(filename)?;
417
418 let raw: *mut c_char = c_string.into_raw();
419
420 sfcgal_geometry_as_obj_file(self.c_geom.as_ptr(), raw);
421 };
422
423 Ok(())
424 }
425
426 pub fn to_obj_in_memory(&self) -> Result<Vec<u8>> {
428 let mut ptr = MaybeUninit::<*mut c_char>::uninit();
429
430 let mut length: usize = 0;
431
432 unsafe {
433 sfcgal_geometry_as_obj(self.c_geom.as_ref(), ptr.as_mut_ptr(), &mut length);
434
435 Ok(get_raw_bytes(ptr.assume_init(), length))
436 }
437 }
438
439 pub fn to_vtk_file(&self, filename: &str) -> Result<()> {
441 unsafe {
442 let c_string = CString::new(filename)?;
443
444 let raw: *mut c_char = c_string.into_raw();
445
446 sfcgal_geometry_as_vtk_file(self.c_geom.as_ptr(), raw);
447 };
448
449 Ok(())
450 }
451
452 pub fn to_vtk_in_memory(&self) -> Result<Vec<u8>> {
454 let mut ptr = MaybeUninit::<*mut c_char>::uninit();
455
456 let mut length: usize = 0;
457
458 unsafe {
459 sfcgal_geometry_as_vtk(self.c_geom.as_ref(), ptr.as_mut_ptr(), &mut length);
460
461 Ok(get_raw_bytes(ptr.assume_init(), length))
462 }
463 }
464
465 pub fn is_empty(&self) -> Result<bool> {
467 let rv = unsafe { sfcgal_geometry_is_empty(self.c_geom.as_ptr()) };
468
469 check_predicate(rv)
470 }
471
472 pub fn is_valid(&self) -> Result<bool> {
474 let rv = unsafe { sfcgal_geometry_is_valid(self.c_geom.as_ptr()) };
475
476 check_predicate(rv)
477 }
478
479 pub fn is_measured(&self) -> Result<bool> {
481 let rv = unsafe { sfcgal_geometry_is_measured(self.c_geom.as_ptr()) };
482
483 check_predicate(rv)
484 }
485
486 pub fn is_planar(&self) -> Result<bool> {
488 precondition_match_validity!(self);
489
490 let rv = unsafe { sfcgal_geometry_is_planar(self.c_geom.as_ptr()) };
491
492 check_predicate(rv)
493 }
494
495 pub fn is_3d(&self) -> Result<bool> {
497 let rv = unsafe { sfcgal_geometry_is_3d(self.c_geom.as_ptr()) };
498
499 check_predicate(rv)
500 }
501
502 pub fn validity_detail(&self) -> Result<Option<String>> {
504 let mut ptr = MaybeUninit::<*mut c_char>::uninit();
505
506 unsafe {
507 let rv = sfcgal_geometry_is_valid_detail(
508 self.c_geom.as_ptr(),
509 ptr.as_mut_ptr(),
510 std::ptr::null::<sfcgal_geometry_t>() as *mut *mut sfcgal_geometry_t,
511 );
512
513 match rv {
514 1 => Ok(None),
515 0 => Ok(Some(_string(ptr.assume_init()))),
516 _ => Err(format_err!("SFCGAL error: {}", get_last_error())),
517 }
518 }
519 }
520
521 pub fn _type(&self) -> Result<GeomType> {
523 let type_geom = unsafe { sfcgal_geometry_type_id(self.c_geom.as_ptr()) };
524
525 GeomType::from_u32(type_geom)
526 .ok_or_else(|| format_err!("Unknown geometry type (val={})", type_geom))
527 }
528
529 pub fn distance(&self, other: &SFCGeometry) -> Result<f64> {
531 precondition_match_validity!(self);
532
533 precondition_match_validity_other!(other);
534
535 let distance =
536 unsafe { sfcgal_geometry_distance(self.c_geom.as_ptr(), other.c_geom.as_ptr()) };
537
538 check_computed_value(distance)
539 }
540
541 pub fn distance_3d(&self, other: &SFCGeometry) -> Result<f64> {
543 precondition_match_validity!(self);
544
545 precondition_match_validity_other!(other);
546
547 let distance =
548 unsafe { sfcgal_geometry_distance_3d(self.c_geom.as_ptr(), other.c_geom.as_ptr()) };
549
550 check_computed_value(distance)
551 }
552
553 pub fn area(&self) -> Result<f64> {
555 precondition_match_validity!(self);
556
557 let area = unsafe { sfcgal_geometry_area(self.c_geom.as_ptr()) };
558
559 check_computed_value(area)
560 }
561
562 pub fn area_3d(&self) -> Result<f64> {
564 precondition_match_validity!(self);
565
566 let area = unsafe { sfcgal_geometry_area_3d(self.c_geom.as_ptr()) };
567
568 check_computed_value(area)
569 }
570
571 pub fn volume(&self) -> Result<f64> {
573 precondition_match_validity!(self);
574
575 let volume = unsafe { sfcgal_geometry_volume(self.c_geom.as_ptr()) };
576
577 check_computed_value(volume)
578 }
579
580 pub fn orientation(&self) -> Result<Orientation> {
583 precondition_match_type!(self, GeomType::Polygon);
584
585 precondition_match_validity!(self);
586
587 let orientation = unsafe { sfcgal_geometry_orientation(self.c_geom.as_ptr()) };
588
589 Orientation::from_i32(orientation)
590 .ok_or_else(|| format_err!("Error while retrieving orientation (val={})", orientation))
591 }
592
593 pub fn intersects(&self, other: &SFCGeometry) -> Result<bool> {
595 precondition_match_validity!(self);
596
597 precondition_match_validity_other!(other);
598
599 let rv = unsafe { sfcgal_geometry_intersects(self.c_geom.as_ptr(), other.c_geom.as_ptr()) };
600
601 check_predicate(rv)
602 }
603
604 pub fn intersects_3d(&self, other: &SFCGeometry) -> Result<bool> {
606 precondition_match_validity!(self);
607
608 precondition_match_validity_other!(other);
609
610 let rv =
611 unsafe { sfcgal_geometry_intersects_3d(self.c_geom.as_ptr(), other.c_geom.as_ptr()) };
612
613 check_predicate(rv)
614 }
615
616 pub fn intersection(&self, other: &SFCGeometry) -> Result<SFCGeometry> {
619 precondition_match_validity!(self);
620
621 precondition_match_validity_other!(other);
622
623 let result =
624 unsafe { sfcgal_geometry_intersection(self.c_geom.as_ptr(), other.c_geom.as_ptr()) };
625
626 unsafe { SFCGeometry::new_from_raw(result, true) }
627 }
628
629 pub fn intersection_3d(&self, other: &SFCGeometry) -> Result<SFCGeometry> {
632 precondition_match_validity!(self);
633
634 precondition_match_validity_other!(other);
635
636 let result =
637 unsafe { sfcgal_geometry_intersection_3d(self.c_geom.as_ptr(), other.c_geom.as_ptr()) };
638
639 unsafe { SFCGeometry::new_from_raw(result, true) }
640 }
641
642 pub fn covers(&self, other: &SFCGeometry) -> Result<bool> {
644 precondition_match_validity!(self);
645
646 precondition_match_validity_other!(other);
647
648 let rv = unsafe { sfcgal_geometry_covers(self.c_geom.as_ptr(), other.c_geom.as_ptr()) };
649
650 check_predicate(rv)
651 }
652
653 pub fn covers_3d(&self, other: &SFCGeometry) -> Result<bool> {
655 precondition_match_validity!(self);
656
657 precondition_match_validity_other!(other);
658
659 let rv = unsafe { sfcgal_geometry_covers_3d(self.c_geom.as_ptr(), other.c_geom.as_ptr()) };
660
661 check_predicate(rv)
662 }
663
664 pub fn difference(&self, other: &SFCGeometry) -> Result<SFCGeometry> {
667 precondition_match_validity!(self);
668
669 precondition_match_validity_other!(other);
670
671 let result =
672 unsafe { sfcgal_geometry_difference(self.c_geom.as_ptr(), other.c_geom.as_ptr()) };
673
674 unsafe { SFCGeometry::new_from_raw(result, true) }
675 }
676
677 pub fn difference_3d(&self, other: &SFCGeometry) -> Result<SFCGeometry> {
680 precondition_match_validity!(self);
681
682 precondition_match_validity_other!(other);
683
684 let result =
685 unsafe { sfcgal_geometry_difference_3d(self.c_geom.as_ptr(), other.c_geom.as_ptr()) };
686
687 unsafe { SFCGeometry::new_from_raw(result, true) }
688 }
689
690 pub fn union(&self, other: &SFCGeometry) -> Result<SFCGeometry> {
693 precondition_match_validity!(self);
694
695 precondition_match_validity_other!(other);
696
697 let result = unsafe { sfcgal_geometry_union(self.c_geom.as_ptr(), other.c_geom.as_ptr()) };
698
699 unsafe { SFCGeometry::new_from_raw(result, true) }
700 }
701
702 pub fn union_3d(&self, other: &SFCGeometry) -> Result<SFCGeometry> {
705 precondition_match_validity!(self);
706
707 precondition_match_validity_other!(other);
708
709 let result =
710 unsafe { sfcgal_geometry_union_3d(self.c_geom.as_ptr(), other.c_geom.as_ptr()) };
711
712 unsafe { SFCGeometry::new_from_raw(result, true) }
713 }
714
715 pub fn minkowski_sum(&self, other: &SFCGeometry) -> Result<SFCGeometry> {
718 precondition_match_validity!(self);
719
720 precondition_match_validity_other!(other);
721
722 let result =
723 unsafe { sfcgal_geometry_minkowski_sum(self.c_geom.as_ptr(), other.c_geom.as_ptr()) };
724
725 unsafe { SFCGeometry::new_from_raw(result, true) }
726 }
727
728 pub fn straight_skeleton(&self) -> Result<SFCGeometry> {
730 precondition_match_validity!(self);
731
732 let result = unsafe { sfcgal_geometry_straight_skeleton(self.c_geom.as_ptr()) };
733
734 unsafe { SFCGeometry::new_from_raw(result, true) }
735 }
736
737 pub fn straight_skeleton_distance_in_m(&self) -> Result<SFCGeometry> {
740 precondition_match_validity!(self);
741
742 let result =
743 unsafe { sfcgal_geometry_straight_skeleton_distance_in_m(self.c_geom.as_ptr()) };
744
745 unsafe { SFCGeometry::new_from_raw(result, true) }
746 }
747
748 pub fn extrude_straight_skeleton(&self, height: f64) -> Result<SFCGeometry> {
750 precondition_match_type!(self, GeomType::Polygon);
751
752 precondition_match_validity!(self);
753
754 if height == 0. {
755 bail!("Height cannot be 0.");
756 }
757
758 let result =
759 unsafe { sfcgal_geometry_extrude_straight_skeleton(self.c_geom.as_ptr(), height) };
760
761 unsafe { SFCGeometry::new_from_raw(result, true) }
762 }
763
764 pub fn extrude_polygon_straight_skeleton(
768 &self,
769 building_height: f64,
770 roof_height: f64,
771 ) -> Result<SFCGeometry> {
772 precondition_match_type!(self, GeomType::Polygon);
773
774 precondition_match_validity!(self);
775
776 let result = unsafe {
777 sfcgal_geometry_extrude_polygon_straight_skeleton(
778 self.c_geom.as_ptr(),
779 building_height,
780 roof_height,
781 )
782 };
783
784 unsafe { SFCGeometry::new_from_raw(result, true) }
785 }
786
787 pub fn approximate_medial_axis(&self) -> Result<SFCGeometry> {
790 precondition_match_validity!(self);
791
792 let result = unsafe { sfcgal_geometry_approximate_medial_axis(self.c_geom.as_ptr()) };
793
794 unsafe { SFCGeometry::new_from_raw(result, true) }
795 }
796
797 pub fn offset_polygon(&self, radius: f64) -> Result<SFCGeometry> {
799 precondition_match_validity!(self);
800
801 let result = unsafe { sfcgal_geometry_offset_polygon(self.c_geom.as_ptr(), radius) };
802
803 unsafe { SFCGeometry::new_from_raw(result, true) }
804 }
805
806 pub fn extrude(&self, ex: f64, ey: f64, ez: f64) -> Result<SFCGeometry> {
809 precondition_match_validity!(self);
810
811 let result = unsafe { sfcgal_geometry_extrude(self.c_geom.as_ptr(), ex, ey, ez) };
812
813 unsafe { SFCGeometry::new_from_raw(result, true) }
814 }
815
816 pub fn tesselate(&self) -> Result<SFCGeometry> {
818 precondition_match_validity!(self);
819
820 let result = unsafe { sfcgal_geometry_tesselate(self.c_geom.as_ptr()) };
821
822 unsafe { SFCGeometry::new_from_raw(result, true) }
823 }
824
825 pub fn triangulate_2dz(&self) -> Result<SFCGeometry> {
827 precondition_match_validity!(self);
828
829 let result = unsafe { sfcgal_geometry_triangulate_2dz(self.c_geom.as_ptr()) };
830
831 unsafe { SFCGeometry::new_from_raw(result, true) }
832 }
833
834 pub fn convexhull(&self) -> Result<SFCGeometry> {
836 precondition_match_validity!(self);
837
838 let result = unsafe { sfcgal_geometry_convexhull(self.c_geom.as_ptr()) };
839
840 unsafe { SFCGeometry::new_from_raw(result, true) }
841 }
842
843 pub fn convexhull_3d(&self) -> Result<SFCGeometry> {
845 precondition_match_validity!(self);
846
847 let result = unsafe { sfcgal_geometry_convexhull_3d(self.c_geom.as_ptr()) };
848
849 unsafe { SFCGeometry::new_from_raw(result, true) }
850 }
851
852 pub fn line_substring(&self, start: f64, end: f64) -> Result<SFCGeometry> {
855 precondition_match_type!(self, GeomType::Linestring);
856
857 precondition_match_validity!(self);
858
859 precondition_index_in_range!(start, (-1.0..=1.0));
860
861 precondition_index_in_range!(end, (-1.0..=1.0));
862
863 let result = unsafe { sfcgal_geometry_line_sub_string(self.c_geom.as_ptr(), start, end) };
864
865 unsafe { SFCGeometry::new_from_raw(result, true) }
866 }
867
868 pub fn alpha_shapes(&self, alpha: f64, allow_holes: bool) -> Result<SFCGeometry> {
870 if !self.is_valid().unwrap() {
871 return Err(format_err!(
872 "Error: alpha shapes can only be computed on valid geometries"
873 ));
874 }
875
876 if alpha < 0.0 || !alpha.is_finite() {
877 return Err(format_err!(
878 "Error: alpha parameter must be positive or equal to 0.0, got {}",
879 alpha,
880 ));
881 }
882
883 let result =
884 unsafe { sfcgal_geometry_alpha_shapes(self.c_geom.as_ptr(), alpha, allow_holes) };
885
886 unsafe { SFCGeometry::new_from_raw(result, true) }
887 }
888
889 pub fn optimal_alpha_shapes(
891 &self,
892 allow_holes: bool,
893 nb_components: usize,
894 ) -> Result<SFCGeometry> {
895 precondition_match_validity!(self);
896
897 let result = unsafe {
898 sfcgal_geometry_optimal_alpha_shapes(self.c_geom.as_ptr(), allow_holes, nb_components)
899 };
900
901 unsafe { SFCGeometry::new_from_raw(result, true) }
902 }
903
904 pub fn create_collection(geoms: &mut [SFCGeometry]) -> Result<SFCGeometry> {
919 if geoms.is_empty() {
920 let res_geom = unsafe { sfcgal_geometry_collection_create() };
921
922 return unsafe { SFCGeometry::new_from_raw(res_geom, true) };
923 }
924
925 let types = geoms
926 .iter()
927 .map(|g| g._type().unwrap())
928 .collect::<Vec<GeomType>>();
929
930 let multis = types
931 .iter()
932 .map(|gt| gt.is_collection_type())
933 .collect::<Vec<bool>>();
934
935 if !is_all_same(&types) || multis.iter().any(|&x| x) {
936 let res_geom = unsafe { sfcgal_geometry_collection_create() };
937
938 make_multi_geom(res_geom, geoms)
939 } else if types[0] == GeomType::Point {
940 let res_geom = unsafe { sfcgal_multi_point_create() };
941
942 make_multi_geom(res_geom, geoms)
943 } else if types[0] == GeomType::Linestring {
944 let res_geom = unsafe { sfcgal_multi_linestring_create() };
945
946 make_multi_geom(res_geom, geoms)
947 } else if types[0] == GeomType::Polygon {
948 let res_geom = unsafe { sfcgal_multi_polygon_create() };
949
950 make_multi_geom(res_geom, geoms)
951 } else if types[0] == GeomType::Solid {
952 let mut res_geom = SFCGeometry::new("MULTISOLID EMPTY")?;
953
954 res_geom.owned = false;
955
956 make_multi_geom(res_geom.c_geom.as_ptr(), geoms)
957 } else {
958 unreachable!();
959 }
960 }
961
962 pub fn get_collection_members(self) -> Result<Vec<SFCGeometry>> {
981 let _type = self._type()?;
982
983 if !_type.is_collection_type() {
984 return Err(format_err!(
985 "Error: the given geometry doesn't have any member ({:?} is not a collection type)",
986 _type,
987 ));
988 }
989
990 unsafe {
991 let ptr = self.c_geom.as_ptr();
992
993 let n_geom = sfcgal_geometry_collection_num_geometries(ptr);
994
995 let mut result = Vec::new();
996
997 for n in 0..n_geom {
998 let _original_c_geom = sfcgal_geometry_collection_geometry_n(ptr, n);
999
1000 let clone_c_geom = sfcgal_geometry_clone(_original_c_geom);
1001
1002 result.push(SFCGeometry::new_from_raw(clone_c_geom, true)?);
1003 }
1004
1005 Ok(result)
1006 }
1007 }
1008
1009 pub fn point_create() -> Result<SFCGeometry> {
1011 let result = unsafe { sfcgal_point_create() };
1012
1013 unsafe { SFCGeometry::new_from_raw(result, true) }
1014 }
1015
1016 pub fn point_create_from_xy(x: f64, y: f64) -> Result<SFCGeometry> {
1018 let result = unsafe { sfcgal_point_create_from_xy(x, y) };
1019
1020 unsafe { SFCGeometry::new_from_raw(result, true) }
1021 }
1022
1023 pub fn point_create_from_xym(x: f64, y: f64, m: f64) -> Result<SFCGeometry> {
1025 let result = unsafe { sfcgal_point_create_from_xym(x, y, m) };
1026
1027 unsafe { SFCGeometry::new_from_raw(result, true) }
1028 }
1029
1030 pub fn point_create_from_xyz(x: f64, y: f64, z: f64) -> Result<SFCGeometry> {
1032 let result = unsafe { sfcgal_point_create_from_xyz(x, y, z) };
1033
1034 unsafe { SFCGeometry::new_from_raw(result, true) }
1035 }
1036
1037 pub fn point_create_from_xyzm(&self, x: f64, y: f64, z: f64, m: f64) -> Result<SFCGeometry> {
1039 let result = unsafe { sfcgal_point_create_from_xyzm(x, y, z, m) };
1040
1041 unsafe { SFCGeometry::new_from_raw(result, true) }
1042 }
1043
1044 pub fn point_x(&self) -> Result<f64> {
1046 precondition_match_type!(self, GeomType::Point);
1047
1048 precondition_match_not_empty!(self);
1049
1050 unsafe { Ok(sfcgal_point_x(self.c_geom.as_ptr())) }
1051 }
1052
1053 pub fn point_y(&self) -> Result<f64> {
1055 precondition_match_type!(self, GeomType::Point);
1056
1057 precondition_match_not_empty!(self);
1058
1059 unsafe { Ok(sfcgal_point_y(self.c_geom.as_ptr())) }
1060 }
1061
1062 pub fn point_z(&self) -> Result<f64> {
1064 precondition_match_type!(self, GeomType::Point);
1065
1066 precondition_match_not_empty!(self);
1067
1068 unsafe {
1069 let result = sfcgal_point_z(self.c_geom.as_ptr());
1070
1071 check_nan_value(result)
1072 }
1073 }
1074
1075 pub fn point_m(&self) -> Result<f64> {
1077 precondition_match_type!(self, GeomType::Point);
1078
1079 precondition_match_not_empty!(self);
1080
1081 unsafe {
1082 let result = sfcgal_point_m(self.c_geom.as_ptr());
1083
1084 check_nan_value(result)
1085 }
1086 }
1087
1088 pub fn triangle_set_vertex(&self, index: i32, vertex: &SFCGeometry) -> Result<()> {
1090 precondition_match_type!(self, GeomType::Triangle);
1091
1092 precondition_index_in_range!(index, (0..3));
1093
1094 unsafe {
1095 let explicit_converted_int: ::std::os::raw::c_int = index;
1096
1097 sfcgal_triangle_set_vertex(
1098 self.c_geom.as_ptr(),
1099 explicit_converted_int,
1100 vertex.c_geom.as_ptr(),
1101 );
1102
1103 Ok(())
1104 }
1105 }
1106
1107 pub fn triangle_set_vertex_from_xy(&self, index: i32, x: f64, y: f64) -> Result<()> {
1109 precondition_match_type!(self, GeomType::Triangle);
1110
1111 precondition_index_in_range!(index, (0..3));
1112
1113 unsafe {
1114 let explicit_converted_int: ::std::os::raw::c_int = index;
1115
1116 sfcgal_triangle_set_vertex_from_xy(self.c_geom.as_ptr(), explicit_converted_int, x, y);
1117
1118 Ok(())
1119 }
1120 }
1121
1122 pub fn triangle_set_vertex_from_xyz(&self, index: i32, x: f64, y: f64, z: f64) -> Result<()> {
1124 precondition_match_type!(self, GeomType::Triangle);
1125
1126 precondition_index_in_range!(index, (0..3));
1127
1128 unsafe {
1129 let explicit_converted_int: ::std::os::raw::c_int = index;
1130
1131 sfcgal_triangle_set_vertex_from_xyz(
1132 self.c_geom.as_ptr(),
1133 explicit_converted_int,
1134 x,
1135 y,
1136 z,
1137 );
1138
1139 Ok(())
1140 }
1141 }
1142
1143 pub fn straight_skeleton_partition(&self, auto_orientation: bool) -> Result<SFCGeometry> {
1145 match self._type()? {
1146 GeomType::Polygon | GeomType::Multipolygon | GeomType::Triangle => (),
1147 _ => {
1148 bail!("Wrong input Geometry type");
1149 }
1150 }
1151
1152 precondition_match_validity!(self);
1153
1154 let result = unsafe {
1155 sfcgal_geometry_straight_skeleton_partition(self.c_geom.as_ptr(), auto_orientation)
1156 };
1157
1158 unsafe { SFCGeometry::new_from_raw(result, true) }
1159 }
1160
1161 pub fn visibility_point(&self, point: &SFCGeometry) -> Result<SFCGeometry> {
1163 precondition_match_type!(self, GeomType::Polygon);
1164
1165 precondition_match_type_other!(point, GeomType::Point);
1166
1167 precondition_match_validity!(self);
1168
1169 if !self.covers(point)? {
1170 bail!("Point must be inside the Polygon");
1171 }
1172
1173 let result = unsafe {
1174 sfcgal_geometry_visibility_point(self.c_geom.as_ptr(), point.c_geom.as_ptr())
1175 };
1176
1177 unsafe { SFCGeometry::new_from_raw(result, true) }
1178 }
1179
1180 pub fn rotate(&self, angle: f64) -> Result<SFCGeometry> {
1182 let result = unsafe { sfcgal_geometry_rotate(self.c_geom.as_ptr(), angle) };
1183
1184 unsafe { SFCGeometry::new_from_raw(result, true) }
1185 }
1186
1187 pub fn rotate_x(&self, angle: f64) -> Result<SFCGeometry> {
1189 let result = unsafe { sfcgal_geometry_rotate_x(self.c_geom.as_ptr(), angle) };
1190
1191 unsafe { SFCGeometry::new_from_raw(result, true) }
1192 }
1193
1194 pub fn rotate_y(&self, angle: f64) -> Result<SFCGeometry> {
1196 let result = unsafe { sfcgal_geometry_rotate_y(self.c_geom.as_ptr(), angle) };
1197
1198 unsafe { SFCGeometry::new_from_raw(result, true) }
1199 }
1200
1201 pub fn rotate_z(&self, angle: f64) -> Result<SFCGeometry> {
1203 let result = unsafe { sfcgal_geometry_rotate_z(self.c_geom.as_ptr(), angle) };
1204
1205 unsafe { SFCGeometry::new_from_raw(result, true) }
1206 }
1207
1208 pub fn rotate_2d(&self, angle: f64, origin_x: f64, origin_y: f64) -> Result<SFCGeometry> {
1210 let result =
1211 unsafe { sfcgal_geometry_rotate_2d(self.c_geom.as_ptr(), angle, origin_x, origin_y) };
1212
1213 unsafe { SFCGeometry::new_from_raw(result, true) }
1214 }
1215
1216 pub fn rotate_3d(
1218 &self,
1219 angle: f64,
1220 axis_x_angle: f64,
1221 axis_y_angle: f64,
1222 axis_z_angle: f64,
1223 ) -> Result<SFCGeometry> {
1224 let result = unsafe {
1225 sfcgal_geometry_rotate_3d(
1226 self.c_geom.as_ptr(),
1227 angle,
1228 axis_x_angle,
1229 axis_y_angle,
1230 axis_z_angle,
1231 )
1232 };
1233
1234 unsafe { SFCGeometry::new_from_raw(result, true) }
1235 }
1236
1237 #[allow(clippy::too_many_arguments)]
1240 pub fn rotate_3d_around_center(
1241 &self,
1242 angle: f64,
1243 axis_x_angle: f64,
1244 axis_y_angle: f64,
1245 axis_z_angle: f64,
1246 center_x: f64,
1247 center_y: f64,
1248 center_z: f64,
1249 ) -> Result<SFCGeometry> {
1250 let result = unsafe {
1251 sfcgal_geometry_rotate_3d_around_center(
1252 self.c_geom.as_ptr(),
1253 angle,
1254 axis_x_angle,
1255 axis_y_angle,
1256 axis_z_angle,
1257 center_x,
1258 center_y,
1259 center_z,
1260 )
1261 };
1262
1263 unsafe { SFCGeometry::new_from_raw(result, true) }
1264 }
1265
1266 pub fn force_rhr(&self) -> Result<SFCGeometry> {
1268 precondition_match_validity!(self);
1269
1270 let result = unsafe { sfcgal_geometry_force_rhr(self.c_geom.as_ptr()) };
1271
1272 unsafe { SFCGeometry::new_from_raw(result, true) }
1273 }
1274
1275 pub fn force_lhr(&self) -> Result<SFCGeometry> {
1277 precondition_match_validity!(self);
1278
1279 let result = unsafe { sfcgal_geometry_force_lhr(self.c_geom.as_ptr()) };
1280
1281 unsafe { SFCGeometry::new_from_raw(result, true) }
1282 }
1283
1284 pub fn scale(&self, scale: f64) -> Result<SFCGeometry> {
1286 let result = unsafe { sfcgal_geometry_scale(self.c_geom.as_ptr(), scale) };
1287
1288 unsafe { SFCGeometry::new_from_raw(result, true) }
1289 }
1290
1291 pub fn scale_3d(&self, scale_x: f64, scale_y: f64, scale_z: f64) -> Result<SFCGeometry> {
1293 let result =
1294 unsafe { sfcgal_geometry_scale_3d(self.c_geom.as_ptr(), scale_x, scale_y, scale_z) };
1295
1296 unsafe { SFCGeometry::new_from_raw(result, true) }
1297 }
1298
1299 pub fn scale_3d_around_center(
1302 &self,
1303 factor_x: f64,
1304 factor_y: f64,
1305 factor_z: f64,
1306 center_x: f64,
1307 center_y: f64,
1308 center_z: f64,
1309 ) -> Result<SFCGeometry> {
1310 let result = unsafe {
1311 sfcgal_geometry_scale_3d_around_center(
1312 self.c_geom.as_ptr(),
1313 factor_x,
1314 factor_y,
1315 factor_z,
1316 center_x,
1317 center_y,
1318 center_z,
1319 )
1320 };
1321
1322 unsafe { SFCGeometry::new_from_raw(result, true) }
1323 }
1324
1325 pub fn round(&self, value: i32) -> Result<SFCGeometry> {
1327 precondition_match_validity!(self);
1328
1329 let explicit_conversion: ::std::os::raw::c_int = value;
1330
1331 let result = unsafe { sfcgal_geometry_round(self.c_geom.as_ptr(), explicit_conversion) };
1332
1333 unsafe { SFCGeometry::new_from_raw(result, true) }
1334 }
1335
1336 pub fn buffer3d(
1338 &self,
1339 radius: f64,
1340 segments: i32,
1341 buffer_type: BufferType,
1342 ) -> Result<SFCGeometry> {
1343 match self._type()? {
1344 GeomType::Point | GeomType::Linestring => (),
1345 _ => bail!("Geometry must be a Point or a Linestring"),
1346 }
1347
1348 precondition_match_validity!(self);
1349
1350 if radius <= 0. {
1351 bail!("Radius must be greater than 0.0");
1352 }
1353
1354 if segments <= 3 {
1355 bail!("The algorithm needs at least 3 segments");
1356 }
1357
1358 unsafe {
1359 let result = sfcgal_geometry_buffer3d(
1360 self.c_geom.as_ptr(),
1361 radius,
1362 segments,
1363 buffer_type.to_u32().unwrap(),
1364 );
1365
1366 SFCGeometry::new_from_raw(result, true)
1367 }
1368 }
1369
1370 pub fn has_validity_flag(&self) -> i32 {
1372 unsafe { sfcgal_geometry_has_validity_flag(self.c_geom.as_ptr()) }
1373 }
1374
1375 pub fn visibility_segment(
1378 &self,
1379 point_a: &SFCGeometry,
1380 point_b: &SFCGeometry,
1381 ) -> Result<SFCGeometry> {
1382 precondition_match_validity!(self);
1383
1384 let result = unsafe {
1385 sfcgal_geometry_visibility_segment(
1386 self.c_geom.as_ptr(),
1387 point_a.c_geom.as_ptr(),
1388 point_b.c_geom.as_ptr(),
1389 )
1390 };
1391
1392 unsafe { SFCGeometry::new_from_raw(result, true) }
1393 }
1394
1395 pub fn make_solid(&self) -> Result<SFCGeometry> {
1397 precondition_match_validity!(self);
1398
1399 let result = unsafe { sfcgal_geometry_make_solid(self.c_geom.as_ptr()) };
1400
1401 unsafe { SFCGeometry::new_from_raw(result, true) }
1402 }
1403
1404 pub fn translate_2d(&self, dx: f64, dy: f64) -> Result<SFCGeometry> {
1406 let result = unsafe { sfcgal_geometry_translate_2d(self.c_geom.as_ptr(), dx, dy) };
1407
1408 unsafe { SFCGeometry::new_from_raw(result, true) }
1409 }
1410
1411 pub fn translate_3d(
1413 &self,
1414 translation_x: f64,
1415 translation_y: f64,
1416 translation_z: f64,
1417 ) -> Result<SFCGeometry> {
1418 let result = unsafe {
1419 sfcgal_geometry_translate_3d(
1420 self.c_geom.as_ptr(),
1421 translation_x,
1422 translation_y,
1423 translation_z,
1424 )
1425 };
1426
1427 unsafe { SFCGeometry::new_from_raw(result, true) }
1428 }
1429
1430 pub fn force_valid(&self, validity: i32) {
1432 unsafe { sfcgal_geometry_force_valid(self.c_geom.as_ptr(), validity) };
1433 }
1434
1435 pub fn set_geometry_validation(enabled: i32) {
1437 unsafe { sfcgal_set_geometry_validation(enabled) };
1438 }
1439
1440 pub fn triangle_create() -> Result<SFCGeometry> {
1442 let result = unsafe { sfcgal_triangle_create() };
1443
1444 unsafe { SFCGeometry::new_from_raw(result, true) }
1445 }
1446
1447 pub fn triangle_vertex(&self, index: i32) -> Result<SFCGeometry> {
1449 precondition_match_type!(self, GeomType::Triangle);
1450
1451 precondition_index_in_range!(index, (0..3));
1452
1453 let result = unsafe { sfcgal_triangle_vertex(self.c_geom.as_ptr(), index) };
1454
1455 let convert_mutability = result as *mut c_void;
1456
1457 unsafe { SFCGeometry::new_from_raw(convert_mutability, true) }
1458 }
1459
1460 pub fn triangle_create_from_points(
1462 point_a: &SFCGeometry,
1463 point_b: &SFCGeometry,
1464 point_c: &SFCGeometry,
1465 ) -> Result<SFCGeometry> {
1466 precondition_match_type!(point_a, GeomType::Point);
1467
1468 precondition_match_type!(point_a, GeomType::Point);
1469
1470 precondition_match_type!(point_a, GeomType::Point);
1471
1472 let result = unsafe {
1473 sfcgal_triangle_create_from_points(
1474 point_a.c_geom.as_ptr(),
1475 point_b.c_geom.as_ptr(),
1476 point_c.c_geom.as_ptr(),
1477 )
1478 };
1479
1480 unsafe { SFCGeometry::new_from_raw(result, true) }
1481 }
1482
1483 pub fn triangulated_surface_create() -> Result<SFCGeometry> {
1485 let result = unsafe { sfcgal_triangulated_surface_create() };
1486
1487 unsafe { SFCGeometry::new_from_raw(result, true) }
1488 }
1489
1490 pub fn triangulated_surface_triangle_n(&self, index: usize) -> Result<SFCGeometry> {
1492 precondition_match_type!(self, GeomType::Triangulatedsurface);
1493
1494 precondition_index_in_result_value!(index, self.triangulated_surface_num_triangles());
1495
1496 unsafe {
1497 let result = sfcgal_triangulated_surface_triangle_n(self.c_geom.as_ptr(), index);
1498
1499 let convert_mutability = result as *mut c_void;
1500
1501 SFCGeometry::new_from_raw(convert_mutability, true)
1502 }
1503 }
1504
1505 pub fn triangulated_surface_add_triangle(&self, triangle: &SFCGeometry) -> Result<()> {
1507 precondition_match_type!(self, GeomType::Triangulatedsurface);
1508
1509 precondition_match_type_other!(triangle, GeomType::Triangle);
1510
1511 unsafe {
1512 sfcgal_triangulated_surface_add_triangle(self.c_geom.as_ptr(), triangle.c_geom.as_ptr())
1513 };
1514
1515 Ok(())
1516 }
1517
1518 pub fn triangulated_surface_num_triangles(&self) -> Result<usize> {
1520 precondition_match_type!(self, GeomType::Triangulatedsurface);
1521
1522 unsafe {
1523 Ok(sfcgal_triangulated_surface_num_triangles(
1524 self.c_geom.as_ptr(),
1525 ))
1526 }
1527 }
1528
1529 pub fn optimal_convex_partition_2(&self) -> Result<SFCGeometry> {
1532 precondition_match_validity!(self);
1533
1534 let result = unsafe { sfcgal_optimal_convex_partition_2(self.c_geom.as_ptr()) };
1535
1536 unsafe { SFCGeometry::new_from_raw(result, true) }
1537 }
1538
1539 pub fn approx_convex_partition_2(&self) -> Result<SFCGeometry> {
1542 precondition_match_validity!(self);
1543
1544 let result = unsafe { sfcgal_approx_convex_partition_2(self.c_geom.as_ptr()) };
1545
1546 unsafe { SFCGeometry::new_from_raw(result, true) }
1547 }
1548
1549 pub fn greene_approx_convex_partition_2(&self) -> Result<SFCGeometry> {
1552 precondition_match_validity!(self);
1553
1554 let result = unsafe { sfcgal_greene_approx_convex_partition_2(self.c_geom.as_ptr()) };
1555
1556 unsafe { SFCGeometry::new_from_raw(result, true) }
1557 }
1558
1559 pub fn y_monotone_partition_2(&self) -> Result<SFCGeometry> {
1562 precondition_match_validity!(self);
1563
1564 let result = unsafe { sfcgal_y_monotone_partition_2(self.c_geom.as_ptr()) };
1565
1566 unsafe { SFCGeometry::new_from_raw(result, true) }
1567 }
1568
1569 pub fn linestring_num_points(&self) -> Result<usize> {
1571 precondition_match_type!(self, GeomType::Linestring);
1572
1573 unsafe { Ok(sfcgal_linestring_num_points(self.c_geom.as_ptr())) }
1574 }
1575
1576 pub fn linestring_add_point(&self, point: &SFCGeometry) -> Result<()> {
1578 precondition_match_type!(self, GeomType::Linestring);
1579
1580 precondition_match_type!(point, GeomType::Point);
1581
1582 unsafe { sfcgal_linestring_add_point(self.c_geom.as_ptr(), point.c_geom.as_ptr()) };
1583
1584 Ok(())
1585 }
1586
1587 pub fn linestring_point_n(&self, index: usize) -> Result<SFCGeometry> {
1589 precondition_match_type!(self, GeomType::Point);
1590
1591 match self.linestring_num_points() {
1592 Ok(num) => {
1593 if index >= num {
1594 bail!("Overflowing index")
1595 }
1596 }
1597 Err(e) => bail!(e),
1598 }
1599
1600 unsafe {
1601 let result = sfcgal_linestring_point_n(self.c_geom.as_ptr(), index);
1602
1603 let convert_mutability = result as *mut c_void;
1604
1605 SFCGeometry::new_from_raw(convert_mutability, true)
1606 }
1607 }
1608
1609 pub fn linestring_create() -> Result<SFCGeometry> {
1611 let result = unsafe { sfcgal_linestring_create() };
1612
1613 unsafe { SFCGeometry::new_from_raw(result, true) }
1614 }
1615
1616 pub fn polyhedral_surface_create() -> Result<SFCGeometry> {
1618 let result = unsafe { sfcgal_polyhedral_surface_create() };
1619
1620 unsafe { SFCGeometry::new_from_raw(result, true) }
1621 }
1622
1623 pub fn polyhedral_surface_num_polygons(&self) -> Result<usize> {
1625 precondition_match_type!(self, GeomType::Polyhedralsurface);
1626
1627 unsafe { Ok(sfcgal_polyhedral_surface_num_polygons(self.c_geom.as_ptr())) }
1628 }
1629
1630 pub fn polyhedral_surface_add_polygon(&self, other: &SFCGeometry) -> Result<()> {
1632 precondition_match_type!(self, GeomType::Polyhedralsurface);
1633
1634 precondition_match_type_other!(other, GeomType::Polygon);
1635
1636 unsafe {
1637 sfcgal_polyhedral_surface_add_polygon(self.c_geom.as_ptr(), other.c_geom.as_ptr())
1638 };
1639
1640 Ok(())
1641 }
1642
1643 pub fn polyhedral_surface_polygon_n(&self, index: usize) -> Result<SFCGeometry> {
1645 precondition_match_type!(self, GeomType::Polyhedralsurface);
1646
1647 precondition_index_in_result_value!(index, self.polyhedral_surface_num_polygons());
1648
1649 let result = unsafe { sfcgal_polyhedral_surface_polygon_n(self.c_geom.as_ptr(), index) };
1650
1651 let convert_mutability = result as *mut c_void;
1652
1653 unsafe { SFCGeometry::new_from_raw(convert_mutability, true) }
1654 }
1655
1656 pub fn set_error_handlers(
1659 warning_handler: sfcgal_error_handler_t,
1660 error_handler: sfcgal_error_handler_t,
1661 ) {
1662 unsafe { sfcgal_set_error_handlers(warning_handler, error_handler) }
1663 }
1664
1665 pub fn set_alloc_handlers(
1668 alloc_handler: sfcgal_alloc_handler_t,
1669 free_handler: sfcgal_free_handler_t,
1670 ) {
1671 unsafe { sfcgal_set_alloc_handlers(alloc_handler, free_handler) };
1672 }
1673
1674 pub fn solid_create() -> Result<SFCGeometry> {
1676 let result = unsafe { sfcgal_solid_create() };
1677
1678 unsafe { SFCGeometry::new_from_raw(result, true) }
1679 }
1680
1681 pub fn solid_num_shells(&self) -> Result<usize> {
1683 precondition_match_type!(self, GeomType::Solid);
1684
1685 unsafe { Ok(sfcgal_solid_num_shells(self.c_geom.as_ptr())) }
1686 }
1687
1688 pub fn solid_create_from_exterior_shell(exterior_shell: &SFCGeometry) -> Result<SFCGeometry> {
1690 precondition_match_type_other!(exterior_shell, GeomType::Polyhedralsurface);
1691
1692 unsafe {
1693 let result = sfcgal_solid_create_from_exterior_shell(exterior_shell.c_geom.as_ptr());
1694
1695 SFCGeometry::new_from_raw(result, true)
1696 }
1697 }
1698
1699 pub fn solid_shell_n(&self, index: usize) -> Result<SFCGeometry> {
1701 precondition_match_type!(self, GeomType::Solid);
1702
1703 precondition_index_in_result_value!(index, self.solid_num_shells());
1704
1705 let result = unsafe { sfcgal_solid_shell_n(self.c_geom.as_ptr(), index) };
1706
1707 let convert_mutability = result as *mut c_void;
1708
1709 unsafe { SFCGeometry::new_from_raw(convert_mutability, true) }
1710 }
1711
1712 pub fn solid_add_interior_shell(&self, shell: &SFCGeometry) -> Result<()> {
1714 precondition_match_type!(self, GeomType::Solid);
1715
1716 precondition_match_type_other!(shell, GeomType::Polyhedralsurface);
1717
1718 unsafe { sfcgal_solid_add_interior_shell(self.c_geom.as_ptr(), shell.c_geom.as_ptr()) };
1719
1720 Ok(())
1721 }
1722
1723 pub fn polygon_create() -> Result<SFCGeometry> {
1725 let result = unsafe { sfcgal_polygon_create() };
1726
1727 unsafe { SFCGeometry::new_from_raw(result, true) }
1728 }
1729
1730 pub fn polygon_num_interior_rings(&self) -> Result<usize> {
1732 precondition_match_type!(self, GeomType::Polygon);
1733
1734 unsafe { Ok(sfcgal_polygon_num_interior_rings(self.c_geom.as_ptr())) }
1735 }
1736
1737 pub fn polygon_exterior_ring(&self) -> Result<SFCGeometry> {
1739 let result = unsafe { sfcgal_polygon_exterior_ring(self.c_geom.as_ptr()) };
1740
1741 let convert_mutability = result as *mut c_void;
1742
1743 unsafe { SFCGeometry::new_from_raw(convert_mutability, true) }
1744 }
1745
1746 pub fn polygon_create_from_exterior_ring(&self) -> Result<SFCGeometry> {
1748 precondition_match_type!(self, GeomType::Linestring);
1749
1750 let result = unsafe { sfcgal_polygon_create_from_exterior_ring(self.c_geom.as_ptr()) };
1751
1752 unsafe { SFCGeometry::new_from_raw(result, true) }
1753 }
1754
1755 pub fn polygon_add_interior_ring(&self, ring: &SFCGeometry) -> Result<()> {
1757 precondition_match_type!(self, GeomType::Polygon);
1758
1759 precondition_match_type!(ring, GeomType::Linestring);
1760
1761 unsafe { sfcgal_polygon_add_interior_ring(self.c_geom.as_ptr(), ring.c_geom.as_ptr()) };
1762
1763 Ok(())
1764 }
1765
1766 pub fn polygon_interior_ring_n(&self, index: usize) -> Result<SFCGeometry> {
1768 precondition_match_type!(self, GeomType::Polygon);
1769
1770 precondition_index_in_result_value!(index, self.polygon_num_interior_rings());
1771
1772 let result = unsafe { sfcgal_polygon_interior_ring_n(self.c_geom.as_ptr(), index) };
1773
1774 let convert_mutability = result as *mut c_void;
1775
1776 unsafe { SFCGeometry::new_from_raw(convert_mutability, true) }
1777 }
1778
1779 pub fn multi_solid_create() -> Result<SFCGeometry> {
1781 let result = unsafe { sfcgal_multi_solid_create() };
1782
1783 unsafe { SFCGeometry::new_from_raw(result, true) }
1784 }
1785
1786 pub unsafe fn prepared_geometry_set_srid(
1793 prepared_geometry: *mut sfcgal_prepared_geometry_t,
1794 srid: srid_t,
1795 ) {
1796 sfcgal_prepared_geometry_set_srid(prepared_geometry, srid);
1797 }
1798
1799 pub unsafe fn prepared_geometry_delete(prepared_geometry: *mut sfcgal_prepared_geometry_t) {
1802 sfcgal_prepared_geometry_delete(prepared_geometry);
1803 }
1804
1805 pub fn prepared_geometry_create_from_geometry(
1808 &self,
1809 srid: srid_t,
1810 ) -> Result<*mut sfcgal_prepared_geometry_t> {
1811 let result =
1812 unsafe { sfcgal_prepared_geometry_create_from_geometry(self.c_geom.as_ptr(), srid) };
1813
1814 check_null_prepared_geom(result)?;
1815
1816 Ok(result)
1817 }
1818
1819 pub unsafe fn prepared_geometry_set_geometry(&self, prepared: *mut sfcgal_prepared_geometry_t) {
1822 sfcgal_prepared_geometry_set_geometry(prepared, self.c_geom.as_ptr());
1823 }
1824
1825 pub unsafe fn prepared_geometry_srid(prepared: *mut sfcgal_prepared_geometry_t) -> u32 {
1828 sfcgal_prepared_geometry_srid(prepared)
1829 }
1830
1831 pub fn prepared_geometry_create() -> Result<*mut sfcgal_prepared_geometry_t> {
1833 let result = unsafe { sfcgal_prepared_geometry_create() };
1834
1835 check_null_prepared_geom(result)?;
1836
1837 Ok(result)
1838 }
1839
1840 pub unsafe fn prepared_geometry_geometry(
1843 prepared_geometry: *mut sfcgal_prepared_geometry_t,
1844 ) -> Result<SFCGeometry> {
1845 let result = unsafe { sfcgal_prepared_geometry_geometry(prepared_geometry) };
1846
1847 let converted = result as *mut c_void;
1849
1850 SFCGeometry::new_from_raw(converted, true)
1851 }
1852}
1853fn is_all_same<T>(arr: &[T]) -> bool
1854where
1855 T: Ord + Eq,
1856{
1857 arr.iter().min() == arr.iter().max()
1858}
1859fn make_multi_geom(
1860 out_multi: *mut sfcgal_geometry_t,
1861 geoms: &mut [SFCGeometry],
1862) -> Result<SFCGeometry> {
1863 for sfcgal_geom in geoms.iter_mut() {
1864 unsafe {
1865 sfcgal_geom.owned = false;
1866
1867 sfcgal_geometry_collection_add_geometry(
1868 out_multi,
1869 sfcgal_geom.c_geom.as_ptr() as *mut sfcgal_geometry_t,
1870 )
1871 };
1872 }
1873
1874 unsafe { SFCGeometry::new_from_raw(out_multi, true) }
1875}
1876
1877pub fn _sfcgal_get_full_version() -> String {
1878 let result = unsafe { sfcgal_full_version() };
1879
1880 _string(result)
1881}
1882
1883pub fn _sfcgal_get_version() -> String {
1884 let result = unsafe { sfcgal_version() };
1885
1886 _string(result)
1887}
1888
1889#[cfg(test)]
1890
1891mod tests {
1892
1893 use std::{env, f64::consts::PI};
1894
1895 use num_traits::abs;
1896
1897 use super::*;
1898 use crate::{Point2d, Point3d, ToCoordinates};
1899
1900 #[test]
1901
1902 fn creation_point_from_wkt() {
1903 let geom = SFCGeometry::new("POINT (1.0 1.0)");
1904
1905 assert!(geom.is_ok());
1906 }
1907
1908 #[test]
1909
1910 fn creation_polygon_from_wkt() {
1911 let geom = SFCGeometry::new("POLYGON((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 0.0))");
1912
1913 assert!(geom.is_ok());
1914
1915 let geom = geom.unwrap();
1916
1917 assert!(geom.is_valid().unwrap());
1918
1919 let geom1 = SFCGeometry::new("POINT (1.0 1.0)").unwrap();
1920
1921 assert!(geom.intersects(&geom1).unwrap());
1922 }
1923
1924 #[test]
1925
1926 fn writing_to_wkt() {
1927 let geom = SFCGeometry::new("POINT (1.0 1.0)");
1928
1929 assert!(geom.is_ok());
1930
1931 let wkt = geom.unwrap().to_wkt();
1932
1933 assert!(wkt.is_ok());
1934
1935 assert_eq!(wkt.unwrap(), String::from("POINT (1/1 1/1)"));
1936 }
1937
1938 #[test]
1939
1940 fn writing_to_wkt_with_decimals() {
1941 let geom = SFCGeometry::new("POINT (1.0 1.0)");
1942
1943 assert!(geom.is_ok());
1944
1945 let wkt = geom.unwrap().to_wkt_decim(1);
1946
1947 assert!(wkt.is_ok());
1948
1949 assert_eq!(wkt.unwrap(), String::from("POINT (1.0 1.0)"));
1950 }
1951
1952 #[test]
1953
1954 fn creation_failed_with_error_message() {
1955 let geom = SFCGeometry::new("POINT (1, 1)");
1956
1957 assert!(geom.is_err());
1958
1959 assert_eq!(geom.err().unwrap().to_string(), "Obtained null pointer when creating geometry: WKT parse error, Coordinate dimension < 2 (, 1))",)
1960 }
1961
1962 #[test]
1963
1964 fn distance_to_other() {
1965 let pt1 = SFCGeometry::new("POINT (1.0 1.0)").unwrap();
1966
1967 let pt2 = SFCGeometry::new("POINT (10.0 1.0)").unwrap();
1968
1969 let distance = pt1.distance(&pt2).unwrap();
1970
1971 assert_eq!(distance, 9.0);
1972 }
1973
1974 #[test]
1975
1976 fn distance_3d_to_other() {
1977 let pt1 = SFCGeometry::new("POINT (1.0 1.0 2.0)").unwrap();
1978
1979 let pt2 = SFCGeometry::new("POINT (10.0 1.0 2.0)").unwrap();
1980
1981 let distance = pt1.distance_3d(&pt2).unwrap();
1982
1983 assert_eq!(distance, 9.0);
1984 }
1985
1986 #[test]
1987
1988 fn measured_geometry() {
1989 let pt1 = SFCGeometry::new("POINT (1.0 1.0)").unwrap();
1990
1991 let pt2 = SFCGeometry::new("POINTM(1.0 1.0 2.0)").unwrap();
1992
1993 assert!(!pt1.is_measured().unwrap());
1994
1995 assert!(pt2.is_measured().unwrap());
1996 }
1997
1998 #[test]
1999
2000 fn area() {
2001 let polygon = SFCGeometry::new("POLYGON((1 1, 3 1, 4 4, 1 3, 1 1))").unwrap();
2002
2003 assert_eq!(polygon.area().unwrap(), 6.0);
2004 }
2005
2006 #[test]
2007
2008 fn area_3d() {
2009 let polygon = SFCGeometry::new("POLYGON((1 1 1, 3 1 1, 4 4 1, 1 3 1, 1 1 1))").unwrap();
2010
2011 assert_ulps_eq!(polygon.area_3d().unwrap(), 6.0);
2012 }
2013
2014 #[test]
2015
2016 fn volume() {
2017 let cube = SFCGeometry::new(
2018 "SOLID((((0 0 0,0 0 1,0 1 1,0 1 0,0 0 0)),\
2019 ((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)),\
2020 ((0 0 0,1 0 0,1 0 1,0 0 1,0 0 0)),\
2021 ((1 0 0,1 1 0,1 1 1,1 0 1,1 0 0)),\
2022 ((0 0 1,1 0 1,1 1 1,0 1 1,0 0 1)),\
2023 ((0 1 0,0 1 1,1 1 1,1 1 0,0 1 0))))",
2024 )
2025 .unwrap();
2026
2027 assert_eq!(cube.volume().unwrap(), 1.);
2028 }
2029
2030 #[test]
2031
2032 fn volume_on_not_volume_geometry() {
2033 let surface = SFCGeometry::new(
2034 "POLYHEDRALSURFACE Z \
2035 (((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0)),\
2036 ((0 0 0, 0 1 0, 0 1 1, 0 0 1, 0 0 0)),\
2037 ((0 0 0, 1 0 0, 1 0 1, 0 0 1, 0 0 0)),\
2038 ((1 1 1, 1 0 1, 0 0 1, 0 1 1, 1 1 1)),\
2039 ((1 1 1, 1 0 1, 1 0 0, 1 1 0, 1 1 1)),\
2040 ((1 1 1, 1 1 0, 0 1 0, 0 1 1, 1 1 1)))",
2041 )
2042 .unwrap();
2043
2044 assert!(surface.volume().is_err());
2045 }
2046
2047 #[test]
2048
2049 fn predicates() {
2050 let pt = SFCGeometry::new("POINT (1.0 1.0)").unwrap();
2051
2052 assert!(pt.is_valid().unwrap());
2053
2054 assert!(!pt.is_3d().unwrap());
2055
2056 assert!(!pt.is_empty().unwrap());
2057
2058 assert_eq!(
2059 pt.is_planar().err().unwrap().to_string(),
2060 "SFCGAL error: is_planar() only applies to polygons",
2061 );
2062
2063 let linestring_3d = SFCGeometry::new("LINESTRING (10.0 1.0 2.0, 1.0 2.0 1.7)").unwrap();
2064
2065 assert!(linestring_3d.is_valid().unwrap());
2066
2067 assert!(linestring_3d.is_3d().unwrap());
2068
2069 assert!(!linestring_3d.is_empty().unwrap());
2070
2071 assert_eq!(
2072 linestring_3d.is_planar().err().unwrap().to_string(),
2073 "SFCGAL error: is_planar() only applies to polygons",
2074 );
2075
2076 let empty_geom = SFCGeometry::new("LINESTRING EMPTY").unwrap();
2077
2078 assert!(empty_geom.is_valid().unwrap());
2079
2080 assert!(!empty_geom.is_3d().unwrap());
2081
2082 assert!(empty_geom.is_empty().unwrap());
2083
2084 assert_eq!(
2085 linestring_3d.is_planar().err().unwrap().to_string(),
2086 "SFCGAL error: is_planar() only applies to polygons",
2087 );
2088
2089 let polyg = SFCGeometry::new("POLYGON ((1 1, 3 1, 4 4, 1 3, 1 1))").unwrap();
2090
2091 assert!(polyg.is_valid().unwrap());
2092
2093 assert!(!polyg.is_3d().unwrap());
2094
2095 assert!(!polyg.is_empty().unwrap());
2096
2097 assert!(polyg.is_planar().unwrap());
2098
2099 assert!(pt.intersects(&polyg).unwrap());
2100
2101 assert!(!pt.intersects_3d(&linestring_3d).unwrap());
2102 }
2103
2104 #[test]
2105
2106 fn validity_detail_on_valid_geom() {
2107 let line = SFCGeometry::new("LINESTRING (10.0 1.0 2.0, 1.0 2.0 1.7)").unwrap();
2108
2109 assert!(line.is_valid().unwrap());
2110
2111 assert_eq!(line.validity_detail().unwrap(), None);
2112 }
2113
2114 #[test]
2115
2116 fn validity_detail_on_invalid_geom_1() {
2117 let surface = SFCGeometry::new(
2118 "POLYHEDRALSURFACE Z \
2119 (((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0)),\
2120 ((0 0 0, 0 1 0, 0 1 1, 0 0 1, 0 0 0)),\
2121 ((0 0 0, 1 0 0, 1 0 1, 0 0 1, 0 0 0)),\
2122 ((1 1 1, 1 0 1, 0 0 1, 0 1 1, 1 1 1)),\
2123 ((1 1 1, 1 0 1, 1 0 0, 1 1 0, 1 1 1)),\
2124 ((1 1 1, 1 1 0, 0 1 0, 0 1 1, 1 1 1)))",
2125 )
2126 .unwrap();
2127
2128 assert!(!surface.is_valid().unwrap());
2129
2130 assert_eq!(surface.validity_detail().unwrap(), Some(String::from("inconsistent orientation of PolyhedralSurface detected at edge 3 (4-7) of polygon 5")),);
2131 }
2132
2133 #[test]
2134
2135 fn validity_detail_on_invalid_geom_2() {
2136 let surface = SFCGeometry::new("POLYGON ((1 2,1 2,1 2,1 2))").unwrap();
2137
2138 assert!(!surface.is_valid().unwrap());
2139
2140 assert_eq!(
2141 surface.validity_detail().unwrap(),
2142 Some(String::from("ring 0 degenerated to a point")),
2143 );
2144 }
2145
2146 #[test]
2147
2148 fn validity_detail_on_invalid_geom_3() {
2149 let surface = SFCGeometry::new("LINESTRING (1 2, 1 2, 1 2)").unwrap();
2150
2151 assert!(!surface.is_valid().unwrap());
2152
2153 assert_eq!(
2154 surface.validity_detail().unwrap(),
2155 Some(String::from("no length")),
2156 );
2157 }
2158
2159 #[test]
2160
2161 fn straight_skeleton() {
2162 let geom = SFCGeometry::new("POLYGON ((0 0,1 0,1 1,0 1,0 0))").unwrap();
2163
2164 let result = geom.straight_skeleton().unwrap();
2165
2166 let wkt = result.to_wkt_decim(1).unwrap();
2167
2168 assert_eq!(wkt, "MULTILINESTRING ((0.0 0.0,0.5 0.5),(1.0 0.0,0.5 0.5),(1.0 1.0,0.5 0.5),(0.0 1.0,0.5 0.5))",);
2169 }
2170
2171 #[test]
2172
2173 fn straight_skeleton_distance_in_m() {
2174 let geom = SFCGeometry::new("POLYGON ((0 0,1 0,1 1,0 1,0 0))").unwrap();
2175
2176 let result = geom.straight_skeleton_distance_in_m().unwrap();
2177
2178 let wkt = result.to_wkt_decim(1).unwrap();
2179
2180 assert_eq!(
2181 wkt,
2182 "MULTILINESTRING M (\
2183 (0.0 0.0 0.0,0.5 0.5 0.5),\
2184 (1.0 0.0 0.0,0.5 0.5 0.5),\
2185 (1.0 1.0 0.0,0.5 0.5 0.5),\
2186 (0.0 1.0 0.0,0.5 0.5 0.5))",
2187 );
2188 }
2189
2190 #[test]
2191
2192 fn extrude_straight_skeleton() {
2193 let geom = SFCGeometry::new("POLYGON ((0 0, 5 0, 5 5, 4 5, 4 4, 0 4, 0 0))").unwrap();
2195
2196 let result = geom.extrude_straight_skeleton(2.).unwrap();
2197
2198 let wkt = result.to_wkt_decim(2).unwrap();
2199
2200 assert_eq!(
2201 wkt,
2202 "POLYHEDRALSURFACE Z (((4.00 5.00 0.00,5.00 5.00 0.00,4.00 4.00 0.00,4.00 \
2203 5.00 0.00)),((0.00 4.00 0.00,4.00 4.00 0.00,0.00 0.00 0.00,0.00 4.00 \
2204 0.00)),((4.00 4.00 0.00,5.00 0.00 0.00,0.00 0.00 0.00,4.00 4.00 \
2205 0.00)),((5.00 5.00 0.00,5.00 0.00 0.00,4.00 4.00 0.00,5.00 5.00 \
2206 0.00)),((0.00 4.00 0.00,0.00 0.00 0.00,2.00 2.00 2.00,0.00 4.00 \
2207 0.00)),((0.00 0.00 0.00,5.00 0.00 0.00,3.00 2.00 2.00,0.00 0.00 \
2208 0.00)),((2.00 2.00 2.00,0.00 0.00 0.00,3.00 2.00 2.00,2.00 2.00 \
2209 2.00)),((4.50 3.50 0.50,5.00 5.00 0.00,4.50 4.50 0.50,4.50 3.50 \
2210 0.50)),((3.00 2.00 2.00,5.00 0.00 0.00,4.50 3.50 0.50,3.00 2.00 \
2211 2.00)),((4.50 3.50 0.50,5.00 0.00 0.00,5.00 5.00 0.00,4.50 3.50 \
2212 0.50)),((5.00 5.00 0.00,4.00 5.00 0.00,4.50 4.50 0.50,5.00 5.00 \
2213 0.00)),((4.50 4.50 0.50,4.00 4.00 0.00,4.50 3.50 0.50,4.50 4.50 \
2214 0.50)),((4.50 4.50 0.50,4.00 5.00 0.00,4.00 4.00 0.00,4.50 4.50 \
2215 0.50)),((4.00 4.00 0.00,0.00 4.00 0.00,2.00 2.00 2.00,4.00 4.00 \
2216 0.00)),((4.50 3.50 0.50,4.00 4.00 0.00,3.00 2.00 2.00,4.50 3.50 \
2217 0.50)),((3.00 2.00 2.00,4.00 4.00 0.00,2.00 2.00 2.00,3.00 2.00 \
2218 2.00)))"
2219 );
2220 }
2221
2222 #[test]
2223
2224 fn extrude_polygon_straight_skeleton() {
2225 let geom = SFCGeometry::new(
2227 "POLYGON (( 0 0, 5 0, 5 5, 4 5, 4 4, 0 4, 0 0 ), (1 1, 1 2, 2 2, 2 1, 1 1))",
2228 )
2229 .unwrap();
2230
2231 let result = geom.extrude_polygon_straight_skeleton(9., 2.).unwrap();
2232
2233 let wkt = result.to_wkt_decim(1).unwrap();
2234
2235 assert_eq!(
2236 wkt,
2237 "POLYHEDRALSURFACE Z (((0.0 0.0 0.0,0.0 4.0 0.0,4.0 4.0 \
2238 0.0,4.0 5.0 0.0,5.0 5.0 0.0,5.0 0.0 0.0,0.0 0.0 0.0),\
2239 (1.0 1.0 0.0,2.0 1.0 0.0,2.0 2.0 0.0,1.0 2.0 0.0,1.0 1.0 0.0)),\
2240 ((0.0 0.0 0.0,0.0 0.0 9.0,0.0 4.0 9.0,0.0 4.0 0.0,0.0 0.0 0.0)),\
2241 ((0.0 4.0 0.0,0.0 4.0 9.0,4.0 4.0 9.0,4.0 4.0 0.0,0.0 4.0 0.0)),\
2242 ((4.0 4.0 0.0,4.0 4.0 9.0,4.0 5.0 9.0,4.0 5.0 0.0,4.0 4.0 0.0)),\
2243 ((4.0 5.0 0.0,4.0 5.0 9.0,5.0 5.0 9.0,5.0 5.0 0.0,4.0 5.0 0.0)),\
2244 ((5.0 5.0 0.0,5.0 5.0 9.0,5.0 0.0 9.0,5.0 0.0 0.0,5.0 5.0 0.0)),\
2245 ((5.0 0.0 0.0,5.0 0.0 9.0,0.0 0.0 9.0,0.0 0.0 0.0,5.0 0.0 0.0)),\
2246 ((1.0 1.0 0.0,1.0 1.0 9.0,2.0 1.0 9.0,2.0 1.0 0.0,1.0 1.0 0.0)),\
2247 ((2.0 1.0 0.0,2.0 1.0 9.0,2.0 2.0 9.0,2.0 2.0 0.0,2.0 1.0 0.0)),\
2248 ((2.0 2.0 0.0,2.0 2.0 9.0,1.0 2.0 9.0,1.0 2.0 0.0,2.0 2.0 0.0)),\
2249 ((1.0 2.0 0.0,1.0 2.0 9.0,1.0 1.0 9.0,1.0 1.0 0.0,1.0 2.0 0.0)),\
2250 ((4.0 5.0 9.0,5.0 5.0 9.0,4.0 4.0 9.0,4.0 5.0 9.0)),\
2251 ((2.0 1.0 9.0,5.0 0.0 9.0,0.0 0.0 9.0,2.0 1.0 9.0)),\
2252 ((5.0 5.0 9.0,5.0 0.0 9.0,4.0 4.0 9.0,5.0 5.0 9.0)),\
2253 ((2.0 1.0 9.0,0.0 0.0 9.0,1.0 1.0 9.0,2.0 1.0 9.0)),\
2254 ((1.0 2.0 9.0,1.0 1.0 9.0,0.0 0.0 9.0,1.0 2.0 9.0)),\
2255 ((0.0 4.0 9.0,2.0 2.0 9.0,1.0 2.0 9.0,0.0 4.0 9.0)),\
2256 ((0.0 4.0 9.0,1.0 2.0 9.0,0.0 0.0 9.0,0.0 4.0 9.0)),\
2257 ((4.0 4.0 9.0,5.0 0.0 9.0,2.0 2.0 9.0,4.0 4.0 9.0)),\
2258 ((4.0 4.0 9.0,2.0 2.0 9.0,0.0 4.0 9.0,4.0 4.0 9.0)),\
2259 ((2.0 2.0 9.0,5.0 0.0 9.0,2.0 1.0 9.0,2.0 2.0 9.0)),\
2260 ((0.5 2.5 9.5,0.0 0.0 9.0,0.5 0.5 9.5,0.5 2.5 9.5)),\
2261 ((1.0 3.0 10.0,0.0 4.0 9.0,0.5 2.5 9.5,1.0 3.0 10.0)),\
2262 ((0.5 2.5 9.5,0.0 4.0 9.0,0.0 0.0 9.0,0.5 2.5 9.5)),\
2263 ((2.5 0.5 9.5,5.0 0.0 9.0,3.5 1.5 10.5,2.5 0.5 9.5)),\
2264 ((0.0 0.0 9.0,5.0 0.0 9.0,2.5 0.5 9.5,0.0 0.0 9.0)),\
2265 ((0.5 0.5 9.5,0.0 0.0 9.0,2.5 0.5 9.5,0.5 0.5 9.5)),\
2266 ((4.5 3.5 9.5,5.0 5.0 9.0,4.5 4.5 9.5,4.5 3.5 9.5)),\
2267 ((3.5 2.5 10.5,3.5 1.5 10.5,4.5 3.5 9.5,3.5 2.5 10.5)),\
2268 ((4.5 3.5 9.5,5.0 0.0 9.0,5.0 5.0 9.0,4.5 3.5 9.5)),\
2269 ((3.5 1.5 10.5,5.0 0.0 9.0,4.5 3.5 9.5,3.5 1.5 10.5)),\
2270 ((5.0 5.0 9.0,4.0 5.0 9.0,4.5 4.5 9.5,5.0 5.0 9.0)),\
2271 ((4.5 4.5 9.5,4.0 4.0 9.0,4.5 3.5 9.5,4.5 4.5 9.5)),\
2272 ((4.5 4.5 9.5,4.0 5.0 9.0,4.0 4.0 9.0,4.5 4.5 9.5)),\
2273 ((3.0 3.0 10.0,0.0 4.0 9.0,1.0 3.0 10.0,3.0 3.0 10.0)),\
2274 ((3.5 2.5 10.5,4.5 3.5 9.5,3.0 3.0 10.0,3.5 2.5 10.5)),\
2275 ((3.0 3.0 10.0,4.0 4.0 9.0,0.0 4.0 9.0,3.0 3.0 10.0)),\
2276 ((4.5 3.5 9.5,4.0 4.0 9.0,3.0 3.0 10.0,4.5 3.5 9.5)),\
2277 ((2.0 1.0 9.0,1.0 1.0 9.0,0.5 0.5 9.5,2.0 1.0 9.0)),\
2278 ((2.5 0.5 9.5,2.0 1.0 9.0,0.5 0.5 9.5,2.5 0.5 9.5)),\
2279 ((1.0 1.0 9.0,1.0 2.0 9.0,0.5 2.5 9.5,1.0 1.0 9.0)),\
2280 ((0.5 0.5 9.5,1.0 1.0 9.0,0.5 2.5 9.5,0.5 0.5 9.5)),\
2281 ((1.0 3.0 10.0,2.0 2.0 9.0,3.0 3.0 10.0,1.0 3.0 10.0)),\
2282 ((0.5 2.5 9.5,1.0 2.0 9.0,1.0 3.0 10.0,0.5 2.5 9.5)),\
2283 ((1.0 3.0 10.0,1.0 2.0 9.0,2.0 2.0 9.0,1.0 3.0 10.0)),\
2284 ((2.0 2.0 9.0,2.0 1.0 9.0,2.5 0.5 9.5,2.0 2.0 9.0)),\
2285 ((3.5 2.5 10.5,3.0 3.0 10.0,3.5 1.5 10.5,3.5 2.5 10.5)),\
2286 ((3.5 1.5 10.5,2.0 2.0 9.0,2.5 0.5 9.5,3.5 1.5 10.5)),\
2287 ((3.0 3.0 10.0,2.0 2.0 9.0,3.5 1.5 10.5,3.0 3.0 10.0)))"
2288 );
2289 }
2290
2291 #[test]
2292
2293 fn tesselate() {
2294 let geom = SFCGeometry::new("POLYGON ((0.0 0.0,1.0 0.0,1.0 1.0,0.0 1.0,0.0 0.0))").unwrap();
2295
2296 let result = geom.tesselate().unwrap();
2297
2298 let output_wkt = result.to_wkt_decim(1).unwrap();
2299
2300 assert_eq!(
2301 output_wkt,
2302 "TIN (((0.0 1.0,1.0 0.0,1.0 1.0,0.0 1.0)),((0.0 1.0,0.0 0.0,1.0 0.0,0.0 1.0)))",
2303 );
2304 }
2305
2306 #[test]
2307
2308 fn offset_polygon() {
2309 let geom = SFCGeometry::new("POLYGON ((0.0 0.0,1.0 0.0,1.0 1.0,0.0 1.0,0.0 0.0))").unwrap();
2310
2311 let buff = geom.offset_polygon(1.).unwrap();
2312
2313 assert!(buff.is_valid().unwrap());
2314
2315 assert!(!buff.is_empty().unwrap());
2316 }
2317
2318 #[test]
2319
2320 fn extrude_polygon() {
2321 let geom = SFCGeometry::new("POLYGON ((0.0 0.0,1.0 0.0,1.0 1.0,0.0 1.0,0.0 0.0))").unwrap();
2322
2323 let extr = geom.extrude(0., 0., 1.).unwrap();
2324
2325 assert!(extr.is_valid().unwrap());
2326
2327 assert!(!extr.is_empty().unwrap());
2328
2329 assert_eq!(extr._type().unwrap(), GeomType::Solid);
2330 }
2331
2332 #[test]
2333
2334 fn tesselate_invariant_geom() {
2335 let input_wkt = String::from("POINT (1.0 1.0)");
2336
2337 let pt = SFCGeometry::new(&input_wkt).unwrap();
2338
2339 let result = pt.tesselate().unwrap();
2340
2341 let output_wkt = result.to_wkt_decim(1).unwrap();
2342
2343 assert_eq!(input_wkt, output_wkt);
2344 }
2345
2346 #[test]
2347
2348 fn line_substring() {
2349 let g = SFCGeometry::new("LINESTRING Z (10.0 1.0 2.0, 1.0 2.0 1.7)").unwrap();
2350
2351 let result = g.line_substring(-0.2, 0.2).unwrap();
2352
2353 assert_eq!(
2354 result.to_wkt_decim(1).unwrap(),
2355 "LINESTRING Z (2.8 1.8 1.8,8.2 1.2 1.9)"
2356 );
2357
2358 assert_eq!(
2360 g.line_substring(-2., 0.2).err().unwrap().to_string(),
2361 "Index not in the expected range"
2362 );
2363 }
2364
2365 #[test]
2366
2367 fn difference_3d() {
2368 let cube1 = SFCGeometry::new(
2369 "
2370 SOLID((((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0)),\
2371 ((0 0 0, 0 0 1, 0 1 1, 0 1 0, 0 0 0)),\
2372 ((0 0 0, 1 0 0, 1 0 1, 0 0 1, 0 0 0)),\
2373 ((1 1 1, 0 1 1, 0 0 1, 1 0 1, 1 1 1)),\
2374 ((1 1 1, 1 0 1, 1 0 0, 1 1 0, 1 1 1)),\
2375 ((1 1 1, 1 1 0, 0 1 0, 0 1 1, 1 1 1))))",
2376 )
2377 .unwrap();
2378
2379 let cube2 = SFCGeometry::new(
2380 "
2381 SOLID((((0 0 0.5, 0 1 0.5, 1 1 0.5, 1 0 0.5, 0 0 0.5)),\
2382 ((0 0 0.5, 0 0 1, 0 1 1, 0 1 0.5, 0 0 0.5)),\
2383 ((0 0 0.5, 1 0 0.5, 1 0 1, 0 0 1, 0 0 0.5)),\
2384 ((1 1 1, 0 1 1, 0 0 1, 1 0 1, 1 1 1)),\
2385 ((1 1 1, 1 0 1, 1 0 0.5, 1 1 0.5, 1 1 1)),\
2386 ((1 1 1, 1 1 0.5, 0 1 0.5, 0 1 1, 1 1 1))))",
2387 )
2388 .unwrap();
2389
2390 let diff = cube1.difference_3d(&cube2).unwrap();
2391
2392 assert!(diff.is_valid().unwrap());
2393
2394 assert_ulps_eq!(diff.volume().unwrap(), 0.5);
2395 }
2396
2397 #[test]
2398
2399 fn intersection_3d() {
2400 let cube1 = SFCGeometry::new(
2401 "
2402 SOLID((((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0)),\
2403 ((0 0 0, 0 0 1, 0 1 1, 0 1 0, 0 0 0)),\
2404 ((0 0 0, 1 0 0, 1 0 1, 0 0 1, 0 0 0)),\
2405 ((1 1 1, 0 1 1, 0 0 1, 1 0 1, 1 1 1)),\
2406 ((1 1 1, 1 0 1, 1 0 0, 1 1 0, 1 1 1)),\
2407 ((1 1 1, 1 1 0, 0 1 0, 0 1 1, 1 1 1))))",
2408 )
2409 .unwrap();
2410
2411 let cube2 = SFCGeometry::new(
2412 "
2413 SOLID((((0 0 0.5, 0 1 0.5, 1 1 0.5, 1 0 0.5, 0 0 0.5)),\
2414 ((0 0 0.5, 0 0 1, 0 1 1, 0 1 0.5, 0 0 0.5)),\
2415 ((0 0 0.5, 1 0 0.5, 1 0 1, 0 0 1, 0 0 0.5)),\
2416 ((1 1 1, 0 1 1, 0 0 1, 1 0 1, 1 1 1)),\
2417 ((1 1 1, 1 0 1, 1 0 0.5, 1 1 0.5, 1 1 1)),\
2418 ((1 1 1, 1 1 0.5, 0 1 0.5, 0 1 1, 1 1 1))))",
2419 )
2420 .unwrap();
2421
2422 let diff = cube1.intersection_3d(&cube2).unwrap();
2423
2424 assert!(diff.is_valid().unwrap());
2425
2426 assert_ulps_eq!(diff.volume().unwrap(), 0.5);
2427 }
2428
2429 #[test]
2430
2431 fn alpha_shapes_on_point() {
2432 let multipoint = SFCGeometry::new("POINT (1 3)").unwrap();
2433
2434 let res = multipoint.alpha_shapes(20.0, false).unwrap();
2435
2436 assert_eq!(res.to_wkt_decim(1).unwrap(), "GEOMETRYCOLLECTION EMPTY");
2437 }
2438
2439 #[test]
2440
2441 fn alpha_shapes_on_multipoint() {
2442 let multipoint = SFCGeometry::new("MULTIPOINT ((1 2),(2 2),(3 0),(1 3))").unwrap();
2443
2444 let res = multipoint.alpha_shapes(20.0, false).unwrap();
2445
2446 assert_eq!(
2447 res.to_wkt_decim(1).unwrap(),
2448 "POLYGON ((1.0 2.0,1.0 3.0,2.0 2.0,3.0 0.0,1.0 2.0))"
2449 );
2450 }
2451
2452 #[test]
2453
2454 fn alpha_shapes_on_linestring() {
2455 let multipoint = SFCGeometry::new("LINESTRING (1 2,2 2,3 0,1 3)").unwrap();
2456
2457 let res = multipoint.alpha_shapes(20.0, false).unwrap();
2458
2459 assert_eq!(
2460 res.to_wkt_decim(1).unwrap(),
2461 "POLYGON ((1.0 2.0,1.0 3.0,2.0 2.0,3.0 0.0,1.0 2.0))"
2462 );
2463 }
2464
2465 #[test]
2466
2467 fn alpha_shapes_on_multilinestring() {
2468 let multipoint =
2469 SFCGeometry::new("MULTILINESTRING ((1 2,2 2,3 0,1 3), (2 6,3 5,4 2))").unwrap();
2470
2471 let res = multipoint.alpha_shapes(20.0, false).unwrap();
2472
2473 assert_eq!(
2474 res.to_wkt_decim(1).unwrap(),
2475 "POLYGON ((1.0 2.0,1.0 3.0,2.0 6.0,3.0 5.0,4.0 2.0,3.0 0.0,1.0 2.0))"
2476 );
2477 }
2478
2479 #[test]
2480
2481 fn alpha_shapes_on_polygon() {
2482 let pol1 = SFCGeometry::new("POLYGON ((0 0, 0 4, 4 4, 4 0, 0 0))").unwrap();
2483
2484 let res = pol1.alpha_shapes(10.0, false).unwrap();
2485
2486 assert_eq!(
2487 res.to_wkt_decim(1).unwrap(),
2488 "POLYGON ((0.0 0.0,0.0 4.0,4.0 4.0,4.0 0.0,0.0 0.0))"
2489 );
2490 }
2491
2492 #[test]
2493
2494 fn alpha_shapes_on_invalid() {
2495 let pol1 = SFCGeometry::new("LINESTRING (1 2, 1 2, 1 2, 1 2)").unwrap();
2496
2497 let res = pol1.alpha_shapes(10.0, false);
2498
2499 assert!(res.is_err());
2500 }
2501
2502 #[test]
2503
2504 fn create_collection_empty() {
2505 let g = SFCGeometry::create_collection(&mut []).unwrap();
2506
2507 assert_eq!(g.to_wkt_decim(1).unwrap(), "GEOMETRYCOLLECTION EMPTY",);
2508 }
2509
2510 #[test]
2511
2512 fn create_collection_heterogenous() {
2513 let a = SFCGeometry::new("POINT (1.0 1.0)").unwrap();
2514
2515 let b = SFCGeometry::new("LINESTRING Z (10.0 1.0 2.0, 1.0 2.0 1.7)").unwrap();
2516
2517 let g = SFCGeometry::create_collection(&mut [a, b]).unwrap();
2518
2519 assert_eq!(
2520 g.to_wkt_decim(1).unwrap(),
2521 "GEOMETRYCOLLECTION (POINT (1.0 1.0),LINESTRING Z (10.0 1.0 2.0,1.0 2.0 1.7))",
2522 );
2523 }
2524
2525 #[test]
2526
2527 fn create_collection_multipoint_from_points() {
2528 let a = SFCGeometry::new("POINT (1.0 1.0)").unwrap();
2529
2530 let b = SFCGeometry::new("POINT (2.0 2.0)").unwrap();
2531
2532 let g = SFCGeometry::create_collection(&mut [a, b]).unwrap();
2533
2534 assert_eq!(
2535 g.to_wkt_decim(1).unwrap(),
2536 "MULTIPOINT ((1.0 1.0),(2.0 2.0))",
2537 );
2538 }
2539
2540 #[test]
2541
2542 fn create_collection_multilinestring_from_linestrings() {
2543 let a = SFCGeometry::new("LINESTRING (10.0 1.0 2.0, 1.0 2.0 1.7)").unwrap();
2544
2545 let b = SFCGeometry::new("LINESTRING (10.0 1.0 2.0, 1.0 2.0 1.7)").unwrap();
2546
2547 let g = SFCGeometry::create_collection(&mut [a, b]).unwrap();
2548
2549 assert_eq!(
2550 g.to_wkt_decim(1).unwrap(),
2551 "MULTILINESTRING Z ((10.0 1.0 2.0,1.0 2.0 1.7),(10.0 1.0 2.0,1.0 2.0 1.7))",
2552 );
2553 }
2554
2555 #[test]
2556
2557 fn create_collection_multisolid_from_solids() {
2558 let a = SFCGeometry::new(
2559 "SOLID((((0 0 0,0 0 1,0 1 1,0 1 0,0 0 0)),\
2560 ((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)),\
2561 ((0 0 0,1 0 0,1 0 1,0 0 1,0 0 0)),\
2562 ((1 0 0,1 1 0,1 1 1,1 0 1,1 0 0)),\
2563 ((0 0 1,1 0 1,1 1 1,0 1 1,0 0 1)),\
2564 ((0 1 0,0 1 1,1 1 1,1 1 0,0 1 0))))",
2565 )
2566 .unwrap();
2567
2568 let b = SFCGeometry::new(
2569 "SOLID((((0 0 0,0 0 1,0 1 1,0 1 0,0 0 0)),\
2570 ((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)),\
2571 ((0 0 0,1 0 0,1 0 1,0 0 1,0 0 0)),\
2572 ((1 0 0,1 1 0,1 1 1,1 0 1,1 0 0)),\
2573 ((0 0 1,1 0 1,1 1 1,0 1 1,0 0 1)),\
2574 ((0 1 0,0 1 1,1 1 1,1 1 0,0 1 0))))",
2575 )
2576 .unwrap();
2577
2578 let g = SFCGeometry::create_collection(&mut [a, b]).unwrap();
2579
2580 assert_eq!(
2581 g.to_wkt_decim(1).unwrap(),
2582 "MULTISOLID Z (\
2583 ((\
2584 ((0.0 0.0 0.0,0.0 0.0 1.0,0.0 1.0 1.0,0.0 1.0 0.0,0.0 0.0 0.0)),\
2585 ((0.0 0.0 0.0,0.0 1.0 0.0,1.0 1.0 0.0,1.0 0.0 0.0,0.0 0.0 0.0)),\
2586 ((0.0 0.0 0.0,1.0 0.0 0.0,1.0 0.0 1.0,0.0 0.0 1.0,0.0 0.0 0.0)),\
2587 ((1.0 0.0 0.0,1.0 1.0 0.0,1.0 1.0 1.0,1.0 0.0 1.0,1.0 0.0 0.0)),\
2588 ((0.0 0.0 1.0,1.0 0.0 1.0,1.0 1.0 1.0,0.0 1.0 1.0,0.0 0.0 1.0)),\
2589 ((0.0 1.0 0.0,0.0 1.0 1.0,1.0 1.0 1.0,1.0 1.0 0.0,0.0 1.0 0.0))\
2590 )),\
2591 ((\
2592 ((0.0 0.0 0.0,0.0 0.0 1.0,0.0 1.0 1.0,0.0 1.0 0.0,0.0 0.0 0.0)),\
2593 ((0.0 0.0 0.0,0.0 1.0 0.0,1.0 1.0 0.0,1.0 0.0 0.0,0.0 0.0 0.0)),\
2594 ((0.0 0.0 0.0,1.0 0.0 0.0,1.0 0.0 1.0,0.0 0.0 1.0,0.0 0.0 0.0)),\
2595 ((1.0 0.0 0.0,1.0 1.0 0.0,1.0 1.0 1.0,1.0 0.0 1.0,1.0 0.0 0.0)),\
2596 ((0.0 0.0 1.0,1.0 0.0 1.0,1.0 1.0 1.0,0.0 1.0 1.0,0.0 0.0 1.0)),\
2597 ((0.0 1.0 0.0,0.0 1.0 1.0,1.0 1.0 1.0,1.0 1.0 0.0,0.0 1.0 0.0))\
2598 ))\
2599 )",
2600 );
2601 }
2602
2603 fn points_are_close2d(p1: (f64, f64), p2: (f64, f64)) -> bool {
2604 let tolerance = 1e-10;
2605
2606 abs(p1.0 - p2.0) < tolerance && abs(p1.1 - p2.1) < tolerance
2607 }
2608
2609 fn points_are_close3d(p1: (f64, f64, f64), p2: (f64, f64, f64)) -> bool {
2610 let tolerance = 1e-10;
2611
2612 abs(p1.0 - p2.0) < tolerance && abs(p1.1 - p2.1) < tolerance && abs(p1.2 - p2.2) < tolerance
2613 }
2614
2615 #[test]
2616
2617 fn test_polygon_translation_2d() {
2618 let line = CoordSeq::<Point2d>::Linestring(vec![(0., 0.), (1.0, 1.0)])
2619 .to_sfcgal()
2620 .unwrap();
2621
2622 let line = line.translate_2d(2., 3.).unwrap();
2623
2624 let coords: CoordSeq<Point2d> = line.to_coordinates().unwrap();
2625
2626 match coords {
2627 CoordSeq::Linestring(vec) => {
2628 assert!(points_are_close2d(vec[0], (2.0, 3.0)));
2629
2630 assert!(points_are_close2d(vec[1], (3.0, 4.0)));
2631 }
2632 _ => panic!("Bad coordinates variant"),
2633 }
2634 }
2635
2636 #[test]
2637
2638 fn test_polygon_translation_3d() {
2639 let poly = CoordSeq::<Point3d>::Polygon(vec![vec![
2640 (0., 0., 0.),
2641 (1., 0., 0.),
2642 (1., 1., 0.),
2643 (0., 1., 0.),
2644 (0., 0., 0.),
2645 ]])
2646 .to_sfcgal()
2647 .unwrap();
2648
2649 let poly = poly.translate_3d(1.0, 2.0, 3.0).unwrap();
2650
2651 let coords: CoordSeq<Point3d> = poly.to_coordinates().unwrap();
2652
2653 match coords {
2654 CoordSeq::Polygon(vec) => {
2655 let poly_test = &vec[0];
2656
2657 assert!(points_are_close3d(poly_test[0], (1., 2., 3.)));
2658
2659 assert!(points_are_close3d(poly_test[2], (2., 3., 3.)));
2660 }
2661 _ => panic!("Bad coordinate variant"),
2662 }
2663 }
2664
2665 #[test]
2666
2667 fn test_rotate_point() {
2668 let point = CoordSeq::Point((1., 0., 0.)).to_sfcgal().unwrap();
2669
2670 let point = point.rotate_z(5. * PI / 2.).unwrap();
2671
2672 let coords: CoordSeq<Point3d> = point.to_coordinates().unwrap();
2673
2674 match coords {
2675 CoordSeq::Point(pt) => {
2676 assert!(points_are_close3d(pt, (0., 1., 0.)))
2677 }
2678 _ => panic!("Bad coordinate variant"),
2679 }
2680 }
2681
2682 #[test]
2683
2684 fn test_obj_export() {
2685 let temp_dir = env::temp_dir();
2690
2691 let final_path = format!("{}/sfcgal_test.obj", temp_dir.to_str().unwrap());
2692
2693 println!("Writing to {:?}", temp_dir);
2694
2695 let input = "MULTISOLID Z (((((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)),((0 0 1,1 0 1,1 1 1,0 1 1,0 0 1)),((0 0 0,1 0 0,1 0 1,0 0 1,0 0 0)),((1 1 0,0 1 0,0 1 1,1 1 1,1 1 0)),((1 0 0,1 1 0,1 1 1,1 0 1,1 0 0)),((0 0 0,0 0 1,0 1 1,0 1 0,0 0 0)))),((((2 4 6,2 5 6,3 5 6,3 4 6,2 4 6)),((2 4 7,3 4 7,3 5 7,2 5 7,2 4 7)),((2 4 6,3 4 6,3 4 7,2 4 7,2 4 6)),((3 5 6,2 5 6,2 5 7,3 5 7,3 5 6)),((3 4 6,3 5 6,3 5 7,3 4 7,3 4 6)),((2 4 6,2 4 7,2 5 7,2 5 6,2 4 6)))))";
2696
2697 let shape = SFCGeometry::new(input).unwrap();
2698
2699 assert!(shape.to_obj_file(final_path.as_str()).is_ok());
2700 }
2701
2702 #[test]
2703
2704 fn show_full_version() {
2705 println!("{:?}", _sfcgal_get_full_version());
2706 }
2707}