mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-18 02:05:59 +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
|
mkdir -p $WD/${APP}.AppDir/usr/bin
|
||||||
# Copy primary Slic3r script here and perl-local, as well as var
|
# 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/
|
cp -R $srcfolder/$i $WD/${APP}.AppDir/usr/bin/
|
||||||
done
|
done
|
||||||
|
|
||||||
@ -48,8 +48,6 @@ for i in $(cat $WD/libpaths.appimage.txt | grep -v "^#" | awk -F# '{print $1}');
|
|||||||
done
|
done
|
||||||
|
|
||||||
|
|
||||||
cp -R $srcfolder/local-lib ${WD}/${APP}.AppDir/usr/lib/local-lib
|
|
||||||
|
|
||||||
cat > $WD/${APP}.AppDir/AppRun << 'EOF'
|
cat > $WD/${APP}.AppDir/AppRun << 'EOF'
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# some magic to find out the real location of this script dealing with symlinks
|
# 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/liblzma.so.5
|
||||||
/lib/x86_64-linux-gnu/libpng12.so.0
|
/lib/x86_64-linux-gnu/libpng12.so.0
|
||||||
/usr/lib/x86_64-linux-gnu/libjpeg.so.8
|
/usr/lib/x86_64-linux-gnu/libjpeg.so.8
|
||||||
|
@ -10,7 +10,6 @@ if [ "$#" -ne 1 ]; then
|
|||||||
echo "Usage: $(basename $0) arch_name"
|
echo "Usage: $(basename $0) arch_name"
|
||||||
exit 1;
|
exit 1;
|
||||||
fi
|
fi
|
||||||
libdirs=$(find ./local-lib -iname *.so -exec dirname {} \; | sort -u | paste -sd ";" -)
|
|
||||||
WD=./$(dirname $0)
|
WD=./$(dirname $0)
|
||||||
source $(dirname $0)/../common/util.sh
|
source $(dirname $0)/../common/util.sh
|
||||||
# Determine if this is a tagged (release) commit.
|
# Determine if this is a tagged (release) commit.
|
||||||
@ -22,7 +21,6 @@ set_build_id
|
|||||||
set_branch
|
set_branch
|
||||||
set_app_name
|
set_app_name
|
||||||
set_pr_id
|
set_pr_id
|
||||||
install_par
|
|
||||||
|
|
||||||
# If we're on a branch, add the branch name to the app name.
|
# If we're on a branch, add the branch name to the app name.
|
||||||
if [ "$current_branch" == "master" ]; then
|
if [ "$current_branch" == "master" ]; then
|
||||||
@ -35,8 +33,7 @@ else
|
|||||||
dmgfile=slic3r-${SLIC3R_BUILD_ID}-${1}-${current_branch}.tar.bz2
|
dmgfile=slic3r-${SLIC3R_BUILD_ID}-${1}-${current_branch}.tar.bz2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rm -rf $WD/_tmp
|
mkdir -p $WD
|
||||||
mkdir -p $WD/_tmp
|
|
||||||
|
|
||||||
# Set the application folder infomation.
|
# Set the application folder infomation.
|
||||||
appfolder="$WD/${appname}"
|
appfolder="$WD/${appname}"
|
||||||
@ -46,8 +43,6 @@ resourcefolder=$appfolder
|
|||||||
echo "Appfolder: $appfolder, archivefolder: $archivefolder"
|
echo "Appfolder: $appfolder, archivefolder: $archivefolder"
|
||||||
|
|
||||||
# Our slic3r dir and location of perl
|
# Our slic3r dir and location of perl
|
||||||
PERL_BIN=$(which perl)
|
|
||||||
PP_BIN=$(which pp)
|
|
||||||
SLIC3R_DIR="./"
|
SLIC3R_DIR="./"
|
||||||
|
|
||||||
if [[ -d "${appfolder}" ]]; then
|
if [[ -d "${appfolder}" ]]; then
|
||||||
@ -67,14 +62,12 @@ echo "Copying resources..."
|
|||||||
cp -rf $SLIC3R_DIR/var $resourcefolder/
|
cp -rf $SLIC3R_DIR/var $resourcefolder/
|
||||||
|
|
||||||
echo "Copying Slic3r..."
|
echo "Copying Slic3r..."
|
||||||
cp $SLIC3R_DIR/slic3r.pl $archivefolder/slic3r.pl
|
cp $SLIC3R_DIR/slic3r $archivefolder/Slic3r
|
||||||
cp -fRP $SLIC3R_DIR/local-lib $archivefolder/local-lib
|
|
||||||
cp -fRP $SLIC3R_DIR/lib/* $archivefolder/local-lib/lib/perl5/
|
|
||||||
|
|
||||||
mkdir $archivefolder/bin
|
mkdir $archivefolder/bin
|
||||||
echo "Installing libraries to $archivefolder/bin ..."
|
echo "Installing libraries to $archivefolder/bin ..."
|
||||||
if [ -z ${WXDIR+x} ]; then
|
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}')"
|
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
|
for dylib in $(LD_LIBRARY_PATH=$libdirs ldd $bundle | grep .so | grep local-lib | awk '{print $3}'); do
|
||||||
install -v $dylib $archivefolder/bin
|
install -v $dylib $archivefolder/bin
|
||||||
@ -91,37 +84,8 @@ echo "Copying startup script..."
|
|||||||
cp -f $WD/startup_script.sh $archivefolder/$appname
|
cp -f $WD/startup_script.sh $archivefolder/$appname
|
||||||
chmod +x $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
|
for i in $(cat $WD/libpaths.txt | grep -v "^#" | awk -F# '{print $1}'); do
|
||||||
install -v $i $archivefolder/bin
|
install -v $i $archivefolder/bin
|
||||||
done
|
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"
|
tar -C$(pwd)/$(dirname $appfolder) -cjf $(pwd)/$dmgfile "$appname"
|
||||||
|
@ -3,4 +3,4 @@
|
|||||||
BIN=$(readlink "$0")
|
BIN=$(readlink "$0")
|
||||||
DIR=$(dirname "$BIN")
|
DIR=$(dirname "$BIN")
|
||||||
export LD_LIBRARY_PATH="$DIR/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/PrintEditor.cpp
|
||||||
${GUI_LIBDIR}/Dialogs/PrinterEditor.cpp
|
${GUI_LIBDIR}/Dialogs/PrinterEditor.cpp
|
||||||
${GUI_LIBDIR}/Dialogs/MaterialEditor.cpp
|
${GUI_LIBDIR}/Dialogs/MaterialEditor.cpp
|
||||||
|
${GUI_LIBDIR}/Dialogs/ObjectCutDialog.cpp
|
||||||
${GUI_LIBDIR}/GUI.cpp
|
${GUI_LIBDIR}/GUI.cpp
|
||||||
${GUI_LIBDIR}/MainFrame.cpp
|
${GUI_LIBDIR}/MainFrame.cpp
|
||||||
${GUI_LIBDIR}/Plater.cpp
|
${GUI_LIBDIR}/Plater.cpp
|
||||||
|
${GUI_LIBDIR}/Scene3D.cpp
|
||||||
${GUI_LIBDIR}/Plater/Plate2D.cpp
|
${GUI_LIBDIR}/Plater/Plate2D.cpp
|
||||||
|
${GUI_LIBDIR}/Plater/Plate3D.cpp
|
||||||
|
${GUI_LIBDIR}/Plater/Preview3D.cpp
|
||||||
${GUI_LIBDIR}/Plater/PlaterObject.cpp
|
${GUI_LIBDIR}/Plater/PlaterObject.cpp
|
||||||
${GUI_LIBDIR}/ProgressStatusBar.cpp
|
${GUI_LIBDIR}/ProgressStatusBar.cpp
|
||||||
${GUI_LIBDIR}/Settings.cpp
|
${GUI_LIBDIR}/Settings.cpp
|
||||||
@ -298,6 +302,7 @@ IF(wxWidgets_FOUND)
|
|||||||
${GUI_LIBDIR}/OptionsGroup/UI_Choice.cpp
|
${GUI_LIBDIR}/OptionsGroup/UI_Choice.cpp
|
||||||
${GUI_LIBDIR}/OptionsGroup/UI_Point.cpp
|
${GUI_LIBDIR}/OptionsGroup/UI_Point.cpp
|
||||||
${GUI_LIBDIR}/OptionsGroup/UI_Point3.cpp
|
${GUI_LIBDIR}/OptionsGroup/UI_Point3.cpp
|
||||||
|
${LIBDIR}/slic3r/GUI/3DScene.cpp
|
||||||
)
|
)
|
||||||
target_compile_features(slic3r_gui PUBLIC cxx_std_14)
|
target_compile_features(slic3r_gui PUBLIC cxx_std_14)
|
||||||
#only build GUI lib if building with wx
|
#only build GUI lib if building with wx
|
||||||
|
@ -7,7 +7,7 @@ class DefaultColor : public ColorScheme {
|
|||||||
public:
|
public:
|
||||||
const std::string name() const { return "Default"; }
|
const std::string name() const { return "Default"; }
|
||||||
const bool SOLID_BACKGROUNDCOLOR() const { return false; };
|
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 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 TOP_COLOR() const { return wxColour(10,98,144); }; //<TOP Backgroud color
|
||||||
const wxColour BOTTOM_COLOR() const { return wxColour(0,0,0); }; //<BOTTOM 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 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 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_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_PARTS() const { return wxColour(255, 255*0.95, 255*0.2); }; //<Perimeter color
|
||||||
const wxColour COLOR_INFILL() const { return wxColour(1, 255*0.45, 255*0.45, 1); };
|
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, 1); };
|
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, 1); };
|
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_COLOR() const { return wxColour(255, 255, 255); };
|
||||||
const wxColour BED_GRID() const { return wxColour(230, 230, 230); };
|
const wxColour BED_GRID() const { return wxColour(230, 230, 230); };
|
||||||
const wxColour BED_SELECTED() const { return wxColour(255, 166, 128); };
|
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 "BoundingBox.hpp"
|
||||||
#include "Geometry.hpp"
|
#include "Geometry.hpp"
|
||||||
#include "Dialogs/AnglePicker.hpp"
|
#include "Dialogs/AnglePicker.hpp"
|
||||||
|
#include "Dialogs/ObjectCutDialog.hpp"
|
||||||
|
|
||||||
|
|
||||||
namespace Slic3r { namespace GUI {
|
namespace Slic3r { namespace GUI {
|
||||||
@ -88,7 +89,10 @@ Plater::Plater(wxWindow* parent, const wxString& title) :
|
|||||||
canvas3D = new Plate3D(preview_notebook, wxDefaultSize, objects, model, config);
|
canvas3D = new Plate3D(preview_notebook, wxDefaultSize, objects, model, config);
|
||||||
preview_notebook->AddPage(canvas3D, _("3D"));
|
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"));
|
preview_notebook->AddPage(preview3D, _("Preview"));
|
||||||
|
|
||||||
preview2D = new Preview2D(preview_notebook, wxDefaultSize, objects, model, config);
|
preview2D = new Preview2D(preview_notebook, wxDefaultSize, objects, model, config);
|
||||||
@ -494,13 +498,15 @@ void Plater::arrange() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Plater::on_model_change(bool force_autocenter) {
|
void Plater::on_model_change(bool force_autocenter) {
|
||||||
|
Log::info(LogChannel, L"Called on_modal_change");
|
||||||
|
|
||||||
// reload the select submenu (if already initialized)
|
// reload the select submenu (if already initialized)
|
||||||
{
|
{
|
||||||
auto* menu = this->GetFrame()->plater_select_menu;
|
auto* menu = this->GetFrame()->plater_select_menu;
|
||||||
|
|
||||||
if (menu != nullptr) {
|
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) {
|
for (const auto& obj : this->objects) {
|
||||||
const auto idx {obj.identifier};
|
const auto idx {obj.identifier};
|
||||||
auto name {wxString(obj.name)};
|
auto name {wxString(obj.name)};
|
||||||
@ -561,6 +567,7 @@ void Plater::select_object() {
|
|||||||
void Plater::selection_changed() {
|
void Plater::selection_changed() {
|
||||||
// Remove selection in 2D plater
|
// Remove selection in 2D plater
|
||||||
this->canvas2D->set_selected(-1, -1);
|
this->canvas2D->set_selected(-1, -1);
|
||||||
|
this->canvas3D->selection_changed();
|
||||||
|
|
||||||
auto obj = this->selected_object();
|
auto obj = this->selected_object();
|
||||||
bool have_sel {obj != this->objects.end()};
|
bool have_sel {obj != this->objects.end()};
|
||||||
@ -882,6 +889,13 @@ void Plater::changescale() {
|
|||||||
|
|
||||||
void Plater::object_cut_dialog() {
|
void Plater::object_cut_dialog() {
|
||||||
//TODO
|
//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() {
|
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
|
#ifndef WX_PRECOMP
|
||||||
#include <wx/wx.h>
|
#include <wx/wx.h>
|
||||||
#endif
|
#endif
|
||||||
|
#include "Plater/PlaterObject.hpp"
|
||||||
|
#include "Scene3D.hpp"
|
||||||
|
#include "Settings.hpp"
|
||||||
#include "Model.hpp"
|
#include "Model.hpp"
|
||||||
#include "Config.hpp"
|
#include "Config.hpp"
|
||||||
|
|
||||||
namespace Slic3r { namespace GUI {
|
namespace Slic3r { namespace GUI {
|
||||||
|
|
||||||
class Plate3D : public wxPanel {
|
class Plate3D : public Scene3D {
|
||||||
public:
|
public:
|
||||||
void update() {};
|
Plate3D(wxWindow* parent, const wxSize& size, std::vector<PlaterObject>& _objects, std::shared_ptr<Model> _model, std::shared_ptr<Config> _config);
|
||||||
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)
|
/// 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:
|
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::vector<PlaterObject>& objects; //< reference to parent vector
|
||||||
std::shared_ptr<Slic3r::Model> model;
|
std::shared_ptr<Slic3r::Model> model;
|
||||||
std::shared_ptr<Slic3r::Config> config;
|
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>
|
#include <wx/wx.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "PlaterObject.hpp"
|
||||||
|
#include "Scene3D.hpp"
|
||||||
#include "Model.hpp"
|
#include "Model.hpp"
|
||||||
#include "Config.hpp"
|
#include "Config.hpp"
|
||||||
|
#include "Print.hpp"
|
||||||
|
|
||||||
namespace Slic3r { namespace GUI {
|
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 {
|
class Preview3D : public wxPanel {
|
||||||
public:
|
public:
|
||||||
void reload_print() {};
|
void reload_print();
|
||||||
Preview3D(wxWindow* parent, const wxSize& size, std::vector<PlaterObject>& _objects, std::shared_ptr<Model> _model, std::shared_ptr<Config> _config) :
|
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), objects(_objects), model(_model), config(_config)
|
|
||||||
{}
|
|
||||||
|
|
||||||
void enabled(bool enable = true) {}
|
void enabled(bool enable = true) {}
|
||||||
private:
|
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::vector<PlaterObject>& objects; //< reference to parent vector
|
||||||
std::shared_ptr<Slic3r::Model> model;
|
std::shared_ptr<Slic3r::Model> model;
|
||||||
std::shared_ptr<Slic3r::Config> config;
|
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 strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
@ -15,6 +15,22 @@ use Slic3r::Test;
|
|||||||
my $config = Slic3r::Config->new_from_defaults;
|
my $config = Slic3r::Config->new_from_defaults;
|
||||||
$config->set('perimeter_extrusion_width', '250%');
|
$config->set('perimeter_extrusion_width', '250%');
|
||||||
ok $config->validate, 'percent extrusion width is validated';
|
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__
|
__END__
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
class InvalidObjectException : public std::exception {};
|
class InvalidObjectException : public std::exception {};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user