123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613 |
- package Slic3r::GUI::Plater::2DToolpaths;
- use strict;
- use warnings;
- use utf8;
- use Slic3r::Print::State ':steps';
- use Wx qw(:misc :sizer :slider :statictext wxWHITE);
- use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN);
- use base qw(Wx::Panel Class::Accessor);
- __PACKAGE__->mk_accessors(qw(print enabled));
- sub new {
- my $class = shift;
- my ($parent, $print) = @_;
-
- my $self = $class->SUPER::new($parent, -1, wxDefaultPosition);
- $self->SetBackgroundColour(wxWHITE);
-
- # init GUI elements
- my $canvas = $self->{canvas} = Slic3r::GUI::Plater::2DToolpaths::Canvas->new($self, $print);
- my $slider = $self->{slider} = Wx::Slider->new(
- $self, -1,
- 0, # default
- 0, # min
- # we set max to a bogus non-zero value because the MSW implementation of wxSlider
- # will skip drawing the slider if max <= min:
- 1, # max
- wxDefaultPosition,
- wxDefaultSize,
- wxVERTICAL | wxSL_INVERSE,
- );
- my $z_label = $self->{z_label} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
- [40,-1], wxALIGN_CENTRE_HORIZONTAL);
- $z_label->SetFont($Slic3r::GUI::small_font);
-
- my $vsizer = Wx::BoxSizer->new(wxVERTICAL);
- $vsizer->Add($slider, 1, wxALL | wxEXPAND | wxALIGN_CENTER, 3);
- $vsizer->Add($z_label, 0, wxALL | wxEXPAND | wxALIGN_CENTER, 3);
-
- my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
- $sizer->Add($canvas, 1, wxALL | wxEXPAND, 0);
- $sizer->Add($vsizer, 0, wxTOP | wxBOTTOM | wxEXPAND, 5);
-
- EVT_SLIDER($self, $slider, sub {
- $self->set_z($self->{layers_z}[$slider->GetValue])
- if $self->enabled;
- });
- EVT_KEY_DOWN($canvas, sub {
- my ($s, $event) = @_;
-
- my $key = $event->GetKeyCode;
- if ($key == 85 || $key == 315) {
- $slider->SetValue($slider->GetValue + 1);
- $self->set_z($self->{layers_z}[$slider->GetValue]);
- } elsif ($key == 68 || $key == 317) {
- $slider->SetValue($slider->GetValue - 1);
- $self->set_z($self->{layers_z}[$slider->GetValue]);
- }
- });
-
- $self->SetSizer($sizer);
- $self->SetMinSize($self->GetSize);
- $sizer->SetSizeHints($self);
-
- # init print
- $self->{print} = $print;
- $self->reload_print;
-
- return $self;
- }
- sub reload_print {
- my ($self) = @_;
-
- # we require that there's at least one object and the posSlice step
- # is performed on all of them (this ensures that _shifted_copies was
- # populated and we know the number of layers)
- if (!$self->print->object_step_done(STEP_SLICE)) {
- $self->enabled(0);
- $self->{slider}->Hide;
- $self->{canvas}->Refresh; # clears canvas
- return;
- }
-
- $self->{canvas}->bb($self->print->total_bounding_box);
-
- my %z = (); # z => 1
- foreach my $object (@{$self->{print}->objects}) {
- foreach my $layer (@{$object->layers}, @{$object->support_layers}) {
- $z{$layer->print_z} = 1;
- }
- }
- $self->enabled(1);
- $self->{layers_z} = [ sort { $a <=> $b } keys %z ];
- $self->{slider}->SetRange(0, scalar(@{$self->{layers_z}})-1);
- if ((my $z_idx = $self->{slider}->GetValue) <= $#{$self->{layers_z}}) {
- $self->set_z($self->{layers_z}[$z_idx]);
- } else {
- $self->{slider}->SetValue(0);
- $self->set_z($self->{layers_z}[0]) if @{$self->{layers_z}};
- }
- $self->{slider}->Show;
- $self->Layout;
- }
- sub set_z {
- my ($self, $z) = @_;
-
- return if !$self->enabled;
- $self->{z_label}->SetLabel(sprintf '%.2f', $z);
- $self->{canvas}->set_z($z);
- }
- package Slic3r::GUI::Plater::2DToolpaths::Canvas;
- use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS);
- use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants);
- use base qw(Wx::GLCanvas Class::Accessor);
- use Wx::GLCanvas qw(:all);
- use List::Util qw(min first);
- use Slic3r::Geometry qw(scale unscale epsilon);
- use Slic3r::Print::State ':steps';
- __PACKAGE__->mk_accessors(qw(print z layers color init bb _dirty));
- # make OpenGL::Array thread-safe
- {
- no warnings 'redefine';
- *OpenGL::Array::CLONE_SKIP = sub { 1 };
- }
- sub new {
- my ($class, $parent, $print) = @_;
-
- my $self = $class->SUPER::new($parent);
- $self->print($print);
-
- EVT_PAINT($self, sub {
- my $dc = Wx::PaintDC->new($self);
- $self->Render($dc);
- });
- EVT_SIZE($self, sub { $self->_dirty(1) });
- EVT_IDLE($self, sub {
- return unless $self->_dirty;
- return if !$self->IsShownOnScreen;
- $self->Resize( $self->GetSizeWH );
- $self->Refresh;
- });
-
- return $self;
- }
- sub set_z {
- my ($self, $z) = @_;
-
- my $print = $self->print;
-
- # can we have interlaced layers?
- my $interlaced = (defined first { $_->config->support_material } @{$print->objects})
- || (defined first { $_->config->infill_every_layers > 1 } @{$print->regions});
-
- my $max_layer_height = $print->max_allowed_layer_height;
-
- my @layers = ();
- foreach my $object (@{$print->objects}) {
- foreach my $layer (@{$object->layers}, @{$object->support_layers}) {
- if ($interlaced) {
- push @layers, $layer
- if $z > ($layer->print_z - $max_layer_height - epsilon)
- && $z <= $layer->print_z + epsilon;
- } else {
- push @layers, $layer if abs($layer->print_z - $z) < epsilon;
- }
- }
- }
-
- $self->z($z);
- $self->layers([ @layers ]);
- $self->Refresh;
- }
- sub Render {
- my ($self, $dc) = @_;
-
- # prevent calling SetCurrent() when window is not shown yet
- return unless $self->IsShownOnScreen;
- return unless my $context = $self->GetContext;
- $self->SetCurrent($context);
- $self->InitGL;
-
- glClearColor(1, 1, 1, 0);
- glClear(GL_COLOR_BUFFER_BIT);
-
- if (!$self->GetParent->enabled || !$self->layers) {
- glFlush();
- $self->SwapBuffers;
- return;
- }
-
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- my $bb = $self->bb;
- my ($x1, $y1, $x2, $y2) = ($bb->x_min, $bb->y_min, $bb->x_max, $bb->y_max);
- my ($x, $y) = $self->GetSizeWH;
- if (($x2 - $x1)/($y2 - $y1) > $x/$y) {
- # adjust Y
- my $new_y = $y * ($x2 - $x1) / $x;
- $y1 = ($y2 + $y1)/2 - $new_y/2;
- $y2 = $y1 + $new_y;
- } else {
- my $new_x = $x * ($y2 - $y1) / $y;
- $x1 = ($x2 + $x1)/2 - $new_x/2;
- $x2 = $x1 + $new_x;
- }
- glOrtho($x1, $x2, $y1, $y2, 0, 1);
- glDisable(GL_DEPTH_TEST);
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
-
- # anti-alias
- if (0) {
- glEnable(GL_LINE_SMOOTH);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
- glHint(GL_POLYGON_SMOOTH_HINT, GL_DONT_CARE);
- }
-
- my $tess;
- if (!(&Wx::wxMSW && $OpenGL::VERSION < 0.6704)) {
- # We can't use the GLU tesselator on MSW with older OpenGL versions
- # because of an upstream bug:
- # http://sourceforge.net/p/pogl/bugs/16/
- $tess = gluNewTess();
- gluTessCallback($tess, GLU_TESS_BEGIN, 'DEFAULT');
- gluTessCallback($tess, GLU_TESS_END, 'DEFAULT');
- gluTessCallback($tess, GLU_TESS_VERTEX, 'DEFAULT');
- gluTessCallback($tess, GLU_TESS_COMBINE, 'DEFAULT');
- gluTessCallback($tess, GLU_TESS_ERROR, 'DEFAULT');
- gluTessCallback($tess, GLU_TESS_EDGE_FLAG, 'DEFAULT');
- }
-
- foreach my $layer (@{$self->layers}) {
- my $object = $layer->object;
-
- # only draw the slice for the current layer
- next unless abs($layer->print_z - $self->z) < epsilon;
-
- # draw slice contour
- glLineWidth(1);
- foreach my $copy (@{ $object->_shifted_copies }) {
- glPushMatrix();
- glTranslatef(@$copy, 0);
-
- foreach my $slice (@{$layer->slices}) {
- glColor3f(0.95, 0.95, 0.95);
-
- if ($tess) {
- gluTessBeginPolygon($tess);
- foreach my $polygon (@$slice) {
- gluTessBeginContour($tess);
- gluTessVertex_p($tess, @$_, 0) for @$polygon;
- gluTessEndContour($tess);
- }
- gluTessEndPolygon($tess);
- }
-
- glColor3f(0.9, 0.9, 0.9);
- foreach my $polygon (@$slice) {
- foreach my $line (@{$polygon->lines}) {
- glBegin(GL_LINES);
- glVertex2f(@{$line->a});
- glVertex2f(@{$line->b});
- glEnd();
- }
- }
- }
- glPopMatrix();
- }
- }
-
- my $skirt_drawn = 0;
- my $brim_drawn = 0;
- foreach my $layer (@{$self->layers}) {
- my $object = $layer->object;
- my $print_z = $layer->print_z;
-
- # draw brim
- if ($self->print->step_done(STEP_BRIM) && $layer->id == 0 && !$brim_drawn) {
- $self->color([0, 0, 0]);
- $self->_draw(undef, $print_z, $_) for @{$self->print->brim};
- $brim_drawn = 1;
- }
- if ($self->print->step_done(STEP_SKIRT)
- && ($self->print->has_infinite_skirt() || $self->print->config->skirt_height > $layer->id)
- && !$skirt_drawn) {
- $self->color([0, 0, 0]);
- $self->_draw(undef, $print_z, $_) for @{$self->print->skirt};
- $skirt_drawn = 1;
- }
-
- foreach my $layerm (@{$layer->regions}) {
- if ($object->step_done(STEP_PERIMETERS)) {
- $self->color([0.7, 0, 0]);
- $self->_draw($object, $print_z, $_) for map @$_, @{$layerm->perimeters};
- }
-
- if ($object->step_done(STEP_INFILL)) {
- $self->color([0, 0, 0.7]);
- $self->_draw($object, $print_z, $_) for map @$_, @{$layerm->fills};
- }
- }
-
- if ($object->step_done(STEP_SUPPORTMATERIAL)) {
- if ($layer->isa('Slic3r::Layer::Support')) {
- $self->color([0, 0, 0]);
- $self->_draw($object, $print_z, $_) for @{$layer->support_fills};
- $self->_draw($object, $print_z, $_) for @{$layer->support_interface_fills};
- }
- }
- }
-
- gluDeleteTess($tess) if $tess;
- glFlush();
- $self->SwapBuffers;
- }
- sub _draw {
- my ($self, $object, $print_z, $path) = @_;
-
- my @paths = $path->isa('Slic3r::ExtrusionLoop')
- ? @$path
- : ($path);
-
- $self->_draw_path($object, $print_z, $_) for @paths;
- }
- sub _draw_path {
- my ($self, $object, $print_z, $path) = @_;
-
- return if $print_z - $path->height > $self->z - epsilon;
-
- if (abs($print_z - $self->z) < epsilon) {
- glColor3f(@{$self->color});
- } else {
- glColor3f(0.8, 0.8, 0.8);
- }
-
- glLineWidth(1);
-
- if (defined $object) {
- foreach my $copy (@{ $object->_shifted_copies }) {
- glPushMatrix();
- glTranslatef(@$copy, 0);
- foreach my $line (@{$path->polyline->lines}) {
- glBegin(GL_LINES);
- glVertex2f(@{$line->a});
- glVertex2f(@{$line->b});
- glEnd();
- }
- glPopMatrix();
- }
- } else {
- foreach my $line (@{$path->polyline->lines}) {
- glBegin(GL_LINES);
- glVertex2f(@{$line->a});
- glVertex2f(@{$line->b});
- glEnd();
- }
- }
- }
- sub InitGL {
- my $self = shift;
-
- return if $self->init;
- return unless $self->GetContext;
- $self->init(1);
- }
- sub GetContext {
- my ($self) = @_;
-
- if (Wx::wxVERSION >= 2.009) {
- return $self->{context} ||= Wx::GLContext->new($self);
- } else {
- return $self->SUPER::GetContext;
- }
- }
-
- sub SetCurrent {
- my ($self, $context) = @_;
-
- if (Wx::wxVERSION >= 2.009) {
- return $self->SUPER::SetCurrent($context);
- } else {
- return $self->SUPER::SetCurrent;
- }
- }
- sub Resize {
- my ($self, $x, $y) = @_;
-
- return unless $self->GetContext;
-
- $self->SetCurrent($self->GetContext);
- glViewport(0, 0, $x, $y);
- }
- sub line {
- my (
- $x1, $y1, $x2, $y2, # coordinates of the line
- $w, # width/thickness of the line in pixel
- $Cr, $Cg, $Cb, # RGB color components
- $Br, $Bg, $Bb, # color of background when alphablend=false
- # Br=alpha of color when alphablend=true
- $alphablend, # use alpha blend or not
- ) = @_;
-
- my $t;
- my $R;
- my $f = $w - int($w);
- my $A;
-
- if ($alphablend) {
- $A = $Br;
- } else {
- $A = 1;
- }
-
- # determine parameters t,R
- if ($w >= 0 && $w < 1) {
- $t = 0.05; $R = 0.48 + 0.32 * $f;
- if (!$alphablend) {
- $Cr += 0.88 * (1-$f);
- $Cg += 0.88 * (1-$f);
- $Cb += 0.88 * (1-$f);
- $Cr = 1.0 if ($Cr > 1.0);
- $Cg = 1.0 if ($Cg > 1.0);
- $Cb = 1.0 if ($Cb > 1.0);
- } else {
- $A *= $f;
- }
- } elsif ($w >= 1.0 && $w < 2.0) {
- $t = 0.05 + $f*0.33; $R = 0.768 + 0.312*$f;
- } elsif ($w >= 2.0 && $w < 3.0) {
- $t = 0.38 + $f*0.58; $R = 1.08;
- } elsif ($w >= 3.0 && $w < 4.0) {
- $t = 0.96 + $f*0.48; $R = 1.08;
- } elsif ($w >= 4.0 && $w < 5.0) {
- $t= 1.44 + $f*0.46; $R = 1.08;
- } elsif ($w >= 5.0 && $w < 6.0) {
- $t= 1.9 + $f*0.6; $R = 1.08;
- } elsif ($w >= 6.0) {
- my $ff = $w - 6.0;
- $t = 2.5 + $ff*0.50; $R = 1.08;
- }
- #printf( "w=%f, f=%f, C=%.4f\n", $w, $f, $C);
-
- # determine angle of the line to horizontal
- my $tx = 0; my $ty = 0; # core thinkness of a line
- my $Rx = 0; my $Ry = 0; # fading edge of a line
- my $cx = 0; my $cy = 0; # cap of a line
- my $ALW = 0.01;
- my $dx = $x2 - $x1;
- my $dy = $y2 - $y1;
- if (abs($dx) < $ALW) {
- # vertical
- $tx = $t; $ty = 0;
- $Rx = $R; $Ry = 0;
- if ($w > 0.0 && $w < 1.0) {
- $tx *= 8;
- } elsif ($w == 1.0) {
- $tx *= 10;
- }
- } elsif (abs($dy) < $ALW) {
- #horizontal
- $tx = 0; $ty = $t;
- $Rx = 0; $Ry = $R;
- if ($w > 0.0 && $w < 1.0) {
- $ty *= 8;
- } elsif ($w == 1.0) {
- $ty *= 10;
- }
- } else {
- if ($w < 3) { # approximate to make things even faster
- my $m = $dy/$dx;
- # and calculate tx,ty,Rx,Ry
- if ($m > -0.4142 && $m <= 0.4142) {
- # -22.5 < $angle <= 22.5, approximate to 0 (degree)
- $tx = $t * 0.1; $ty = $t;
- $Rx = $R * 0.6; $Ry = $R;
- } elsif ($m > 0.4142 && $m <= 2.4142) {
- # 22.5 < $angle <= 67.5, approximate to 45 (degree)
- $tx = $t * -0.7071; $ty = $t * 0.7071;
- $Rx = $R * -0.7071; $Ry = $R * 0.7071;
- } elsif ($m > 2.4142 || $m <= -2.4142) {
- # 67.5 < $angle <= 112.5, approximate to 90 (degree)
- $tx = $t; $ty = $t*0.1;
- $Rx = $R; $Ry = $R*0.6;
- } elsif ($m > -2.4142 && $m < -0.4142) {
- # 112.5 < angle < 157.5, approximate to 135 (degree)
- $tx = $t * 0.7071; $ty = $t * 0.7071;
- $Rx = $R * 0.7071; $Ry = $R * 0.7071;
- } else {
- # error in determining angle
- printf("error in determining angle: m=%.4f\n", $m);
- }
- } else { # calculate to exact
- $dx= $y1 - $y2;
- $dy= $x2 - $x1;
- my $L = sqrt($dx*$dx + $dy*$dy);
- $dx /= $L;
- $dy /= $L;
- $cx = -0.6*$dy; $cy=0.6*$dx;
- $tx = $t*$dx; $ty = $t*$dy;
- $Rx = $R*$dx; $Ry = $R*$dy;
- }
- }
- # draw the line by triangle strip
- glBegin(GL_TRIANGLE_STRIP);
- if (!$alphablend) {
- glColor3f($Br, $Bg, $Bb);
- } else {
- glColor4f($Cr, $Cg, $Cb, 0);
- }
- glVertex2f($x1 - $tx - $Rx, $y1 - $ty - $Ry); # fading edge
- glVertex2f($x2 - $tx - $Rx, $y2 - $ty - $Ry);
-
- if (!$alphablend) {
- glColor3f($Cr, $Cg, $Cb);
- } else {
- glColor4f($Cr, $Cg, $Cb, $A);
- }
- glVertex2f($x1 - $tx, $y1 - $ty); # core
- glVertex2f($x2 - $tx, $y2 - $ty);
- glVertex2f($x1 + $tx, $y1 + $ty);
- glVertex2f($x2 + $tx, $y2 + $ty);
-
- if ((abs($dx) < $ALW || abs($dy) < $ALW) && $w <= 1.0) {
- # printf("skipped one fading edge\n");
- } else {
- if (!$alphablend) {
- glColor3f($Br, $Bg, $Bb);
- } else {
- glColor4f($Cr, $Cg, $Cb, 0);
- }
- glVertex2f($x1 + $tx+ $Rx, $y1 + $ty + $Ry); # fading edge
- glVertex2f($x2 + $tx+ $Rx, $y2 + $ty + $Ry);
- }
- glEnd();
- # cap
- if ($w < 3) {
- # do not draw cap
- } else {
- # draw cap
- glBegin(GL_TRIANGLE_STRIP);
- if (!$alphablend) {
- glColor3f($Br, $Bg, $Bb);
- } else {
- glColor4f($Cr, $Cg, $Cb, 0);
- }
- glVertex2f($x1 - $Rx + $cx, $y1 - $Ry + $cy);
- glVertex2f($x1 + $Rx + $cx, $y1 + $Ry + $cy);
- glColor3f($Cr, $Cg, $Cb);
- glVertex2f($x1 - $tx - $Rx, $y1 - $ty - $Ry);
- glVertex2f($x1 + $tx + $Rx, $y1 + $ty + $Ry);
- glEnd();
- glBegin(GL_TRIANGLE_STRIP);
- if (!$alphablend) {
- glColor3f($Br, $Bg, $Bb);
- } else {
- glColor4f($Cr, $Cg, $Cb, 0);
- }
- glVertex2f($x2 - $Rx - $cx, $y2 - $Ry - $cy);
- glVertex2f($x2 + $Rx - $cx, $y2 + $Ry - $cy);
- glColor3f($Cr, $Cg, $Cb);
- glVertex2f($x2 - $tx - $Rx, $y2 - $ty - $Ry);
- glVertex2f($x2 + $tx + $Rx, $y2 + $ty + $Ry);
- glEnd();
- }
- }
- package Slic3r::GUI::Plater::2DToolpaths::Dialog;
- use Wx qw(:dialog :id :misc :sizer);
- use Wx::Event qw(EVT_CLOSE);
- use base 'Wx::Dialog';
- sub new {
- my $class = shift;
- my ($parent, $print) = @_;
- my $self = $class->SUPER::new($parent, -1, "Toolpaths", wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
-
- my $sizer = Wx::BoxSizer->new(wxVERTICAL);
- $sizer->Add(Slic3r::GUI::Plater::2DToolpaths->new($self, $print), 1, wxEXPAND, 0);
- $self->SetSizer($sizer);
- $self->SetMinSize($self->GetSize);
-
- # needed to actually free memory
- EVT_CLOSE($self, sub {
- $self->EndModal(wxID_OK);
- $self->Destroy;
- });
-
- return $self;
- }
- 1;
|