@@ -45,14 +45,36 @@ struct XY
45
45
46
46
typedef std::vector<XY> Polygon;
47
47
48
- inline void
49
- _finalize_polygon (std::vector<Polygon> &result, bool closed_only)
48
+ struct XYZ
49
+ {
50
+ double x;
51
+ double y;
52
+ double z;
53
+
54
+ XYZ () : x(0 ), y(0 ), z(0 ) {}
55
+ XYZ (double x_, double y_, double z_ = 0.0 ) : x(x_), y(y_), z(z_) {}
56
+
57
+ bool operator ==(const XYZ& o)
58
+ {
59
+ return (x == o.x && y == o.y && z == o.z );
60
+ }
61
+
62
+ bool operator !=(const XYZ& o)
63
+ {
64
+ return (x != o.x || y != o.y || z != o.z );
65
+ }
66
+ };
67
+
68
+ typedef std::vector<XYZ> Polygon3D;
69
+
70
+ template <class PolygonT >
71
+ void _finalize_polygon (std::vector<PolygonT> &result, bool closed_only)
50
72
{
51
73
if (result.size () == 0 ) {
52
74
return ;
53
75
}
54
76
55
- Polygon &polygon = result.back ();
77
+ PolygonT &polygon = result.back ();
56
78
57
79
/* Clean up the last polygon in the result. */
58
80
if (polygon.size () == 0 ) {
@@ -511,10 +533,13 @@ bool path_in_path(PathIterator1 &a,
511
533
512
534
namespace clip_to_rect_filters
513
535
{
514
- /* There are four different passes needed to create/remove
515
- vertices (one for each side of the rectangle). The differences
516
- between those passes are encapsulated in these functor classes.
536
+ /* In 2D, there are four different passes needed to create/remove vertices (one for each
537
+ * side of the rectangle). In 3d, there are six passes instead (one for each side of the
538
+ * cube).
539
+ *
540
+ * The differences between those passes are encapsulated in these functor classes.
517
541
*/
542
+ template <class PointT >
518
543
struct bisectx
519
544
{
520
545
double m_x;
@@ -523,41 +548,49 @@ struct bisectx
523
548
{
524
549
}
525
550
526
- inline XY bisect (const XY s, const XY p) const
551
+ inline PointT bisect (const PointT s, const PointT p) const
527
552
{
528
553
double dx = p.x - s.x ;
529
554
double dy = p.y - s.y ;
530
- return {
555
+ auto result = PointT {
531
556
m_x,
532
557
s.y + dy * ((m_x - s.x ) / dx),
533
558
};
559
+ if constexpr (std::is_same_v<PointT, XYZ>) {
560
+ double dz = p.z - s.z ;
561
+ result.z = s.z + dz * ((m_x - s.x ) / dx);
562
+ }
563
+ return result;
534
564
}
535
565
};
536
566
537
- struct xlt : public bisectx
567
+ template <class PointT >
568
+ struct xlt : public bisectx <PointT>
538
569
{
539
- xlt (double x) : bisectx(x)
570
+ xlt (double x) : bisectx<PointT> (x)
540
571
{
541
572
}
542
573
543
- inline bool is_inside (const XY point) const
574
+ inline bool is_inside (const PointT point) const
544
575
{
545
- return point.x <= m_x;
576
+ return point.x <= this -> m_x ;
546
577
}
547
578
};
548
579
549
- struct xgt : public bisectx
580
+ template <class PointT >
581
+ struct xgt : public bisectx <PointT>
550
582
{
551
- xgt (double x) : bisectx(x)
583
+ xgt (double x) : bisectx<PointT> (x)
552
584
{
553
585
}
554
586
555
- inline bool is_inside (const XY point) const
587
+ inline bool is_inside (const PointT point) const
556
588
{
557
- return point.x >= m_x;
589
+ return point.x >= this -> m_x ;
558
590
}
559
591
};
560
592
593
+ template <class PointT >
561
594
struct bisecty
562
595
{
563
596
double m_y;
@@ -566,44 +599,90 @@ struct bisecty
566
599
{
567
600
}
568
601
569
- inline XY bisect (const XY s, const XY p) const
602
+ inline PointT bisect (const PointT s, const PointT p) const
570
603
{
571
604
double dx = p.x - s.x ;
572
605
double dy = p.y - s.y ;
573
- return {
606
+ auto result = PointT {
574
607
s.x + dx * ((m_y - s.y ) / dy),
575
608
m_y,
576
609
};
610
+ if constexpr (std::is_same_v<PointT, XYZ>) {
611
+ double dz = p.z - s.z ;
612
+ result.z = s.z + dz * ((m_y - s.y ) / dy);
613
+ }
614
+ return result;
577
615
}
578
616
};
579
617
580
- struct ylt : public bisecty
618
+ template <class PointT >
619
+ struct ylt : public bisecty <PointT>
581
620
{
582
- ylt (double y) : bisecty(y)
621
+ ylt (double y) : bisecty<PointT> (y)
583
622
{
584
623
}
585
624
586
- inline bool is_inside (const XY point) const
625
+ inline bool is_inside (const PointT point) const
587
626
{
588
- return point.y <= m_y;
627
+ return point.y <= this -> m_y ;
589
628
}
590
629
};
591
630
592
- struct ygt : public bisecty
631
+ template <class PointT >
632
+ struct ygt : public bisecty <PointT>
593
633
{
594
- ygt (double y) : bisecty(y)
634
+ ygt (double y) : bisecty<PointT> (y)
595
635
{
596
636
}
597
637
598
- inline bool is_inside (const XY point) const
638
+ inline bool is_inside (const PointT point) const
599
639
{
600
- return point.y >= m_y;
640
+ return point.y >= this ->m_y ;
641
+ }
642
+ };
643
+
644
+ struct bisectz
645
+ {
646
+ double m_z;
647
+
648
+ bisectz (double z) : m_z(z) {}
649
+
650
+ inline XYZ bisect (const XYZ s, const XYZ p) const
651
+ {
652
+ double dx = p.x - s.x ;
653
+ double dy = p.y - s.y ;
654
+ double dz = p.z - s.z ;
655
+ return {
656
+ s.x + dx * ((m_z - s.z ) / dz),
657
+ s.y + dy * ((m_z - s.z ) / dz),
658
+ m_z,
659
+ };
660
+ }
661
+ };
662
+
663
+ struct zlt : public bisectz
664
+ {
665
+ zlt (double z) : bisectz(z) {}
666
+
667
+ inline bool is_inside (const XYZ point) const
668
+ {
669
+ return point.z <= m_z;
670
+ }
671
+ };
672
+
673
+ struct zgt : public bisectz
674
+ {
675
+ zgt (double z) : bisectz(z) {}
676
+
677
+ inline bool is_inside (const XYZ point) const
678
+ {
679
+ return point.z >= m_z;
601
680
}
602
681
};
603
682
}
604
683
605
- template <class Filter >
606
- inline void clip_to_rect_one_step (const Polygon &polygon, Polygon &result, const Filter &filter)
684
+ template <class PolygonT , class Filter >
685
+ inline void clip_to_rect_one_step (const PolygonT &polygon, PolygonT &result, const Filter &filter)
607
686
{
608
687
bool sinside, pinside;
609
688
result.clear ();
@@ -672,10 +751,10 @@ clip_path_to_rect(PathIterator &path, agg::rect_d &rect, bool inside)
672
751
673
752
// The result of each step is fed into the next (note the
674
753
// swapping of polygon1 and polygon2 at each step).
675
- clip_to_rect_one_step (polygon1, polygon2, clip_to_rect_filters::xlt (xmax));
676
- clip_to_rect_one_step (polygon2, polygon1, clip_to_rect_filters::xgt (xmin));
677
- clip_to_rect_one_step (polygon1, polygon2, clip_to_rect_filters::ylt (ymax));
678
- clip_to_rect_one_step (polygon2, polygon1, clip_to_rect_filters::ygt (ymin));
754
+ clip_to_rect_one_step (polygon1, polygon2, clip_to_rect_filters::xlt<XY> (xmax));
755
+ clip_to_rect_one_step (polygon2, polygon1, clip_to_rect_filters::xgt<XY> (xmin));
756
+ clip_to_rect_one_step (polygon1, polygon2, clip_to_rect_filters::ylt<XY> (ymax));
757
+ clip_to_rect_one_step (polygon2, polygon1, clip_to_rect_filters::ygt<XY> (ymin));
679
758
680
759
// Empty polygons aren't very useful, so skip them
681
760
if (polygon1.size ()) {
@@ -689,6 +768,67 @@ clip_path_to_rect(PathIterator &path, agg::rect_d &rect, bool inside)
689
768
return results;
690
769
}
691
770
771
+ inline std::vector<Polygon3D>
772
+ clip_paths_to_box (py::array_t <double > paths,
773
+ std::array<std::pair<double , double >, 3 > &box,
774
+ bool inside)
775
+ {
776
+ auto xmin = std::get<0 >(box).first , xmax = std::get<0 >(box).second ;
777
+ auto ymin = std::get<1 >(box).first , ymax = std::get<1 >(box).second ;
778
+ auto zmin = std::get<2 >(box).first , zmax = std::get<2 >(box).second ;
779
+
780
+ if (xmin > xmax) {
781
+ std::swap (xmin, xmax);
782
+ }
783
+ if (ymin > ymax) {
784
+ std::swap (ymin, ymax);
785
+ }
786
+ if (zmin > zmax) {
787
+ std::swap (zmin, zmax);
788
+ }
789
+
790
+ if (!inside) {
791
+ std::swap (xmin, xmax);
792
+ std::swap (ymin, ymax);
793
+ std::swap (zmin, zmax);
794
+ }
795
+
796
+ Polygon3D polygon1, polygon2;
797
+ std::vector<Polygon3D> results;
798
+
799
+ auto paths_iter = paths.unchecked <3 >();
800
+ for (auto i = 0 ; i < paths.shape (0 ); i++) {
801
+ // Grab the next subpath and store it in polygon1
802
+ polygon1.clear ();
803
+ for (auto j = 0 ; j < paths.shape (1 ); j++) {
804
+ polygon1.emplace_back (
805
+ paths_iter (i, j, 0 ),
806
+ paths_iter (i, j, 1 ),
807
+ paths_iter (i, j, 2 )
808
+ );
809
+ }
810
+
811
+ // The result of each step is fed into the next (note the
812
+ // swapping of polygon1 and polygon2 at each step).
813
+ clip_to_rect_one_step (polygon1, polygon2, clip_to_rect_filters::xlt<XYZ>(xmax));
814
+ clip_to_rect_one_step (polygon2, polygon1, clip_to_rect_filters::xgt<XYZ>(xmin));
815
+ clip_to_rect_one_step (polygon1, polygon2, clip_to_rect_filters::ylt<XYZ>(ymax));
816
+ clip_to_rect_one_step (polygon2, polygon1, clip_to_rect_filters::ygt<XYZ>(ymin));
817
+ clip_to_rect_one_step (polygon1, polygon2, clip_to_rect_filters::zlt (zmax));
818
+ clip_to_rect_one_step (polygon2, polygon1, clip_to_rect_filters::zgt (zmin));
819
+
820
+ // Empty polygons aren't very useful, so skip them
821
+ if (polygon1.size ()) {
822
+ _finalize_polygon (results, true );
823
+ results.push_back (polygon1);
824
+ }
825
+ }
826
+
827
+ _finalize_polygon (results, true );
828
+
829
+ return results;
830
+ }
831
+
692
832
template <class VerticesArray , class ResultArray >
693
833
void affine_transform_2d (VerticesArray &vertices, agg::trans_affine &trans, ResultArray &result)
694
834
{
0 commit comments