diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index aed2ff53f..a5aa9013e 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2596,18 +2596,19 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou // the side depends on the original winding order of the polygon (left for contours, right for holes) //FIXME improve the algorithm in case the loop is tiny. //FIXME improve the algorithm in case the loop is split into segments with a low number of points (see the Point b query). - Point a = paths.front().polyline.points[1]; // second point - Point b = *(paths.back().polyline.points.end()-3); // second to last point + // Angle from the 2nd point to the last point. + double angle_inside = angle(paths.front().polyline.points[1] - paths.front().first_point(), + *(paths.back().polyline.points.end()-3) - paths.front().first_point()); + assert(angle_inside >= -M_PI && angle_inside <= M_PI); + // 3rd of this angle will be taken, thus make the angle monotonic before interpolation. if (was_clockwise) { - // swap points - Point c = a; a = b; b = c; + if (angle_inside > 0) + angle_inside -= 2.0 * M_PI; + } else { + if (angle_inside < 0) + angle_inside += 2.0 * M_PI; } - double angle = paths.front().first_point().ccw_angle(a, b) / 3; - - // turn left if contour, turn right if hole - if (was_clockwise) angle *= -1; - // create the destination point along the first segment and rotate it // we make sure we don't exceed the segment length because we don't know // the rotation of the second segment so we might cross the object boundary @@ -2619,7 +2620,8 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou // Shift by no more than a nozzle diameter. //FIXME Hiding the seams will not work nicely for very densely discretized contours! Point pt = ((nd * nd >= l2) ? p2 : (p1 + v * (nd / sqrt(l2)))).cast(); - pt.rotate(angle, paths.front().polyline.points.front()); + // Rotate pt inside around the seam point. + pt.rotate(angle_inside / 3., paths.front().polyline.points.front()); // generate the travel move gcode += m_writer.travel_to_xy(this->point_to_gcode(pt), "move inwards before travel"); } diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index c825f8254..82ffbd8d1 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -36,9 +36,9 @@ enum Orientation static inline Orientation orient(const Point &a, const Point &b, const Point &c) { static_assert(sizeof(coord_t) * 2 == sizeof(int64_t), "orient works with 32 bit coordinates"); - int64_t u = int64_t(b(0)) * int64_t(c(1)) - int64_t(b(1)) * int64_t(c(0)); - int64_t v = int64_t(a(0)) * int64_t(c(1)) - int64_t(a(1)) * int64_t(c(0)); - int64_t w = int64_t(a(0)) * int64_t(b(1)) - int64_t(a(1)) * int64_t(b(0)); + int64_t u = int64_t(b.x()) * int64_t(c.y()) - int64_t(b.y()) * int64_t(c.x()); + int64_t v = int64_t(a.x()) * int64_t(c.y()) - int64_t(a.y()) * int64_t(c.x()); + int64_t w = int64_t(a.x()) * int64_t(b.y()) - int64_t(a.y()) * int64_t(b.x()); int64_t d = u - v + w; return (d > 0) ? ORIENTATION_CCW : ((d == 0) ? ORIENTATION_COLINEAR : ORIENTATION_CW); } diff --git a/src/libslic3r/Geometry/ConvexHull.cpp b/src/libslic3r/Geometry/ConvexHull.cpp index b1ff77f80..2e92535f2 100644 --- a/src/libslic3r/Geometry/ConvexHull.cpp +++ b/src/libslic3r/Geometry/ConvexHull.cpp @@ -1,6 +1,7 @@ #include "libslic3r.h" #include "ConvexHull.hpp" #include "BoundingBox.hpp" +#include "../Geometry.hpp" #include @@ -19,13 +20,13 @@ Polygon convex_hull(Points pts) hull.points.resize(2 * n); // Build lower hull for (int i = 0; i < n; ++ i) { - while (k >= 2 && pts[i].ccw(hull[k-2], hull[k-1]) <= 0) + while (k >= 2 && Geometry::orient(pts[i], hull[k-2], hull[k-1]) != Geometry::ORIENTATION_CCW) -- k; hull[k ++] = pts[i]; } // Build upper hull for (int i = n-2, t = k+1; i >= 0; i--) { - while (k >= t && pts[i].ccw(hull[k-2], hull[k-1]) <= 0) + while (k >= t && Geometry::orient(pts[i], hull[k-2], hull[k-1]) != Geometry::ORIENTATION_CCW) -- k; hull[k ++] = pts[i]; } @@ -58,7 +59,7 @@ Pointf3s convex_hull(Pointf3s points) Point k1 = Point::new_scale(hull[k - 1](0), hull[k - 1](1)); Point k2 = Point::new_scale(hull[k - 2](0), hull[k - 2](1)); - if (p.ccw(k2, k1) <= 0) + if (Geometry::orient(p, k2, k1) != Geometry::ORIENTATION_CCW) --k; else break; @@ -76,7 +77,7 @@ Pointf3s convex_hull(Pointf3s points) Point k1 = Point::new_scale(hull[k - 1](0), hull[k - 1](1)); Point k2 = Point::new_scale(hull[k - 2](0), hull[k - 2](1)); - if (p.ccw(k2, k1) <= 0) + if (Geometry::orient(p, k2, k1) != Geometry::ORIENTATION_CCW) --k; else break; diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp index 751e59458..8631ee08b 100644 --- a/src/libslic3r/Line.hpp +++ b/src/libslic3r/Line.hpp @@ -113,7 +113,6 @@ public: Vector vector() const { return this->b - this->a; } Vector normal() const { return Vector((this->b(1) - this->a(1)), -(this->b(0) - this->a(0))); } bool intersection(const Line& line, Point* intersection) const; - double ccw(const Point& point) const { return point.ccw(*this); } // Clip a line with a bounding box. Returns false if the line is completely outside of the bounding box. bool clip_with_bbox(const BoundingBox &bbox); // Extend the line from both sides by an offset. diff --git a/src/libslic3r/Point.cpp b/src/libslic3r/Point.cpp index 66f125bb6..9f56f96d6 100644 --- a/src/libslic3r/Point.cpp +++ b/src/libslic3r/Point.cpp @@ -108,38 +108,6 @@ bool Point::nearest_point(const Points &points, Point* point) const return true; } -/* Three points are a counter-clockwise turn if ccw > 0, clockwise if - * ccw < 0, and collinear if ccw = 0 because ccw is a determinant that - * gives the signed area of the triangle formed by p1, p2 and this point. - * In other words it is the 2D cross product of p1-p2 and p1-this, i.e. - * z-component of their 3D cross product. - * We return double because it must be big enough to hold 2*max(|coordinate|)^2 - */ -double Point::ccw(const Point &p1, const Point &p2) const -{ - static_assert(sizeof(coord_t) == 4, "Point::ccw() requires a 32 bit coord_t"); - return cross2((p2 - p1).cast(), (*this - p1).cast()); -// return cross2((p2 - p1).cast(), (*this - p1).cast()); -} - -double Point::ccw(const Line &line) const -{ - return this->ccw(line.a, line.b); -} - -// returns the CCW angle between this-p1 and this-p2 -// i.e. this assumes a CCW rotation from p1 to p2 around this -double Point::ccw_angle(const Point &p1, const Point &p2) const -{ - const Point v1 = *this - p1; - const Point v2 = p2 - *this; - int64_t dot = int64_t(v1(0)) * int64_t(v2(0)) + int64_t(v1(1)) * int64_t(v2(1)); - int64_t cross = int64_t(v1(0)) * int64_t(v2(1)) - int64_t(v1(1)) * int64_t(v2(0)); - float angle = float(atan2(float(cross), float(dot))); - // we only want to return only positive angles - return angle <= 0 ? angle + 2*PI : angle; -} - Point Point::projection_onto(const MultiPoint &poly) const { Point running_projection = poly.first_point(); diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index 09ff533fe..8279a377e 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -79,24 +79,35 @@ inline const auto &identity3d = identity<3, double>; inline bool operator<(const Vec2d &lhs, const Vec2d &rhs) { return lhs.x() < rhs.x() || (lhs.x() == rhs.x() && lhs.y() < rhs.y()); } -template -int32_t cross2(const Eigen::MatrixBase> &v1, const Eigen::MatrixBase> &v2) = delete; - -template -inline T cross2(const Eigen::MatrixBase> &v1, const Eigen::MatrixBase> &v2) -{ - return v1.x() * v2.y() - v1.y() * v2.x(); -} - +// Cross product of two 2D vectors. +// None of the vectors may be of int32_t type as the result would overflow. template inline typename Derived::Scalar cross2(const Eigen::MatrixBase &v1, const Eigen::MatrixBase &v2) { + static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "cross2(): first parameter is not a 2D vector"); + static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "cross2(): first parameter is not a 2D vector"); + static_assert(! std::is_same::value, "cross2(): Scalar type must not be int32_t, otherwise the cross product would overflow."); static_assert(std::is_same::value, "cross2(): Scalar types of 1st and 2nd operand must be equal."); return v1.x() * v2.y() - v1.y() * v2.x(); } -template -inline Eigen::Matrix perp(const Eigen::MatrixBase> &v) { return Eigen::Matrix(- v.y(), v.x()); } +// 2D vector perpendicular to the argument. +template +inline Eigen::Matrix perp(const Eigen::MatrixBase &v) +{ + static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "perp(): parameter is not a 2D vector"); + return { - v.y(), v.x() }; +} + +// Angle from v1 to v2, returning double atan2(y, x) normalized to <-PI, PI>. +template +inline double angle(const Eigen::MatrixBase &v1, const Eigen::MatrixBase &v2) { + static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "angle(): first parameter is not a 2D vector"); + static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "angle(): second parameter is not a 2D vector"); + auto v1d = v1.cast(); + auto v2d = v2.cast(); + return atan2(cross2(v1d, v2d), v1d.dot(v2d)); +} template Eigen::Matrix to_2d(const Eigen::MatrixBase> &ptN) { return { ptN.x(), ptN.y() }; } @@ -168,9 +179,6 @@ public: int nearest_point_index(const PointConstPtrs &points) const; int nearest_point_index(const PointPtrs &points) const; bool nearest_point(const Points &points, Point* point) const; - double ccw(const Point &p1, const Point &p2) const; - double ccw(const Line &line) const; - double ccw_angle(const Point &p1, const Point &p2) const; Point projection_onto(const MultiPoint &poly) const; Point projection_onto(const Line &line) const; }; diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index c7bbe9706..09f1c393d 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -171,50 +171,56 @@ Point Polygon::centroid() const return Point(Vec2d(c / (3. * area_sum))); } -// find all concave vertices (i.e. having an internal angle greater than the supplied angle) -// (external = right side, thus we consider ccw orientation) -Points Polygon::concave_points(double angle) const +// Filter points from poly to the output with the help of FilterFn. +// filter function receives two vectors: +// v1: this_point - previous_point +// v2: next_point - this_point +// and returns true if the point is to be copied to the output. +template +Points filter_points_by_vectors(const Points &poly, FilterFn filter) { - Points points; - angle = 2. * PI - angle + EPSILON; - - // check whether first point forms a concave angle - if (this->points.front().ccw_angle(this->points.back(), *(this->points.begin()+1)) <= angle) - points.push_back(this->points.front()); - - // check whether points 1..(n-1) form concave angles - for (Points::const_iterator p = this->points.begin()+1; p != this->points.end()-1; ++ p) - if (p->ccw_angle(*(p-1), *(p+1)) <= angle) - points.push_back(*p); - - // check whether last point forms a concave angle - if (this->points.back().ccw_angle(*(this->points.end()-2), this->points.front()) <= angle) - points.push_back(this->points.back()); - - return points; -} + // Last point is the first point visited. + Point p1 = poly.back(); + // Previous vector to p1. + Vec2d v1 = (p1 - *(poly.end() - 2)).cast(); -// find all convex vertices (i.e. having an internal angle smaller than the supplied angle) -// (external = right side, thus we consider ccw orientation) -Points Polygon::convex_points(double angle) const -{ - Points points; - angle = 2*PI - angle - EPSILON; - - // check whether first point forms a convex angle - if (this->points.front().ccw_angle(this->points.back(), *(this->points.begin()+1)) >= angle) - points.push_back(this->points.front()); - - // check whether points 1..(n-1) form convex angles - for (Points::const_iterator p = this->points.begin()+1; p != this->points.end()-1; ++p) { - if (p->ccw_angle(*(p-1), *(p+1)) >= angle) points.push_back(*p); + Points out; + for (Point p2 : poly) { + // p2 is next point to the currently visited point p1. + Vec2d v2 = (p2 - p1).cast(); + if (filter(v1, v2)) + out.emplace_back(p2); + v1 = v2; + p1 = p2; } - // check whether last point forms a convex angle - if (this->points.back().ccw_angle(*(this->points.end()-2), this->points.front()) >= angle) - points.push_back(this->points.back()); - - return points; + return out; +} + +template +Points filter_convex_concave_points_by_angle_threshold(const Points &poly, double angle_threshold, ConvexConcaveFilterFn convex_concave_filter) +{ + assert(angle_threshold >= 0.); + if (angle_threshold < EPSILON) { + double cos_angle = cos(angle_threshold); + return filter_points_by_vectors(poly, [convex_concave_filter, cos_angle](const Vec2d &v1, const Vec2d &v2){ + return convex_concave_filter(v1, v2) && v1.normalized().dot(v2.normalized()) < cos_angle; + }); + } else { + return filter_points_by_vectors(poly, [convex_concave_filter](const Vec2d &v1, const Vec2d &v2){ + return convex_concave_filter(v1, v2); + }); + } +} + +Points Polygon::convex_points(double angle_threshold) const +{ + return filter_convex_concave_points_by_angle_threshold(this->points, angle_threshold, [](const Vec2d &v1, const Vec2d &v2){ return cross2(v1, v2) > 0.; }); +} + +Points Polygon::concave_points(double angle_threshold) const +{ + return filter_convex_concave_points_by_angle_threshold(this->points, angle_threshold, [](const Vec2d &v1, const Vec2d &v2){ return cross2(v1, v2) < 0.; }); } // Projection of a point onto the polygon. diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index 089820565..77f3020be 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -65,8 +65,11 @@ public: void densify(float min_length, std::vector* lengths = nullptr); void triangulate_convex(Polygons* polygons) const; Point centroid() const; - Points concave_points(double angle = PI) const; - Points convex_points(double angle = PI) const; + // Considering CCW orientation of this polygon, find all convex resp. concave points + // with the angle at the vertex larger than a threshold. + // Zero angle_threshold means to accept all convex resp. concave points. + Points convex_points(double angle_threshold = 0.) const; + Points concave_points(double angle_threshold = 0.) const; // Projection of a point onto the polygon. Point point_projection(const Point &point) const; std::vector parameter_by_length() const; diff --git a/t/geometry.t b/t/geometry.t index 0f37c0aa3..bb72b22e1 100644 --- a/t/geometry.t +++ b/t/geometry.t @@ -2,7 +2,7 @@ use Test::More; use strict; use warnings; -plan tests => 42; +plan tests => 30; BEGIN { use FindBin; @@ -189,63 +189,6 @@ my $polygons = [ is +Slic3r::Point->new(10, 0)->distance_to_line($line), 0, 'distance_to'; } -#========================================================== - -{ - my $square = Slic3r::Polygon->new_scale( - [100,100], - [200,100], - [200,200], - [100,200], - ); - is scalar(@{$square->concave_points(PI*4/3)}), 0, 'no concave vertices detected in ccw square'; - is scalar(@{$square->convex_points(PI*2/3)}), 4, 'four convex vertices detected in ccw square'; - - $square->make_clockwise; - is scalar(@{$square->concave_points(PI*4/3)}), 4, 'fuor concave vertices detected in cw square'; - is scalar(@{$square->convex_points(PI*2/3)}), 0, 'no convex vertices detected in cw square'; -} - -{ - my $square = Slic3r::Polygon->new_scale( - [150,100], - [200,100], - [200,200], - [100,200], - [100,100], - ); - is scalar(@{$square->concave_points(PI*4/3)}), 0, 'no concave vertices detected in convex polygon'; - is scalar(@{$square->convex_points(PI*2/3)}), 4, 'four convex vertices detected in square'; -} - -{ - my $square = Slic3r::Polygon->new_scale( - [200,200], - [100,200], - [100,100], - [150,100], - [200,100], - ); - is scalar(@{$square->concave_points(PI*4/3)}), 0, 'no concave vertices detected in convex polygon'; - is scalar(@{$square->convex_points(PI*2/3)}), 4, 'four convex vertices detected in square'; -} - -{ - my $triangle = Slic3r::Polygon->new( - [16000170,26257364], [714223,461012], [31286371,461008], - ); - is scalar(@{$triangle->concave_points(PI*4/3)}), 0, 'no concave vertices detected in triangle'; - is scalar(@{$triangle->convex_points(PI*2/3)}), 3, 'three convex vertices detected in triangle'; -} - -{ - my $triangle = Slic3r::Polygon->new( - [16000170,26257364], [714223,461012], [20000000,461012], [31286371,461012], - ); - is scalar(@{$triangle->concave_points(PI*4/3)}), 0, 'no concave vertices detected in triangle having collinear point'; - is scalar(@{$triangle->convex_points(PI*2/3)}), 3, 'three convex vertices detected in triangle having collinear point'; -} - { my $triangle = Slic3r::Polygon->new( [16000170,26257364], [714223,461012], [31286371,461008], diff --git a/tests/libslic3r/test_geometry.cpp b/tests/libslic3r/test_geometry.cpp index 21f9a9663..34e625e6d 100644 --- a/tests/libslic3r/test_geometry.cpp +++ b/tests/libslic3r/test_geometry.cpp @@ -373,7 +373,43 @@ SCENARIO("Line distances", "[Geometry]"){ } } +SCENARIO("Calculating angles", "[Geometry]") +{ + GIVEN(("Vectors 30 degrees apart")) + { + std::vector> pts { + { {1000, 0}, { 866, 500 } }, + { { 866, 500 }, { 500, 866 } }, + { { 500, 866 }, { 0, 1000 } }, + { { -500, 866 }, { -866, 500 } } + }; + + THEN("Angle detected is 30 degrees") + { + for (auto &p : pts) + REQUIRE(is_approx(angle(p.first, p.second), M_PI / 6.)); + } + } + + GIVEN(("Vectors 30 degrees apart")) + { + std::vector> pts { + { { 866, 500 }, {1000, 0} }, + { { 500, 866 }, { 866, 500 } }, + { { 0, 1000 }, { 500, 866 } }, + { { -866, 500 }, { -500, 866 } } + }; + + THEN("Angle detected is -30 degrees") + { + for (auto &p : pts) + REQUIRE(is_approx(angle(p.first, p.second), - M_PI / 6.)); + } + } +} + SCENARIO("Polygon convex/concave detection", "[Geometry]"){ + static constexpr const double angle_threshold = M_PI / 3.; GIVEN(("A Square with dimension 100")){ auto square = Slic3r::Polygon /*new_scale*/(std::vector({ Point(100,100), @@ -381,13 +417,13 @@ SCENARIO("Polygon convex/concave detection", "[Geometry]"){ Point(200,200), Point(100,200)})); THEN("It has 4 convex points counterclockwise"){ - REQUIRE(square.concave_points(PI*4/3).size() == 0); - REQUIRE(square.convex_points(PI*2/3).size() == 4); + REQUIRE(square.concave_points(angle_threshold).size() == 0); + REQUIRE(square.convex_points(angle_threshold).size() == 4); } THEN("It has 4 concave points clockwise"){ square.make_clockwise(); - REQUIRE(square.concave_points(PI*4/3).size() == 4); - REQUIRE(square.convex_points(PI*2/3).size() == 0); + REQUIRE(square.concave_points(angle_threshold).size() == 4); + REQUIRE(square.convex_points(angle_threshold).size() == 0); } } GIVEN("A Square with an extra colinearvertex"){ @@ -398,8 +434,8 @@ SCENARIO("Polygon convex/concave detection", "[Geometry]"){ Point(100,200), Point(100,100)})); THEN("It has 4 convex points counterclockwise"){ - REQUIRE(square.concave_points(PI*4/3).size() == 0); - REQUIRE(square.convex_points(PI*2/3).size() == 4); + REQUIRE(square.concave_points(angle_threshold).size() == 0); + REQUIRE(square.convex_points(angle_threshold).size() == 4); } } GIVEN("A Square with an extra collinear vertex in different order"){ @@ -410,8 +446,8 @@ SCENARIO("Polygon convex/concave detection", "[Geometry]"){ Point(150,100), Point(200,100)})); THEN("It has 4 convex points counterclockwise"){ - REQUIRE(square.concave_points(PI*4/3).size() == 0); - REQUIRE(square.convex_points(PI*2/3).size() == 4); + REQUIRE(square.concave_points(angle_threshold).size() == 0); + REQUIRE(square.convex_points(angle_threshold).size() == 4); } } @@ -422,8 +458,8 @@ SCENARIO("Polygon convex/concave detection", "[Geometry]"){ Point(31286371,461008) })); THEN("it has three convex vertices"){ - REQUIRE(triangle.concave_points(PI*4/3).size() == 0); - REQUIRE(triangle.convex_points(PI*2/3).size() == 3); + REQUIRE(triangle.concave_points(angle_threshold).size() == 0); + REQUIRE(triangle.convex_points(angle_threshold).size() == 3); } } @@ -435,8 +471,8 @@ SCENARIO("Polygon convex/concave detection", "[Geometry]"){ Point(31286371,461012) })); THEN("it has three convex vertices"){ - REQUIRE(triangle.concave_points(PI*4/3).size() == 0); - REQUIRE(triangle.convex_points(PI*2/3).size() == 3); + REQUIRE(triangle.concave_points(angle_threshold).size() == 0); + REQUIRE(triangle.convex_points(angle_threshold).size() == 3); } } GIVEN("A polygon with concave vertices with angles of specifically 4/3pi"){ @@ -453,8 +489,8 @@ SCENARIO("Polygon convex/concave detection", "[Geometry]"){ Point(38092663,692699),Point(52100125,692699) })); THEN("the correct number of points are detected"){ - REQUIRE(polygon.concave_points(PI*4/3).size() == 6); - REQUIRE(polygon.convex_points(PI*2/3).size() == 10); + REQUIRE(polygon.concave_points(angle_threshold).size() == 6); + REQUIRE(polygon.convex_points(angle_threshold).size() == 10); } } } diff --git a/xs/xsp/Line.xsp b/xs/xsp/Line.xsp index 777dc41fa..36181c3ba 100644 --- a/xs/xsp/Line.xsp +++ b/xs/xsp/Line.xsp @@ -41,7 +41,7 @@ Clone normal(); Clone vector(); double ccw(Point* point) - %code{% RETVAL = THIS->ccw(*point); %}; + %code{% RETVAL = cross2((THIS->a - *point).cast(), (THIS->b - THIS->a).cast()); %}; %{ Line* diff --git a/xs/xsp/Point.xsp b/xs/xsp/Point.xsp index beefc6249..2d70e0203 100644 --- a/xs/xsp/Point.xsp +++ b/xs/xsp/Point.xsp @@ -39,13 +39,7 @@ double perp_distance_to_line(Line* line) %code{% RETVAL = line->perp_distance_to(*THIS); %}; double ccw(Point* p1, Point* p2) - %code{% RETVAL = THIS->ccw(*p1, *p2); %}; - double ccw_angle(Point* p1, Point* p2) - %code{% RETVAL = THIS->ccw_angle(*p1, *p2); %}; - Point* projection_onto_polygon(Polygon* polygon) - %code{% RETVAL = new Point(THIS->projection_onto(*polygon)); %}; - Point* projection_onto_polyline(Polyline* polyline) - %code{% RETVAL = new Point(THIS->projection_onto(*polyline)); %}; + %code{% RETVAL = cross2((*p1 - *THIS).cast(), (*p2 - *p1).cast()); %}; Point* projection_onto_line(Line* line) %code{% RETVAL = new Point(THIS->projection_onto(*line)); %}; Point* negative() diff --git a/xs/xsp/Polygon.xsp b/xs/xsp/Polygon.xsp index a94425477..6b6d52524 100644 --- a/xs/xsp/Polygon.xsp +++ b/xs/xsp/Polygon.xsp @@ -39,8 +39,6 @@ %code{% THIS->triangulate_convex(&RETVAL); %}; Clone centroid(); Clone bounding_box(); - Points concave_points(double angle); - Points convex_points(double angle); Clone point_projection(Point* point) %code{% RETVAL = THIS->point_projection(*point); %}; Clone intersection(Line* line)