<commit_before>package Slic3r::GUI::Plater::2DToolpaths;
use strict;
use warnings;
use utf8;

use List::Util qw();
use Slic3r::Geometry qw();
use Wx qw(:misc :sizer :slider);
use Wx::Event qw(EVT_SLIDER);
use base 'Wx::Panel';

sub new {
    my $class = shift;
    my ($parent, $print) = @_;
    
    my $self = $class->SUPER::new($parent, -1, wxDefaultPosition);
    $self->{print} = $print;
    my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
    
    my $canvas = $self->{canvas} = Slic3r::GUI::Plater::2DToolpaths::Canvas->new($self, $print);
    $sizer->Add($canvas, 1, wxALL | wxEXPAND, 10);
    
    my $slider = $self->{slider} = Wx::Slider->new(
        $self, -1,
        0,                              # default
        0,                              # min
        $print->total_layer_count-1,    # max
        wxDefaultPosition,
        wxDefaultSize,
        wxVERTICAL | wxSL_INVERSE,
    );
    $sizer->Add($slider, 0, wxALL | wxEXPAND, 10);
    
    EVT_SLIDER($self, $slider, sub {
        $canvas->set_layer($slider->GetValue);
    });
    
    $self->SetSizer($sizer);
    $self->SetMinSize($self->GetSize);
    $sizer->SetSizeHints($self);
    
    $canvas->set_layer(0);
    
    return $self;
}


package Slic3r::GUI::Plater::2DToolpaths::Canvas;

use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS);
use OpenGL qw(:glconstants :glfunctions :glufunctions);
use base qw(Wx::GLCanvas Class::Accessor);
use Wx::GLCanvas qw(:all);
use List::Util qw(min);
use Slic3r::Geometry qw(scale unscale);

__PACKAGE__->mk_accessors(qw(print layer_id init dirty bb));

# 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);
    $self->bb($self->print->bounding_box);
    
    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_layer {
    my ($self, $layer_id) = @_;
    
    $self->layer_id($layer_id);
    $self->dirty(1);
}

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;
    
    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();
    
    glClearColor(1, 1, 1, 0);
    glClear(GL_COLOR_BUFFER_BIT);
    
    foreach my $object (@{$self->print->objects}) {
        my $layer = $object->get_layer($self->layer_id);
        foreach my $layerm (@{$layer->regions}) {
            glColor3f(0.7, 0, 0);
            $self->_draw_extrusionpath($object, $_) for @{$layerm->perimeters};
            
            glColor3f(0, 0, 0.7);
            $self->_draw_extrusionpath($object, $_) for map @$_, @{$layerm->fills};
        }
    }
    
    glFlush();
    $self->SwapBuffers;
}

sub _draw_extrusionpath {
    my ($self, $object, $path) = @_;
    
    my $polyline = $path->isa('Slic3r::ExtrusionLoop')
        ? $path->polygon->split_at_first_point<commit_msg>Fix toolpaths preview when objects have distinct layer heights<commit_after>package Slic3r::GUI::Plater::2DToolpaths;
use strict;
use warnings;
use utf8;

use List::Util qw();
use Slic3r::Geometry qw();
use Wx qw(:misc :sizer :slider);
use Wx::Event qw(EVT_SLIDER);
use base 'Wx::Panel';

sub new {
    my $class = shift;
    my ($parent, $print) = @_;
    
    my $self = $class->SUPER::new($parent, -1, wxDefaultPosition);
    
    # init print
    $self->{print} = $print;
    $self->{layers} = {};  # print_z => [ layer*, layer* ... ]
    foreach my $object (@{$print->objects}) {
        foreach my $layer (@{$object->layers}) {
            $self->{layers}{$layer->print_z} //= [];
            push @{ $self->{layers}{$layer->print_z} }, $layer;
        }
    }
    $self->{layers_z} = [ sort keys %{$self->{layers}} ];   # [ z, z ... ]
    
    # init GUI elements
    my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
    my $canvas = $self->{canvas} = Slic3r::GUI::Plater::2DToolpaths::Canvas->new($self, $print);
    $sizer->Add($canvas, 1, wxALL | wxEXPAND, 10);
    my $slider = $self->{slider} = Wx::Slider->new(
        $self, -1,
        0,                              # default
        0,                              # min
        scalar(@{$self->{layers_z}})-1,    # max
        wxDefaultPosition,
        wxDefaultSize,
        wxVERTICAL | wxSL_INVERSE,
    );
    $sizer->Add($slider, 0, wxALL | wxEXPAND, 10);
    
    EVT_SLIDER($self, $slider, sub {
        my $z = $self->{layers_z}[$slider->GetValue];
        $canvas->set_layers($self->{layers}{$z});
    });
    
    $self->SetSizer($sizer);
    $self->SetMinSize($self->GetSize);
    $sizer->SetSizeHints($self);
    
    $canvas->set_layers($self->{layers}{ $self->{layers_z}[0] });
    
    return $self;
}


package Slic3r::GUI::Plater::2DToolpaths::Canvas;

use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS);
use OpenGL qw(:glconstants :glfunctions :glufunctions);
use base qw(Wx::GLCanvas Class::Accessor);
use Wx::GLCanvas qw(:all);
use List::Util qw(min);
use Slic3r::Geometry qw(scale unscale);

__PACKAGE__->mk_accessors(qw(print layers init dirty bb));

# 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);
    $self->bb($self->print->bounding_box);
    
    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_layers {
    my ($self, $layers) = @_;
    
    $self->layers($layers);
    $self->dirty(1);
}

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;
    
    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();
    
    glClearColor(1, 1, 1, 0);
    glClear(GL_COLOR_BUFFER_BIT);
    
    foreach my $layer (@{$self->layers}) {
        my $object = $layer->object;
        foreach my $layerm (@{$layer->regions}) {
            glColor3f(0.7, 0, 0);
            $self->_draw_extrusionpath($object, $_) for @{$layerm->perimeters};
            
            glColor3f(0, 0, 0.7);
            $self->_draw_extrusionpath($object, $_) for map @$_, @{$layerm->fills};
        }
    }
    
    glFlush();
    $self->SwapBuffers;
}

sub _draw_extrusionpath {
    my ($self, $object, $path) = @_;
    
    my $polyline = $path->isa('Slic3r::ExtrusionLoop')
        ? $path->polygon->split_at_first_point
{'fraction_non_alphanumeric': 0.1860300200921877, 'fraction_numerical': 0.016782886183666232, 'mean_word_length': 2.5554621848739494, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 129, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5883116', 'n_tokens_mistral': 3298, 'n_tokens_neox': 3263, 'n_words': 624}