@@ -45,8 +45,30 @@ struct XY
4545
4646typedef std::vector<XY> Polygon;
4747
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 Polygon >
71+ void _finalize_polygon (std::vector<Polygon> &result, bool closed_only)
5072{
5173 if (result.size () == 0 ) {
5274 return ;
@@ -511,10 +533,13 @@ bool path_in_path(PathIterator1 &a,
511533
512534namespace clip_to_rect_filters
513535{
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.
517541*/
542+ template <class Point >
518543struct bisectx
519544{
520545 double m_x;
@@ -523,41 +548,49 @@ struct bisectx
523548 {
524549 }
525550
526- inline XY bisect (const XY s, const XY p) const
551+ inline Point bisect (const Point s, const Point p) const
527552 {
528553 double dx = p.x - s.x ;
529554 double dy = p.y - s.y ;
530- return {
555+ auto result = Point {
531556 m_x,
532557 s.y + dy * ((m_x - s.x ) / dx),
533558 };
559+ if constexpr (std::is_same_v<Point, XYZ>) {
560+ double dz = p.z - s.z ;
561+ result.z = s.z + dz * ((m_x - s.x ) / dx);
562+ }
563+ return result;
534564 }
535565};
536566
537- struct xlt : public bisectx
567+ template <class Point >
568+ struct xlt : public bisectx <Point>
538569{
539- xlt (double x) : bisectx(x)
570+ xlt (double x) : bisectx<Point> (x)
540571 {
541572 }
542573
543- inline bool is_inside (const XY point) const
574+ inline bool is_inside (const Point point) const
544575 {
545- return point.x <= m_x;
576+ return point.x <= this -> m_x ;
546577 }
547578};
548579
549- struct xgt : public bisectx
580+ template <class Point >
581+ struct xgt : public bisectx <Point>
550582{
551- xgt (double x) : bisectx(x)
583+ xgt (double x) : bisectx<Point> (x)
552584 {
553585 }
554586
555- inline bool is_inside (const XY point) const
587+ inline bool is_inside (const Point point) const
556588 {
557- return point.x >= m_x;
589+ return point.x >= this -> m_x ;
558590 }
559591};
560592
593+ template <class Point >
561594struct bisecty
562595{
563596 double m_y;
@@ -566,43 +599,89 @@ struct bisecty
566599 {
567600 }
568601
569- inline XY bisect (const XY s, const XY p) const
602+ inline Point bisect (const Point s, const Point p) const
570603 {
571604 double dx = p.x - s.x ;
572605 double dy = p.y - s.y ;
573- return {
606+ auto result = Point {
574607 s.x + dx * ((m_y - s.y ) / dy),
575608 m_y,
576609 };
610+ if constexpr (std::is_same_v<Point, XYZ>) {
611+ double dz = p.z - s.z ;
612+ result.z = s.z + dz * ((m_y - s.y ) / dy);
613+ }
614+ return result;
577615 }
578616};
579617
580- struct ylt : public bisecty
618+ template <class Point >
619+ struct ylt : public bisecty <Point>
581620{
582- ylt (double y) : bisecty(y)
621+ ylt (double y) : bisecty<Point> (y)
583622 {
584623 }
585624
586- inline bool is_inside (const XY point) const
625+ inline bool is_inside (const Point point) const
587626 {
588- return point.y <= m_y;
627+ return point.y <= this -> m_y ;
589628 }
590629};
591630
592- struct ygt : public bisecty
631+ template <class Point >
632+ struct ygt : public bisecty <Point>
593633{
594- ygt (double y) : bisecty(y)
634+ ygt (double y) : bisecty<Point> (y)
595635 {
596636 }
597637
598- inline bool is_inside (const XY point) const
638+ inline bool is_inside (const Point point) const
599639 {
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;
601680 }
602681};
603682}
604683
605- template <class Filter >
684+ template <class Polygon , class Filter >
606685inline void clip_to_rect_one_step (const Polygon &polygon, Polygon &result, const Filter &filter)
607686{
608687 bool sinside, pinside;
@@ -672,10 +751,10 @@ clip_path_to_rect(PathIterator &path, agg::rect_d &rect, bool inside)
672751
673752 // The result of each step is fed into the next (note the
674753 // 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));
679758
680759 // Empty polygons aren't very useful, so skip them
681760 if (polygon1.size ()) {
@@ -689,6 +768,67 @@ clip_path_to_rect(PathIterator &path, agg::rect_d &rect, bool inside)
689768 return results;
690769}
691770
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+
692832template <class VerticesArray , class ResultArray >
693833void affine_transform_2d (VerticesArray &vertices, agg::trans_affine &trans, ResultArray &result)
694834{
0 commit comments