Merge remote-tracking branch 'thethirdone/Scene3D' into test-merge-thethirdone-3d-plater

# Conflicts:
#	.gitignore
#	.travis.yml
#	README.md
#	package/linux/travis-setup.sh
#	src/CMakeLists.txt
#	src/GUI/ColorScheme/Default.hpp
#	src/GUI/Plater.cpp
#	src/GUI/Plater/Plate2D.cpp
#	src/GUI/Plater/Plate3D.hpp
#	src/GUI/Plater/Preview3D.hpp
#	src/GUI/Preset.hpp
#	src/GUI/misc_ui.cpp
#	src/GUI/misc_ui.hpp
#	src/slic3r.cpp
#	src/test/GUI/test_field_checkbox.cpp
#	xs/src/libslic3r/Config.cpp
#	xs/src/libslic3r/Config.hpp
#	xs/src/libslic3r/ConfigBase.cpp
#	xs/src/libslic3r/ConfigBase.hpp
#	xs/src/libslic3r/Log.hpp
This commit is contained in:
Joseph Lenox 2018-07-17 17:35:05 -05:00
commit a9b3b0ab7f
18 changed files with 1563 additions and 66 deletions

View File

@ -32,7 +32,7 @@ cd $WD/${APP}.AppDir
mkdir -p $WD/${APP}.AppDir/usr/bin
# Copy primary Slic3r script here and perl-local, as well as var
for i in {var,slic3r.pl,perl-local}; do
for i in {var,Slic3r}; do
cp -R $srcfolder/$i $WD/${APP}.AppDir/usr/bin/
done
@ -48,8 +48,6 @@ for i in $(cat $WD/libpaths.appimage.txt | grep -v "^#" | awk -F# '{print $1}');
done
cp -R $srcfolder/local-lib ${WD}/${APP}.AppDir/usr/lib/local-lib
cat > $WD/${APP}.AppDir/AppRun << 'EOF'
#!/usr/bin/env bash
# some magic to find out the real location of this script dealing with symlinks

View File

@ -1,8 +1,3 @@
/home/travis/builds/alexrj/Slic3r/local-lib/lib/perl5/x86_64-linux-thread-multi/Alien/wxWidgets/gtk_3_0_2_uni/lib/libwx_baseu-3.0.so.0
/home/travis/builds/alexrj/Slic3r/local-lib/lib/perl5/x86_64-linux-thread-multi/Alien/wxWidgets/gtk_3_0_2_uni/lib/libwx_gtk2u_adv-3.0.so.0
/home/travis/builds/alexrj/Slic3r/local-lib/lib/perl5/x86_64-linux-thread-multi/Alien/wxWidgets/gtk_3_0_2_uni/lib/libwx_gtk2u_core-3.0.so.0
/home/travis/builds/alexrj/Slic3r/local-lib/lib/perl5/x86_64-linux-thread-multi/Alien/wxWidgets/gtk_3_0_2_uni/lib/libwx_gtk2u_gl-3.0.so.0
/home/travis/builds/alexrj/Slic3r/local-lib/lib/perl5/x86_64-linux-thread-multi/Alien/wxWidgets/gtk_3_0_2_uni/lib/libwx_gtk2u_html-3.0.so.0
/lib/x86_64-linux-gnu/liblzma.so.5
/lib/x86_64-linux-gnu/libpng12.so.0
/usr/lib/x86_64-linux-gnu/libjpeg.so.8

View File

@ -10,7 +10,6 @@ if [ "$#" -ne 1 ]; then
echo "Usage: $(basename $0) arch_name"
exit 1;
fi
libdirs=$(find ./local-lib -iname *.so -exec dirname {} \; | sort -u | paste -sd ";" -)
WD=./$(dirname $0)
source $(dirname $0)/../common/util.sh
# Determine if this is a tagged (release) commit.
@ -22,7 +21,6 @@ set_build_id
set_branch
set_app_name
set_pr_id
install_par
# If we're on a branch, add the branch name to the app name.
if [ "$current_branch" == "master" ]; then
@ -35,8 +33,7 @@ else
dmgfile=slic3r-${SLIC3R_BUILD_ID}-${1}-${current_branch}.tar.bz2
fi
rm -rf $WD/_tmp
mkdir -p $WD/_tmp
mkdir -p $WD
# Set the application folder infomation.
appfolder="$WD/${appname}"
@ -46,8 +43,6 @@ resourcefolder=$appfolder
echo "Appfolder: $appfolder, archivefolder: $archivefolder"
# Our slic3r dir and location of perl
PERL_BIN=$(which perl)
PP_BIN=$(which pp)
SLIC3R_DIR="./"
if [[ -d "${appfolder}" ]]; then
@ -67,14 +62,12 @@ echo "Copying resources..."
cp -rf $SLIC3R_DIR/var $resourcefolder/
echo "Copying Slic3r..."
cp $SLIC3R_DIR/slic3r.pl $archivefolder/slic3r.pl
cp -fRP $SLIC3R_DIR/local-lib $archivefolder/local-lib
cp -fRP $SLIC3R_DIR/lib/* $archivefolder/local-lib/lib/perl5/
cp $SLIC3R_DIR/slic3r $archivefolder/Slic3r
mkdir $archivefolder/bin
echo "Installing libraries to $archivefolder/bin ..."
if [ -z ${WXDIR+x} ]; then
for bundle in $(find $archivefolder/local-lib/lib/perl5 -name '*.so' | grep "Wx") $(find $archivefolder/local-lib/lib/perl5 -name '*.so' -type f | grep "wxWidgets"); do
for bundle in $archivefolder/Slic3r; do
echo "$(LD_LIBRARY_PATH=$libdirs ldd $bundle | grep .so | grep local-lib | awk '{print $3}')"
for dylib in $(LD_LIBRARY_PATH=$libdirs ldd $bundle | grep .so | grep local-lib | awk '{print $3}'); do
install -v $dylib $archivefolder/bin
@ -91,37 +84,8 @@ echo "Copying startup script..."
cp -f $WD/startup_script.sh $archivefolder/$appname
chmod +x $archivefolder/$appname
echo "Copying perl from $PERL_BIN"
# Edit package/common/coreperl to add/remove core Perl modules added to this package, one per line.
cp -f $PERL_BIN $archivefolder/perl-local
${PP_BIN} wxextension .0 \
-M $(grep -v "^#" ${WD}/../common/coreperl | xargs | awk 'BEGIN { OFS=" -M "}; {$1=$1; print $0}') \
-B -p -e "print 123" -o $WD/_tmp/test.par
unzip -qq -o $WD/_tmp/test.par -d $WD/_tmp/
cp -rf $WD/_tmp/lib/* $archivefolder/local-lib/lib/perl5/
cp -rf $WD/_tmp/shlib $archivefolder/
rm -rf $WD/_tmp
for i in $(cat $WD/libpaths.txt | grep -v "^#" | awk -F# '{print $1}'); do
install -v $i $archivefolder/bin
done
echo "Cleaning local-lib"
rm -rf $archivefolder/local-lib/bin
rm -rf $archivefolder/local-lib/man
rm -f $archivefolder/local-lib/lib/perl5/Algorithm/*.pl
rm -rf $archivefolder/local-lib/lib/perl5/unicore
rm -rf $archivefolder/local-lib/lib/perl5/App
rm -rf $archivefolder/local-lib/lib/perl5/Devel/CheckLib.pm
rm -rf $archivefolder/local-lib/lib/perl5/ExtUtils
rm -rf $archivefolder/local-lib/lib/perl5/Module/Build*
rm -rf $(pwd)$archivefolder/local-lib/lib/perl5/TAP
rm -rf $(pwd)/$archivefolder/local-lib/lib/perl5/Test*
find $(pwd)/$archivefolder/local-lib -type d -path '*/Wx/*' \( -name WebView \
-or -name DocView -or -name STC -or -name IPC \
-or -name Calendar -or -name DataView \
-or -name DateTime -or -name Media -or -name PerlTest \
-or -name Ribbon \) -exec rm -rf "{}" \;
rm -rf $archivefolder/local-lib/lib/perl5/*/Alien/wxWidgets/*/include
find $archivefolder/local-lib -depth -type d -empty -exec rmdir "{}" \;
tar -C$(pwd)/$(dirname $appfolder) -cjf $(pwd)/$dmgfile "$appname"

View File

@ -3,4 +3,4 @@
BIN=$(readlink "$0")
DIR=$(dirname "$BIN")
export LD_LIBRARY_PATH="$DIR/bin"
exec "$DIR/perl-local" -I"$DIR/local-lib/lib/perl5" "$DIR/slic3r.pl" $@
exec "$DIR/Slic3r"

View File

@ -286,10 +286,14 @@ IF(wxWidgets_FOUND)
${GUI_LIBDIR}/Dialogs/PrintEditor.cpp
${GUI_LIBDIR}/Dialogs/PrinterEditor.cpp
${GUI_LIBDIR}/Dialogs/MaterialEditor.cpp
${GUI_LIBDIR}/Dialogs/ObjectCutDialog.cpp
${GUI_LIBDIR}/GUI.cpp
${GUI_LIBDIR}/MainFrame.cpp
${GUI_LIBDIR}/Plater.cpp
${GUI_LIBDIR}/Scene3D.cpp
${GUI_LIBDIR}/Plater/Plate2D.cpp
${GUI_LIBDIR}/Plater/Plate3D.cpp
${GUI_LIBDIR}/Plater/Preview3D.cpp
${GUI_LIBDIR}/Plater/PlaterObject.cpp
${GUI_LIBDIR}/ProgressStatusBar.cpp
${GUI_LIBDIR}/Settings.cpp
@ -298,6 +302,7 @@ IF(wxWidgets_FOUND)
${GUI_LIBDIR}/OptionsGroup/UI_Choice.cpp
${GUI_LIBDIR}/OptionsGroup/UI_Point.cpp
${GUI_LIBDIR}/OptionsGroup/UI_Point3.cpp
${LIBDIR}/slic3r/GUI/3DScene.cpp
)
target_compile_features(slic3r_gui PUBLIC cxx_std_14)
#only build GUI lib if building with wx

View File

@ -7,7 +7,7 @@ class DefaultColor : public ColorScheme {
public:
const std::string name() const { return "Default"; }
const bool SOLID_BACKGROUNDCOLOR() const { return false; };
const wxColour SELECTED_COLOR() const { return wxColour(0, 1, 0); };
const wxColour SELECTED_COLOR() const { return wxColour(0, 255, 0); };
const wxColour HOVER_COLOR() const { return wxColour(255*0.4, 255*0.9, 0); }; //<Hover over Model
const wxColour TOP_COLOR() const { return wxColour(10,98,144); }; //<TOP Backgroud color
const wxColour BOTTOM_COLOR() const { return wxColour(0,0,0); }; //<BOTTOM Backgroud color
@ -15,10 +15,10 @@ public:
const wxColour GRID_COLOR() const { return wxColour(255*0.2, 255*0.2, 255*0.2, 255*0.4); }; //<Grid color
const wxColour GROUND_COLOR() const { return wxColour(255*0.8, 255*0.6, 255*0.5, 255*0.4); }; //<Ground or Plate color
const wxColour COLOR_CUTPLANE() const { return wxColour(255*0.8, 255*0.8, 255*0.8, 255*0.5); };
const wxColour COLOR_PARTS() const { return wxColour(1, 255*0.95, 255*0.2, 1); }; //<Perimeter color
const wxColour COLOR_INFILL() const { return wxColour(1, 255*0.45, 255*0.45, 1); };
const wxColour COLOR_SUPPORT() const { return wxColour(255*0.5, 1, 255*0.5, 1); };
const wxColour COLOR_UNKNOWN() const { return wxColour(255*0.5, 255*0.5, 1, 1); };
const wxColour COLOR_PARTS() const { return wxColour(255, 255*0.95, 255*0.2); }; //<Perimeter color
const wxColour COLOR_INFILL() const { return wxColour(255, 255*0.45, 255*0.45); };
const wxColour COLOR_SUPPORT() const { return wxColour(255*0.5, 1, 255*0.5); };
const wxColour COLOR_UNKNOWN() const { return wxColour(255*0.5, 255*0.5, 1); };
const wxColour BED_COLOR() const { return wxColour(255, 255, 255); };
const wxColour BED_GRID() const { return wxColour(230, 230, 230); };
const wxColour BED_SELECTED() const { return wxColour(255, 166, 128); };

View File

@ -0,0 +1,351 @@
#include "ObjectCutDialog.hpp"
// Cut an object at a Z position, keep either the top or the bottom of the object.
// This dialog gets opened with the "Cut..." button above the platter.
namespace Slic3r { namespace GUI {
ObjectCutDialog::ObjectCutDialog(wxWindow* parent, ModelObject* _model_object/*, ...*/): wxDialog(parent, -1, _(_model_object->name), wxDefaultPosition, wxSize(500, 500), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), model_object(_model_object) {
/*
$self->{model_object}->transform_by_instance($self->{model_object}->get_instance(0), 1);
// cut options
my $size_z = $self->{model_object}->instance_bounding_box(0)->size->z;
$self->{cut_options} = {
axis => Z,
z => $size_z/2,
keep_upper => 0,
keep_lower => 1,
rotate_lower => 0,
preview => 1,
};
my $optgroup;
$optgroup = $self->{optgroup} = Slic3r::GUI::OptionsGroup->new(
parent => $self,
title => 'Cut',
on_change => sub {
my ($opt_id) = @_;
# There seems to be an issue with wxWidgets 3.0.2/3.0.3, where the slider
# genates tens of events for a single value change.
# Only trigger the recalculation if the value changes
# or a live preview was activated and the mesh cut is not valid yet.
if ($self->{cut_options}{$opt_id} != $optgroup->get_value($opt_id) ||
! $self->{mesh_cut_valid} && $self->_life_preview_active()) {
$self->{cut_options}{$opt_id} = $optgroup->get_value($opt_id);
$self->{mesh_cut_valid} = 0;
wxTheApp->CallAfter(sub {
$self->_update;
});
}
},
label_width => 120,
);
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'axis',
type => 'select',
label => 'Axis',
labels => ['X','Y','Z'],
values => [X,Y,Z],
default => $self->{cut_options}{axis},
));
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'z',
type => 'slider',
label => 'Z',
default => $self->{cut_options}{z},
min => 0,
max => $size_z,
full_width => 1,
));
{
my $line = Slic3r::GUI::OptionsGroup::Line->new(
label => 'Keep',
);
$line->append_option(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'keep_upper',
type => 'bool',
label => 'Upper part',
default => $self->{cut_options}{keep_upper},
));
$line->append_option(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'keep_lower',
type => 'bool',
label => 'Lower part',
default => $self->{cut_options}{keep_lower},
));
$optgroup->append_line($line);
}
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'rotate_lower',
label => 'Rotate lower part upwards',
type => 'bool',
tooltip => 'If enabled, the lower part will be rotated by 180° so that the flat cut surface lies on the print bed.',
default => $self->{cut_options}{rotate_lower},
));
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
opt_id => 'preview',
label => 'Show preview',
type => 'bool',
tooltip => 'If enabled, object will be cut in real time.',
default => $self->{cut_options}{preview},
));
{
my $cut_button_sizer = Wx::BoxSizer->new(wxVERTICAL);
$self->{btn_cut} = Wx::Button->new($self, -1, "Perform cut", wxDefaultPosition, wxDefaultSize);
$self->{btn_cut}->SetDefault;
$cut_button_sizer->Add($self->{btn_cut}, 0, wxALIGN_RIGHT | wxALL, 10);
$self->{btn_cut_grid} = Wx::Button->new($self, -1, "Cut by grid…", wxDefaultPosition, wxDefaultSize);
$cut_button_sizer->Add($self->{btn_cut_grid}, 0, wxALIGN_RIGHT | wxALL, 10);
$optgroup->append_line(Slic3r::GUI::OptionsGroup::Line->new(
sizer => $cut_button_sizer,
));
}
# left pane with tree
my $left_sizer = Wx::BoxSizer->new(wxVERTICAL);
$left_sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
# right pane with preview canvas
my $canvas;
if ($Slic3r::GUI::have_OpenGL) {
$canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self);
$canvas->load_object($self->{model_object}, undef, [0]);
$canvas->set_auto_bed_shape;
$canvas->SetSize([500,500]);
$canvas->SetMinSize($canvas->GetSize);
$canvas->zoom_to_volumes;
}
$self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL);
$self->{sizer}->Add($left_sizer, 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
$self->{sizer}->Add($canvas, 1, wxEXPAND | wxALL, 0) if $canvas;
$self->SetSizer($self->{sizer});
$self->SetMinSize($self->GetSize);
$self->{sizer}->SetSizeHints($self);
EVT_BUTTON($self, $self->{btn_cut}, sub {
// Recalculate the cut if the preview was not active.
$self->_perform_cut() unless $self->{mesh_cut_valid};
// Adjust position / orientation of the split object halves.
if (my $lower = $self->{new_model_objects}[0]) {
if ($self->{cut_options}{rotate_lower} && $self->{cut_options}{axis} == Z) {
$lower->rotate(PI, X);
}
$lower->center_around_origin; # align to Z = 0
}
if (my $upper = $self->{new_model_objects}[1]) {
$upper->center_around_origin; # align to Z = 0
}
// Note that the window was already closed, so a pending update will not be executed.
already_closed = true;
EndModal(wxID_OK);
Destroy();
});
EVT_BUTTON($self, $self->{btn_cut_grid}, sub {
my $grid_x = Wx::GetTextFromUser("Enter the width of the desired tiles along the X axis:",
"Cut by Grid", 100, $self);
return if !looks_like_number($grid_x) || $grid_x <= 0;
my $grid_y = Wx::GetTextFromUser("Enter the width of the desired tiles along the Y axis:",
"Cut by Grid", 100, $self);
return if !looks_like_number($grid_y) || $grid_y <= 0;
my $process_dialog = Wx::ProgressDialog->new('Cutting', "Cutting model by grid…", 100, $self, 0);
$process_dialog->Pulse;
my $meshes = $self->{model_object}->mesh->cut_by_grid(Slic3r::Pointf->new($grid_x, $grid_y));
$self->{new_model_objects} = [];
my $bb = $self->{model_object}->bounding_box;
$self->{new_model} = my $model = Slic3r::Model->new;
for my $i (0..$#$meshes) {
push @{$self->{new_model_objects}}, my $o = $model->add_object(
name => sprintf('%s (%d)', $self->{model_object}->name, $i+1),
);
my $v = $o->add_volume(
mesh => $meshes->[$i],
name => $o->name,
);
$o->center_around_origin;
my $i = $o->add_instance(
offset => Slic3r::Pointf->new(@{$o->origin_translation->negative}[X,Y]),
);
$i->offset->translate(
5 * ceil(($i->offset->x - $bb->center->x) / $grid_x),
5 * ceil(($i->offset->y - $bb->center->y) / $grid_y),
);
}
$process_dialog->Destroy;
# Note that the window was already closed, so a pending update will not be executed.
$self->{already_closed} = 1;
$self->EndModal(wxID_OK);
$self->Destroy();
});
EVT_CLOSE($self, sub {
# Note that the window was already closed, so a pending update will not be executed.
already_closed = true;
EndModal(wxID_CANCEL);
Destroy();
});
*/
_update();
}
// scale Z down to original size since we're using the transformed mesh for 3D preview
// and cut dialog but ModelObject::cut() needs Z without any instance transformation
void ObjectCutDialog::_mesh_slice_z_pos(){
/*
my $bb = $self->{model_object}->instance_bounding_box(0);
my $z = $self->{cut_options}{axis} == X ? $bb->x_min
: $self->{cut_options}{axis} == Y ? $bb->y_min
: $bb->z_min;
$z += $self->{cut_options}{z} / $self->{model_object}->instances->[0]->scaling_factor;
return $z;
*/
}
// Only perform live preview if just a single part of the object shall survive.
void ObjectCutDialog::_life_preview_active() {
/*
return $self->{cut_options}{preview} && ($self->{cut_options}{keep_upper} != $self->{cut_options}{keep_lower});
*/
}
// Slice the mesh, keep the top / bottom part.
void ObjectCutDialog::_perform_cut(){
// Early exit. If the cut is valid, don't recalculate it.
if (mesh_cut_valid) return;
/*
my $z = $self->_mesh_slice_z_pos();
my ($new_model) = $self->{model_object}->cut($self->{cut_options}{axis}, $z);
my ($upper_object, $lower_object) = @{$new_model->objects};
$self->{new_model} = $new_model;
$self->{new_model_objects} = [];
if ($self->{cut_options}{keep_upper} && $upper_object->volumes_count > 0) {
$self->{new_model_objects}[1] = $upper_object;
}
if ($self->{cut_options}{keep_lower} && $lower_object->volumes_count > 0) {
$self->{new_model_objects}[0] = $lower_object;
}
*/
mesh_cut_valid = true;
}
void ObjectCutDialog::_update() {
// Don't update if the window was already closed.
// We are not sure whether the action planned by wxTheApp->CallAfter() may be triggered after the window is closed.
// Probably not, but better be safe than sorry, which is espetially true on multiple platforms.
if (already_closed) return;
/*
# Only recalculate the cut, if the live cut preview is active.
my $life_preview_active = $self->_life_preview_active();
$self->_perform_cut() if $life_preview_active;
{
# scale Z down to original size since we're using the transformed mesh for 3D preview
# and cut dialog but ModelObject::cut() needs Z without any instance transformation
my $z = $self->_mesh_slice_z_pos();
# update canvas
if ($self->{canvas}) {
# get volumes to render
my @objects = ();
if ($life_preview_active) {
push @objects, grep defined, @{$self->{new_model_objects}};
} else {
push @objects, $self->{model_object};
}
# get section contour
my @expolygons = ();
foreach my $volume (@{$self->{model_object}->volumes}) {
next if !$volume->mesh;
next if $volume->modifier;
my $expp = $volume->mesh->slice_at($self->{cut_options}{axis}, $z);
push @expolygons, @$expp;
}
my $offset = $self->{model_object}->instances->[0]->offset;
foreach my $expolygon (@expolygons) {
$self->{model_object}->instances->[0]->transform_polygon($_)
for @$expolygon;
if ($self->{cut_options}{axis} != X) {
$expolygon->translate(0, Slic3r::Geometry::scale($offset->y)); #)
}
if ($self->{cut_options}{axis} != Y) {
$expolygon->translate(Slic3r::Geometry::scale($offset->x), 0);
}
}
$self->{canvas}->reset_objects;
$self->{canvas}->load_object($_, undef, [0]) for @objects;
my $plane_z = $self->{cut_options}{z};
$plane_z += 0.02 if !$self->{cut_options}{keep_upper};
$plane_z -= 0.02 if !$self->{cut_options}{keep_lower};
$self->{canvas}->SetCuttingPlane(
$self->{cut_options}{axis},
$plane_z,
[@expolygons],
);
$self->{canvas}->Render;
}
}
# update controls
{
my $z = $self->{cut_options}{z};
my $optgroup = $self->{optgroup};
{
my $bb = $self->{model_object}->instance_bounding_box(0);
my $max = $self->{cut_options}{axis} == X ? $bb->size->x
: $self->{cut_options}{axis} == Y ? $bb->size->y ###
: $bb->size->z;
$optgroup->get_field('z')->set_range(0, $max);
}
$optgroup->get_field('keep_upper')->toggle(my $have_upper = abs($z - $optgroup->get_option('z')->max) > 0.1);
$optgroup->get_field('keep_lower')->toggle(my $have_lower = $z > 0.1);
$optgroup->get_field('rotate_lower')->toggle($z > 0 && $self->{cut_options}{keep_lower} && $self->{cut_options}{axis} == Z);
$optgroup->get_field('preview')->toggle($self->{cut_options}{keep_upper} != $self->{cut_options}{keep_lower});
# update cut button
if (($self->{cut_options}{keep_upper} && $have_upper)
|| ($self->{cut_options}{keep_lower} && $have_lower)) {
$self->{btn_cut}->Enable;
} else {
$self->{btn_cut}->Disable;
}
}
*/
}
void ObjectCutDialog::NewModelObjects() {
/*
my ($self) = @_;
return grep defined, @{ $self->{new_model_objects} };
*/
}
}} // namespace Slic3r::GUI

View File

@ -0,0 +1,51 @@
#ifndef OBJECTCUTDIALOG_HPP
#define OBJECTCUTDIALOG_HPP
#include <wx/dialog.h>
#include "Scene3D.hpp"
#include "Model.hpp"
namespace Slic3r { namespace GUI {
class ObjectCutCanvas : public Scene3D {
// Not sure yet
protected:
// Draws the cutting plane
void after_render();
};
struct CutOptions {
float z;
enum {X,Y,Z} axis;
bool keep_upper, keep_lower, rotate_lower;
bool preview;
};
class ObjectCutDialog : public wxDialog {
public:
ObjectCutDialog(wxWindow* parent, ModelObject* _model);
private:
// Mark whether the mesh cut is valid.
// If not, it needs to be recalculated by _update() on wxTheApp->CallAfter() or on exit of the dialog.
bool mesh_cut_valid = false;
// Note whether the window was already closed, so a pending update is not executed.
bool already_closed = false;
// ObjectCutCanvas canvas;
ModelObject* model_object;
//std::shared_ptr<Slic3r::Model> model;
void _mesh_slice_z_pos();
void _life_preview_active();
void _perform_cut();
void _update();
void NewModelObjects();
};
}} // namespace Slic3r::GUI
#endif // OBJECTCUTDIALOG_HPP

View File

@ -13,6 +13,7 @@
#include "BoundingBox.hpp"
#include "Geometry.hpp"
#include "Dialogs/AnglePicker.hpp"
#include "Dialogs/ObjectCutDialog.hpp"
namespace Slic3r { namespace GUI {
@ -88,7 +89,10 @@ Plater::Plater(wxWindow* parent, const wxString& title) :
canvas3D = new Plate3D(preview_notebook, wxDefaultSize, objects, model, config);
preview_notebook->AddPage(canvas3D, _("3D"));
preview3D = new Preview3D(preview_notebook, wxDefaultSize, objects, model, config);
canvas3D->on_select_object = std::function<void (ObjIdx obj_idx)>(on_select_object);
canvas3D->on_instances_moved = std::function<void ()>(on_instances_moved);
preview3D = new Preview3D(preview_notebook, wxDefaultSize, print, objects, model, config);
preview_notebook->AddPage(preview3D, _("Preview"));
preview2D = new Preview2D(preview_notebook, wxDefaultSize, objects, model, config);
@ -494,13 +498,15 @@ void Plater::arrange() {
}
void Plater::on_model_change(bool force_autocenter) {
Log::info(LogChannel, L"Called on_modal_change");
// reload the select submenu (if already initialized)
{
auto* menu = this->GetFrame()->plater_select_menu;
if (menu != nullptr) {
for (auto* item : menu->GetMenuItems() ) { menu->Delete(item); }
auto list = menu->GetMenuItems();
for (auto it = list.begin();it!=list.end(); it++) { menu->Delete(*it); }
for (const auto& obj : this->objects) {
const auto idx {obj.identifier};
auto name {wxString(obj.name)};
@ -561,6 +567,7 @@ void Plater::select_object() {
void Plater::selection_changed() {
// Remove selection in 2D plater
this->canvas2D->set_selected(-1, -1);
this->canvas3D->selection_changed();
auto obj = this->selected_object();
bool have_sel {obj != this->objects.end()};
@ -882,6 +889,13 @@ void Plater::changescale() {
void Plater::object_cut_dialog() {
//TODO
ObjRef obj {this->selected_object()};
if (obj == this->objects.end()) return;
auto* model_object {this->model->objects.at(obj->identifier)};
auto cut_dialog = new ObjectCutDialog(nullptr, model_object);
cut_dialog->ShowModal();
cut_dialog->Destroy();
}
void Plater::object_layers_dialog() {

171
src/GUI/Plater/3DPreview.pm Normal file
View File

@ -0,0 +1,171 @@
package Slic3r::GUI::Plater::3DPreview;
use strict;
use warnings;
use utf8;
use Slic3r::Print::State ':steps';
use Wx qw(:misc :sizer :slider :statictext);
use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN);
use base qw(Wx::Panel Class::Accessor);
__PACKAGE__->mk_accessors(qw(print enabled _loaded canvas slider));
sub new {
my $class = shift;
my ($parent, $print) = @_;
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition);
# init GUI elements
my $canvas = Slic3r::GUI::3DScene->new($self);
$self->canvas($canvas);
my $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,
);
$self->slider($slider);
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]);
} else {
$event->Skip;
}
});
$self->SetSizer($sizer);
$self->SetMinSize($self->GetSize);
$sizer->SetSizeHints($self);
# init canvas
$self->print($print);
$self->reload_print;
return $self;
}
sub reload_print {
my ($self, $obj_idx) = @_;
$self->canvas->reset_objects;
$self->_loaded(0);
$self->load_print($obj_idx);
}
sub load_print {
my ($self, $obj_idx) = @_;
return if $self->_loaded;
# 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;
}
my $z_idx;
{
my %z = (); # z => 1
if(defined $obj_idx) { # Load only given object
foreach my $layer (@{$self->{print}->get_object($obj_idx)->layers}) {
$z{$layer->print_z} = 1;
}
}else{ # Load all objects on the plater + support material
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 (($z_idx = $self->slider->GetValue) <= $#{$self->{layers_z}} && $self->slider->GetValue != 0) {
# use $z_idx
} else {
$self->slider->SetValue(scalar(@{$self->{layers_z}})-1);
$z_idx = @{$self->{layers_z}} ? -1 : undef;
}
$self->slider->Show;
$self->Layout;
}
if ($self->IsShown) {
# set colors
$self->canvas->color_toolpaths_by($Slic3r::GUI::Settings->{_}{color_toolpaths_by});
if ($self->canvas->color_toolpaths_by eq 'extruder') {
my @filament_colors = map { s/^#//; [ map $_/255, (unpack 'C*', pack 'H*', $_), 255 ] }
@{$self->print->config->filament_colour};
$self->canvas->colors->[$_] = $filament_colors[$_] for 0..$#filament_colors;
} else {
$self->canvas->colors([ $self->canvas->default_colors ]);
}
if(defined $obj_idx) { # Load only one object
$self->canvas->load_print_object_toolpaths($self->{print}->get_object($obj_idx));
}else{ # load all objects
# load skirt and brim
$self->canvas->load_print_toolpaths($self->print);
foreach my $object (@{$self->print->objects}) {
$self->canvas->load_print_object_toolpaths($object);
#my @volume_ids = $self->canvas->load_object($object->model_object);
#$self->canvas->volumes->[$_]->color->[3] = 0.2 for @volume_ids;
}
}
$self->_loaded(1);
}
$self->set_z($self->{layers_z}[$z_idx]);
}
sub set_z {
my ($self, $z) = @_;
return if !$self->enabled;
$self->{z_label}->SetLabel(sprintf '%.2f', $z);
$self->canvas->set_toolpaths_range(0, $z);
$self->canvas->Refresh if $self->IsShown;
}
sub set_bed_shape {
my ($self, $bed_shape) = @_;
$self->canvas->set_bed_shape($bed_shape);
}
1;

176
src/GUI/Plater/Plate3D.cpp Normal file
View File

@ -0,0 +1,176 @@
#include "Plater/Plate3D.hpp"
#include "misc_ui.hpp"
namespace Slic3r { namespace GUI {
Plate3D::Plate3D(wxWindow* parent, const wxSize& size, std::vector<PlaterObject>& _objects, std::shared_ptr<Model> _model, std::shared_ptr<Config> _config) :
Scene3D(parent, size), objects(_objects), model(_model), config(_config)
{
// Bind the extra mouse events
this->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) { this->mouse_down(e); });
this->Bind(wxEVT_RIGHT_DOWN, [this](wxMouseEvent &e) { this->mouse_down(e); });
}
void Plate3D::mouse_down(wxMouseEvent &e){
if(hover){
on_select_object(hover_object);
moving = true;
moving_volume = hover_volume;
move_start = Point(e.GetX(),e.GetY());
}else{
on_select_object(-1);
}
hover = false;
}
void Plate3D::mouse_up(wxMouseEvent &e){
if(moving){
//translate object
moving = false;
uint i = 0;
for(const PlaterObject &object: objects){
const auto &modelobj = model->objects.at(object.identifier);
for(ModelInstance *instance: modelobj->instances){
uint size = modelobj->volumes.size();
if(i <= moving_volume && moving_volume < i+size){
instance->offset.translate(volumes.at(i).origin);
modelobj->update_bounding_box();
on_instances_moved();
Refresh();
return;
}else{
i+=size;
}
}
}
}
Scene3D::mouse_up(e);
}
void Plate3D::mouse_move(wxMouseEvent &e){
if(!e.Dragging()){
pos = Point(e.GetX(),e.GetY());
mouse = true;
Refresh();
} else if(moving){
const auto p = Point(e.GetX(),e.GetY());
const auto current = mouse_ray(p).intersect_plane(0);
const auto old = mouse_ray(move_start).intersect_plane(0);
move_start = p;
uint i = 0;
for(const PlaterObject &object: objects){
const auto &modelobj = model->objects.at(object.identifier);
for(ModelInstance *instance: modelobj->instances){
uint size = modelobj->volumes.size();
if(i <= moving_volume && moving_volume < i+size){
for(ModelVolume* volume: modelobj->volumes){
volumes.at(i).origin.translate(old.vector_to(current));
i++;
}
Refresh();
return;
}else{
i+=size;
}
}
}
} else {
Scene3D::mouse_move(e);
}
}
void Plate3D::update(){
volumes.clear();
for(const PlaterObject &object: objects){
const auto &modelobj = model->objects.at(object.identifier);
for(ModelInstance *instance: modelobj->instances){
for(ModelVolume* volume: modelobj->volumes){
TriangleMesh copy = volume->mesh;
instance->transform_mesh(&copy);
GLVertexArray model;
model.load_mesh(copy);
volumes.push_back(Volume{ wxColor(200,200,200), Pointf3(0,0,0), model, copy.bounding_box()});
}
}
}
color_volumes();
Refresh();
}
void Plate3D::color_volumes(){
uint i = 0;
for(const PlaterObject &object: objects){
const auto &modelobj = model->objects.at(object.identifier);
bool hover_object = hover && i <= hover_volume && hover_volume < i+modelobj->instances.size()*modelobj->volumes.size();
for(ModelInstance *instance: modelobj->instances){
for(ModelVolume* volume: modelobj->volumes){
auto& rendervolume = volumes.at(i);
if(object.selected){
rendervolume.color = ui_settings->color->SELECTED_COLOR();
}else if(hover_object){
rendervolume.color = ui_settings->color->HOVER_COLOR();
} else {
rendervolume.color = ui_settings->color->COLOR_PARTS();
}
i++;
}
}
}
}
void Plate3D::before_render(){
if (!mouse){
color_volumes();
return;
}
// Color each volume a different color, render and test which color is beneath the mouse.
//glDisable(GL_MULTISAMPLE) if ($self->{can_multisample});
glDisable(GL_LIGHTING);
uint i = 1;
for(Volume &volume : volumes){
volume.color = wxColor((i>>16)&0xFF,(i>>8)&0xFF,i&0xFF);
i++;
}
draw_volumes();
glFlush();
glFinish();
GLubyte color[4] = {0,0,0,0};
glReadPixels(pos.x, GetSize().GetHeight()- pos.y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, color);
// Handle the hovered volume
uint index = (color[0]<<16) + (color[1]<<8) + color[2];
hover = false;
///*$self->_hover_volume_idx(undef);
//$_->hover(0) for @{$self->volumes};
if (index != 0 && index <= volumes.size()) {
hover = true;
hover_volume = index - 1;
uint k = 0;
for(const PlaterObject &object: objects){
const auto &modelobj = model->objects.at(object.identifier);
if(k <= hover_volume && hover_volume < k+modelobj->instances.size()*modelobj->volumes.size()){
hover_object = k;
break;
}
k++;
}
/*
$self->volumes->[$volume_idx]->hover(1);
my $group_id = $self->volumes->[$volume_idx]->select_group_id;
if ($group_id != -1) {
$_->hover(1) for grep { $_->select_group_id == $group_id } @{$self->volumes};
}*/
//$self->on_hover->($volume_idx) if $self->on_hover;
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glFlush();
glFinish();
glEnable(GL_LIGHTING);
color_volumes();
mouse = false;
}
} } // Namespace Slic3r::GUI

View File

@ -4,18 +4,44 @@
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include "Plater/PlaterObject.hpp"
#include "Scene3D.hpp"
#include "Settings.hpp"
#include "Model.hpp"
#include "Config.hpp"
namespace Slic3r { namespace GUI {
class Plate3D : public wxPanel {
class Plate3D : public Scene3D {
public:
void update() {};
Plate3D(wxWindow* parent, const wxSize& size, std::vector<PlaterObject>& _objects, std::shared_ptr<Model> _model, std::shared_ptr<Config> _config) :
wxPanel(parent, wxID_ANY, wxDefaultPosition, size, wxTAB_TRAVERSAL), objects(_objects), model(_model), config(_config)
{}
Plate3D(wxWindow* parent, const wxSize& size, std::vector<PlaterObject>& _objects, std::shared_ptr<Model> _model, std::shared_ptr<Config> _config);
/// Called to regenerate rendered volumes from the model
void update();
/// Registered function to fire when objects are selected.
std::function<void (const unsigned int obj_idx)> on_select_object {};
/// Registered function to fire when an instance is moved.
std::function<void ()> on_instances_moved {};
void selection_changed(){Refresh();}
protected:
// Render each volume as a different color and check what color is beneath
// the mouse to detemine the hovered volume
void before_render();
// Mouse events are needed to handle selecting and moving objects
void mouse_up(wxMouseEvent &e);
void mouse_move(wxMouseEvent &e);
void mouse_down(wxMouseEvent &e);
private:
void color_volumes();
Point pos, move_start;
bool hover = false, mouse = false, moving = false;
uint hover_volume, hover_object, moving_volume;
std::vector<PlaterObject>& objects; //< reference to parent vector
std::shared_ptr<Slic3r::Model> model;
std::shared_ptr<Slic3r::Config> config;

View File

@ -0,0 +1,145 @@
#include "Preview3D.hpp"
#include <wx/event.h>
namespace Slic3r { namespace GUI {
Preview3D::Preview3D(wxWindow* parent, const wxSize& size, std::shared_ptr<Slic3r::Print> _print, std::vector<PlaterObject>& _objects, std::shared_ptr<Model> _model, std::shared_ptr<Config> _config) :
wxPanel(parent, wxID_ANY, wxDefaultPosition, size, wxTAB_TRAVERSAL), print(_print), objects(_objects), model(_model), config(_config), canvas(this,size)
{
// init GUI elements
slider = new wxSlider(
this, -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
);
this->z_label = new wxStaticText(this, -1, "", wxDefaultPosition,
wxSize(40,-1), wxALIGN_CENTRE_HORIZONTAL);
//z_label->SetFont(Slic3r::GUI::small_font);
auto* vsizer = new wxBoxSizer(wxVERTICAL);
vsizer->Add(slider, 1, wxALL | wxEXPAND | wxALIGN_CENTER, 3);
vsizer->Add(z_label, 0, wxALL | wxEXPAND | wxALIGN_CENTER, 3);
auto* sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(&canvas, 1, wxALL | wxEXPAND, 0);
sizer->Add(vsizer, 0, wxTOP | wxBOTTOM | wxEXPAND, 5);
this->Bind(wxEVT_SLIDER, [this](wxCommandEvent &e){
//$self->set_z($self->{layers_z}[$slider->GetValue])
// if $self->enabled;
});
this->Bind(wxEVT_CHAR, [this](wxKeyEvent &e) {
/*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]);
} else {
$event->Skip;
}*/
});
SetSizer(sizer);
SetMinSize(GetSize());
sizer->SetSizeHints(this);
// init canvas
reload_print();
}
void Preview3D::reload_print(){
canvas.resetObjects();
loaded = false;
load_print();
}
void Preview3D::load_print() {
if(loaded) return;
// 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(!print->step_done(posSlice)) {
_enabled = false;
slider->Hide();
canvas.Refresh(); // clears canvas
return;
}
size_t z_idx = 0;
{
layers_z.clear();
// Load all objects on the plater + support material
for(auto* object : print->objects) {
for(auto layer : object->layers){
layers_z.push_back(layer->print_z);
}
for(auto layer : object->support_layers) {
layers_z.push_back(layer->print_z);
}
}
_enabled = true;
std::sort(layers_z.begin(),layers_z.end());
slider->SetRange(0, layers_z.size()-1);
z_idx = slider->GetValue();
// If invalide z_idx, move the slider to the top
if (z_idx >= layers_z.size() || slider->GetValue() == 0) {
slider->SetValue(layers_z.size()-1);
//$z_idx = @{$self->{layer_z}} ? -1 : undef;
z_idx = slider->GetValue(); // not sure why the perl version makes z_idx invalid
}
slider->Show();
Layout();
}
if (IsShown()) {
// set colors
/*canvas.color_toolpaths_by($Slic3r::GUI::Settings->{_}{color_toolpaths_by});
if ($self->canvas->color_toolpaths_by eq 'extruder') {
my @filament_colors = map { s/^#//; [ map $_/255, (unpack 'C*', pack 'H*', $_), 255 ] }
@{$self->print->config->filament_colour};
$self->canvas->colors->[$_] = $filament_colors[$_] for 0..$#filament_colors;
} else {
$self->canvas->colors([ $self->canvas->default_colors ]);
}*/
// load skirt and brim
//$self->canvas->load_print_toolpaths($self->print);
/*foreach my $object (@{$self->print->objects}) {
canvas.load_print_object_toolpaths($object);
}*/
loaded = true;
}
set_z(layers_z.at(z_idx));
}
void Preview3D::set_z(float z) {
if(!_enabled) return;
z_label->SetLabel(std::to_string(z));
//canvas.set_toolpaths_range(0, $z);
if(IsShown())canvas.Refresh();
}
/*
void set_bed_shape() {
my ($self, $bed_shape) = @_;
$self->canvas->set_bed_shape($bed_shape);
}
*/
} } // Namespace Slic3r::GUI

View File

@ -5,20 +5,36 @@
#include <wx/wx.h>
#endif
#include "PlaterObject.hpp"
#include "Scene3D.hpp"
#include "Model.hpp"
#include "Config.hpp"
#include "Print.hpp"
namespace Slic3r { namespace GUI {
class PreviewScene3D : public Scene3D {
public:
PreviewScene3D(wxWindow* parent, const wxSize& size) : Scene3D(parent,size){}
// TODO: load_print_toolpaths(Print);
// TODO: load_print_object_toolpaths(PrintObject);
void resetObjects(){volumes.clear();}
};
class Preview3D : public wxPanel {
public:
void reload_print() {};
Preview3D(wxWindow* parent, const wxSize& size, std::vector<PlaterObject>& _objects, std::shared_ptr<Model> _model, std::shared_ptr<Config> _config) :
wxPanel(parent, wxID_ANY, wxDefaultPosition, size, wxTAB_TRAVERSAL), objects(_objects), model(_model), config(_config)
{}
void reload_print();
Preview3D(wxWindow* parent, const wxSize& size, std::shared_ptr<Slic3r::Print> _print, std::vector<PlaterObject>& _objects, std::shared_ptr<Model> _model, std::shared_ptr<Config> _config);
void enabled(bool enable = true) {}
private:
void load_print();
void set_z(float z);
bool loaded = false, _enabled = false;
std::vector<float> layers_z;
std::shared_ptr<Slic3r::Print> print;
PreviewScene3D canvas;
wxSlider* slider;
wxStaticText* z_label;
std::vector<PlaterObject>& objects; //< reference to parent vector
std::shared_ptr<Slic3r::Model> model;
std::shared_ptr<Slic3r::Config> config;

494
src/GUI/Scene3D.cpp Normal file
View File

@ -0,0 +1,494 @@
#include "Scene3D.hpp"
#include "Line.hpp"
#include "ClipperUtils.hpp"
#include "misc_ui.hpp"
#include <GL/glu.h>
namespace Slic3r { namespace GUI {
Scene3D::Scene3D(wxWindow* parent, const wxSize& size) :
wxGLCanvas(parent, wxID_ANY, wxDefaultPosition, size)
{
this->glContext = new wxGLContext(this);
this->Bind(wxEVT_PAINT, [this](wxPaintEvent &e) { this->repaint(e); });
this->Bind(wxEVT_SIZE, [this](wxSizeEvent &e ){
dirty = true;
Refresh();
});
// Bind the varying mouse events
this->Bind(wxEVT_MOTION, [this](wxMouseEvent &e) { this->mouse_move(e); });
this->Bind(wxEVT_LEFT_UP, [this](wxMouseEvent &e) { this->mouse_up(e); });
this->Bind(wxEVT_RIGHT_UP, [this](wxMouseEvent &e) { this->mouse_up(e); });
this->Bind(wxEVT_MIDDLE_DCLICK, [this](wxMouseEvent &e) { this->mouse_dclick(e); });
this->Bind(wxEVT_MOUSEWHEEL, [this](wxMouseEvent &e) { this->mouse_wheel(e); });
Points p;
const coord_t w = scale_(200), z = 0;
p.push_back(Point(z,z));
p.push_back(Point(z,w));
p.push_back(Point(w,w));
p.push_back(Point(w,z));
set_bed_shape(p);
}
float clamp(float low, float x, float high){
if(x < low) return low;
if(x > high) return high;
return x;
}
Linef3 Scene3D::mouse_ray(Point win){
GLdouble proj[16], mview[16];
glGetDoublev(GL_MODELVIEW_MATRIX, mview);
glGetDoublev(GL_PROJECTION_MATRIX, proj);
GLint view[4];
glGetIntegerv(GL_VIEWPORT, view);
win.y = view[3]-win.y;
GLdouble x = 0.0, y = 0.0, z = 0.0;
gluUnProject(win.x,win.y,0,mview,proj,view,&x,&y,&z);
Pointf3 first = Pointf3(x,y,z);
GLint a = gluUnProject(win.x,win.y,1,mview,proj,view,&x,&y,&z);
return Linef3(first,Pointf3(x,y,z));
}
void Scene3D::mouse_move(wxMouseEvent &e){
if(e.Dragging()){
//const auto s = GetSize();
const auto pos = Point(e.GetX(),e.GetY());
if(dragging){
if (e.ShiftDown()) { // TODO: confirm alt -> shift is ok
// Move the camera center on the Z axis based on mouse Y axis movement
_camera_target.translate(0, 0, (pos.y - drag_start.y));
} else if (e.LeftIsDown()) {
// if dragging over blank area with left button, rotate
//if (TURNTABLE_MODE) {
const float TRACKBALLSIZE = 0.8f, GIMBAL_LOCK_THETA_MAX = 170.0f;
phi += (pos.x - drag_start.x) * TRACKBALLSIZE;
theta -= (pos.y - drag_start.y) * TRACKBALLSIZE;
theta = clamp(0, theta, GIMBAL_LOCK_THETA_MAX);
/*} else {
my $size = $self->GetClientSize;
my @quat = trackball(
$orig->x / ($size->width / 2) - 1,
1 - $orig->y / ($size->height / 2), #/
$pos->x / ($size->width / 2) - 1,
1 - $pos->y / ($size->height / 2), #/
);
$self->_quat(mulquats($self->_quat, \@quat));
}*/
} else if (e.MiddleIsDown() || e.RightIsDown()) {
// if dragging over blank area with right button, translate
// get point in model space at Z = 0
const auto current = mouse_ray(pos).intersect_plane(0);
const auto old = mouse_ray(drag_start).intersect_plane(0);
_camera_target.translate(current.vector_to(old));
}
//$self->on_viewport_changed->() if $self->on_viewport_changed;
Refresh();
}
dragging = true;
drag_start = pos;
}else{
e.Skip();
}
}
void Scene3D::mouse_up(wxMouseEvent &e){
dragging = false;
Refresh();
}
void Scene3D::mouse_wheel(wxMouseEvent &e){
// Calculate the zoom delta and apply it to the current zoom factor
auto _zoom = ((float)e.GetWheelRotation()) / e.GetWheelDelta();
/*if ($Slic3r::GUI::Settings->{_}{invert_zoom}) {
_zoom *= -1;
}*/
_zoom = clamp(-4, _zoom,4);
_zoom /= 10;
zoom /= 1-_zoom;
/*
# In order to zoom around the mouse point we need to translate
# the camera target
my $size = Slic3r::Pointf->new($self->GetSizeWH);
my $pos = Slic3r::Pointf->new($e->GetX, $size->y - $e->GetY); #-
$self->_camera_target->translate(
# ($pos - $size/2) represents the vector from the viewport center
# to the mouse point. By multiplying it by $zoom we get the new,
# transformed, length of such vector.
# Since we want that point to stay fixed, we move our camera target
# in the opposite direction by the delta of the length of such vector
# ($zoom - 1). We then scale everything by 1/$self->_zoom since
# $self->_camera_target is expressed in terms of model units.
-($pos->x - $size->x/2) * ($zoom) / $self->_zoom,
-($pos->y - $size->y/2) * ($zoom) / $self->_zoom,
0,
) if 0;
*/
dirty = true;
Refresh();
}
void Scene3D::mouse_dclick(wxMouseEvent &e){
/*
if (@{$self->volumes}) {
$self->zoom_to_volumes;
} else {
$self->zoom_to_bed;
}*/
dirty = true;
Refresh();
}
void Scene3D::resize(){
if(!dirty)return;
dirty = false;
const auto s = GetSize();
glViewport(0,0,s.GetWidth(),s.GetHeight());
const auto x = s.GetWidth()/zoom,
y = s.GetHeight()/zoom,
depth = 1000.0f; // my $depth = 10 * max(@{ $self->max_bounding_box->size });
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(
-x/2, x/2, -y/2, y/2,
-depth, 2*depth
);
glMatrixMode(GL_MODELVIEW);
}
void Scene3D::set_bed_shape(Points _bed_shape){
bed_shape = _bed_shape;
const float GROUND_Z = -0.02f;
// triangulate bed
const auto expoly = ExPolygon(Polygon(bed_shape));
const auto box = expoly.bounding_box();
bed_bound = box;
{
std::vector<Polygon> triangles;
expoly.triangulate(&triangles);
bed_verts.clear();
for(const auto &triangle : triangles){
for(const auto &point : triangle.points){
bed_verts.push_back(unscale(point.x));
bed_verts.push_back(unscale(point.y));
bed_verts.push_back(GROUND_Z);
}
}
}
{
std::vector<Polyline> lines;
Points tmp;
for (coord_t x = box.min.x; x <= box.max.x; x += scale_(10)) {
lines.push_back(Polyline());
lines.back().append(Point(x,box.min.y));
lines.back().append(Point(x,box.max.y));
}
for (coord_t y = box.min.y; y <= box.max.y; y += scale_(10)) {
lines.push_back(Polyline());
lines.back().append(Point(box.min.x,y));
lines.back().append(Point(box.max.x,y));
}
// clip with a slightly grown expolygon because our lines lay on the contours and
// may get erroneously clipped
// my @lines = map Slic3r::Line->new(@$_[0,-1]),
grid_verts.clear();
const Polylines clipped = intersection_pl(lines,offset_ex(expoly,SCALED_EPSILON).at(0));
for(const Polyline &line : clipped){
for(const Point &point : line.points){
grid_verts.push_back(unscale(point.x));
grid_verts.push_back(unscale(point.y));
grid_verts.push_back(GROUND_Z);
}
}
// append bed contours
for(const Line &line : expoly.lines()){
grid_verts.push_back(unscale(line.a.x));
grid_verts.push_back(unscale(line.a.y));
grid_verts.push_back(GROUND_Z);
grid_verts.push_back(unscale(line.b.x));
grid_verts.push_back(unscale(line.b.y));
grid_verts.push_back(GROUND_Z);
}
}
//$self->origin(Slic3r::Pointf->new(0,0));
}
void Scene3D::init_gl(){
if(this->init)return;
this->init = true;
glClearColor(0, 0, 0, 1);
glColor3f(1, 0, 0);
glEnable(GL_DEPTH_TEST);
glClearDepth(1.0);
glDepthFunc(GL_LEQUAL);
glEnable(GL_CULL_FACE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Set antialiasing/multisampling
glDisable(GL_LINE_SMOOTH);
glDisable(GL_POLYGON_SMOOTH);
//glEnable(GL_MULTISAMPLE) if ($self->{can_multisample});
// ambient lighting
GLfloat ambient[] = {0.1f, 0.1f, 0.1f, 1.0f};
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHT1);
// light from camera
GLfloat pos[] = {1.0f, 0.0f, 1.0f, 0.0f}, spec[] = {0.8f, 0.8f, 0.8f, 1.0f}, diff[] = {0.4f, 0.4f, 0.4f, 1.0f};
glLightfv(GL_LIGHT1, GL_POSITION, pos);
glLightfv(GL_LIGHT1, GL_SPECULAR, spec);
glLightfv(GL_LIGHT1, GL_DIFFUSE, diff);
// Enables Smooth Color Shading; try GL_FLAT for (lack of) fun. Default: GL_SMOOTH
glShadeModel(GL_SMOOTH);
GLfloat fbdiff[] = {0.3f, 0.3f, 0.3f,1}, fbspec[] = {1.0f, 1.0f, 1.0f, 1.0f}, fbemis[] = {0.1f,0.1f,0.1f,0.9f};
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, fbdiff);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, fbspec);
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 50);
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, fbemis);
// A handy trick -- have surface material mirror the color.
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
//glEnable(GL_MULTISAMPLE) if ($self->{can_multisample});
}
void Scene3D::draw_background(){
glDisable(GL_LIGHTING);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glBegin(GL_QUADS);
auto bottom = ui_settings->color->BOTTOM_COLOR(), top = ui_settings->color->TOP_COLOR();
if(ui_settings->color->SOLID_BACKGROUNDCOLOR()){
bottom = top = ui_settings->color->BACKGROUND_COLOR();
}
glColor3ub(bottom.Red(), bottom.Green(), bottom.Blue());
glVertex2f(-1.0,-1.0);
glVertex2f(1,-1.0);
glColor3ub(top.Red(), top.Green(), top.Blue());
glVertex2f(1, 1);
glVertex2f(-1.0, 1);
glEnd();
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
void Scene3D::draw_ground(){
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnableClientState(GL_VERTEX_ARRAY);
/*my $triangle_vertex;
if (HAS_VBO) {
($triangle_vertex) =
glGenBuffersARB_p(1);
$self->bed_triangles->bind($triangle_vertex);
glBufferDataARB_p(GL_ARRAY_BUFFER_ARB, $self->bed_triangles, GL_STATIC_DRAW_ARB);
glVertexPointer_c(3, GL_FLOAT, 0, 0);
} else {*/
// fall back on old behavior
glVertexPointer(3, GL_FLOAT, 0, bed_verts.data());
const auto ground = ui_settings->color->GROUND_COLOR(), grid = ui_settings->color->GRID_COLOR();
glColor4ub(ground.Red(), ground.Green(), ground.Blue(),ground.Alpha());
glNormal3d(0,0,1);
glDrawArrays(GL_TRIANGLES, 0, bed_verts.size() / 3);
// we need depth test for grid, otherwise it would disappear when looking
// the object from below
glEnable(GL_DEPTH_TEST);
// draw grid
glLineWidth(2);
/*my $grid_vertex;
if (HAS_VBO) {
($grid_vertex) =
glGenBuffersARB_p(1);
$self->bed_grid_lines->bind($grid_vertex);
glBufferDataARB_p(GL_ARRAY_BUFFER_ARB, $self->bed_grid_lines, GL_STATIC_DRAW_ARB);
glVertexPointer_c(3, GL_FLOAT, 0, 0);
} else {*/
// fall back on old behavior
glVertexPointer(3, GL_FLOAT, 0, grid_verts.data());
glColor4ub(grid.Red(), grid.Green(), grid.Blue(),grid.Alpha());
glNormal3d(0,0,1);
glDrawArrays(GL_LINES, 0, grid_verts.size() / 3);
glDisableClientState(GL_VERTEX_ARRAY);
glDisable(GL_BLEND);
/*if (HAS_VBO) {
# Turn off buffer objects to let the rest of the draw code work.
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
glDeleteBuffersARB_p($grid_vertex);
glDeleteBuffersARB_p($triangle_vertex);
}*/
}
void Scene3D::draw_axes (Pointf3 center, float length, int width, bool always_visible){
/*
my $volumes_bb = $self->volumes_bounding_box;
{
# draw axes
# disable depth testing so that axes are not covered by ground
glDisable(GL_DEPTH_TEST);
my $origin = $self->origin;
my $axis_len = max(
max(@{ $self->bed_bounding_box->size }),
1.2 * max(@{ $volumes_bb->size }),
);
glLineWidth(2);
glBegin(GL_LINES);
# draw line for x axis
glColor3f(1, 0, 0);
glVertex3f(@$origin, $ground_z);
glVertex3f($origin->x + $axis_len, $origin->y, $ground_z); #,,
# draw line for y axis
glColor3f(0, 1, 0);
glVertex3f(@$origin, $ground_z);
glVertex3f($origin->x, $origin->y + $axis_len, $ground_z); #++
glEnd();
# draw line for Z axis
# (re-enable depth test so that axis is correctly shown when objects are behind it)
glEnable(GL_DEPTH_TEST);
glBegin(GL_LINES);
glColor3f(0, 0, 1);
glVertex3f(@$origin, $ground_z);
glVertex3f(@$origin, $ground_z+$axis_len);
glEnd();
}
*/
if (always_visible) {
glDisable(GL_DEPTH_TEST);
} else {
glEnable(GL_DEPTH_TEST);
}
glLineWidth(width);
glBegin(GL_LINES);
// draw line for x axis
glColor3f(1, 0, 0);
glVertex3f(center.x, center.y, center.z);
glVertex3f(center.x + length, center.y, center.z);
// draw line for y axis
glColor3f(0, 1, 0);
glVertex3f(center.x, center.y, center.z);
glVertex3f(center.x, center.y + length, center.z);
// draw line for Z axis
glColor3f(0, 0, 1);
glVertex3f(center.x, center.y, center.z);
glVertex3f(center.x, center.y, center.z + length);
glEnd();
glEnable(GL_DEPTH_TEST);
}
void Scene3D::draw_volumes(){
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
for(const Volume &volume : volumes){
glPushMatrix();
glTranslatef(volume.origin.x, volume.origin.y, volume.origin.z);
glCullFace(GL_BACK);
glVertexPointer(3, GL_FLOAT, 0, volume.model.verts.data());
glNormalPointer(GL_FLOAT, 0, volume.model.norms.data());
glColor4ub(volume.color.Red(), volume.color.Green(), volume.color.Blue(), volume.color.Alpha());
glDrawArrays(GL_TRIANGLES, 0, volume.model.verts.size()/3);
glPopMatrix();
}
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisable(GL_BLEND);
}
void Scene3D::repaint(wxPaintEvent& e) {
if(!this->IsShownOnScreen())return;
// There should be a context->IsOk check once wx is updated
if(!this->SetCurrent(*(this->glContext)))return;
init_gl();
resize();
glClearColor(1, 1, 1, 1);
glClearDepth(1);
glDepthFunc(GL_LESS);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(-theta, 1, 0, 0); // pitch
glRotatef(phi, 0, 0, 1); // yaw
/*} else {
my @rotmat = quat_to_rotmatrix($self->quat);
glMultMatrixd_p(@rotmat[0..15]);
}*/
glTranslatef(-_camera_target.x, -_camera_target.y, -_camera_target.z);
// light from above
GLfloat pos[] = {-0.5f, -0.5f, 1.0f, 0.0f}, spec[] = {0.2f, 0.2f, 0.2f, 1.0f}, diff[] = {0.5f, 0.5f, 0.5f, 1.0f};
glLightfv(GL_LIGHT0, GL_POSITION, pos);
glLightfv(GL_LIGHT0, GL_SPECULAR, spec);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diff);
before_render();
draw_background();
draw_ground();
/*my $origin = $self->origin;
my $axis_len = max(
max(@{ $self->bed_bounding_box->size }),
1.2 * max(@{ $volumes_bb->size }),
);*/
draw_axes(Pointf3(0.0f,0.0f,0.0f),
unscale(bed_bound.radius()),2,true/*origin,calulcated length,2, true*/);
// draw objects
glEnable(GL_LIGHTING);
draw_volumes();
after_render();
if (dragging/*defined $self->_drag_start_pos || defined $self->_drag_start_xy*/) {
draw_axes(_camera_target, 10.0f, 1, true/*camera,10,1,true*/);
draw_axes(_camera_target, 10.0f, 4, false/*camera,10,4,false*/);
}
glFlush();
SwapBuffers();
// Calling glFinish has a performance penalty, but it seems to fix some OpenGL driver hang-up with extremely large scenes.
glFinish();
}
} } // Namespace Slic3r::GUI

73
src/GUI/Scene3D.hpp Normal file
View File

@ -0,0 +1,73 @@
#ifndef SCENE3D_HPP
#define SCENE3D_HPP
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <wx/glcanvas.h>
#include "Settings.hpp"
#include "Point.hpp"
#include "3DScene.hpp"
#include "BoundingBox.hpp"
#include "Model.hpp"
namespace Slic3r { namespace GUI {
struct Volume {
wxColor color;
Pointf3 origin;
GLVertexArray model;
BoundingBoxf3 bb;
};
class Scene3D : public wxGLCanvas {
public:
Scene3D(wxWindow* parent, const wxSize& size);
private:
wxGLContext* glContext;
// Camera settings
float zoom = 5.0f, phi = 0.0f, theta = 0.0f;
Pointf3 _camera_target = Pointf3(0.0f,0.0f,0.0f);
// Optional point used for dragging calculations
bool dragging = false;
Point drag_start = Point(0,0);
// Bed Stuff
std::vector<float> bed_verts, grid_verts;
Points bed_shape;
BoundingBox bed_bound;
void repaint(wxPaintEvent &e); // Redraws every frame
bool dirty = true; // Resize needs to be called before render
void resize(); // Handle glViewport and projection matrices
bool init = false; // Has opengl been initted
void init_gl(); // Handles lights and materials
// Useded in repaint
void draw_background();
void draw_ground();
void draw_axes(Pointf3 center, float length, int width, bool alwaysvisible);
protected:
Linef3 mouse_ray(Point win); // Utility for backtracking from window coordinates
void draw_volumes(); // Draws volumes (for use in before_render)
void set_bed_shape(Points _bed_shape);
std::vector<Volume> volumes;
Volume load_object(ModelVolume &mv, ModelInstance &mi);
// Virtual methods to override
virtual void mouse_up(wxMouseEvent &e);
virtual void mouse_move(wxMouseEvent &e);
virtual void mouse_dclick(wxMouseEvent &e);
virtual void mouse_wheel(wxMouseEvent &e);
virtual void before_render(){};
virtual void after_render(){};
};
} } // Namespace Slic3r::GUI
#endif

View File

@ -1,4 +1,4 @@
use Test::More tests => 1;
use Test::More tests => 4;
use strict;
use warnings;
@ -15,6 +15,22 @@ use Slic3r::Test;
my $config = Slic3r::Config->new_from_defaults;
$config->set('perimeter_extrusion_width', '250%');
ok $config->validate, 'percent extrusion width is validated';
my $print = Slic3r::Test::init_print('20mm_cube', config => $config, scale => 2);
{
my $invalid = $print->apply_config($config);
ok !($invalid), 're-applying same config does not invalidate';
}
$config->set('perimeters', 20);
{
my $invalid = $print->apply_config($config);
ok $invalid, 're-applying with changed perimeters does invalidate previous config';
}
$config->set('fill_density', '75%');
{
my $invalid = $print->apply_config($config);
ok $invalid, 're-applying with changed fill_density does invalidate previous config';
}
}
__END__

View File

@ -20,6 +20,8 @@
#include <exception>
#include <exception>
namespace Slic3r {
class InvalidObjectException : public std::exception {};