Limit the object movement to the vincinity of the print bed.
This commit is contained in:
parent
dabcff1c07
commit
8b5f7f0fb2
8 changed files with 142 additions and 25 deletions
|
@ -48,6 +48,7 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init
|
||||||
bed_shape
|
bed_shape
|
||||||
bed_triangles
|
bed_triangles
|
||||||
bed_grid_lines
|
bed_grid_lines
|
||||||
|
bed_polygon
|
||||||
background
|
background
|
||||||
origin
|
origin
|
||||||
_mouse_pos
|
_mouse_pos
|
||||||
|
@ -55,6 +56,7 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init
|
||||||
|
|
||||||
_drag_volume_idx
|
_drag_volume_idx
|
||||||
_drag_start_pos
|
_drag_start_pos
|
||||||
|
_drag_volume_center_offset
|
||||||
_drag_start_xy
|
_drag_start_xy
|
||||||
_dragged
|
_dragged
|
||||||
|
|
||||||
|
@ -388,8 +390,18 @@ sub mouse_event {
|
||||||
|
|
||||||
if ($volume_idx != -1) {
|
if ($volume_idx != -1) {
|
||||||
if ($e->LeftDown && $self->enable_moving) {
|
if ($e->LeftDown && $self->enable_moving) {
|
||||||
$self->_drag_volume_idx($volume_idx);
|
my $pos3d = $self->mouse_to_3d(@$pos);
|
||||||
$self->_drag_start_pos($self->mouse_to_3d(@$pos));
|
# Only accept the initial position, if it is inside the volume bounding box.
|
||||||
|
my $volume_bbox = $self->volumes->[$volume_idx]->transformed_bounding_box;
|
||||||
|
$volume_bbox->offset(0.01);
|
||||||
|
if ($volume_bbox->contains_point($pos3d)) {
|
||||||
|
# The dragging operation is initiated.
|
||||||
|
$self->_drag_volume_idx($volume_idx);
|
||||||
|
$self->_drag_start_pos($pos3d);
|
||||||
|
# Remember the shift to to the object center. The object center will later be used
|
||||||
|
# to limit the object placement close to the bed.
|
||||||
|
$self->_drag_volume_center_offset($pos3d->vector_to($volume_bbox->center));
|
||||||
|
}
|
||||||
} elsif ($e->RightDown) {
|
} elsif ($e->RightDown) {
|
||||||
# if right clicking on volume, propagate event through callback
|
# if right clicking on volume, propagate event through callback
|
||||||
$self->on_right_click->($e->GetPosition)
|
$self->on_right_click->($e->GetPosition)
|
||||||
|
@ -398,26 +410,29 @@ sub mouse_event {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elsif ($e->Dragging && $e->LeftIsDown && ! $self->_layer_height_edited && defined($self->_drag_volume_idx)) {
|
} elsif ($e->Dragging && $e->LeftIsDown && ! $self->_layer_height_edited && defined($self->_drag_volume_idx)) {
|
||||||
# get new position at the same Z of the initial click point
|
# Get new position at the same Z of the initial click point.
|
||||||
my $mouse_ray = $self->mouse_ray($e->GetX, $e->GetY);
|
my $cur_pos = $self->mouse_ray($e->GetX, $e->GetY)->intersect_plane($self->_drag_start_pos->z);
|
||||||
my $cur_pos = $mouse_ray->intersect_plane($self->_drag_start_pos->z);
|
# Clip the new position, so the object center remains close to the bed.
|
||||||
|
{
|
||||||
# calculate the translation vector
|
$cur_pos->translate(@{$self->_drag_volume_center_offset});
|
||||||
my $vector = $self->_drag_start_pos->vector_to($cur_pos);
|
my $cur_pos2 = Slic3r::Point->new(scale($cur_pos->x), scale($cur_pos->y));
|
||||||
|
if (! $self->bed_polygon->contains_point($cur_pos2)) {
|
||||||
# get volume being dragged
|
my $ip = $self->bed_polygon->point_projection($cur_pos2);
|
||||||
my $volume = $self->volumes->[$self->_drag_volume_idx];
|
$cur_pos->set_x(unscale($ip->x));
|
||||||
|
$cur_pos->set_y(unscale($ip->y));
|
||||||
# get all volumes belonging to the same group, if any
|
}
|
||||||
my @volumes;
|
$cur_pos->translate(@{$self->_drag_volume_center_offset->negative});
|
||||||
if ($volume->drag_group_id == -1) {
|
|
||||||
@volumes = ($volume);
|
|
||||||
} else {
|
|
||||||
@volumes = grep $_->drag_group_id == $volume->drag_group_id, @{$self->volumes};
|
|
||||||
}
|
}
|
||||||
|
# Calculate the translation vector.
|
||||||
# apply new temporary volume origin and ignore Z
|
my $vector = $self->_drag_start_pos->vector_to($cur_pos);
|
||||||
$_->translate($vector->x, $vector->y, 0) for @volumes; #,,
|
# Get the volume being dragged.
|
||||||
|
my $volume = $self->volumes->[$self->_drag_volume_idx];
|
||||||
|
# Get all volumes belonging to the same group, if any.
|
||||||
|
my @volumes = ($volume->drag_group_id == -1) ?
|
||||||
|
($volume) :
|
||||||
|
grep $_->drag_group_id == $volume->drag_group_id, @{$self->volumes};
|
||||||
|
# Apply new temporary volume origin and ignore Z.
|
||||||
|
$_->translate($vector->x, $vector->y, 0) for @volumes;
|
||||||
$self->_drag_start_pos($cur_pos);
|
$self->_drag_start_pos($cur_pos);
|
||||||
$self->_dragged(1);
|
$self->_dragged(1);
|
||||||
$self->Refresh;
|
$self->Refresh;
|
||||||
|
@ -430,6 +445,7 @@ sub mouse_event {
|
||||||
if (defined $self->_drag_start_pos) {
|
if (defined $self->_drag_start_pos) {
|
||||||
my $orig = $self->_drag_start_pos;
|
my $orig = $self->_drag_start_pos;
|
||||||
if (TURNTABLE_MODE) {
|
if (TURNTABLE_MODE) {
|
||||||
|
# Turntable mode is enabled by default.
|
||||||
$self->_sphi($self->_sphi + ($pos->x - $orig->x) * TRACKBALLSIZE);
|
$self->_sphi($self->_sphi + ($pos->x - $orig->x) * TRACKBALLSIZE);
|
||||||
$self->_stheta($self->_stheta - ($pos->y - $orig->y) * TRACKBALLSIZE); #-
|
$self->_stheta($self->_stheta - ($pos->y - $orig->y) * TRACKBALLSIZE); #-
|
||||||
$self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX;
|
$self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX;
|
||||||
|
@ -668,6 +684,8 @@ sub max_bounding_box {
|
||||||
return $bb;
|
return $bb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Used by ObjectCutDialog and ObjectPartsPanel to generate a rectangular ground plane
|
||||||
|
# to support the scene objects.
|
||||||
sub set_auto_bed_shape {
|
sub set_auto_bed_shape {
|
||||||
my ($self, $bed_shape) = @_;
|
my ($self, $bed_shape) = @_;
|
||||||
|
|
||||||
|
@ -680,9 +698,14 @@ sub set_auto_bed_shape {
|
||||||
[ $center->x + $max_size, $center->y + $max_size ], #++
|
[ $center->x + $max_size, $center->y + $max_size ], #++
|
||||||
[ $center->x - $max_size, $center->y + $max_size ], #++
|
[ $center->x - $max_size, $center->y + $max_size ], #++
|
||||||
]);
|
]);
|
||||||
|
# Set the origin for painting of the coordinate system axes.
|
||||||
$self->origin(Slic3r::Pointf->new(@$center[X,Y]));
|
$self->origin(Slic3r::Pointf->new(@$center[X,Y]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Set the bed shape to a single closed 2D polygon (array of two element arrays),
|
||||||
|
# triangulate the bed and store the triangles into $self->bed_triangles,
|
||||||
|
# fills the $self->bed_grid_lines and sets $self->origin.
|
||||||
|
# Sets $self->bed_polygon to limit the object placement.
|
||||||
sub set_bed_shape {
|
sub set_bed_shape {
|
||||||
my ($self, $bed_shape) = @_;
|
my ($self, $bed_shape) = @_;
|
||||||
|
|
||||||
|
@ -695,7 +718,7 @@ sub set_bed_shape {
|
||||||
{
|
{
|
||||||
my @points = ();
|
my @points = ();
|
||||||
foreach my $triangle (@{ $expolygon->triangulate }) {
|
foreach my $triangle (@{ $expolygon->triangulate }) {
|
||||||
push @points, map {+ unscale($_->x), unscale($_->y), GROUND_Z } @$triangle; #))
|
push @points, map {+ unscale($_->x), unscale($_->y), GROUND_Z } @$triangle;
|
||||||
}
|
}
|
||||||
$self->bed_triangles(OpenGL::Array->new_list(GL_FLOAT, @points));
|
$self->bed_triangles(OpenGL::Array->new_list(GL_FLOAT, @points));
|
||||||
}
|
}
|
||||||
|
@ -723,7 +746,10 @@ sub set_bed_shape {
|
||||||
$self->bed_grid_lines(OpenGL::Array->new_list(GL_FLOAT, @points));
|
$self->bed_grid_lines(OpenGL::Array->new_list(GL_FLOAT, @points));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Set the origin for painting of the coordinate system axes.
|
||||||
$self->origin(Slic3r::Pointf->new(0,0));
|
$self->origin(Slic3r::Pointf->new(0,0));
|
||||||
|
|
||||||
|
$self->bed_polygon(offset_ex([$expolygon->contour], $bed_bb->radius * 1.7)->[0]->contour->clone);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub deselect_volumes {
|
sub deselect_volumes {
|
||||||
|
@ -1073,6 +1099,7 @@ sub Render {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TURNTABLE_MODE) {
|
if (TURNTABLE_MODE) {
|
||||||
|
# Turntable mode is enabled by default.
|
||||||
glRotatef(-$self->_stheta, 1, 0, 0); # pitch
|
glRotatef(-$self->_stheta, 1, 0, 0); # pitch
|
||||||
glRotatef($self->_sphi, 0, 0, 1); # yaw
|
glRotatef($self->_sphi, 0, 0, 1); # yaw
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -15,7 +15,7 @@ typedef std::vector<ThickLine> ThickLines;
|
||||||
|
|
||||||
class Line
|
class Line
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Point a;
|
Point a;
|
||||||
Point b;
|
Point b;
|
||||||
Line() {};
|
Line() {};
|
||||||
|
|
|
@ -140,6 +140,29 @@ MultiPoint::intersection(const Line& line, Point* intersection) const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MultiPoint::first_intersection(const Line& line, Point* intersection) const
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
double dmin = 0.;
|
||||||
|
for (const Line &l : this->lines()) {
|
||||||
|
Point ip;
|
||||||
|
if (l.intersection(line, &ip)) {
|
||||||
|
if (! found) {
|
||||||
|
found = true;
|
||||||
|
dmin = ip.distance_to(line.a);
|
||||||
|
*intersection = ip;
|
||||||
|
} else {
|
||||||
|
double d = ip.distance_to(line.a);
|
||||||
|
if (d < dmin) {
|
||||||
|
dmin = d;
|
||||||
|
*intersection = ip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
MultiPoint::dump_perl() const
|
MultiPoint::dump_perl() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,7 +13,7 @@ class BoundingBox;
|
||||||
|
|
||||||
class MultiPoint
|
class MultiPoint
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Points points;
|
Points points;
|
||||||
|
|
||||||
operator Points() const;
|
operator Points() const;
|
||||||
|
@ -35,8 +35,24 @@ class MultiPoint
|
||||||
double length() const;
|
double length() const;
|
||||||
bool is_valid() const { return this->points.size() >= 2; }
|
bool is_valid() const { return this->points.size() >= 2; }
|
||||||
|
|
||||||
int find_point(const Point &point) const;
|
int find_point(const Point &point) const;
|
||||||
bool has_boundary_point(const Point &point) const;
|
bool has_boundary_point(const Point &point) const;
|
||||||
|
int closest_point_index(const Point &point) const {
|
||||||
|
int idx = -1;
|
||||||
|
if (! this->points.empty()) {
|
||||||
|
idx = 0;
|
||||||
|
double dist_min = this->points.front().distance_to(point);
|
||||||
|
for (int i = 1; i < int(this->points.size()); ++ i) {
|
||||||
|
double d = this->points[i].distance_to(point);
|
||||||
|
if (d < dist_min) {
|
||||||
|
dist_min = d;
|
||||||
|
idx = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
const Point* closest_point(const Point &point) const { return this->points.empty() ? nullptr : &this->points[this->closest_point_index(point)]; }
|
||||||
BoundingBox bounding_box() const;
|
BoundingBox bounding_box() const;
|
||||||
// Return true if there are exact duplicates.
|
// Return true if there are exact duplicates.
|
||||||
bool has_duplicate_points() const;
|
bool has_duplicate_points() const;
|
||||||
|
@ -56,6 +72,7 @@ class MultiPoint
|
||||||
}
|
}
|
||||||
|
|
||||||
bool intersection(const Line& line, Point* intersection) const;
|
bool intersection(const Line& line, Point* intersection) const;
|
||||||
|
bool first_intersection(const Line& line, Point* intersection) const;
|
||||||
std::string dump_perl() const;
|
std::string dump_perl() const;
|
||||||
|
|
||||||
static Points _douglas_peucker(const Points &points, const double tolerance);
|
static Points _douglas_peucker(const Points &points, const double tolerance);
|
||||||
|
|
|
@ -293,6 +293,44 @@ Polygon::convex_points(double angle) const
|
||||||
return points;
|
return points;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Projection of a point onto the polygon.
|
||||||
|
Point Polygon::point_projection(const Point &point) const
|
||||||
|
{
|
||||||
|
Point proj = point;
|
||||||
|
double dmin = std::numeric_limits<double>::max();
|
||||||
|
if (! this->points.empty()) {
|
||||||
|
for (size_t i = 0; i < this->points.size(); ++ i) {
|
||||||
|
const Point &pt0 = this->points[i];
|
||||||
|
const Point &pt1 = this->points[(i + 1 == this->points.size()) ? 0 : i + 1];
|
||||||
|
double d = pt0.distance_to(point);
|
||||||
|
if (d < dmin) {
|
||||||
|
dmin = d;
|
||||||
|
proj = pt0;
|
||||||
|
}
|
||||||
|
d = pt1.distance_to(point);
|
||||||
|
if (d < dmin) {
|
||||||
|
dmin = d;
|
||||||
|
proj = pt1;
|
||||||
|
}
|
||||||
|
Pointf v1(coordf_t(pt1.x - pt0.x), coordf_t(pt1.y - pt0.y));
|
||||||
|
coordf_t div = dot(v1);
|
||||||
|
if (div > 0.) {
|
||||||
|
Pointf v2(coordf_t(point.x - pt0.x), coordf_t(point.y - pt0.y));
|
||||||
|
coordf_t t = dot(v1, v2) / div;
|
||||||
|
if (t > 0. && t < 1.) {
|
||||||
|
Point foot(coord_t(floor(coordf_t(pt0.x) + t * v1.x + 0.5)), coord_t(floor(coordf_t(pt0.y) + t * v1.y + 0.5)));
|
||||||
|
d = foot.distance_to(point);
|
||||||
|
if (d < dmin) {
|
||||||
|
dmin = d;
|
||||||
|
proj = foot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return proj;
|
||||||
|
}
|
||||||
|
|
||||||
BoundingBox get_extents(const Polygon &poly)
|
BoundingBox get_extents(const Polygon &poly)
|
||||||
{
|
{
|
||||||
return poly.bounding_box();
|
return poly.bounding_box();
|
||||||
|
|
|
@ -51,6 +51,8 @@ public:
|
||||||
std::string wkt() const;
|
std::string wkt() const;
|
||||||
Points concave_points(double angle = PI) const;
|
Points concave_points(double angle = PI) const;
|
||||||
Points convex_points(double angle = PI) const;
|
Points convex_points(double angle = PI) const;
|
||||||
|
// Projection of a point onto the polygon.
|
||||||
|
Point point_projection(const Point &point) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern BoundingBox get_extents(const Polygon &poly);
|
extern BoundingBox get_extents(const Polygon &poly);
|
||||||
|
|
|
@ -95,6 +95,8 @@ new_from_points(CLASS, points)
|
||||||
void merge_point(Pointf3* point) %code{% THIS->merge(*point); %};
|
void merge_point(Pointf3* point) %code{% THIS->merge(*point); %};
|
||||||
void scale(double factor);
|
void scale(double factor);
|
||||||
void translate(double x, double y, double z);
|
void translate(double x, double y, double z);
|
||||||
|
void offset(double delta);
|
||||||
|
bool contains_point(Pointf3* point) %code{% RETVAL = THIS->contains(*point); %};
|
||||||
Clone<Pointf3> size();
|
Clone<Pointf3> size();
|
||||||
Clone<Pointf3> center();
|
Clone<Pointf3> center();
|
||||||
double radius();
|
double radius();
|
||||||
|
|
|
@ -42,12 +42,20 @@
|
||||||
std::string wkt();
|
std::string wkt();
|
||||||
Points concave_points(double angle);
|
Points concave_points(double angle);
|
||||||
Points convex_points(double angle);
|
Points convex_points(double angle);
|
||||||
|
Clone<Point> point_projection(Point* point)
|
||||||
|
%code{% RETVAL = THIS->point_projection(*point); %};
|
||||||
Clone<Point> intersection(Line* line)
|
Clone<Point> intersection(Line* line)
|
||||||
%code{%
|
%code{%
|
||||||
Point p;
|
Point p;
|
||||||
(void)THIS->intersection(*line, &p);
|
(void)THIS->intersection(*line, &p);
|
||||||
RETVAL = p;
|
RETVAL = p;
|
||||||
%};
|
%};
|
||||||
|
Clone<Point> first_intersection(Line* line)
|
||||||
|
%code{%
|
||||||
|
Point p;
|
||||||
|
(void)THIS->first_intersection(*line, &p);
|
||||||
|
RETVAL = p;
|
||||||
|
%};
|
||||||
%{
|
%{
|
||||||
|
|
||||||
Polygon*
|
Polygon*
|
||||||
|
|
Loading…
Reference in a new issue