New Point::projection_onto() methods

master
Alessandro Ranellucci 2014-05-21 20:08:21 +02:00
parent 874c7a6e8b
commit 254ab29a97
4 changed files with 83 additions and 1 deletions

View File

@ -1,5 +1,6 @@
#include "Point.hpp"
#include "Line.hpp"
#include "MultiPoint.hpp"
#include <cmath>
#include <sstream>
@ -138,6 +139,63 @@ Point::ccw(const Line &line) const
return this->ccw(line.a, line.b);
}
Point
Point::projection_onto(const MultiPoint &poly) const
{
Point running_projection = poly.first_point();
double running_min = this->distance_to(running_projection);
Lines lines = poly.lines();
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) {
Point point_temp = this->projection_onto(*line);
if (this->distance_to(point_temp) < running_min) {
running_projection = point_temp;
running_min = this->distance_to(running_projection);
}
}
return running_projection;
}
Point
Point::projection_onto(const Line &line) const
{
if (line.a.coincides_with(line.b)) return line.a;
/*
(Ported from VisiLibity by Karl J. Obermeyer)
The projection of point_temp onto the line determined by
line_segment_temp can be represented as an affine combination
expressed in the form projection of
Point = theta*line_segment_temp.first + (1.0-theta)*line_segment_temp.second.
If theta is outside the interval [0,1], then one of the Line_Segment's endpoints
must be closest to calling Point.
*/
double theta = ( (double)(line.b.x - this->x)*(double)(line.b.x - line.a.x) + (double)(line.b.y- this->y)*(double)(line.b.y - line.a.y) )
/ ( (double)pow(line.b.x - line.a.x, 2) + (double)pow(line.b.y - line.a.y, 2) );
if (0.0 <= theta && theta <= 1.0)
return theta * line.a + (1.0-theta) * line.b;
// Else pick closest endpoint.
if (this->distance_to(line.a) < this->distance_to(line.b)) {
return line.a;
} else {
return line.b;
}
}
Point
operator+(const Point& point1, const Point& point2)
{
return Point(point1.x + point2.x, point1.y + point2.y);
}
Point
operator*(double scalar, const Point& point2)
{
return Point(scalar * point2.x, scalar * point2.y);
}
#ifdef SLIC3RXS
REGISTER_CLASS(Point, "Point");

View File

@ -9,6 +9,7 @@
namespace Slic3r {
class Line;
class MultiPoint;
class Point;
class Pointf;
typedef Point Vector;
@ -37,6 +38,8 @@ class Point
double distance_to(const Line &line) const;
double ccw(const Point &p1, const Point &p2) const;
double ccw(const Line &line) const;
Point projection_onto(const MultiPoint &poly) const;
Point projection_onto(const Line &line) const;
#ifdef SLIC3RXS
void from_SV(SV* point_sv);
@ -45,6 +48,9 @@ class Point
#endif
};
Point operator+(const Point& point1, const Point& point2);
Point operator*(double scalar, const Point& point2);
class Point3 : public Point
{
public:

View File

@ -4,7 +4,7 @@ use strict;
use warnings;
use Slic3r::XS;
use Test::More tests => 10;
use Test::More tests => 13;
my $point = Slic3r::Point->new(10, 15);
is_deeply [ @$point ], [10, 15], 'point roundtrip';
@ -46,4 +46,16 @@ ok !$point->coincides_with($point2), 'coincides_with';
ok $p0->ccw($p1, $p2) < 0, 'ccw() does not overflow';
}
{
my $point = Slic3r::Point->new(15,15);
my $line = Slic3r::Line->new([10,10], [20,10]);
is_deeply $point->projection_onto_line($line)->pp, [15,10], 'project_onto_line';
$point = Slic3r::Point->new(0, 15);
is_deeply $point->projection_onto_line($line)->pp, [10,10], 'project_onto_line';
$point = Slic3r::Point->new(25, 15);
is_deeply $point->projection_onto_line($line)->pp, [20,10], 'project_onto_line';
}
__END__

View File

@ -29,6 +29,12 @@
%code{% RETVAL = THIS->distance_to(*line); %};
double ccw(Point* p1, Point* p2)
%code{% RETVAL = THIS->ccw(*p1, *p2); %};
Clone<Point> projection_onto_polygon(Polygon* polygon)
%code{% RETVAL = new Point(THIS->projection_onto(*polygon)); %};
Clone<Point> projection_onto_polyline(Polyline* polyline)
%code{% RETVAL = new Point(THIS->projection_onto(*polyline)); %};
Clone<Point> projection_onto_line(Line* line)
%code{% RETVAL = new Point(THIS->projection_onto(*line)); %};
%{