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_triangles
|
||||
bed_grid_lines
|
||||
bed_polygon
|
||||
background
|
||||
origin
|
||||
_mouse_pos
|
||||
|
@ -55,6 +56,7 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init
|
|||
|
||||
_drag_volume_idx
|
||||
_drag_start_pos
|
||||
_drag_volume_center_offset
|
||||
_drag_start_xy
|
||||
_dragged
|
||||
|
||||
|
@ -388,8 +390,18 @@ sub mouse_event {
|
|||
|
||||
if ($volume_idx != -1) {
|
||||
if ($e->LeftDown && $self->enable_moving) {
|
||||
$self->_drag_volume_idx($volume_idx);
|
||||
$self->_drag_start_pos($self->mouse_to_3d(@$pos));
|
||||
my $pos3d = $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) {
|
||||
# if right clicking on volume, propagate event through callback
|
||||
$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)) {
|
||||
# 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 = $mouse_ray->intersect_plane($self->_drag_start_pos->z);
|
||||
|
||||
# calculate the translation vector
|
||||
my $vector = $self->_drag_start_pos->vector_to($cur_pos);
|
||||
|
||||
# get volume being dragged
|
||||
my $volume = $self->volumes->[$self->_drag_volume_idx];
|
||||
|
||||
# get all volumes belonging to the same group, if any
|
||||
my @volumes;
|
||||
if ($volume->drag_group_id == -1) {
|
||||
@volumes = ($volume);
|
||||
} else {
|
||||
@volumes = grep $_->drag_group_id == $volume->drag_group_id, @{$self->volumes};
|
||||
# Get new position at the same Z of the initial click point.
|
||||
my $cur_pos = $self->mouse_ray($e->GetX, $e->GetY)->intersect_plane($self->_drag_start_pos->z);
|
||||
# Clip the new position, so the object center remains close to the bed.
|
||||
{
|
||||
$cur_pos->translate(@{$self->_drag_volume_center_offset});
|
||||
my $cur_pos2 = Slic3r::Point->new(scale($cur_pos->x), scale($cur_pos->y));
|
||||
if (! $self->bed_polygon->contains_point($cur_pos2)) {
|
||||
my $ip = $self->bed_polygon->point_projection($cur_pos2);
|
||||
$cur_pos->set_x(unscale($ip->x));
|
||||
$cur_pos->set_y(unscale($ip->y));
|
||||
}
|
||||
$cur_pos->translate(@{$self->_drag_volume_center_offset->negative});
|
||||
}
|
||||
|
||||
# apply new temporary volume origin and ignore Z
|
||||
$_->translate($vector->x, $vector->y, 0) for @volumes; #,,
|
||||
# Calculate the translation vector.
|
||||
my $vector = $self->_drag_start_pos->vector_to($cur_pos);
|
||||
# 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->_dragged(1);
|
||||
$self->Refresh;
|
||||
|
@ -430,6 +445,7 @@ sub mouse_event {
|
|||
if (defined $self->_drag_start_pos) {
|
||||
my $orig = $self->_drag_start_pos;
|
||||
if (TURNTABLE_MODE) {
|
||||
# Turntable mode is enabled by default.
|
||||
$self->_sphi($self->_sphi + ($pos->x - $orig->x) * TRACKBALLSIZE);
|
||||
$self->_stheta($self->_stheta - ($pos->y - $orig->y) * TRACKBALLSIZE); #-
|
||||
$self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX;
|
||||
|
@ -668,6 +684,8 @@ sub max_bounding_box {
|
|||
return $bb;
|
||||
}
|
||||
|
||||
# Used by ObjectCutDialog and ObjectPartsPanel to generate a rectangular ground plane
|
||||
# to support the scene objects.
|
||||
sub set_auto_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 ], #++
|
||||
]);
|
||||
# Set the origin for painting of the coordinate system axes.
|
||||
$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 {
|
||||
my ($self, $bed_shape) = @_;
|
||||
|
||||
|
@ -695,7 +718,7 @@ sub set_bed_shape {
|
|||
{
|
||||
my @points = ();
|
||||
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));
|
||||
}
|
||||
|
@ -723,7 +746,10 @@ sub set_bed_shape {
|
|||
$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->bed_polygon(offset_ex([$expolygon->contour], $bed_bb->radius * 1.7)->[0]->contour->clone);
|
||||
}
|
||||
|
||||
sub deselect_volumes {
|
||||
|
@ -1073,6 +1099,7 @@ sub Render {
|
|||
}
|
||||
|
||||
if (TURNTABLE_MODE) {
|
||||
# Turntable mode is enabled by default.
|
||||
glRotatef(-$self->_stheta, 1, 0, 0); # pitch
|
||||
glRotatef($self->_sphi, 0, 0, 1); # yaw
|
||||
} else {
|
||||
|
|
|
@ -15,7 +15,7 @@ typedef std::vector<ThickLine> ThickLines;
|
|||
|
||||
class Line
|
||||
{
|
||||
public:
|
||||
public:
|
||||
Point a;
|
||||
Point b;
|
||||
Line() {};
|
||||
|
|
|
@ -140,6 +140,29 @@ MultiPoint::intersection(const Line& line, Point* intersection) const
|
|||
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
|
||||
MultiPoint::dump_perl() const
|
||||
{
|
||||
|
|
|
@ -13,7 +13,7 @@ class BoundingBox;
|
|||
|
||||
class MultiPoint
|
||||
{
|
||||
public:
|
||||
public:
|
||||
Points points;
|
||||
|
||||
operator Points() const;
|
||||
|
@ -35,8 +35,24 @@ class MultiPoint
|
|||
double length() const;
|
||||
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;
|
||||
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;
|
||||
// Return true if there are exact duplicates.
|
||||
bool has_duplicate_points() const;
|
||||
|
@ -56,6 +72,7 @@ class MultiPoint
|
|||
}
|
||||
|
||||
bool intersection(const Line& line, Point* intersection) const;
|
||||
bool first_intersection(const Line& line, Point* intersection) const;
|
||||
std::string dump_perl() const;
|
||||
|
||||
static Points _douglas_peucker(const Points &points, const double tolerance);
|
||||
|
|
|
@ -293,6 +293,44 @@ Polygon::convex_points(double angle) const
|
|||
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)
|
||||
{
|
||||
return poly.bounding_box();
|
||||
|
|
|
@ -51,6 +51,8 @@ public:
|
|||
std::string wkt() const;
|
||||
Points concave_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);
|
||||
|
|
|
@ -95,6 +95,8 @@ new_from_points(CLASS, points)
|
|||
void merge_point(Pointf3* point) %code{% THIS->merge(*point); %};
|
||||
void scale(double factor);
|
||||
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> center();
|
||||
double radius();
|
||||
|
|
|
@ -42,12 +42,20 @@
|
|||
std::string wkt();
|
||||
Points concave_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)
|
||||
%code{%
|
||||
Point p;
|
||||
(void)THIS->intersection(*line, &p);
|
||||
RETVAL = p;
|
||||
%};
|
||||
Clone<Point> first_intersection(Line* line)
|
||||
%code{%
|
||||
Point p;
|
||||
(void)THIS->first_intersection(*line, &p);
|
||||
RETVAL = p;
|
||||
%};
|
||||
%{
|
||||
|
||||
Polygon*
|
||||
|
|
Loading…
Reference in a new issue