Out of bed detection - 1st installment

This commit is contained in:
Enrico Turri 2018-03-09 10:40:42 +01:00
parent 83a2d2af4b
commit bdd2d725c8
15 changed files with 689 additions and 188 deletions

View File

@ -68,6 +68,7 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init
_zoom _zoom
_legend_enabled _legend_enabled
_warning_enabled
_apply_zoom_to_volumes_filter _apply_zoom_to_volumes_filter
) ); ) );
@ -142,6 +143,7 @@ sub new {
$self->_sphi(45); $self->_sphi(45);
$self->_zoom(1); $self->_zoom(1);
$self->_legend_enabled(0); $self->_legend_enabled(0);
$self->_warning_enabled(0);
$self->use_plain_shader(0); $self->use_plain_shader(0);
$self->_apply_zoom_to_volumes_filter(0); $self->_apply_zoom_to_volumes_filter(0);
@ -217,7 +219,12 @@ sub new {
sub set_legend_enabled { sub set_legend_enabled {
my ($self, $value) = @_; my ($self, $value) = @_;
$self->_legend_enabled($value); $self->_legend_enabled($value);
}
sub set_warning_enabled {
my ($self, $value) = @_;
$self->_warning_enabled($value);
} }
sub Destroy { sub Destroy {
@ -1296,11 +1303,14 @@ sub Render {
} }
glEnable(GL_LIGHTING); glEnable(GL_LIGHTING);
# draw objects # draw objects
if (! $self->use_plain_shader) { if (! $self->use_plain_shader) {
$self->draw_volumes; $self->draw_volumes;
} elsif ($self->UseVBOs) { } elsif ($self->UseVBOs) {
if ($self->enable_picking) {
$self->volumes->set_print_box($self->bed_bounding_box->x_min, $self->bed_bounding_box->y_min, 0.0, $self->bed_bounding_box->x_max, $self->bed_bounding_box->y_max, $self->{config}->get('max_print_height'));
}
$self->{plain_shader}->enable if $self->{plain_shader}; $self->{plain_shader}->enable if $self->{plain_shader};
$self->volumes->render_VBOs; $self->volumes->render_VBOs;
$self->{plain_shader}->disable; $self->{plain_shader}->disable;
@ -1327,6 +1337,9 @@ sub Render {
glDisable(GL_BLEND); glDisable(GL_BLEND);
} }
# draw warning message
$self->draw_warning;
# draw gcode preview legend # draw gcode preview legend
$self->draw_legend; $self->draw_legend;
@ -1599,31 +1612,65 @@ sub draw_active_object_annotations {
sub draw_legend { sub draw_legend {
my ($self) = @_; my ($self) = @_;
if ($self->_legend_enabled) if (!$self->_legend_enabled) {
{ return;
# If the legend texture has not been loaded into the GPU, do it now. }
my $tex_id = Slic3r::GUI::_3DScene::finalize_legend_texture;
if ($tex_id > 0)
{
my $tex_w = Slic3r::GUI::_3DScene::get_legend_texture_width;
my $tex_h = Slic3r::GUI::_3DScene::get_legend_texture_height;
if (($tex_w > 0) && ($tex_h > 0))
{
glDisable(GL_DEPTH_TEST);
glPushMatrix();
glLoadIdentity();
my ($cw, $ch) = $self->GetSizeWH;
my $l = (-0.5 * $cw) / $self->_zoom;
my $t = (0.5 * $ch) / $self->_zoom;
my $r = $l + $tex_w / $self->_zoom;
my $b = $t - $tex_h / $self->_zoom;
$self->_render_texture($tex_id, $l, $r, $b, $t);
glPopMatrix(); # If the legend texture has not been loaded into the GPU, do it now.
glEnable(GL_DEPTH_TEST); my $tex_id = Slic3r::GUI::_3DScene::finalize_legend_texture;
} if ($tex_id > 0)
{
my $tex_w = Slic3r::GUI::_3DScene::get_legend_texture_width;
my $tex_h = Slic3r::GUI::_3DScene::get_legend_texture_height;
if (($tex_w > 0) && ($tex_h > 0))
{
glDisable(GL_DEPTH_TEST);
glPushMatrix();
glLoadIdentity();
my ($cw, $ch) = $self->GetSizeWH;
my $l = (-0.5 * $cw) / $self->_zoom;
my $t = (0.5 * $ch) / $self->_zoom;
my $r = $l + $tex_w / $self->_zoom;
my $b = $t - $tex_h / $self->_zoom;
$self->_render_texture($tex_id, $l, $r, $b, $t);
glPopMatrix();
glEnable(GL_DEPTH_TEST);
}
}
}
sub draw_warning {
my ($self) = @_;
if (!$self->_warning_enabled) {
return;
}
# If the warning texture has not been loaded into the GPU, do it now.
my $tex_id = Slic3r::GUI::_3DScene::finalize_warning_texture;
if ($tex_id > 0)
{
my $tex_w = Slic3r::GUI::_3DScene::get_warning_texture_width;
my $tex_h = Slic3r::GUI::_3DScene::get_warning_texture_height;
if (($tex_w > 0) && ($tex_h > 0))
{
glDisable(GL_DEPTH_TEST);
glPushMatrix();
glLoadIdentity();
my ($cw, $ch) = $self->GetSizeWH;
my $l = (-0.5 * $tex_w) / $self->_zoom;
my $t = (-0.5 * $ch + $tex_h) / $self->_zoom;
my $r = $l + $tex_w / $self->_zoom;
my $b = $t - $tex_h / $self->_zoom;
$self->_render_texture($tex_id, $l, $r, $b, $t);
glPopMatrix();
glEnable(GL_DEPTH_TEST);
} }
} }
} }
@ -1694,39 +1741,55 @@ sub _vertex_shader_Gouraud {
#define INTENSITY_CORRECTION 0.7 #define INTENSITY_CORRECTION 0.7
#define LIGHT_TOP_DIR -0.6/1.31, 0.6/1.31, 1./1.31 // normalized values for (-0.6/1.31, 0.6/1.31, 1./1.31)
#define LIGHT_TOP_DIR vec3(-0.4574957, 0.4574957, 0.7624929)
#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) #define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION)
#define LIGHT_TOP_SPECULAR (0.5 * INTENSITY_CORRECTION) #define LIGHT_TOP_SPECULAR (0.5 * INTENSITY_CORRECTION)
#define LIGHT_TOP_SHININESS 50. #define LIGHT_TOP_SHININESS 50.
#define LIGHT_FRONT_DIR 1./1.43, 0.2/1.43, 1./1.43 // normalized values for (1./1.43, 0.2/1.43, 1./1.43)
#define LIGHT_FRONT_DIR vec3(0.6985074, 0.1397015, 0.6985074)
#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) #define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION)
#define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION) #define LIGHT_FRONT_SPECULAR (0.0 * INTENSITY_CORRECTION)
#define LIGHT_FRONT_SHININESS 50. #define LIGHT_FRONT_SHININESS 50.
#define INTENSITY_AMBIENT 0.3 #define INTENSITY_AMBIENT 0.3
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
struct PrintBoxDetection
{
vec3 min;
vec3 max;
// xyz contains the offset, if w == 1.0 detection needs to be performed
vec4 volume_origin;
};
uniform PrintBoxDetection print_box;
varying float intensity_specular; varying float intensity_specular;
varying float intensity_tainted; varying float intensity_tainted;
varying vec3 delta_box_min;
varying vec3 delta_box_max;
void main() void main()
{ {
vec3 eye, normal, lightDir, viewVector, halfVector; vec3 normal, viewVector, halfVector;
float NdotL, NdotHV; float NdotL, NdotHV;
eye = vec3(0., 0., 1.); vec3 eye = -(gl_ModelViewMatrix * gl_Vertex).xyz;
// First transform the normal into eye space and normalize the result. // First transform the normal into eye space and normalize the result.
normal = normalize(gl_NormalMatrix * gl_Normal); normal = normalize(gl_NormalMatrix * gl_Normal);
// Now normalize the light's direction. Note that according to the OpenGL specification, the light is stored in eye space. // Now normalize the light's direction. Note that according to the OpenGL specification, the light is stored in eye space.
// Also since we're talking about a directional light, the position field is actually direction. // Also since we're talking about a directional light, the position field is actually direction.
lightDir = vec3(LIGHT_TOP_DIR); halfVector = normalize(LIGHT_TOP_DIR + eye);
halfVector = normalize(lightDir + eye);
// Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
// Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
NdotL = max(dot(normal, lightDir), 0.0); NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0);
intensity_tainted = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; intensity_tainted = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
intensity_specular = 0.; intensity_specular = 0.;
@ -1735,15 +1798,27 @@ void main()
intensity_specular = LIGHT_TOP_SPECULAR * pow(max(dot(normal, halfVector), 0.0), LIGHT_TOP_SHININESS); intensity_specular = LIGHT_TOP_SPECULAR * pow(max(dot(normal, halfVector), 0.0), LIGHT_TOP_SHININESS);
// Perform the same lighting calculation for the 2nd light source. // Perform the same lighting calculation for the 2nd light source.
lightDir = vec3(LIGHT_FRONT_DIR); // halfVector = normalize(LIGHT_FRONT_DIR + eye);
// halfVector = normalize(lightDir + eye); NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0);
NdotL = max(dot(normal, lightDir), 0.0);
intensity_tainted += NdotL * LIGHT_FRONT_DIFFUSE; intensity_tainted += NdotL * LIGHT_FRONT_DIFFUSE;
// compute the specular term if NdotL is larger than zero // compute the specular term if NdotL is larger than zero
// if (NdotL > 0.0) // if (NdotL > 0.0)
// intensity_specular += LIGHT_FRONT_SPECULAR * pow(max(dot(normal, halfVector), 0.0), LIGHT_FRONT_SHININESS); // intensity_specular += LIGHT_FRONT_SPECULAR * pow(max(dot(normal, halfVector), 0.0), LIGHT_FRONT_SHININESS);
// compute deltas for out of print volume detection (world coordinates)
if (print_box.volume_origin.w == 1.0)
{
vec3 v = gl_Vertex.xyz + print_box.volume_origin.xyz;
delta_box_min = v - print_box.min;
delta_box_max = v - print_box.max;
}
else
{
delta_box_min = ZERO;
delta_box_max = ZERO;
}
gl_Position = ftransform(); gl_Position = ftransform();
} }
@ -1756,14 +1831,23 @@ sub _fragment_shader_Gouraud {
varying float intensity_specular; varying float intensity_specular;
varying float intensity_tainted; varying float intensity_tainted;
varying vec3 delta_box_min;
varying vec3 delta_box_max;
uniform vec4 uniform_color; uniform vec4 uniform_color;
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
void main() void main()
{ {
gl_FragColor = gl_FragColor =
vec4(intensity_specular, intensity_specular, intensity_specular, 0.) + uniform_color * intensity_tainted; vec4(intensity_specular, intensity_specular, intensity_specular, 0.) + uniform_color * intensity_tainted;
gl_FragColor.a = uniform_color.a;
// if the fragment is outside the print volume darken it and set it as transparent
if (any(lessThan(delta_box_min, ZERO)) || any(greaterThan(delta_box_max, ZERO)))
gl_FragColor = vec4(mix(gl_FragColor.xyz, ZERO, 0.5), 0.5 * uniform_color.a);
else
gl_FragColor.a = uniform_color.a;
} }
FRAGMENT FRAGMENT
@ -1828,6 +1912,7 @@ void main() {
// compute the specular term into spec // compute the specular term into spec
// intensity_specular += LIGHT_FRONT_SPECULAR * pow(max(dot(h,normal), 0.0), LIGHT_FRONT_SHININESS); // intensity_specular += LIGHT_FRONT_SPECULAR * pow(max(dot(h,normal), 0.0), LIGHT_FRONT_SHININESS);
} }
gl_FragColor = max( gl_FragColor = max(
vec4(intensity_specular, intensity_specular, intensity_specular, 0.) + uniform_color * intensity_tainted, vec4(intensity_specular, intensity_specular, intensity_specular, 0.) + uniform_color * intensity_tainted,
INTENSITY_AMBIENT * uniform_color); INTENSITY_AMBIENT * uniform_color);
@ -1840,12 +1925,12 @@ sub _vertex_shader_variable_layer_height {
return <<'VERTEX'; return <<'VERTEX';
#version 110 #version 110
#define LIGHT_TOP_DIR 0., 1., 0. const vec3 LIGHT_TOP_DIR = vec3(0.0, 1.0, 0.0);
#define LIGHT_TOP_DIFFUSE 0.2 #define LIGHT_TOP_DIFFUSE 0.2
#define LIGHT_TOP_SPECULAR 0.3 #define LIGHT_TOP_SPECULAR 0.3
#define LIGHT_TOP_SHININESS 50. #define LIGHT_TOP_SHININESS 50.
#define LIGHT_FRONT_DIR 0., 0., 1. const vec3 LIGHT_FRONT_DIR = vec3(0.0, 0.0, 1.0);
#define LIGHT_FRONT_DIFFUSE 0.5 #define LIGHT_FRONT_DIFFUSE 0.5
#define LIGHT_FRONT_SPECULAR 0.3 #define LIGHT_FRONT_SPECULAR 0.3
#define LIGHT_FRONT_SHININESS 50. #define LIGHT_FRONT_SHININESS 50.
@ -1859,7 +1944,7 @@ varying float object_z;
void main() void main()
{ {
vec3 eye, normal, lightDir, viewVector, halfVector; vec3 eye, normal, viewVector, halfVector;
float NdotL, NdotHV; float NdotL, NdotHV;
// eye = gl_ModelViewMatrixInverse[3].xyz; // eye = gl_ModelViewMatrixInverse[3].xyz;
@ -1870,12 +1955,11 @@ void main()
// Now normalize the light's direction. Note that according to the OpenGL specification, the light is stored in eye space. // Now normalize the light's direction. Note that according to the OpenGL specification, the light is stored in eye space.
// Also since we're talking about a directional light, the position field is actually direction. // Also since we're talking about a directional light, the position field is actually direction.
lightDir = vec3(LIGHT_TOP_DIR); halfVector = normalize(LIGHT_TOP_DIR + eye);
halfVector = normalize(lightDir + eye);
// Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex. // Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
// Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range. // Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
NdotL = max(dot(normal, lightDir), 0.0); NdotL = max(dot(normal, LIGHT_TOP_DIR), 0.0);
intensity_tainted = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; intensity_tainted = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
intensity_specular = 0.; intensity_specular = 0.;
@ -1884,9 +1968,8 @@ void main()
// intensity_specular = LIGHT_TOP_SPECULAR * pow(max(dot(normal, halfVector), 0.0), LIGHT_TOP_SHININESS); // intensity_specular = LIGHT_TOP_SPECULAR * pow(max(dot(normal, halfVector), 0.0), LIGHT_TOP_SHININESS);
// Perform the same lighting calculation for the 2nd light source. // Perform the same lighting calculation for the 2nd light source.
lightDir = vec3(LIGHT_FRONT_DIR); halfVector = normalize(LIGHT_FRONT_DIR + eye);
halfVector = normalize(lightDir + eye); NdotL = max(dot(normal, LIGHT_FRONT_DIR), 0.0);
NdotL = max(dot(normal, lightDir), 0.0);
intensity_tainted += NdotL * LIGHT_FRONT_DIFFUSE; intensity_tainted += NdotL * LIGHT_FRONT_DIFFUSE;
// compute the specular term if NdotL is larger than zero // compute the specular term if NdotL is larger than zero

View File

@ -55,6 +55,7 @@ sub new {
serial_port serial_speed octoprint_host octoprint_apikey octoprint_cafile serial_port serial_speed octoprint_host octoprint_apikey octoprint_cafile
nozzle_diameter single_extruder_multi_material nozzle_diameter single_extruder_multi_material
wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe extruder_colour filament_colour wipe_tower wipe_tower_x wipe_tower_y wipe_tower_width wipe_tower_per_color_wipe extruder_colour filament_colour
max_print_height
)]); )]);
# C++ Slic3r::Model with Perl extensions in Slic3r/Model.pm # C++ Slic3r::Model with Perl extensions in Slic3r/Model.pm
$self->{model} = Slic3r::Model->new; $self->{model} = Slic3r::Model->new;
@ -113,6 +114,7 @@ sub new {
$self->{canvas3D}->set_on_decrease_objects(sub { $self->decrease() }); $self->{canvas3D}->set_on_decrease_objects(sub { $self->decrease() });
$self->{canvas3D}->set_on_remove_object(sub { $self->remove() }); $self->{canvas3D}->set_on_remove_object(sub { $self->remove() });
$self->{canvas3D}->set_on_instances_moved($on_instances_moved); $self->{canvas3D}->set_on_instances_moved($on_instances_moved);
$self->{canvas3D}->use_plain_shader(1);
$self->{canvas3D}->set_on_wipe_tower_moved(sub { $self->{canvas3D}->set_on_wipe_tower_moved(sub {
my ($new_pos_3f) = @_; my ($new_pos_3f) = @_;
my $cfg = Slic3r::Config->new; my $cfg = Slic3r::Config->new;
@ -1737,6 +1739,8 @@ sub on_config_change {
$update_scheduled = 1; $update_scheduled = 1;
my $extruder_colors = $config->get('extruder_colour'); my $extruder_colors = $config->get('extruder_colour');
$self->{preview3D}->set_number_extruders(scalar(@{$extruder_colors})); $self->{preview3D}->set_number_extruders(scalar(@{$extruder_colors}));
} elsif ($opt_key eq 'max_print_height') {
$update_scheduled = 1;
} }
} }

View File

@ -8,6 +8,8 @@ use Wx qw(:misc :pen :brush :sizer :font :cursor :keycode wxTAB_TRAVERSAL);
use Wx::Event qw(EVT_KEY_DOWN EVT_CHAR); use Wx::Event qw(EVT_KEY_DOWN EVT_CHAR);
use base qw(Slic3r::GUI::3DScene Class::Accessor); use base qw(Slic3r::GUI::3DScene Class::Accessor);
use Wx::Locale gettext => 'L';
__PACKAGE__->mk_accessors(qw( __PACKAGE__->mk_accessors(qw(
on_arrange on_rotate_object_left on_rotate_object_right on_scale_object_uniformly on_arrange on_rotate_object_left on_rotate_object_right on_scale_object_uniformly
on_remove_object on_increase_objects on_decrease_objects)); on_remove_object on_increase_objects on_decrease_objects));
@ -208,6 +210,20 @@ sub reload_scene {
$self->{model}->bounding_box->z_max, $self->UseVBOs); $self->{model}->bounding_box->z_max, $self->UseVBOs);
} }
} }
# checks for geometry outside the print volume to render it accordingly
if (scalar @{$self->volumes} > 0)
{
if (!$self->{model}->fits_print_volume($self->{config})) {
$self->set_warning_enabled(1);
$self->volumes->update_outside_state($self->{config}, 0);
Slic3r::GUI::_3DScene::generate_warning_texture(L("Detected object outside print volume"));
} else {
$self->set_warning_enabled(0);
$self->volumes->update_outside_state($self->{config}, 1);
Slic3r::GUI::_3DScene::reset_warning_texture();
}
}
} }
sub update_bed_size { sub update_bed_size {

View File

@ -94,6 +94,38 @@ public:
void translate(const Pointf3 &pos) { this->translate(pos.x, pos.y, pos.z); } void translate(const Pointf3 &pos) { this->translate(pos.x, pos.y, pos.z); }
void offset(coordf_t delta); void offset(coordf_t delta);
PointClass center() const; PointClass center() const;
bool contains(const PointClass &point) const {
return BoundingBoxBase<PointClass>::contains(point) && point.z >= this->min.z && point.z <= this->max.z;
}
bool contains(const BoundingBox3Base<PointClass>& other) const {
if (!contains(other.min))
return false;
if (!contains(PointClass(other.max.x, other.min.y, other.min.z)))
return false;
if (!contains(PointClass(other.max.x, other.max.y, other.min.z)))
return false;
if (!contains(PointClass(other.min.x, other.max.y, other.min.z)))
return false;
if (!contains(PointClass(other.min.x, other.min.y, other.max.z)))
return false;
if (!contains(PointClass(other.max.x, other.min.y, other.max.z)))
return false;
if (!contains(other.max))
return false;
if (!contains(PointClass(other.min.x, other.max.y, other.max.z)))
return false;
return true;
}
}; };
class BoundingBox : public BoundingBoxBase<Point> class BoundingBox : public BoundingBoxBase<Point>

View File

@ -222,7 +222,7 @@ bool Model::add_default_instances()
} }
// this returns the bounding box of the *transformed* instances // this returns the bounding box of the *transformed* instances
BoundingBoxf3 Model::bounding_box() BoundingBoxf3 Model::bounding_box() const
{ {
BoundingBoxf3 bb; BoundingBoxf3 bb;
for (ModelObject *o : this->objects) for (ModelObject *o : this->objects)
@ -230,6 +230,57 @@ BoundingBoxf3 Model::bounding_box()
return bb; return bb;
} }
BoundingBoxf3 Model::transformed_bounding_box() const
{
BoundingBoxf3 bb;
for (const ModelObject* obj : this->objects)
{
for (const ModelVolume* vol : obj->volumes)
{
if (!vol->modifier)
{
for (const ModelInstance* inst : obj->instances)
{
double c = cos(inst->rotation);
double s = sin(inst->rotation);
for (int f = 0; f < vol->mesh.stl.stats.number_of_facets; ++f)
{
const stl_facet& facet = vol->mesh.stl.facet_start[f];
for (int i = 0; i < 3; ++i)
{
// original point
const stl_vertex& v = facet.vertex[i];
Pointf3 p((double)v.x, (double)v.y, (double)v.z);
// scale
p.x *= inst->scaling_factor;
p.y *= inst->scaling_factor;
p.z *= inst->scaling_factor;
// rotate Z
double x = p.x;
double y = p.y;
p.x = c * x - s * y;
p.y = s * x + c * y;
// translate
p.x += inst->offset.x;
p.y += inst->offset.y;
bb.merge(p);
}
}
}
}
}
}
return bb;
}
void Model::center_instances_around_point(const Pointf &point) void Model::center_instances_around_point(const Pointf &point)
{ {
// BoundingBoxf3 bb = this->bounding_box(); // BoundingBoxf3 bb = this->bounding_box();
@ -409,6 +460,23 @@ void Model::convert_multipart_object()
this->objects.push_back(object); this->objects.push_back(object);
} }
bool Model::fits_print_volume(const DynamicPrintConfig* config) const
{
if (config == nullptr)
return false;
if (objects.empty())
return true;
const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(config->option("bed_shape"));
if (opt == nullptr)
return false;
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config->opt_float("max_print_height")));
return print_volume.contains(transformed_bounding_box());
}
ModelObject::ModelObject(Model *model, const ModelObject &other, bool copy_volumes) : ModelObject::ModelObject(Model *model, const ModelObject &other, bool copy_volumes) :
name(other.name), name(other.name),
input_file(other.input_file), input_file(other.input_file),
@ -671,7 +739,7 @@ void ModelObject::transform(const float* matrix3x4)
v->mesh.transform(matrix3x4); v->mesh.transform(matrix3x4);
} }
origin_translation = Pointf3(0.0f, 0.0f, 0.0f); origin_translation = Pointf3(0.0, 0.0, 0.0);
invalidate_bounding_box(); invalidate_bounding_box();
} }

View File

@ -260,7 +260,10 @@ public:
void delete_material(t_model_material_id material_id); void delete_material(t_model_material_id material_id);
void clear_materials(); void clear_materials();
bool add_default_instances(); bool add_default_instances();
BoundingBoxf3 bounding_box(); // Returns approximate axis aligned bounding box of this model
BoundingBoxf3 bounding_box() const;
// Returns tight axis aligned bounding box of this model
BoundingBoxf3 transformed_bounding_box() const;
void center_instances_around_point(const Pointf &point); void center_instances_around_point(const Pointf &point);
void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); } void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); }
TriangleMesh mesh() const; TriangleMesh mesh() const;
@ -273,6 +276,9 @@ public:
bool looks_like_multipart_object() const; bool looks_like_multipart_object() const;
void convert_multipart_object(); void convert_multipart_object();
// Returs true if this model is contained into the print volume defined inside the given config
bool fits_print_volume(const DynamicPrintConfig* config) const;
void print_info() const { for (const ModelObject *o : this->objects) o->print_info(); } void print_info() const { for (const ModelObject *o : this->objects) o->print_info(); }
}; };

View File

@ -120,6 +120,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
"layer_gcode", "layer_gcode",
"min_fan_speed", "min_fan_speed",
"max_fan_speed", "max_fan_speed",
"max_print_height",
"min_print_speed", "min_print_speed",
"max_print_speed", "max_print_speed",
"max_volumetric_speed", "max_volumetric_speed",

View File

@ -803,6 +803,13 @@ PrintConfigDef::PrintConfigDef()
def->min = 0; def->min = 0;
def->default_value = new ConfigOptionFloats { 0. }; def->default_value = new ConfigOptionFloats { 0. };
def = this->add("max_print_height", coFloat);
def->label = L("Max print height");
def->tooltip = L("Set this to the maximum height that can be reached by your extruder while printing.");
def->sidetext = L("mm");
def->cli = "max-print-height=f";
def->default_value = new ConfigOptionFloat(200.0);
def = this->add("max_print_speed", coFloat); def = this->add("max_print_speed", coFloat);
def->label = L("Max print speed"); def->label = L("Max print speed");
def->tooltip = L("When setting other speed settings to 0 Slic3r will autocalculate the optimal speed " def->tooltip = L("When setting other speed settings to 0 Slic3r will autocalculate the optimal speed "

View File

@ -583,6 +583,7 @@ public:
ConfigOptionFloats max_layer_height; ConfigOptionFloats max_layer_height;
ConfigOptionInts min_fan_speed; ConfigOptionInts min_fan_speed;
ConfigOptionFloats min_layer_height; ConfigOptionFloats min_layer_height;
ConfigOptionFloat max_print_height;
ConfigOptionFloats min_print_speed; ConfigOptionFloats min_print_speed;
ConfigOptionFloat min_skirt_length; ConfigOptionFloat min_skirt_length;
ConfigOptionString notes; ConfigOptionString notes;
@ -647,6 +648,7 @@ protected:
OPT_PTR(max_layer_height); OPT_PTR(max_layer_height);
OPT_PTR(min_fan_speed); OPT_PTR(min_fan_speed);
OPT_PTR(min_layer_height); OPT_PTR(min_layer_height);
OPT_PTR(max_print_height);
OPT_PTR(min_print_speed); OPT_PTR(min_print_speed);
OPT_PTR(min_skirt_length); OPT_PTR(min_skirt_length);
OPT_PTR(notes); OPT_PTR(notes);

View File

@ -46,6 +46,25 @@ void GLIndexedVertexArray::load_mesh_flat_shading(const TriangleMesh &mesh)
} }
} }
void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh &mesh)
{
assert(triangle_indices.empty() && vertices_and_normals_interleaved_size == 0);
assert(quad_indices.empty() && triangle_indices_size == 0);
assert(vertices_and_normals_interleaved.size() % 6 == 0 && quad_indices_size == vertices_and_normals_interleaved.size());
this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count());
unsigned int vertices_count = 0;
for (int i = 0; i < mesh.stl.stats.number_of_facets; ++i) {
const stl_facet &facet = mesh.stl.facet_start[i];
for (int j = 0; j < 3; ++j)
this->push_geometry(facet.vertex[j].x, facet.vertex[j].y, facet.vertex[j].z, facet.normal.x, facet.normal.y, facet.normal.z);
this->push_triangle(vertices_count, vertices_count + 1, vertices_count + 2);
vertices_count += 3;
}
}
void GLIndexedVertexArray::finalize_geometry(bool use_VBOs) void GLIndexedVertexArray::finalize_geometry(bool use_VBOs)
{ {
assert(this->vertices_and_normals_interleaved_VBO_id == 0); assert(this->vertices_and_normals_interleaved_VBO_id == 0);
@ -173,6 +192,45 @@ void GLIndexedVertexArray::render(
glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_NORMAL_ARRAY);
} }
const float GLVolume::SELECTED_COLOR[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
const float GLVolume::HOVER_COLOR[4] = { 0.4f, 0.9f, 0.1f, 1.0f };
const float GLVolume::OUTSIDE_COLOR[4] = { 0.75f, 0.0f, 0.75f, 1.0f };
const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 1.0f, 0.0f, 1.0f, 1.0f };
void GLVolume::set_render_color(float r, float g, float b, float a)
{
render_color[0] = r;
render_color[1] = g;
render_color[2] = b;
render_color[3] = a;
}
void GLVolume::set_render_color(const float* rgba, unsigned int size)
{
size = std::min((unsigned int)4, size);
for (int i = 0; i < size; ++i)
{
render_color[i] = rgba[i];
}
}
void GLVolume::set_render_color()
{
if (selected)
{
if (is_outside)
set_render_color(SELECTED_OUTSIDE_COLOR, 4);
else
set_render_color(SELECTED_COLOR, 4);
}
else if (hover)
set_render_color(HOVER_COLOR, 4);
else if (is_outside)
set_render_color(OUTSIDE_COLOR, 4);
else
set_render_color(color, 4);
}
void GLVolume::set_range(double min_z, double max_z) void GLVolume::set_range(double min_z, double max_z)
{ {
this->qverts_range.first = 0; this->qverts_range.first = 0;
@ -288,7 +346,11 @@ std::vector<int> GLVolumeCollection::load_object(
color[3] = model_volume->modifier ? 0.5f : 1.f; color[3] = model_volume->modifier ? 0.5f : 1.f;
this->volumes.emplace_back(new GLVolume(color)); this->volumes.emplace_back(new GLVolume(color));
GLVolume &v = *this->volumes.back(); GLVolume &v = *this->volumes.back();
v.indexed_vertex_array.load_mesh_flat_shading(mesh); if (use_VBOs)
v.indexed_vertex_array.load_mesh_full_shading(mesh);
else
v.indexed_vertex_array.load_mesh_flat_shading(mesh);
// finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
v.bounding_box = v.indexed_vertex_array.bounding_box(); v.bounding_box = v.indexed_vertex_array.bounding_box();
v.indexed_vertex_array.finalize_geometry(use_VBOs); v.indexed_vertex_array.finalize_geometry(use_VBOs);
@ -319,7 +381,11 @@ int GLVolumeCollection::load_wipe_tower_preview(
this->volumes.emplace_back(new GLVolume(color)); this->volumes.emplace_back(new GLVolume(color));
GLVolume &v = *this->volumes.back(); GLVolume &v = *this->volumes.back();
auto mesh = make_cube(width, depth, height); auto mesh = make_cube(width, depth, height);
v.indexed_vertex_array.load_mesh_flat_shading(mesh); if (use_VBOs)
v.indexed_vertex_array.load_mesh_full_shading(mesh);
else
v.indexed_vertex_array.load_mesh_flat_shading(mesh);
v.origin = Pointf3(pos_x, pos_y, 0.); v.origin = Pointf3(pos_x, pos_y, 0.);
// finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
v.bounding_box = v.indexed_vertex_array.bounding_box(); v.bounding_box = v.indexed_vertex_array.bounding_box();
@ -332,8 +398,9 @@ int GLVolumeCollection::load_wipe_tower_preview(
void GLVolumeCollection::render_VBOs() const void GLVolumeCollection::render_VBOs() const
{ {
// glEnable(GL_BLEND); glEnable(GL_BLEND);
// glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glCullFace(GL_BACK); glCullFace(GL_BACK);
glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_NORMAL_ARRAY);
@ -341,6 +408,9 @@ void GLVolumeCollection::render_VBOs() const
GLint current_program_id; GLint current_program_id;
glGetIntegerv(GL_CURRENT_PROGRAM, &current_program_id); glGetIntegerv(GL_CURRENT_PROGRAM, &current_program_id);
GLint color_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "uniform_color") : -1; GLint color_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "uniform_color") : -1;
GLint print_box_min_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.min") : -1;
GLint print_box_max_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.max") : -1;
GLint print_box_origin_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_origin") : -1;
for (GLVolume *volume : this->volumes) { for (GLVolume *volume : this->volumes) {
if (!volume->is_active) if (!volume->is_active)
@ -348,42 +418,72 @@ void GLVolumeCollection::render_VBOs() const
if (!volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id) if (!volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id)
continue; continue;
volume->set_render_color();
GLsizei n_triangles = GLsizei(std::min(volume->indexed_vertex_array.triangle_indices_size, volume->tverts_range.second - volume->tverts_range.first)); GLsizei n_triangles = GLsizei(std::min(volume->indexed_vertex_array.triangle_indices_size, volume->tverts_range.second - volume->tverts_range.first));
GLsizei n_quads = GLsizei(std::min(volume->indexed_vertex_array.quad_indices_size, volume->qverts_range.second - volume->qverts_range.first)); GLsizei n_quads = GLsizei(std::min(volume->indexed_vertex_array.quad_indices_size, volume->qverts_range.second - volume->qverts_range.first));
if (n_triangles + n_quads == 0) if (n_triangles + n_quads == 0)
{ {
if (_render_interleaved_only_volumes.enabled) ::glDisableClientState(GL_VERTEX_ARRAY);
::glDisableClientState(GL_NORMAL_ARRAY);
if (color_id >= 0)
{ {
::glDisableClientState(GL_VERTEX_ARRAY); float color[4];
::glDisableClientState(GL_NORMAL_ARRAY); ::memcpy((void*)color, (const void*)volume->render_color, 4 * sizeof(float));
::glEnable(GL_BLEND); ::glUniform4fv(color_id, 1, (const GLfloat*)color);
::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (color_id >= 0)
{
float color[4];
::memcpy((void*)color, (const void*)volume->color, 3 * sizeof(float));
color[3] = _render_interleaved_only_volumes.alpha;
::glUniform4fv(color_id, 1, (const GLfloat*)color);
}
else
::glColor4f(volume->color[0], volume->color[1], volume->color[2], _render_interleaved_only_volumes.alpha);
volume->render();
::glDisable(GL_BLEND);
::glEnableClientState(GL_VERTEX_ARRAY);
::glEnableClientState(GL_NORMAL_ARRAY);
} }
else
::glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]);
if (print_box_min_id != -1)
::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min);
if (print_box_max_id != -1)
::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max);
if (print_box_origin_id != -1)
{
float origin[4] = { volume->origin.x, volume->origin.y, volume->origin.z, volume->outside_printer_detection_enabled ? 1.0f : 0.0f };
::glUniform4fv(print_box_origin_id, 1, (const GLfloat*)origin);
}
volume->render();
::glEnableClientState(GL_VERTEX_ARRAY);
::glEnableClientState(GL_NORMAL_ARRAY);
continue; continue;
} }
if (color_id >= 0) if (color_id >= 0)
glUniform4fv(color_id, 1, (const GLfloat*)volume->color); glUniform4fv(color_id, 1, (const GLfloat*)volume->render_color);
else else
glColor4f(volume->color[0], volume->color[1], volume->color[2], volume->color[3]); glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]);
if (print_box_min_id != -1)
::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min);
if (print_box_max_id != -1)
::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max);
if (print_box_origin_id != -1)
{
float origin[4] = { volume->origin.x, volume->origin.y, volume->origin.z, volume->outside_printer_detection_enabled ? 1.0f : 0.0f };
::glUniform4fv(print_box_origin_id, 1, (const GLfloat*)origin);
}
glBindBuffer(GL_ARRAY_BUFFER, volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id); glBindBuffer(GL_ARRAY_BUFFER, volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id);
glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))); glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float)));
glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr); glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr);
bool has_offset = (volume->origin.x != 0) || (volume->origin.y != 0) || (volume->origin.z != 0);
if (has_offset) {
glPushMatrix();
glTranslated(volume->origin.x, volume->origin.y, volume->origin.z);
}
if (n_triangles > 0) { if (n_triangles > 0) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.triangle_indices_VBO_id); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.triangle_indices_VBO_id);
glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, (const void*)(volume->tverts_range.first * 4)); glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, (const void*)(volume->tverts_range.first * 4));
@ -392,17 +492,25 @@ void GLVolumeCollection::render_VBOs() const
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.quad_indices_VBO_id); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.quad_indices_VBO_id);
glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, (const void*)(volume->qverts_range.first * 4)); glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, (const void*)(volume->qverts_range.first * 4));
} }
if (has_offset)
glPopMatrix();
} }
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_NORMAL_ARRAY);
// glDisable(GL_BLEND);
glDisable(GL_BLEND);
} }
void GLVolumeCollection::render_legacy() const void GLVolumeCollection::render_legacy() const
{ {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glCullFace(GL_BACK); glCullFace(GL_BACK);
glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_NORMAL_ARRAY);
@ -412,27 +520,25 @@ void GLVolumeCollection::render_legacy() const
if (!volume->is_active) if (!volume->is_active)
continue; continue;
volume->set_render_color();
GLsizei n_triangles = GLsizei(std::min(volume->indexed_vertex_array.triangle_indices_size, volume->tverts_range.second - volume->tverts_range.first)); GLsizei n_triangles = GLsizei(std::min(volume->indexed_vertex_array.triangle_indices_size, volume->tverts_range.second - volume->tverts_range.first));
GLsizei n_quads = GLsizei(std::min(volume->indexed_vertex_array.quad_indices_size, volume->qverts_range.second - volume->qverts_range.first)); GLsizei n_quads = GLsizei(std::min(volume->indexed_vertex_array.quad_indices_size, volume->qverts_range.second - volume->qverts_range.first));
if (n_triangles + n_quads == 0) if (n_triangles + n_quads == 0)
{ {
if (_render_interleaved_only_volumes.enabled) ::glDisableClientState(GL_VERTEX_ARRAY);
{ ::glDisableClientState(GL_NORMAL_ARRAY);
::glDisableClientState(GL_VERTEX_ARRAY);
::glDisableClientState(GL_NORMAL_ARRAY);
::glEnable(GL_BLEND);
::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
::glColor4f(volume->color[0], volume->color[1], volume->color[2], _render_interleaved_only_volumes.alpha); ::glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]);
volume->render(); volume->render();
::glEnableClientState(GL_VERTEX_ARRAY);
::glEnableClientState(GL_NORMAL_ARRAY);
::glDisable(GL_BLEND);
::glEnableClientState(GL_VERTEX_ARRAY);
::glEnableClientState(GL_NORMAL_ARRAY);
}
continue; continue;
} }
glColor4f(volume->color[0], volume->color[1], volume->color[2], volume->color[3]);
glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]);
glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), volume->indexed_vertex_array.vertices_and_normals_interleaved.data() + 3); glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), volume->indexed_vertex_array.vertices_and_normals_interleaved.data() + 3);
glNormalPointer(GL_FLOAT, 6 * sizeof(float), volume->indexed_vertex_array.vertices_and_normals_interleaved.data()); glNormalPointer(GL_FLOAT, 6 * sizeof(float), volume->indexed_vertex_array.vertices_and_normals_interleaved.data());
bool has_offset = volume->origin.x != 0 || volume->origin.y != 0 || volume->origin.z != 0; bool has_offset = volume->origin.x != 0 || volume->origin.y != 0 || volume->origin.z != 0;
@ -445,11 +551,37 @@ void GLVolumeCollection::render_legacy() const
if (n_quads > 0) if (n_quads > 0)
glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, volume->indexed_vertex_array.quad_indices.data() + volume->qverts_range.first); glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, volume->indexed_vertex_array.quad_indices.data() + volume->qverts_range.first);
if (has_offset) if (has_offset)
glPushMatrix(); glPopMatrix();
} }
glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_NORMAL_ARRAY);
glDisable(GL_BLEND);
}
void GLVolumeCollection::update_outside_state(const DynamicPrintConfig* config, bool all_inside)
{
if (config == nullptr)
return;
const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(config->option("bed_shape"));
if (opt == nullptr)
return;
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config->opt_float("max_print_height")));
for (GLVolume* volume : this->volumes)
{
if (all_inside)
{
volume->is_outside = false;
continue;
}
volume->is_outside = !print_volume.contains(volume->transformed_bounding_box());
}
} }
std::vector<double> GLVolumeCollection::get_current_print_zs() const std::vector<double> GLVolumeCollection::get_current_print_zs() const
@ -1133,6 +1265,100 @@ static void point3_to_verts(const Point3& point, double width, double height, GL
_3DScene::GCodePreviewVolumeIndex _3DScene::s_gcode_preview_volume_index; _3DScene::GCodePreviewVolumeIndex _3DScene::s_gcode_preview_volume_index;
_3DScene::LegendTexture _3DScene::s_legend_texture; _3DScene::LegendTexture _3DScene::s_legend_texture;
_3DScene::WarningTexture _3DScene::s_warning_texture;
unsigned int _3DScene::TextureBase::finalize()
{
if (!m_data.empty()) {
// sends buffer to gpu
::glGenTextures(1, &m_tex_id);
::glBindTexture(GL_TEXTURE_2D, m_tex_id);
::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_tex_width, (GLsizei)m_tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid*)m_data.data());
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
::glBindTexture(GL_TEXTURE_2D, 0);
m_data.clear();
}
return (m_tex_width > 0 && m_tex_height > 0) ? m_tex_id : 0;
}
void _3DScene::TextureBase::_destroy_texture()
{
if (m_tex_id > 0)
{
::glDeleteTextures(1, &m_tex_id);
m_tex_id = 0;
m_tex_height = 0;
m_tex_width = 0;
}
m_data.clear();
}
const unsigned char _3DScene::WarningTexture::Background_Color[3] = { 9, 91, 134 };
const unsigned char _3DScene::WarningTexture::Opacity = 255;
// Generate a texture data, but don't load it into the GPU yet, as the GPU context may not yet be valid.
bool _3DScene::WarningTexture::generate(const std::string& msg)
{
// Mark the texture as released, but don't release the texture from the GPU yet.
m_tex_width = m_tex_height = 0;
m_data.clear();
if (msg.empty())
return false;
wxMemoryDC memDC;
// select default font
memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
// calculates texture size
wxCoord w, h;
memDC.GetTextExtent(msg, &w, &h);
m_tex_width = (unsigned int)w;
m_tex_height = (unsigned int)h;
// generates bitmap
wxBitmap bitmap(m_tex_width, m_tex_height);
#if defined(__APPLE__) || defined(_MSC_VER)
bitmap.UseAlpha();
#endif
memDC.SelectObject(bitmap);
memDC.SetBackground(wxBrush(wxColour(Background_Color[0], Background_Color[1], Background_Color[2])));
memDC.Clear();
memDC.SetTextForeground(*wxWHITE);
// draw message
memDC.DrawText(msg, 0, 0);
memDC.SelectObject(wxNullBitmap);
// Convert the bitmap into a linear data ready to be loaded into the GPU.
{
wxImage image = bitmap.ConvertToImage();
image.SetMaskColour(Background_Color[0], Background_Color[1], Background_Color[2]);
// prepare buffer
m_data.assign(4 * m_tex_width * m_tex_height, 0);
for (unsigned int h = 0; h < m_tex_height; ++h)
{
unsigned int hh = h * m_tex_width;
unsigned char* px_ptr = m_data.data() + 4 * hh;
for (unsigned int w = 0; w < m_tex_width; ++w)
{
*px_ptr++ = image.GetRed(w, h);
*px_ptr++ = image.GetGreen(w, h);
*px_ptr++ = image.GetBlue(w, h);
*px_ptr++ = image.IsTransparent(w, h) ? 0 : Opacity;
}
}
}
return true;
}
const unsigned char _3DScene::LegendTexture::Squares_Border_Color[3] = { 64, 64, 64 }; const unsigned char _3DScene::LegendTexture::Squares_Border_Color[3] = { 64, 64, 64 };
const unsigned char _3DScene::LegendTexture::Background_Color[3] = { 9, 91, 134 }; const unsigned char _3DScene::LegendTexture::Background_Color[3] = { 9, 91, 134 };
@ -1276,34 +1502,6 @@ bool _3DScene::LegendTexture::generate(const GCodePreviewData& preview_data, con
return true; return true;
} }
unsigned int _3DScene::LegendTexture::finalize()
{
if (! m_data.empty()) {
// sends buffer to gpu
::glGenTextures(1, &m_tex_id);
::glBindTexture(GL_TEXTURE_2D, m_tex_id);
::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_tex_width, (GLsizei)m_tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid*)m_data.data());
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
::glBindTexture(GL_TEXTURE_2D, 0);
m_data.clear();
}
return (m_tex_width > 0 && m_tex_height > 0) ? m_tex_id : 0;
}
void _3DScene::LegendTexture::_destroy_texture()
{
if (m_tex_id > 0)
{
::glDeleteTextures(1, &m_tex_id);
m_tex_id = 0;
m_tex_height = 0;
m_tex_width = 0;
}
m_data.clear();
}
void _3DScene::_glew_init() void _3DScene::_glew_init()
{ {
glewInit(); glewInit();
@ -1353,27 +1551,17 @@ void _3DScene::load_gcode_preview(const Print* print, const GCodePreviewData* pr
_load_gcode_unretractions(*preview_data, *volumes, use_VBOs); _load_gcode_unretractions(*preview_data, *volumes, use_VBOs);
if (volumes->empty()) if (volumes->empty())
{
reset_legend_texture(); reset_legend_texture();
volumes->set_render_interleaved_only_volumes(GLVolumeCollection::RenderInterleavedOnlyVolumes(false, 0.0f));
}
else else
{ {
_generate_legend_texture(*preview_data, tool_colors); _generate_legend_texture(*preview_data, tool_colors);
_load_shells(*print, *volumes, use_VBOs); _load_shells(*print, *volumes, use_VBOs);
volumes->set_render_interleaved_only_volumes(GLVolumeCollection::RenderInterleavedOnlyVolumes(true, 0.25f));
} }
} }
_update_gcode_volumes_visibility(*preview_data, *volumes); _update_gcode_volumes_visibility(*preview_data, *volumes);
} }
unsigned int _3DScene::get_legend_texture_id()
{
return s_legend_texture.get_texture_id();
}
unsigned int _3DScene::get_legend_texture_width() unsigned int _3DScene::get_legend_texture_width()
{ {
return s_legend_texture.get_texture_width(); return s_legend_texture.get_texture_width();
@ -1389,6 +1577,36 @@ void _3DScene::reset_legend_texture()
s_legend_texture.reset_texture(); s_legend_texture.reset_texture();
} }
unsigned int _3DScene::finalize_legend_texture()
{
return s_legend_texture.finalize();
}
unsigned int _3DScene::get_warning_texture_width()
{
return s_warning_texture.get_texture_width();
}
unsigned int _3DScene::get_warning_texture_height()
{
return s_warning_texture.get_texture_height();
}
void _3DScene::generate_warning_texture(const std::string& msg)
{
s_warning_texture.generate(msg);
}
void _3DScene::reset_warning_texture()
{
s_warning_texture.reset_texture();
}
unsigned int _3DScene::finalize_warning_texture()
{
return s_warning_texture.finalize();
}
// Create 3D thick extrusion lines for a skirt and brim. // Create 3D thick extrusion lines for a skirt and brim.
// Adds a new Slic3r::GUI::3DScene::Volume to volumes. // Adds a new Slic3r::GUI::3DScene::Volume to volumes.
void _3DScene::_load_print_toolpaths( void _3DScene::_load_print_toolpaths(
@ -1501,6 +1719,7 @@ void _3DScene::_load_print_object_toolpaths(
auto new_volume = [volumes, &new_volume_mutex](const float *color) -> GLVolume* { auto new_volume = [volumes, &new_volume_mutex](const float *color) -> GLVolume* {
auto *volume = new GLVolume(color); auto *volume = new GLVolume(color);
new_volume_mutex.lock(); new_volume_mutex.lock();
volume->outside_printer_detection_enabled = false;
volumes->volumes.emplace_back(volume); volumes->volumes.emplace_back(volume);
new_volume_mutex.unlock(); new_volume_mutex.unlock();
return volume; return volume;
@ -1651,6 +1870,7 @@ void _3DScene::_load_wipe_tower_toolpaths(
auto new_volume = [volumes, &new_volume_mutex](const float *color) -> GLVolume* { auto new_volume = [volumes, &new_volume_mutex](const float *color) -> GLVolume* {
auto *volume = new GLVolume(color); auto *volume = new GLVolume(color);
new_volume_mutex.lock(); new_volume_mutex.lock();
volume->outside_printer_detection_enabled = false;
volumes->volumes.emplace_back(volume); volumes->volumes.emplace_back(volume);
new_volume_mutex.unlock(); new_volume_mutex.unlock();
return volume; return volume;
@ -2221,6 +2441,7 @@ void _3DScene::_update_gcode_volumes_visibility(const GCodePreviewData& preview_
for (std::vector<GLVolume*>::iterator it = begin; it != end; ++it) for (std::vector<GLVolume*>::iterator it = begin; it != end; ++it)
{ {
GLVolume* volume = *it; GLVolume* volume = *it;
volume->outside_printer_detection_enabled = false;
switch (s_gcode_preview_volume_index.first_volumes[i].type) switch (s_gcode_preview_volume_index.first_volumes[i].type)
{ {
@ -2253,6 +2474,7 @@ void _3DScene::_update_gcode_volumes_visibility(const GCodePreviewData& preview_
case GCodePreviewVolumeIndex::Shell: case GCodePreviewVolumeIndex::Shell:
{ {
volume->is_active = preview_data.shell.is_visible; volume->is_active = preview_data.shell.is_visible;
volume->color[3] = 0.25f;
volume->zoom_to_volumes = false; volume->zoom_to_volumes = false;
break; break;
} }
@ -2272,11 +2494,6 @@ void _3DScene::_generate_legend_texture(const GCodePreviewData& preview_data, co
s_legend_texture.generate(preview_data, tool_colors); s_legend_texture.generate(preview_data, tool_colors);
} }
unsigned int _3DScene::finalize_legend_texture()
{
return s_legend_texture.finalize();
}
void _3DScene::_load_shells(const Print& print, GLVolumeCollection& volumes, bool use_VBOs) void _3DScene::_load_shells(const Print& print, GLVolumeCollection& volumes, bool use_VBOs)
{ {
size_t initial_volumes_count = volumes.volumes.size(); size_t initial_volumes_count = volumes.volumes.size();

View File

@ -16,6 +16,7 @@ class PrintObject;
class Model; class Model;
class ModelObject; class ModelObject;
class GCodePreviewData; class GCodePreviewData;
class DynamicPrintConfig;
// A container for interleaved arrays of 3D vertices and normals, // A container for interleaved arrays of 3D vertices and normals,
// possibly indexed by triangles and / or quads. // possibly indexed by triangles and / or quads.
@ -85,6 +86,7 @@ public:
unsigned int quad_indices_VBO_id; unsigned int quad_indices_VBO_id;
void load_mesh_flat_shading(const TriangleMesh &mesh); void load_mesh_flat_shading(const TriangleMesh &mesh);
void load_mesh_full_shading(const TriangleMesh &mesh);
inline bool has_VBOs() const { return vertices_and_normals_interleaved_VBO_id != 0; } inline bool has_VBOs() const { return vertices_and_normals_interleaved_VBO_id != 0; }
@ -209,6 +211,11 @@ public:
class GLVolume { class GLVolume {
public: public:
static const float SELECTED_COLOR[4];
static const float HOVER_COLOR[4];
static const float OUTSIDE_COLOR[4];
static const float SELECTED_OUTSIDE_COLOR[4];
GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f) : GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f) :
composite_id(-1), composite_id(-1),
select_group_id(-1), select_group_id(-1),
@ -216,6 +223,8 @@ public:
selected(false), selected(false),
is_active(true), is_active(true),
zoom_to_volumes(true), zoom_to_volumes(true),
outside_printer_detection_enabled(true),
is_outside(false),
hover(false), hover(false),
tverts_range(0, size_t(-1)), tverts_range(0, size_t(-1)),
qverts_range(0, size_t(-1)) qverts_range(0, size_t(-1))
@ -224,6 +233,7 @@ public:
color[1] = g; color[1] = g;
color[2] = b; color[2] = b;
color[3] = a; color[3] = a;
set_render_color(r, g, b, a);
} }
GLVolume(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {} GLVolume(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {}
@ -243,7 +253,8 @@ public:
Pointf3 origin; Pointf3 origin;
// Color of the triangles / quads held by this volume. // Color of the triangles / quads held by this volume.
float color[4]; float color[4];
// Color used to render this volume.
float render_color[4];
// An ID containing the object ID, volume ID and instance ID. // An ID containing the object ID, volume ID and instance ID.
int composite_id; int composite_id;
// An ID for group selection. It may be the same for all meshes of all object instances, or for just a single object instance. // An ID for group selection. It may be the same for all meshes of all object instances, or for just a single object instance.
@ -256,6 +267,10 @@ public:
bool is_active; bool is_active;
// Whether or not to use this volume when applying zoom_to_volumes() // Whether or not to use this volume when applying zoom_to_volumes()
bool zoom_to_volumes; bool zoom_to_volumes;
// Wheter or not this volume is enabled for outside print volume detection.
bool outside_printer_detection_enabled;
// Wheter or not this volume is outside print volume.
bool is_outside;
// Boolean: Is mouse over this object? // Boolean: Is mouse over this object?
bool hover; bool hover;
@ -271,6 +286,10 @@ public:
// Offset into qverts & tverts, or offsets into indices stored into an OpenGL name_index_buffer. // Offset into qverts & tverts, or offsets into indices stored into an OpenGL name_index_buffer.
std::vector<size_t> offsets; std::vector<size_t> offsets;
void set_render_color(float r, float g, float b, float a);
void set_render_color(const float* rgba, unsigned int size);
// Sets render color in dependence of current state
void set_render_color();
int object_idx() const { return this->composite_id / 1000000; } int object_idx() const { return this->composite_id / 1000000; }
int volume_idx() const { return (this->composite_id / 1000) % 1000; } int volume_idx() const { return (this->composite_id / 1000) % 1000; }
@ -313,27 +332,9 @@ public:
class GLVolumeCollection class GLVolumeCollection
{ {
public: // min and max vertex of the print box volume
struct RenderInterleavedOnlyVolumes float print_box_min[3];
{ float print_box_max[3];
bool enabled;
float alpha; // [0..1]
RenderInterleavedOnlyVolumes()
: enabled(false)
, alpha(0.0f)
{
}
RenderInterleavedOnlyVolumes(bool enabled, float alpha)
: enabled(enabled)
, alpha(alpha)
{
}
};
private:
RenderInterleavedOnlyVolumes _render_interleaved_only_volumes;
public: public:
std::vector<GLVolume*> volumes; std::vector<GLVolume*> volumes;
@ -370,7 +371,12 @@ public:
bool empty() const { return volumes.empty(); } bool empty() const { return volumes.empty(); }
void set_range(double low, double high) { for (GLVolume *vol : this->volumes) vol->set_range(low, high); } void set_range(double low, double high) { for (GLVolume *vol : this->volumes) vol->set_range(low, high); }
void set_render_interleaved_only_volumes(const RenderInterleavedOnlyVolumes& render_interleaved_only_volumes) { _render_interleaved_only_volumes = render_interleaved_only_volumes; } void set_print_box(float min_x, float min_y, float min_z, float max_x, float max_y, float max_z) {
print_box_min[0] = min_x; print_box_min[1] = min_y; print_box_min[2] = min_z;
print_box_max[0] = max_x; print_box_max[1] = max_y; print_box_max[2] = max_z;
}
void update_outside_state(const DynamicPrintConfig* config, bool all_inside);
// Returns a vector containing the sorted list of all the print_zs of the volumes contained in this collection // Returns a vector containing the sorted list of all the print_zs of the volumes contained in this collection
std::vector<double> get_current_print_zs() const; std::vector<double> get_current_print_zs() const;
@ -411,27 +417,20 @@ class _3DScene
static GCodePreviewVolumeIndex s_gcode_preview_volume_index; static GCodePreviewVolumeIndex s_gcode_preview_volume_index;
class LegendTexture class TextureBase
{ {
static const unsigned int Px_Title_Offset = 5; protected:
static const unsigned int Px_Text_Offset = 5;
static const unsigned int Px_Square = 20;
static const unsigned int Px_Square_Contour = 1;
static const unsigned int Px_Border = Px_Square / 2;
static const unsigned char Squares_Border_Color[3];
static const unsigned char Background_Color[3];
static const unsigned char Opacity;
unsigned int m_tex_id; unsigned int m_tex_id;
unsigned int m_tex_width; unsigned int m_tex_width;
unsigned int m_tex_height; unsigned int m_tex_height;
// generate() fills in m_data with the pixels, while finalize() moves the data to the GPU before rendering.
std::vector<unsigned char> m_data;
public: public:
LegendTexture() : m_tex_id(0), m_tex_width(0), m_tex_height(0) {} TextureBase() : m_tex_id(0), m_tex_width(0), m_tex_height(0) {}
~LegendTexture() { _destroy_texture(); } virtual ~TextureBase() { _destroy_texture(); }
// Generate a texture data, but don't load it into the GPU yet, as the glcontext may not be valid yet.
bool generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
// If not loaded, load the texture data into the GPU. Return a texture ID or 0 if the texture has zero size. // If not loaded, load the texture data into the GPU. Return a texture ID or 0 if the texture has zero size.
unsigned int finalize(); unsigned int finalize();
@ -442,26 +441,61 @@ class _3DScene
void reset_texture() { _destroy_texture(); } void reset_texture() { _destroy_texture(); }
private: private:
bool _create_texture(const GCodePreviewData& preview_data, const wxBitmap& bitmap);
void _destroy_texture(); void _destroy_texture();
// generate() fills in m_data with the pixels, while finalize() moves the data to the GPU before rendering. };
std::vector<unsigned char> m_data;
class WarningTexture : public TextureBase
{
static const unsigned char Background_Color[3];
static const unsigned char Opacity;
public:
WarningTexture() : TextureBase() {}
// Generate a texture data, but don't load it into the GPU yet, as the glcontext may not be valid yet.
bool generate(const std::string& msg);
};
class LegendTexture : public TextureBase
{
static const unsigned int Px_Title_Offset = 5;
static const unsigned int Px_Text_Offset = 5;
static const unsigned int Px_Square = 20;
static const unsigned int Px_Square_Contour = 1;
static const unsigned int Px_Border = Px_Square / 2;
static const unsigned char Squares_Border_Color[3];
static const unsigned char Background_Color[3];
static const unsigned char Opacity;
public:
LegendTexture() : TextureBase() {}
// Generate a texture data, but don't load it into the GPU yet, as the glcontext may not be valid yet.
bool generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
}; };
static LegendTexture s_legend_texture; static LegendTexture s_legend_texture;
static WarningTexture s_warning_texture;
public: public:
static void _glew_init(); static void _glew_init();
static void load_gcode_preview(const Print* print, const GCodePreviewData* preview_data, GLVolumeCollection* volumes, const std::vector<std::string>& str_tool_colors, bool use_VBOs); static void load_gcode_preview(const Print* print, const GCodePreviewData* preview_data, GLVolumeCollection* volumes, const std::vector<std::string>& str_tool_colors, bool use_VBOs);
static unsigned int get_legend_texture_id();
static unsigned int get_legend_texture_width(); static unsigned int get_legend_texture_width();
static unsigned int get_legend_texture_height(); static unsigned int get_legend_texture_height();
static void reset_legend_texture(); static void reset_legend_texture();
static unsigned int finalize_legend_texture(); static unsigned int finalize_legend_texture();
static unsigned int get_warning_texture_width();
static unsigned int get_warning_texture_height();
// generates a warning texture containing the given message
static void generate_warning_texture(const std::string& msg);
static void reset_warning_texture();
static unsigned int finalize_warning_texture();
static void _load_print_toolpaths( static void _load_print_toolpaths(
const Print *print, const Print *print,
GLVolumeCollection *volumes, GLVolumeCollection *volumes,

View File

@ -226,7 +226,7 @@ const std::vector<std::string>& Preset::printer_options()
"bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed", "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed",
"octoprint_host", "octoprint_apikey", "octoprint_cafile", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height", "octoprint_host", "octoprint_apikey", "octoprint_cafile", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
"single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode", "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",
"between_objects_gcode", "printer_notes" "between_objects_gcode", "printer_notes", "max_print_height"
}; };
s_opts.insert(s_opts.end(), Preset::nozzle_options().begin(), Preset::nozzle_options().end()); s_opts.insert(s_opts.end(), Preset::nozzle_options().begin(), Preset::nozzle_options().end());
} }

View File

@ -968,7 +968,8 @@ void TabPrinter::build()
return sizer; return sizer;
}; };
optgroup->append_line(line); optgroup->append_line(line);
optgroup->append_single_option_line("z_offset"); optgroup->append_single_option_line("max_print_height");
optgroup->append_single_option_line("z_offset");
optgroup = page->new_optgroup(_(L("Capabilities"))); optgroup = page->new_optgroup(_(L("Capabilities")));
ConfigOptionDef def; ConfigOptionDef def;

View File

@ -97,6 +97,9 @@
void finalize_geometry(bool use_VBOs); void finalize_geometry(bool use_VBOs);
void release_geometry(); void release_geometry();
void set_print_box(float min_x, float min_y, float min_z, float max_x, float max_y, float max_z);
void update_outside_state(DynamicPrintConfig* config, bool all_inside);
bool move_volume_up(int idx) bool move_volume_up(int idx)
%code%{ %code%{
if (idx > 0 && idx < int(THIS->volumes.size())) { if (idx > 0 && idx < int(THIS->volumes.size())) {
@ -154,13 +157,6 @@ finalize_legend_texture()
OUTPUT: OUTPUT:
RETVAL RETVAL
unsigned int
get_legend_texture_id()
CODE:
RETVAL = _3DScene::get_legend_texture_id();
OUTPUT:
RETVAL
unsigned int unsigned int
get_legend_texture_width() get_legend_texture_width()
CODE: CODE:
@ -179,7 +175,38 @@ void
reset_legend_texture() reset_legend_texture()
CODE: CODE:
_3DScene::reset_legend_texture(); _3DScene::reset_legend_texture();
void
generate_warning_texture(std::string msg)
CODE:
_3DScene::generate_warning_texture(msg);
unsigned int
finalize_warning_texture()
CODE:
RETVAL = _3DScene::finalize_warning_texture();
OUTPUT:
RETVAL
unsigned int
get_warning_texture_width()
CODE:
RETVAL = _3DScene::get_warning_texture_width();
OUTPUT:
RETVAL
unsigned int
get_warning_texture_height()
CODE:
RETVAL = _3DScene::get_warning_texture_height();
OUTPUT:
RETVAL
void
reset_warning_texture()
CODE:
_3DScene::reset_warning_texture();
void void
_load_print_toolpaths(print, volumes, tool_colors, use_VBOs) _load_print_toolpaths(print, volumes, tool_colors, use_VBOs)
Print *print; Print *print;

View File

@ -99,6 +99,9 @@
void print_info() const; void print_info() const;
bool fits_print_volume(DynamicPrintConfig* config) const
%code%{ RETVAL = THIS->fits_print_volume(config); %};
bool store_stl(char *path, bool binary) bool store_stl(char *path, bool binary)
%code%{ TriangleMesh mesh = THIS->mesh(); RETVAL = Slic3r::store_stl(path, &mesh, binary); %}; %code%{ TriangleMesh mesh = THIS->mesh(); RETVAL = Slic3r::store_stl(path, &mesh, binary); %};
bool store_amf(char *path, Print* print) bool store_amf(char *path, Print* print)
@ -361,9 +364,9 @@ ModelMaterial::attributes()
%code%{ RETVAL = &THIS->offset; %}; %code%{ RETVAL = &THIS->offset; %};
void set_rotation(double val) void set_rotation(double val)
%code%{ THIS->rotation = val; %}; %code%{ THIS->rotation = val; THIS->get_object()->invalidate_bounding_box(); %};
void set_scaling_factor(double val) void set_scaling_factor(double val)
%code%{ THIS->scaling_factor = val; %}; %code%{ THIS->scaling_factor = val; THIS->get_object()->invalidate_bounding_box(); %};
void set_offset(Pointf *offset) void set_offset(Pointf *offset)
%code%{ THIS->offset = *offset; %}; %code%{ THIS->offset = *offset; %};