mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-18 07:05:55 +08:00
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:
commit
a9b3b0ab7f
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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); };
|
||||
|
351
src/GUI/Dialogs/ObjectCutDialog.cpp
Normal file
351
src/GUI/Dialogs/ObjectCutDialog.cpp
Normal 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
|
51
src/GUI/Dialogs/ObjectCutDialog.hpp
Normal file
51
src/GUI/Dialogs/ObjectCutDialog.hpp
Normal 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
|
@ -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
171
src/GUI/Plater/3DPreview.pm
Normal 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
176
src/GUI/Plater/Plate3D.cpp
Normal 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(©);
|
||||
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
|
@ -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;
|
||||
|
145
src/GUI/Plater/Preview3D.cpp
Normal file
145
src/GUI/Plater/Preview3D.cpp
Normal 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
|
||||
|
@ -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
494
src/GUI/Scene3D.cpp
Normal 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
73
src/GUI/Scene3D.hpp
Normal 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
|
18
t/config.t
18
t/config.t
@ -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__
|
||||
|
@ -20,6 +20,8 @@
|
||||
|
||||
#include <exception>
|
||||
|
||||
#include <exception>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class InvalidObjectException : public std::exception {};
|
||||
|
Loading…
x
Reference in New Issue
Block a user