2018-05-09 10:47:04 +02:00
#include "GLCanvas3D.hpp"
2018-08-09 16:55:43 +02:00
#include "../../admesh/stl.h"
2018-06-21 08:37:04 +02:00
#include "../../libslic3r/libslic3r.h"
2018-05-14 14:14:19 +02:00
#include "../../slic3r/GUI/3DScene.hpp"
2018-05-23 09:57:44 +02:00
#include "../../slic3r/GUI/GLShader.hpp"
2018-06-08 09:40:00 +02:00
#include "../../slic3r/GUI/GUI.hpp"
2018-06-11 10:46:32 +02:00
#include "../../slic3r/GUI/PresetBundle.hpp"
2018-06-13 09:12:16 +02:00
#include "../../slic3r/GUI/GLGizmo.hpp"
2018-05-15 15:38:25 +02:00
#include "../../libslic3r/ClipperUtils.hpp"
2018-05-23 11:14:49 +02:00
#include "../../libslic3r/PrintConfig.hpp"
2018-06-05 10:56:55 +02:00
#include "../../libslic3r/GCode/PreviewData.hpp"
2018-10-12 10:09:16 +02:00
#include "../../libslic3r/Geometry.hpp"
2018-10-01 15:09:31 +02:00
#include "GUI_App.hpp"
2018-10-05 23:29:15 +02:00
#include "GUI_ObjectList.hpp"
2018-10-04 16:43:10 +02:00
#include "GUI_ObjectManipulation.hpp"
2018-05-14 14:14:19 +02:00
2018-05-23 15:35:11 +02:00
#include <GL/glew.h>
2018-05-24 13:46:17 +02:00
2018-05-09 10:47:04 +02:00
#include <wx/glcanvas.h>
2018-05-30 15:18:45 +02:00
#include <wx/timer.h>
2018-07-19 13:18:19 +02:00
#include <wx/bitmap.h>
#include <wx/dcmemory.h>
#include <wx/image.h>
#include <wx/settings.h>
2018-05-09 10:47:04 +02:00
2018-09-12 13:17:47 +02:00
// Print now includes tbb, and tbb includes Windows. This breaks compilation of wxWidgets if included before wx.
2018-09-12 11:59:02 +02:00
#include "../../libslic3r/Print.hpp"
2018-06-05 14:09:36 +02:00
#include <tbb/parallel_for.h>
#include <tbb/spin_mutex.h>
#include <boost/log/trivial.hpp>
2018-06-11 10:46:32 +02:00
#include <boost/algorithm/string/predicate.hpp>
2018-06-05 14:09:36 +02:00
2018-05-09 10:47:04 +02:00
#include <iostream>
2018-05-24 15:22:53 +02:00
#include <float.h>
2018-06-13 09:12:16 +02:00
#include <algorithm>
2018-05-09 10:47:04 +02:00
2018-06-01 09:00:30 +02:00
static const float TRACKBALLSIZE = 0.8f ;
2018-05-14 12:08:23 +02:00
static const float GIMBALL_LOCK_THETA_MAX = 180.0f ;
2018-05-15 15:38:25 +02:00
static const float GROUND_Z = - 0.02f ;
2018-05-14 12:08:23 +02:00
2018-05-15 11:30:11 +02:00
// phi / theta angles to orient the camera.
static const float VIEW_DEFAULT [ 2 ] = { 45.0f , 45.0f };
static const float VIEW_LEFT [ 2 ] = { 90.0f , 90.0f };
static const float VIEW_RIGHT [ 2 ] = { - 90.0f , 90.0f };
static const float VIEW_TOP [ 2 ] = { 0.0f , 0.0f };
static const float VIEW_BOTTOM [ 2 ] = { 0.0f , 180.0f };
static const float VIEW_FRONT [ 2 ] = { 0.0f , 90.0f };
static const float VIEW_REAR [ 2 ] = { 180.0f , 90.0f };
2018-05-24 13:46:17 +02:00
static const float VARIABLE_LAYER_THICKNESS_BAR_WIDTH = 70.0f ;
static const float VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT = 22.0f ;
2018-06-21 08:37:04 +02:00
static const float UNIT_MATRIX [] = { 1.0f , 0.0f , 0.0f , 0.0f ,
0.0f , 1.0f , 0.0f , 0.0f ,
0.0f , 0.0f , 1.0f , 0.0f ,
0.0f , 0.0f , 0.0f , 1.0f };
2018-07-27 09:38:39 +02:00
static const float DEFAULT_BG_COLOR [ 3 ] = { 10.0f / 255.0f , 98.0f / 255.0f , 144.0f / 255.0f };
static const float ERROR_BG_COLOR [ 3 ] = { 144.0f / 255.0f , 49.0f / 255.0f , 10.0f / 255.0f };
2018-05-09 10:47:04 +02:00
namespace Slic3r {
namespace GUI {
2018-06-11 10:46:32 +02:00
bool GeometryBuffer :: set_from_triangles ( const Polygons & triangles , float z , bool generate_tex_coords )
2018-05-15 15:38:25 +02:00
{
2018-06-11 10:46:32 +02:00
m_vertices . clear ();
m_tex_coords . clear ();
2018-05-15 15:38:25 +02:00
2018-06-11 10:46:32 +02:00
unsigned int v_size = 9 * ( unsigned int ) triangles . size ();
unsigned int t_size = 6 * ( unsigned int ) triangles . size ();
if ( v_size == 0 )
2018-05-15 15:38:25 +02:00
return false ;
2018-06-11 10:46:32 +02:00
m_vertices = std :: vector < float > ( v_size , 0.0f );
if ( generate_tex_coords )
m_tex_coords = std :: vector < float > ( t_size , 0.0f );
2018-05-15 15:38:25 +02:00
2018-08-21 17:43:05 +02:00
float min_x = unscale < float > ( triangles [ 0 ]. points [ 0 ]( 0 ));
float min_y = unscale < float > ( triangles [ 0 ]. points [ 0 ]( 1 ));
2018-06-11 10:46:32 +02:00
float max_x = min_x ;
float max_y = min_y ;
unsigned int v_coord = 0 ;
unsigned int t_coord = 0 ;
2018-05-15 15:38:25 +02:00
for ( const Polygon & t : triangles )
{
for ( unsigned int v = 0 ; v < 3 ; ++ v )
{
const Point & p = t . points [ v ];
2018-08-21 17:43:05 +02:00
float x = unscale < float > ( p ( 0 ));
float y = unscale < float > ( p ( 1 ));
2018-06-11 10:46:32 +02:00
m_vertices [ v_coord ++ ] = x ;
m_vertices [ v_coord ++ ] = y ;
m_vertices [ v_coord ++ ] = z ;
if ( generate_tex_coords )
{
m_tex_coords [ t_coord ++ ] = x ;
m_tex_coords [ t_coord ++ ] = y ;
min_x = std :: min ( min_x , x );
max_x = std :: max ( max_x , x );
min_y = std :: min ( min_y , y );
max_y = std :: max ( max_y , y );
}
}
}
if ( generate_tex_coords )
{
float size_x = max_x - min_x ;
float size_y = max_y - min_y ;
if (( size_x != 0.0f ) && ( size_y != 0.0f ))
{
float inv_size_x = 1.0f / size_x ;
float inv_size_y = - 1.0f / size_y ;
for ( unsigned int i = 0 ; i < m_tex_coords . size (); i += 2 )
{
m_tex_coords [ i ] *= inv_size_x ;
m_tex_coords [ i + 1 ] *= inv_size_y ;
}
2018-05-15 15:38:25 +02:00
}
}
return true ;
}
bool GeometryBuffer :: set_from_lines ( const Lines & lines , float z )
{
2018-06-11 10:46:32 +02:00
m_vertices . clear ();
m_tex_coords . clear ();
2018-05-15 15:38:25 +02:00
unsigned int size = 6 * ( unsigned int ) lines . size ();
if ( size == 0 )
return false ;
2018-06-11 10:46:32 +02:00
m_vertices = std :: vector < float > ( size , 0.0f );
2018-05-15 15:38:25 +02:00
unsigned int coord = 0 ;
for ( const Line & l : lines )
{
2018-08-21 17:43:05 +02:00
m_vertices [ coord ++ ] = unscale < float > ( l . a ( 0 ));
m_vertices [ coord ++ ] = unscale < float > ( l . a ( 1 ));
2018-06-11 10:46:32 +02:00
m_vertices [ coord ++ ] = z ;
2018-08-21 17:43:05 +02:00
m_vertices [ coord ++ ] = unscale < float > ( l . b ( 0 ));
m_vertices [ coord ++ ] = unscale < float > ( l . b ( 1 ));
2018-06-11 10:46:32 +02:00
m_vertices [ coord ++ ] = z ;
2018-05-15 15:38:25 +02:00
}
return true ;
}
2018-06-11 10:46:32 +02:00
const float * GeometryBuffer :: get_vertices () const
2018-05-15 15:38:25 +02:00
{
2018-06-11 10:46:32 +02:00
return m_vertices . data ();
2018-05-15 15:38:25 +02:00
}
2018-06-11 10:46:32 +02:00
const float * GeometryBuffer :: get_tex_coords () const
2018-05-15 15:38:25 +02:00
{
2018-06-11 10:46:32 +02:00
return m_tex_coords . data ();
}
unsigned int GeometryBuffer :: get_vertices_count () const
{
return ( unsigned int ) m_vertices . size () / 3 ;
2018-05-15 15:38:25 +02:00
}
2018-05-24 13:46:17 +02:00
Size :: Size ()
: m_width ( 0 )
, m_height ( 0 )
{
}
Size :: Size ( int width , int height )
: m_width ( width )
, m_height ( height )
{
}
int Size :: get_width () const
{
return m_width ;
}
void Size :: set_width ( int width )
{
m_width = width ;
}
int Size :: get_height () const
{
return m_height ;
}
void Size :: set_height ( int height )
{
m_height = height ;
}
Rect :: Rect ()
: m_left ( 0.0f )
, m_top ( 0.0f )
, m_right ( 0.0f )
, m_bottom ( 0.0f )
{
}
Rect :: Rect ( float left , float top , float right , float bottom )
: m_left ( left )
, m_top ( top )
, m_right ( right )
, m_bottom ( bottom )
{
}
float Rect :: get_left () const
{
return m_left ;
}
void Rect :: set_left ( float left )
{
m_left = left ;
}
float Rect :: get_top () const
{
return m_top ;
}
void Rect :: set_top ( float top )
{
m_top = top ;
}
float Rect :: get_right () const
{
return m_right ;
}
void Rect :: set_right ( float right )
{
m_right = right ;
}
float Rect :: get_bottom () const
{
return m_bottom ;
}
void Rect :: set_bottom ( float bottom )
{
m_bottom = bottom ;
}
2018-05-09 10:47:04 +02:00
GLCanvas3D :: Camera :: Camera ()
2018-06-01 15:54:41 +02:00
: type ( Ortho )
, zoom ( 1.0f )
, phi ( 45.0f )
2018-06-04 12:26:39 +02:00
// , distance(0.0f)
2018-06-01 15:54:41 +02:00
, target ( 0.0 , 0.0 , 0.0 )
2018-05-14 12:08:23 +02:00
, m_theta ( 45.0f )
2018-05-09 10:47:04 +02:00
{
}
2018-05-14 11:31:58 +02:00
std :: string GLCanvas3D :: Camera :: get_type_as_string () const
{
2018-06-01 15:54:41 +02:00
switch ( type )
2018-05-14 11:31:58 +02:00
{
default :
2018-06-01 15:54:41 +02:00
case Unknown :
2018-05-14 11:31:58 +02:00
return "unknown" ;
2018-06-04 12:26:39 +02:00
// case Perspective:
// return "perspective";
2018-06-01 15:54:41 +02:00
case Ortho :
2018-05-14 11:31:58 +02:00
return "ortho" ;
};
}
2018-05-14 12:08:23 +02:00
float GLCanvas3D :: Camera :: get_theta () const
{
return m_theta ;
}
void GLCanvas3D :: Camera :: set_theta ( float theta )
{
2018-06-01 15:54:41 +02:00
m_theta = clamp ( 0.0f , GIMBALL_LOCK_THETA_MAX , theta );
2018-05-14 12:08:23 +02:00
}
2018-06-11 10:46:32 +02:00
GLCanvas3D :: Bed :: Bed ()
: m_type ( Custom )
{
}
2018-06-11 11:40:11 +02:00
bool GLCanvas3D :: Bed :: is_prusa () const
{
return ( m_type == MK2 ) || ( m_type == MK3 );
}
bool GLCanvas3D :: Bed :: is_custom () const
{
return m_type == Custom ;
}
2018-05-14 14:14:19 +02:00
const Pointfs & GLCanvas3D :: Bed :: get_shape () const
{
return m_shape ;
}
2018-08-05 22:52:38 +02:00
bool GLCanvas3D :: Bed :: set_shape ( const Pointfs & shape )
2018-05-14 14:14:19 +02:00
{
2018-08-05 22:52:38 +02:00
EType new_type = _detect_type ();
if ( m_shape == shape && m_type == new_type )
// No change, no need to update the UI.
return false ;
2018-10-03 16:18:23 +02:00
2018-05-14 14:14:19 +02:00
m_shape = shape ;
2018-08-05 22:52:38 +02:00
m_type = new_type ;
2018-05-15 15:38:25 +02:00
2018-05-14 14:14:19 +02:00
_calc_bounding_box ();
2018-05-15 15:38:25 +02:00
ExPolygon poly ;
2018-08-21 21:05:24 +02:00
for ( const Vec2d & p : m_shape )
2018-05-15 15:38:25 +02:00
{
2018-08-17 15:53:43 +02:00
poly . contour . append ( Point ( scale_ ( p ( 0 )), scale_ ( p ( 1 ))));
2018-05-15 15:38:25 +02:00
}
_calc_triangles ( poly );
const BoundingBox & bed_bbox = poly . contour . bounding_box ();
_calc_gridlines ( poly , bed_bbox );
2018-06-11 10:46:32 +02:00
m_polygon = offset_ex ( poly . contour , ( float ) bed_bbox . radius () * 1.7f , jtRound , scale_ ( 0.5 ))[ 0 ]. contour ;
2018-08-05 22:52:38 +02:00
// Let the calee to update the UI.
return true ;
2018-05-14 14:14:19 +02:00
}
const BoundingBoxf3 & GLCanvas3D :: Bed :: get_bounding_box () const
{
return m_bounding_box ;
}
2018-05-31 16:04:59 +02:00
bool GLCanvas3D :: Bed :: contains ( const Point & point ) const
{
return m_polygon . contains ( point );
}
Point GLCanvas3D :: Bed :: point_projection ( const Point & point ) const
{
return m_polygon . point_projection ( point );
}
2018-06-11 10:46:32 +02:00
void GLCanvas3D :: Bed :: render ( float theta ) const
2018-05-15 15:38:25 +02:00
{
2018-06-11 10:46:32 +02:00
switch ( m_type )
2018-05-15 15:38:25 +02:00
{
2018-06-11 10:46:32 +02:00
case MK2 :
{
_render_mk2 ( theta );
break ;
}
case MK3 :
{
_render_mk3 ( theta );
break ;
}
default :
case Custom :
{
_render_custom ();
break ;
}
2018-05-15 15:38:25 +02:00
}
}
2018-05-14 14:14:19 +02:00
void GLCanvas3D :: Bed :: _calc_bounding_box ()
{
m_bounding_box = BoundingBoxf3 ();
2018-08-21 21:05:24 +02:00
for ( const Vec2d & p : m_shape )
2018-05-14 14:14:19 +02:00
{
2018-08-21 17:43:05 +02:00
m_bounding_box . merge ( Vec3d ( p ( 0 ), p ( 1 ), 0.0 ));
2018-05-14 14:14:19 +02:00
}
}
2018-05-15 15:38:25 +02:00
void GLCanvas3D :: Bed :: _calc_triangles ( const ExPolygon & poly )
{
Polygons triangles ;
poly . triangulate ( & triangles );
2018-06-11 10:46:32 +02:00
if ( ! m_triangles . set_from_triangles ( triangles , GROUND_Z , m_type != Custom ))
2018-05-15 15:38:25 +02:00
printf ( "Unable to create bed triangles \n " );
}
void GLCanvas3D :: Bed :: _calc_gridlines ( const ExPolygon & poly , const BoundingBox & bed_bbox )
{
Polylines axes_lines ;
2018-08-17 15:53:43 +02:00
for ( coord_t x = bed_bbox . min ( 0 ); x <= bed_bbox . max ( 0 ); x += scale_ ( 10.0 ))
2018-05-15 15:38:25 +02:00
{
Polyline line ;
2018-08-17 15:53:43 +02:00
line . append ( Point ( x , bed_bbox . min ( 1 )));
line . append ( Point ( x , bed_bbox . max ( 1 )));
2018-05-15 15:38:25 +02:00
axes_lines . push_back ( line );
}
2018-08-17 15:53:43 +02:00
for ( coord_t y = bed_bbox . min ( 1 ); y <= bed_bbox . max ( 1 ); y += scale_ ( 10.0 ))
2018-05-15 15:38:25 +02:00
{
Polyline line ;
2018-08-17 15:53:43 +02:00
line . append ( Point ( bed_bbox . min ( 0 ), y ));
line . append ( Point ( bed_bbox . max ( 0 ), y ));
2018-05-15 15:38:25 +02:00
axes_lines . push_back ( line );
}
// clip with a slightly grown expolygon because our lines lay on the contours and may get erroneously clipped
2018-10-08 15:17:36 +02:00
Lines gridlines = to_lines ( intersection_pl ( axes_lines , offset ( poly , ( float ) SCALED_EPSILON )));
2018-05-15 15:38:25 +02:00
// append bed contours
Lines contour_lines = to_lines ( poly );
std :: copy ( contour_lines . begin (), contour_lines . end (), std :: back_inserter ( gridlines ));
if ( ! m_gridlines . set_from_lines ( gridlines , GROUND_Z ))
printf ( "Unable to create bed grid lines \n " );
}
2018-06-11 10:46:32 +02:00
GLCanvas3D :: Bed :: EType GLCanvas3D :: Bed :: _detect_type () const
{
EType type = Custom ;
2018-10-01 15:09:31 +02:00
auto bundle = wxGetApp (). preset_bundle ;
2018-06-11 10:46:32 +02:00
if ( bundle != nullptr )
{
2018-06-21 10:24:57 +02:00
const Preset * curr = & bundle -> printers . get_selected_preset ();
while ( curr != nullptr )
2018-06-11 10:46:32 +02:00
{
2018-06-21 10:24:57 +02:00
if ( curr -> config . has ( "bed_shape" ) && _are_equal ( m_shape , dynamic_cast < const ConfigOptionPoints *> ( curr -> config . option ( "bed_shape" )) -> values ))
2018-06-11 10:46:32 +02:00
{
2018-06-21 10:24:57 +02:00
if (( curr -> vendor != nullptr ) && ( curr -> vendor -> name == "Prusa Research" ))
{
if ( boost :: contains ( curr -> name , "MK2" ))
{
type = MK2 ;
break ;
}
else if ( boost :: contains ( curr -> name , "MK3" ))
{
type = MK3 ;
break ;
}
}
2018-06-11 10:46:32 +02:00
}
2018-06-21 10:24:57 +02:00
curr = bundle -> printers . get_preset_parent ( * curr );
2018-06-11 10:46:32 +02:00
}
}
return type ;
}
void GLCanvas3D :: Bed :: _render_mk2 ( float theta ) const
{
std :: string filename = resources_dir () + "/icons/bed/mk2_top.png" ;
if (( m_top_texture . get_id () == 0 ) || ( m_top_texture . get_source () != filename ))
{
2018-06-14 10:37:28 +02:00
if ( ! m_top_texture . load_from_file ( filename , true ))
2018-06-11 10:46:32 +02:00
{
_render_custom ();
return ;
}
}
filename = resources_dir () + "/icons/bed/mk2_bottom.png" ;
if (( m_bottom_texture . get_id () == 0 ) || ( m_bottom_texture . get_source () != filename ))
{
2018-06-14 10:37:28 +02:00
if ( ! m_bottom_texture . load_from_file ( filename , true ))
2018-06-11 10:46:32 +02:00
{
_render_custom ();
return ;
}
}
_render_prusa ( theta );
}
void GLCanvas3D :: Bed :: _render_mk3 ( float theta ) const
{
std :: string filename = resources_dir () + "/icons/bed/mk3_top.png" ;
if (( m_top_texture . get_id () == 0 ) || ( m_top_texture . get_source () != filename ))
{
2018-06-14 10:37:28 +02:00
if ( ! m_top_texture . load_from_file ( filename , true ))
2018-06-11 10:46:32 +02:00
{
_render_custom ();
return ;
}
}
filename = resources_dir () + "/icons/bed/mk3_bottom.png" ;
if (( m_bottom_texture . get_id () == 0 ) || ( m_bottom_texture . get_source () != filename ))
{
2018-06-14 10:37:28 +02:00
if ( ! m_bottom_texture . load_from_file ( filename , true ))
2018-06-11 10:46:32 +02:00
{
_render_custom ();
return ;
}
}
_render_prusa ( theta );
}
void GLCanvas3D :: Bed :: _render_prusa ( float theta ) const
{
unsigned int triangles_vcount = m_triangles . get_vertices_count ();
if ( triangles_vcount > 0 )
{
:: glEnable ( GL_DEPTH_TEST );
2018-08-24 12:06:05 +02:00
:: glDepthMask ( GL_FALSE );
2018-06-11 10:46:32 +02:00
:: glEnable ( GL_BLEND );
:: glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA );
2018-06-21 13:03:53 +02:00
:: glEnable ( GL_TEXTURE_2D );
2018-06-22 11:19:38 +02:00
:: glTexEnvi ( GL_TEXTURE_ENV , GL_TEXTURE_ENV_MODE , GL_REPLACE );
2018-06-11 10:46:32 +02:00
:: glEnableClientState ( GL_VERTEX_ARRAY );
:: glEnableClientState ( GL_TEXTURE_COORD_ARRAY );
if ( theta > 90.0f )
:: glFrontFace ( GL_CW );
:: glBindTexture ( GL_TEXTURE_2D , ( theta <= 90.0f ) ? ( GLuint ) m_top_texture . get_id () : ( GLuint ) m_bottom_texture . get_id ());
:: glVertexPointer ( 3 , GL_FLOAT , 0 , ( GLvoid * ) m_triangles . get_vertices ());
:: glTexCoordPointer ( 2 , GL_FLOAT , 0 , ( GLvoid * ) m_triangles . get_tex_coords ());
:: glDrawArrays ( GL_TRIANGLES , 0 , ( GLsizei ) triangles_vcount );
if ( theta > 90.0f )
:: glFrontFace ( GL_CCW );
:: glBindTexture ( GL_TEXTURE_2D , 0 );
:: glDisableClientState ( GL_TEXTURE_COORD_ARRAY );
:: glDisableClientState ( GL_VERTEX_ARRAY );
2018-06-21 13:03:53 +02:00
:: glDisable ( GL_TEXTURE_2D );
2018-06-11 10:46:32 +02:00
:: glDisable ( GL_BLEND );
2018-08-24 12:06:05 +02:00
:: glDepthMask ( GL_TRUE );
2018-06-11 10:46:32 +02:00
}
}
void GLCanvas3D :: Bed :: _render_custom () const
{
m_top_texture . reset ();
m_bottom_texture . reset ();
unsigned int triangles_vcount = m_triangles . get_vertices_count ();
if ( triangles_vcount > 0 )
{
:: glEnable ( GL_LIGHTING );
2018-06-11 11:40:11 +02:00
:: glDisable ( GL_DEPTH_TEST );
2018-06-11 10:46:32 +02:00
:: glEnable ( GL_BLEND );
:: glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA );
:: glEnableClientState ( GL_VERTEX_ARRAY );
:: glColor4f ( 0.8f , 0.6f , 0.5f , 0.4f );
:: glNormal3d ( 0.0f , 0.0f , 1.0f );
:: glVertexPointer ( 3 , GL_FLOAT , 0 , ( GLvoid * ) m_triangles . get_vertices ());
:: glDrawArrays ( GL_TRIANGLES , 0 , ( GLsizei ) triangles_vcount );
// draw grid
unsigned int gridlines_vcount = m_gridlines . get_vertices_count ();
2018-06-11 11:40:11 +02:00
// we need depth test for grid, otherwise it would disappear when looking the object from below
:: glEnable ( GL_DEPTH_TEST );
2018-06-11 10:46:32 +02:00
:: glLineWidth ( 3.0f );
:: glColor4f ( 0.2f , 0.2f , 0.2f , 0.4f );
:: glVertexPointer ( 3 , GL_FLOAT , 0 , ( GLvoid * ) m_gridlines . get_vertices ());
:: glDrawArrays ( GL_LINES , 0 , ( GLsizei ) gridlines_vcount );
:: glDisableClientState ( GL_VERTEX_ARRAY );
:: glDisable ( GL_BLEND );
2018-06-22 12:21:43 +02:00
:: glDisable ( GL_LIGHTING );
2018-06-11 10:46:32 +02:00
}
}
bool GLCanvas3D :: Bed :: _are_equal ( const Pointfs & bed_1 , const Pointfs & bed_2 )
{
if ( bed_1 . size () != bed_2 . size ())
return false ;
for ( unsigned int i = 0 ; i < ( unsigned int ) bed_1 . size (); ++ i )
{
if ( bed_1 [ i ] != bed_2 [ i ])
return false ;
}
return true ;
}
2018-05-18 13:02:47 +02:00
GLCanvas3D :: Axes :: Axes ()
2018-10-11 10:52:50 +02:00
: origin ( Vec3d :: Zero ())
, length ( 0.0f )
2018-05-18 13:02:47 +02:00
{
}
2018-06-13 13:14:17 +02:00
void GLCanvas3D :: Axes :: render ( bool depth_test ) const
2018-05-18 13:02:47 +02:00
{
2018-06-13 13:14:17 +02:00
if ( depth_test )
:: glEnable ( GL_DEPTH_TEST );
else
:: glDisable ( GL_DEPTH_TEST );
2018-05-18 13:02:47 +02:00
:: glLineWidth ( 2.0f );
:: glBegin ( GL_LINES );
// draw line for x axis
:: glColor3f ( 1.0f , 0.0f , 0.0f );
2018-10-11 10:52:50 +02:00
:: glVertex3dv ( origin . data ());
2018-08-17 15:53:43 +02:00
:: glVertex3f (( GLfloat ) origin ( 0 ) + length , ( GLfloat ) origin ( 1 ), ( GLfloat ) origin ( 2 ));
2018-06-01 15:54:41 +02:00
// draw line for y axis
2018-05-18 13:02:47 +02:00
:: glColor3f ( 0.0f , 1.0f , 0.0f );
2018-10-11 10:52:50 +02:00
:: glVertex3dv ( origin . data ());
2018-08-17 15:53:43 +02:00
:: glVertex3f (( GLfloat ) origin ( 0 ), ( GLfloat ) origin ( 1 ) + length , ( GLfloat ) origin ( 2 ));
2018-05-18 13:02:47 +02:00
:: glEnd ();
// draw line for Z axis
// (re-enable depth test so that axis is correctly shown when objects are behind it)
2018-06-13 13:14:17 +02:00
if ( ! depth_test )
:: glEnable ( GL_DEPTH_TEST );
2018-05-18 13:02:47 +02:00
:: glBegin ( GL_LINES );
:: glColor3f ( 0.0f , 0.0f , 1.0f );
2018-10-11 10:52:50 +02:00
:: glVertex3dv ( origin . data ());
2018-08-17 15:53:43 +02:00
:: glVertex3f (( GLfloat ) origin ( 0 ), ( GLfloat ) origin ( 1 ), ( GLfloat ) origin ( 2 ) + length );
2018-05-18 13:02:47 +02:00
:: glEnd ();
}
2018-05-18 11:05:48 +02:00
GLCanvas3D :: CuttingPlane :: CuttingPlane ()
: m_z ( - 1.0f )
{
}
bool GLCanvas3D :: CuttingPlane :: set ( float z , const ExPolygons & polygons )
{
m_z = z ;
// grow slices in order to display them better
2018-10-08 15:17:36 +02:00
ExPolygons expolygons = offset_ex ( polygons , ( float ) scale_ ( 0.1 ));
2018-05-18 11:05:48 +02:00
Lines lines = to_lines ( expolygons );
return m_lines . set_from_lines ( lines , m_z );
}
2018-05-21 15:57:03 +02:00
void GLCanvas3D :: CuttingPlane :: render ( const BoundingBoxf3 & bb ) const
2018-05-21 15:24:52 +02:00
{
_render_plane ( bb );
_render_contour ();
}
2018-05-21 15:57:03 +02:00
void GLCanvas3D :: CuttingPlane :: _render_plane ( const BoundingBoxf3 & bb ) const
2018-05-18 11:05:48 +02:00
{
if ( m_z >= 0.0f )
{
:: glDisable ( GL_CULL_FACE );
:: glEnable ( GL_BLEND );
:: glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA );
float margin = 20.0f ;
2018-08-17 15:53:43 +02:00
float min_x = bb . min ( 0 ) - margin ;
float max_x = bb . max ( 0 ) + margin ;
float min_y = bb . min ( 1 ) - margin ;
float max_y = bb . max ( 1 ) + margin ;
2018-05-18 11:05:48 +02:00
:: glBegin ( GL_QUADS );
:: glColor4f ( 0.8f , 0.8f , 0.8f , 0.5f );
:: glVertex3f ( min_x , min_y , m_z );
:: glVertex3f ( max_x , min_y , m_z );
:: glVertex3f ( max_x , max_y , m_z );
:: glVertex3f ( min_x , max_y , m_z );
:: glEnd ();
:: glEnable ( GL_CULL_FACE );
:: glDisable ( GL_BLEND );
}
}
2018-05-21 15:57:03 +02:00
void GLCanvas3D :: CuttingPlane :: _render_contour () const
2018-05-18 11:05:48 +02:00
{
:: glEnableClientState ( GL_VERTEX_ARRAY );
if ( m_z >= 0.0f )
{
2018-06-11 10:46:32 +02:00
unsigned int lines_vcount = m_lines . get_vertices_count ();
2018-05-18 11:05:48 +02:00
:: glLineWidth ( 2.0f );
:: glColor3f ( 0.0f , 0.0f , 0.0f );
2018-06-11 10:46:32 +02:00
:: glVertexPointer ( 3 , GL_FLOAT , 0 , ( GLvoid * ) m_lines . get_vertices ());
2018-05-18 11:05:48 +02:00
:: glDrawArrays ( GL_LINES , 0 , ( GLsizei ) lines_vcount );
}
:: glDisableClientState ( GL_VERTEX_ARRAY );
}
2018-05-25 14:05:08 +02:00
GLCanvas3D :: Shader :: Shader ()
: m_shader ( nullptr )
{
}
GLCanvas3D :: Shader ::~ Shader ()
{
_reset ();
}
bool GLCanvas3D :: Shader :: init ( const std :: string & vertex_shader_filename , const std :: string & fragment_shader_filename )
{
if ( is_initialized ())
return true ;
m_shader = new GLShader ();
if ( m_shader != nullptr )
{
if ( ! m_shader -> load_from_file ( fragment_shader_filename . c_str (), vertex_shader_filename . c_str ()))
{
std :: cout << "Compilaton of shader failed:" << std :: endl ;
std :: cout << m_shader -> last_error << std :: endl ;
_reset ();
return false ;
}
}
return true ;
}
bool GLCanvas3D :: Shader :: is_initialized () const
{
return ( m_shader != nullptr );
}
bool GLCanvas3D :: Shader :: start_using () const
{
if ( is_initialized ())
{
m_shader -> enable ();
return true ;
}
else
return false ;
}
void GLCanvas3D :: Shader :: stop_using () const
{
if ( m_shader != nullptr )
m_shader -> disable ();
}
2018-05-25 15:56:14 +02:00
void GLCanvas3D :: Shader :: set_uniform ( const std :: string & name , float value ) const
{
if ( m_shader != nullptr )
m_shader -> set_uniform ( name . c_str (), value );
}
2018-06-21 08:37:04 +02:00
void GLCanvas3D :: Shader :: set_uniform ( const std :: string & name , const float * matrix ) const
{
if ( m_shader != nullptr )
m_shader -> set_uniform ( name . c_str (), matrix );
}
2018-05-29 14:34:45 +02:00
const GLShader * GLCanvas3D :: Shader :: get_shader () const
2018-05-25 14:05:08 +02:00
{
return m_shader ;
}
void GLCanvas3D :: Shader :: _reset ()
{
if ( m_shader != nullptr )
{
m_shader -> release ();
delete m_shader ;
m_shader = nullptr ;
}
}
2018-05-18 14:08:59 +02:00
GLCanvas3D :: LayersEditing :: LayersEditing ()
2018-06-01 15:54:41 +02:00
: m_use_legacy_opengl ( false )
2018-05-25 14:05:08 +02:00
, m_enabled ( false )
, m_z_texture_id ( 0 )
2018-06-01 15:54:41 +02:00
, state ( Unknown )
, band_width ( 2.0f )
, strength ( 0.005f )
, last_object_id ( - 1 )
, last_z ( 0.0f )
, last_action ( 0 )
2018-05-18 14:08:59 +02:00
{
}
2018-05-24 13:46:17 +02:00
GLCanvas3D :: LayersEditing ::~ LayersEditing ()
{
2018-05-25 14:05:08 +02:00
if ( m_z_texture_id != 0 )
{
:: glDeleteTextures ( 1 , & m_z_texture_id );
m_z_texture_id = 0 ;
}
}
bool GLCanvas3D :: LayersEditing :: init ( const std :: string & vertex_shader_filename , const std :: string & fragment_shader_filename )
{
if ( ! m_shader . init ( vertex_shader_filename , fragment_shader_filename ))
return false ;
:: glGenTextures ( 1 , ( GLuint * ) & m_z_texture_id );
:: glBindTexture ( GL_TEXTURE_2D , m_z_texture_id );
:: glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , GL_CLAMP );
:: glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , GL_CLAMP );
:: glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR );
:: glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR_MIPMAP_NEAREST );
:: glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAX_LEVEL , 1 );
:: glBindTexture ( GL_TEXTURE_2D , 0 );
return true ;
}
bool GLCanvas3D :: LayersEditing :: is_allowed () const
{
2018-06-04 13:15:28 +02:00
return ! m_use_legacy_opengl && m_shader . is_initialized ();
2018-05-25 14:05:08 +02:00
}
2018-05-25 16:28:24 +02:00
void GLCanvas3D :: LayersEditing :: set_use_legacy_opengl ( bool use_legacy_opengl )
2018-05-25 14:05:08 +02:00
{
2018-05-25 16:28:24 +02:00
m_use_legacy_opengl = use_legacy_opengl ;
2018-05-24 13:46:17 +02:00
}
2018-05-18 14:08:59 +02:00
bool GLCanvas3D :: LayersEditing :: is_enabled () const
{
return m_enabled ;
}
2018-05-25 14:05:08 +02:00
void GLCanvas3D :: LayersEditing :: set_enabled ( bool enabled )
{
2018-05-25 16:28:24 +02:00
m_enabled = is_allowed () && enabled ;
2018-05-25 14:05:08 +02:00
}
unsigned int GLCanvas3D :: LayersEditing :: get_z_texture_id () const
{
return m_z_texture_id ;
}
2018-05-25 15:56:14 +02:00
void GLCanvas3D :: LayersEditing :: render ( const GLCanvas3D & canvas , const PrintObject & print_object , const GLVolume & volume ) const
{
if ( ! m_enabled )
return ;
2018-05-30 15:18:45 +02:00
const Rect & bar_rect = get_bar_rect_viewport ( canvas );
const Rect & reset_rect = get_reset_rect_viewport ( canvas );
2018-05-25 15:56:14 +02:00
:: glDisable ( GL_DEPTH_TEST );
// The viewport and camera are set to complete view and glOrtho(-$x / 2, $x / 2, -$y / 2, $y / 2, -$depth, $depth),
// where x, y is the window size divided by $self->_zoom.
:: glPushMatrix ();
:: glLoadIdentity ();
2018-05-24 13:46:17 +02:00
_render_tooltip_texture ( canvas , bar_rect , reset_rect );
2018-06-13 09:12:16 +02:00
_render_reset_texture ( reset_rect );
2018-05-25 15:56:14 +02:00
_render_active_object_annotations ( canvas , volume , print_object , bar_rect );
2018-05-24 15:17:01 +02:00
_render_profile ( print_object , bar_rect );
2018-05-25 15:56:14 +02:00
// Revert the matrices.
2018-06-11 10:46:32 +02:00
:: glPopMatrix ();
2018-05-25 15:56:14 +02:00
2018-06-11 10:46:32 +02:00
:: glEnable ( GL_DEPTH_TEST );
2018-05-24 13:46:17 +02:00
}
2018-06-01 15:54:41 +02:00
int GLCanvas3D :: LayersEditing :: get_shader_program_id () const
2018-05-25 14:05:08 +02:00
{
2018-06-01 15:54:41 +02:00
const GLShader * shader = m_shader . get_shader ();
return ( shader != nullptr ) ? shader -> shader_program_id : - 1 ;
2018-05-25 14:05:08 +02:00
}
2018-05-28 14:10:02 +02:00
float GLCanvas3D :: LayersEditing :: get_cursor_z_relative ( const GLCanvas3D & canvas )
2018-05-28 13:43:29 +02:00
{
const Point & mouse_pos = canvas . get_local_mouse_position ();
2018-05-30 15:18:45 +02:00
const Rect & rect = get_bar_rect_screen ( canvas );
2018-08-17 15:53:43 +02:00
float x = ( float ) mouse_pos ( 0 );
float y = ( float ) mouse_pos ( 1 );
2018-05-30 15:18:45 +02:00
float t = rect . get_top ();
float b = rect . get_bottom ();
2018-05-28 13:43:29 +02:00
2018-05-30 15:18:45 +02:00
return (( rect . get_left () <= x ) && ( x <= rect . get_right ()) && ( t <= y ) && ( y <= b )) ?
2018-05-28 13:43:29 +02:00
// Inside the bar.
( b - y - 1.0f ) / ( b - t - 1.0f ) :
// Outside the bar.
- 1000.0f ;
}
2018-05-28 14:39:59 +02:00
bool GLCanvas3D :: LayersEditing :: bar_rect_contains ( const GLCanvas3D & canvas , float x , float y )
{
2018-05-30 15:18:45 +02:00
const Rect & rect = get_bar_rect_screen ( canvas );
2018-05-28 14:39:59 +02:00
return ( rect . get_left () <= x ) && ( x <= rect . get_right ()) && ( rect . get_top () <= y ) && ( y <= rect . get_bottom ());
}
bool GLCanvas3D :: LayersEditing :: reset_rect_contains ( const GLCanvas3D & canvas , float x , float y )
{
2018-05-30 15:18:45 +02:00
const Rect & rect = get_reset_rect_screen ( canvas );
2018-05-28 14:39:59 +02:00
return ( rect . get_left () <= x ) && ( x <= rect . get_right ()) && ( rect . get_top () <= y ) && ( y <= rect . get_bottom ());
}
2018-05-30 15:18:45 +02:00
Rect GLCanvas3D :: LayersEditing :: get_bar_rect_screen ( const GLCanvas3D & canvas )
{
const Size & cnv_size = canvas . get_canvas_size ();
float w = ( float ) cnv_size . get_width ();
float h = ( float ) cnv_size . get_height ();
return Rect ( w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH , 0.0f , w , h - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT );
}
Rect GLCanvas3D :: LayersEditing :: get_reset_rect_screen ( const GLCanvas3D & canvas )
{
const Size & cnv_size = canvas . get_canvas_size ();
float w = ( float ) cnv_size . get_width ();
float h = ( float ) cnv_size . get_height ();
return Rect ( w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH , h - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT , w , h );
}
Rect GLCanvas3D :: LayersEditing :: get_bar_rect_viewport ( const GLCanvas3D & canvas )
{
const Size & cnv_size = canvas . get_canvas_size ();
float half_w = 0.5f * ( float ) cnv_size . get_width ();
float half_h = 0.5f * ( float ) cnv_size . get_height ();
float zoom = canvas . get_camera_zoom ();
float inv_zoom = ( zoom != 0.0f ) ? 1.0f / zoom : 0.0f ;
return Rect (( half_w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH ) * inv_zoom , half_h * inv_zoom , half_w * inv_zoom , ( - half_h + VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT ) * inv_zoom );
}
Rect GLCanvas3D :: LayersEditing :: get_reset_rect_viewport ( const GLCanvas3D & canvas )
{
const Size & cnv_size = canvas . get_canvas_size ();
float half_w = 0.5f * ( float ) cnv_size . get_width ();
float half_h = 0.5f * ( float ) cnv_size . get_height ();
float zoom = canvas . get_camera_zoom ();
float inv_zoom = ( zoom != 0.0f ) ? 1.0f / zoom : 0.0f ;
return Rect (( half_w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH ) * inv_zoom , ( - half_h + VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT ) * inv_zoom , half_w * inv_zoom , - half_h * inv_zoom );
}
2018-05-25 14:05:08 +02:00
bool GLCanvas3D :: LayersEditing :: _is_initialized () const
{
return m_shader . is_initialized ();
}
2018-05-24 13:46:17 +02:00
void GLCanvas3D :: LayersEditing :: _render_tooltip_texture ( const GLCanvas3D & canvas , const Rect & bar_rect , const Rect & reset_rect ) const
{
2018-06-11 10:46:32 +02:00
if ( m_tooltip_texture . get_id () == 0 )
2018-05-24 13:46:17 +02:00
{
2018-06-11 10:46:32 +02:00
std :: string filename = resources_dir () + "/icons/variable_layer_height_tooltip.png" ;
2018-06-14 10:37:28 +02:00
if ( ! m_tooltip_texture . load_from_file ( filename , false ))
2018-05-24 13:46:17 +02:00
return ;
}
float zoom = canvas . get_camera_zoom ();
float inv_zoom = ( zoom != 0.0f ) ? 1.0f / zoom : 0.0f ;
float gap = 10.0f * inv_zoom ;
float bar_left = bar_rect . get_left ();
float reset_bottom = reset_rect . get_bottom ();
2018-06-11 10:46:32 +02:00
float l = bar_left - ( float ) m_tooltip_texture . get_width () * inv_zoom - gap ;
2018-05-24 13:46:17 +02:00
float r = bar_left - gap ;
2018-06-11 10:46:32 +02:00
float t = reset_bottom + ( float ) m_tooltip_texture . get_height () * inv_zoom + gap ;
2018-05-24 13:46:17 +02:00
float b = reset_bottom + gap ;
2018-06-13 09:12:16 +02:00
GLTexture :: render_texture ( m_tooltip_texture . get_id (), l , r , b , t );
2018-05-24 13:46:17 +02:00
}
2018-06-13 09:12:16 +02:00
void GLCanvas3D :: LayersEditing :: _render_reset_texture ( const Rect & reset_rect ) const
2018-05-24 13:46:17 +02:00
{
2018-06-11 10:46:32 +02:00
if ( m_reset_texture . get_id () == 0 )
2018-05-24 13:46:17 +02:00
{
2018-06-11 10:46:32 +02:00
std :: string filename = resources_dir () + "/icons/variable_layer_height_reset.png" ;
2018-06-14 10:37:28 +02:00
if ( ! m_reset_texture . load_from_file ( filename , false ))
2018-05-24 13:46:17 +02:00
return ;
}
2018-06-13 09:12:16 +02:00
GLTexture :: render_texture ( m_reset_texture . get_id (), reset_rect . get_left (), reset_rect . get_right (), reset_rect . get_bottom (), reset_rect . get_top ());
2018-05-24 13:46:17 +02:00
}
2018-05-25 15:56:14 +02:00
void GLCanvas3D :: LayersEditing :: _render_active_object_annotations ( const GLCanvas3D & canvas , const GLVolume & volume , const PrintObject & print_object , const Rect & bar_rect ) const
{
2018-08-17 15:53:43 +02:00
float max_z = print_object . model_object () -> bounding_box (). max ( 2 );
2018-05-25 15:56:14 +02:00
m_shader . start_using ();
m_shader . set_uniform ( "z_to_texture_row" , ( float ) volume . layer_height_texture_z_to_row_id ());
m_shader . set_uniform ( "z_texture_row_to_normalized" , 1.0f / ( float ) volume . layer_height_texture_height ());
2018-05-28 13:43:29 +02:00
m_shader . set_uniform ( "z_cursor" , max_z * get_cursor_z_relative ( canvas ));
2018-06-01 15:54:41 +02:00
m_shader . set_uniform ( "z_cursor_band_width" , band_width );
2018-06-21 08:37:04 +02:00
// The shader requires the original model coordinates when rendering to the texture, so we pass it the unit matrix
m_shader . set_uniform ( "volume_world_matrix" , UNIT_MATRIX );
2018-05-25 15:56:14 +02:00
GLsizei w = ( GLsizei ) volume . layer_height_texture_width ();
GLsizei h = ( GLsizei ) volume . layer_height_texture_height ();
GLsizei half_w = w / 2 ;
GLsizei half_h = h / 2 ;
2018-06-22 11:19:38 +02:00
:: glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 );
2018-05-25 15:56:14 +02:00
:: glBindTexture ( GL_TEXTURE_2D , m_z_texture_id );
2018-06-22 11:19:38 +02:00
:: glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , w , h , 0 , GL_RGBA , GL_UNSIGNED_BYTE , 0 );
:: glTexImage2D ( GL_TEXTURE_2D , 1 , GL_RGBA , half_w , half_h , 0 , GL_RGBA , GL_UNSIGNED_BYTE , 0 );
2018-05-25 15:56:14 +02:00
:: glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 , w , h , GL_RGBA , GL_UNSIGNED_BYTE , volume . layer_height_texture_data_ptr_level0 ());
:: glTexSubImage2D ( GL_TEXTURE_2D , 1 , 0 , 0 , half_w , half_h , GL_RGBA , GL_UNSIGNED_BYTE , volume . layer_height_texture_data_ptr_level1 ());
// Render the color bar
float l = bar_rect . get_left ();
float r = bar_rect . get_right ();
float t = bar_rect . get_top ();
float b = bar_rect . get_bottom ();
:: glBegin ( GL_QUADS );
:: glVertex3f ( l , b , 0.0f );
:: glVertex3f ( r , b , 0.0f );
:: glVertex3f ( r , t , max_z );
:: glVertex3f ( l , t , max_z );
:: glEnd ();
:: glBindTexture ( GL_TEXTURE_2D , 0 );
m_shader . stop_using ();
}
2018-05-24 15:17:01 +02:00
void GLCanvas3D :: LayersEditing :: _render_profile ( const PrintObject & print_object , const Rect & bar_rect ) const
{
// FIXME show some kind of legend.
// Get a maximum layer height value.
// FIXME This is a duplicate code of Slicing.cpp.
double layer_height_max = DBL_MAX ;
2018-09-12 11:59:02 +02:00
const PrintConfig & print_config = print_object . print () -> config ();
2018-05-24 15:17:01 +02:00
const std :: vector < double >& nozzle_diameters = dynamic_cast < const ConfigOptionFloats *> ( print_config . option ( "nozzle_diameter" )) -> values ;
const std :: vector < double >& layer_heights_min = dynamic_cast < const ConfigOptionFloats *> ( print_config . option ( "min_layer_height" )) -> values ;
const std :: vector < double >& layer_heights_max = dynamic_cast < const ConfigOptionFloats *> ( print_config . option ( "max_layer_height" )) -> values ;
for ( unsigned int i = 0 ; i < ( unsigned int ) nozzle_diameters . size (); ++ i )
{
double lh_min = ( layer_heights_min [ i ] == 0.0 ) ? 0.07 : std :: max ( 0.01 , layer_heights_min [ i ]);
double lh_max = ( layer_heights_max [ i ] == 0.0 ) ? ( 0.75 * nozzle_diameters [ i ]) : layer_heights_max [ i ];
layer_height_max = std :: min ( layer_height_max , std :: max ( lh_min , lh_max ));
}
// Make the vertical bar a bit wider so the layer height curve does not touch the edge of the bar region.
layer_height_max *= 1.12 ;
2018-08-24 10:20:00 +02:00
double max_z = unscale < double > ( print_object . size ( 2 ));
2018-09-12 11:59:02 +02:00
double layer_height = dynamic_cast < const ConfigOptionFloat *> ( print_object . config (). option ( "layer_height" )) -> value ;
2018-05-24 15:17:01 +02:00
float l = bar_rect . get_left ();
float w = bar_rect . get_right () - l ;
float b = bar_rect . get_bottom ();
float t = bar_rect . get_top ();
float h = t - b ;
float scale_x = w / ( float ) layer_height_max ;
float scale_y = h / ( float ) max_z ;
float x = l + ( float ) layer_height * scale_x ;
// Baseline
:: glColor3f ( 0.0f , 0.0f , 0.0f );
:: glBegin ( GL_LINE_STRIP );
:: glVertex2f ( x , b );
:: glVertex2f ( x , t );
:: glEnd ();
// Curve
const ModelObject * model_object = print_object . model_object ();
if ( model_object -> layer_height_profile_valid )
{
2018-08-24 10:20:00 +02:00
const std :: vector < double >& profile = model_object -> layer_height_profile ;
2018-05-24 15:17:01 +02:00
:: glColor3f ( 0.0f , 0.0f , 1.0f );
:: glBegin ( GL_LINE_STRIP );
for ( unsigned int i = 0 ; i < profile . size (); i += 2 )
{
:: glVertex2f ( l + ( float ) profile [ i + 1 ] * scale_x , b + ( float ) profile [ i ] * scale_y );
}
:: glEnd ();
}
}
2018-06-01 15:54:41 +02:00
const Point GLCanvas3D :: Mouse :: Drag :: Invalid_2D_Point ( INT_MAX , INT_MAX );
2018-08-21 17:43:05 +02:00
const Vec3d GLCanvas3D :: Mouse :: Drag :: Invalid_3D_Point ( DBL_MAX , DBL_MAX , DBL_MAX );
2018-06-01 15:54:41 +02:00
GLCanvas3D :: Mouse :: Drag :: Drag ()
: start_position_2D ( Invalid_2D_Point )
, start_position_3D ( Invalid_3D_Point )
2018-10-08 14:02:12 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-08-21 17:43:05 +02:00
, volume_center_offset ( 0 , 0 , 0 )
2018-06-21 11:14:17 +02:00
, move_with_shift ( false )
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-06-21 08:37:04 +02:00
, move_volume_idx ( - 1 )
2018-10-08 14:02:12 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-06-21 08:37:04 +02:00
, gizmo_volume_idx ( - 1 )
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-06-01 15:54:41 +02:00
{
}
2018-05-23 13:56:54 +02:00
GLCanvas3D :: Mouse :: Mouse ()
2018-06-01 15:54:41 +02:00
: dragging ( false )
, position ( DBL_MAX , DBL_MAX )
2018-09-20 09:50:49 +02:00
#if ENABLE_GIZMOS_RESET
, ignore_up_event ( false )
#endif // ENABLE_GIZMOS_RESET
2018-05-23 13:56:54 +02:00
{
}
2018-06-01 15:54:41 +02:00
void GLCanvas3D :: Mouse :: set_start_position_2D_as_invalid ()
2018-05-23 13:56:54 +02:00
{
2018-06-01 15:54:41 +02:00
drag . start_position_2D = Drag :: Invalid_2D_Point ;
2018-05-23 13:56:54 +02:00
}
2018-06-01 15:54:41 +02:00
void GLCanvas3D :: Mouse :: set_start_position_3D_as_invalid ()
2018-05-23 13:56:54 +02:00
{
2018-06-01 15:54:41 +02:00
drag . start_position_3D = Drag :: Invalid_3D_Point ;
2018-05-23 13:56:54 +02:00
}
2018-06-01 15:54:41 +02:00
bool GLCanvas3D :: Mouse :: is_start_position_2D_defined () const
2018-05-23 13:56:54 +02:00
{
2018-06-01 15:54:41 +02:00
return ( drag . start_position_2D != Drag :: Invalid_2D_Point );
2018-05-23 13:56:54 +02:00
}
2018-06-01 15:54:41 +02:00
bool GLCanvas3D :: Mouse :: is_start_position_3D_defined () const
2018-05-23 13:56:54 +02:00
{
2018-06-01 15:54:41 +02:00
return ( drag . start_position_3D != Drag :: Invalid_3D_Point );
2018-05-31 13:51:50 +02:00
}
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
GLCanvas3D :: Selection :: VolumeCache :: VolumeCache ()
2018-10-09 15:56:34 +02:00
: m_position ( Vec3d :: Zero ())
, m_rotation ( Vec3d :: Zero ())
, m_scaling_factor ( Vec3d :: Ones ())
2018-10-08 14:02:12 +02:00
{
m_rotation_matrix = Transform3d :: Identity ();
2018-10-15 13:22:36 +02:00
m_scale_matrix = Transform3d :: Identity ();
2018-10-08 14:02:12 +02:00
}
GLCanvas3D :: Selection :: VolumeCache :: VolumeCache ( const Vec3d & position , const Vec3d & rotation , const Vec3d & scaling_factor )
2018-10-09 15:56:34 +02:00
: m_position ( position )
, m_rotation ( rotation )
, m_scaling_factor ( scaling_factor )
2018-10-08 14:02:12 +02:00
{
2018-10-12 10:09:16 +02:00
m_rotation_matrix = Geometry :: assemble_transform ( Vec3d :: Zero (), m_rotation );
2018-10-15 13:22:36 +02:00
m_scale_matrix = Geometry :: assemble_transform ( Vec3d :: Zero (), Vec3d :: Zero (), m_scaling_factor );
2018-10-08 14:02:12 +02:00
}
GLCanvas3D :: Selection :: Selection ()
: m_volumes ( nullptr )
, m_model ( nullptr )
, m_mode ( Instance )
2018-10-12 10:09:16 +02:00
, m_type ( Empty )
2018-10-08 14:02:12 +02:00
, m_valid ( false )
, m_bounding_box_dirty ( true )
{
}
void GLCanvas3D :: Selection :: set_volumes ( GLVolumePtrs * volumes )
{
m_volumes = volumes ;
2018-10-11 08:26:12 +02:00
_update_valid ();
2018-10-08 14:02:12 +02:00
}
void GLCanvas3D :: Selection :: set_model ( Model * model )
{
m_model = model ;
2018-10-11 08:26:12 +02:00
_update_valid ();
2018-10-08 14:02:12 +02:00
}
void GLCanvas3D :: Selection :: add ( unsigned int volume_idx , bool as_single_selection )
{
if ( ! m_valid || (( unsigned int ) m_volumes -> size () <= volume_idx ))
return ;
// resets the current list if needed
const GLVolume * volume = ( * m_volumes )[ volume_idx ];
if ( as_single_selection || volume -> is_wipe_tower || is_wipe_tower () || volume -> is_modifier || is_modifier ())
clear ();
switch ( m_mode )
{
case Volume :
{
2018-10-11 08:26:12 +02:00
_add_volume ( volume_idx );
2018-10-08 14:02:12 +02:00
break ;
}
case Instance :
{
2018-10-11 08:26:12 +02:00
_add_instance ( volume -> object_idx (), volume -> instance_idx ());
2018-10-08 14:02:12 +02:00
break ;
}
case Object :
{
2018-10-11 08:26:12 +02:00
_add_object ( volume -> object_idx ());
2018-10-08 14:02:12 +02:00
break ;
}
}
2018-10-11 08:26:12 +02:00
_update_type ();
2018-10-08 14:02:12 +02:00
m_bounding_box_dirty = true ;
}
void GLCanvas3D :: Selection :: remove ( unsigned int volume_idx )
{
if ( ! m_valid || (( unsigned int ) m_volumes -> size () <= volume_idx ))
return ;
2018-10-11 08:26:12 +02:00
GLVolume * volume = ( * m_volumes )[ volume_idx ];
2018-10-08 14:02:12 +02:00
switch ( m_mode )
{
case Volume :
{
2018-10-11 08:26:12 +02:00
_remove_volume ( volume_idx );
2018-10-08 14:02:12 +02:00
break ;
}
case Instance :
{
2018-10-11 08:26:12 +02:00
_remove_instance ( volume -> object_idx (), volume -> instance_idx ());
2018-10-08 14:02:12 +02:00
break ;
}
case Object :
{
2018-10-11 08:26:12 +02:00
_remove_object ( volume -> object_idx ());
2018-10-08 14:02:12 +02:00
break ;
}
}
2018-10-11 08:26:12 +02:00
_update_type ();
m_bounding_box_dirty = true ;
}
void GLCanvas3D :: Selection :: add_object ( unsigned int object_idx , bool as_single_selection )
{
if ( ! m_valid )
return ;
// resets the current list if needed
if ( as_single_selection )
clear ();
_add_object ( object_idx );
_update_type ();
m_bounding_box_dirty = true ;
}
void GLCanvas3D :: Selection :: remove_object ( unsigned int object_idx )
{
if ( ! m_valid )
return ;
_remove_object ( object_idx );
_update_type ();
m_bounding_box_dirty = true ;
}
void GLCanvas3D :: Selection :: add_instance ( unsigned int object_idx , unsigned int instance_idx , bool as_single_selection )
{
if ( ! m_valid )
return ;
// resets the current list if needed
if ( as_single_selection )
clear ();
_add_instance ( object_idx , instance_idx );
_update_type ();
m_bounding_box_dirty = true ;
}
void GLCanvas3D :: Selection :: remove_instance ( unsigned int object_idx , unsigned int instance_idx )
{
if ( ! m_valid )
return ;
_remove_instance ( object_idx , instance_idx );
_update_type ();
m_bounding_box_dirty = true ;
}
void GLCanvas3D :: Selection :: add_volume ( unsigned int object_idx , unsigned int volume_idx , bool as_single_selection )
{
if ( ! m_valid )
return ;
// resets the current list if needed
if ( as_single_selection )
clear ();
for ( unsigned int i = 0 ; i < ( unsigned int ) m_volumes -> size (); ++ i )
{
GLVolume * v = ( * m_volumes )[ i ];
if (( v -> object_idx () == object_idx ) && ( v -> volume_idx () == volume_idx ))
_add_volume ( i );
}
_update_type ();
m_bounding_box_dirty = true ;
}
void GLCanvas3D :: Selection :: remove_volume ( unsigned int object_idx , unsigned int volume_idx )
{
if ( ! m_valid )
return ;
for ( unsigned int i = 0 ; i < ( unsigned int ) m_volumes -> size (); ++ i )
{
GLVolume * v = ( * m_volumes )[ i ];
if (( v -> object_idx () == object_idx ) && ( v -> volume_idx () == volume_idx ))
_remove_volume ( i );
}
_update_type ();
2018-10-08 14:02:12 +02:00
m_bounding_box_dirty = true ;
}
void GLCanvas3D :: Selection :: clear ()
{
if ( ! m_valid )
return ;
for ( unsigned int i : m_list )
{
( * m_volumes )[ i ] -> selected = false ;
}
m_list . clear ();
2018-10-11 08:26:12 +02:00
_update_type ();
2018-10-08 14:02:12 +02:00
m_bounding_box_dirty = true ;
}
2018-10-09 15:56:34 +02:00
bool GLCanvas3D :: Selection :: is_single_full_instance () const
2018-10-08 14:02:12 +02:00
{
2018-10-09 15:56:34 +02:00
if ( m_type == SingleFullInstance )
return true ;
int object_idx = m_valid ? get_object_idx () : - 1 ;
if ( object_idx != - 1 )
2018-10-08 14:02:12 +02:00
{
2018-10-16 13:49:40 +02:00
if (( object_idx != - 1 ) && ( object_idx < 1000 ))
2018-10-09 15:56:34 +02:00
return m_model -> objects [ object_idx ] -> volumes . size () == m_list . size ();
2018-10-08 14:02:12 +02:00
}
2018-10-09 15:56:34 +02:00
return false ;
2018-10-08 14:02:12 +02:00
}
2018-10-09 15:56:34 +02:00
int GLCanvas3D :: Selection :: get_object_idx () const
2018-10-08 14:02:12 +02:00
{
2018-10-09 15:56:34 +02:00
return ( m_cache . content . size () == 1 ) ? m_cache . content . begin () -> first : - 1 ;
2018-10-08 14:02:12 +02:00
}
2018-10-09 15:56:34 +02:00
int GLCanvas3D :: Selection :: get_instance_idx () const
2018-10-08 14:02:12 +02:00
{
2018-10-09 15:56:34 +02:00
if ( m_cache . content . size () == 1 )
2018-10-08 14:02:12 +02:00
{
2018-10-09 15:56:34 +02:00
const InstanceIdxsList & idxs = m_cache . content . begin () -> second ;
if ( idxs . size () == 1 )
return * idxs . begin ();
2018-10-08 14:02:12 +02:00
}
2018-10-09 15:56:34 +02:00
return - 1 ;
2018-10-08 14:02:12 +02:00
}
const GLVolume * GLCanvas3D :: Selection :: get_volume ( unsigned int volume_idx ) const
{
2018-10-09 15:56:34 +02:00
return ( m_valid && ( volume_idx < ( unsigned int ) m_volumes -> size ())) ? ( * m_volumes )[ volume_idx ] : nullptr ;
2018-10-08 14:02:12 +02:00
}
const BoundingBoxf3 & GLCanvas3D :: Selection :: get_bounding_box () const
{
if ( m_bounding_box_dirty )
2018-10-11 08:26:12 +02:00
_calc_bounding_box ();
2018-10-08 14:02:12 +02:00
return m_bounding_box ;
}
void GLCanvas3D :: Selection :: start_dragging ()
{
if ( ! m_valid )
return ;
2018-10-11 08:26:12 +02:00
_set_caches ();
2018-10-08 14:02:12 +02:00
}
void GLCanvas3D :: Selection :: translate ( const Vec3d & displacement )
{
if ( ! m_valid )
return ;
for ( unsigned int i : m_list )
{
2018-10-09 15:56:34 +02:00
( * m_volumes )[ i ] -> set_offset ( m_cache . volumes_data [ i ]. get_position () + displacement );
}
m_bounding_box_dirty = true ;
}
void GLCanvas3D :: Selection :: rotate ( const Vec3d & rotation )
{
if ( ! m_valid )
return ;
2018-10-12 10:09:16 +02:00
Transform3d m = Geometry :: assemble_transform ( Vec3d :: Zero (), rotation );
2018-10-09 15:56:34 +02:00
for ( unsigned int i : m_list )
{
2018-10-12 15:43:29 +02:00
if ( is_single_full_instance ())
2018-10-09 15:56:34 +02:00
( * m_volumes )[ i ] -> set_rotation ( rotation );
else
{
2018-10-15 13:22:36 +02:00
// extracts rotations from the composed transformation
Vec3d new_rotation = Geometry :: extract_euler_angles ( m * m_cache . volumes_data [ i ]. get_rotation_matrix ());
2018-10-10 13:51:11 +02:00
2018-10-12 12:19:57 +02:00
( * m_volumes )[ i ] -> set_offset ( m_cache . dragging_center + m * ( m_cache . volumes_data [ i ]. get_position () - m_cache . dragging_center ));
2018-10-12 15:43:29 +02:00
( * m_volumes )[ i ] -> set_rotation ( new_rotation );
}
}
if ( m_mode == Instance )
_synchronize_unselected_instances ();
m_bounding_box_dirty = true ;
}
void GLCanvas3D :: Selection :: scale ( const Vec3d & scale )
{
if ( ! m_valid )
return ;
Transform3d m = Transform3d :: Identity ();
m . scale ( scale );
for ( unsigned int i : m_list )
{
if ( is_single_full_instance ())
( * m_volumes )[ i ] -> set_scaling_factor ( scale );
else
{
2018-10-15 13:22:36 +02:00
Eigen :: Matrix < double , 3 , 3 , Eigen :: DontAlign > new_matrix = ( m * m_cache . volumes_data [ i ]. get_scale_matrix ()). matrix (). block ( 0 , 0 , 3 , 3 );
2018-10-12 15:43:29 +02:00
// extracts scaling factors from the composed transformation
Vec3d new_scale ( new_matrix . col ( 0 ). norm (), new_matrix . col ( 1 ). norm (), new_matrix . col ( 2 ). norm ());
( * m_volumes )[ i ] -> set_offset ( m_cache . dragging_center + m * ( m_cache . volumes_data [ i ]. get_position () - m_cache . dragging_center ));
( * m_volumes )[ i ] -> set_scaling_factor ( new_scale );
2018-10-09 15:56:34 +02:00
}
2018-10-08 14:02:12 +02:00
}
2018-10-10 11:04:04 +02:00
if ( m_mode == Instance )
2018-10-11 08:26:12 +02:00
_synchronize_unselected_instances ();
2018-10-10 11:04:04 +02:00
2018-10-08 14:02:12 +02:00
m_bounding_box_dirty = true ;
}
2018-10-10 11:04:04 +02:00
void GLCanvas3D :: Selection :: render ( bool show_indirect_selection ) const
2018-10-08 14:02:12 +02:00
{
if ( is_empty ())
return ;
2018-10-10 11:04:04 +02:00
// render cumulative bounding box of selected volumes
2018-10-11 08:26:12 +02:00
_render_selected_volumes ();
2018-10-10 11:04:04 +02:00
// render bounding boxes of indirectly selected instances
if ( show_indirect_selection && ( m_mode == Instance ))
2018-10-11 08:26:12 +02:00
_render_unselected_instances ();
2018-10-08 14:02:12 +02:00
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _update_valid ()
2018-10-08 14:02:12 +02:00
{
m_valid = ( m_volumes != nullptr ) && ( m_model != nullptr );
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _update_type ()
2018-10-09 15:56:34 +02:00
{
m_cache . content . clear ();
m_type = Mixed ;
for ( unsigned int i : m_list )
{
const GLVolume * volume = ( * m_volumes )[ i ];
int obj_idx = volume -> object_idx ();
int inst_idx = volume -> instance_idx ();
ObjectIdxsToInstanceIdxsMap :: iterator obj_it = m_cache . content . find ( obj_idx );
if ( obj_it == m_cache . content . end ())
obj_it = m_cache . content . insert ( ObjectIdxsToInstanceIdxsMap :: value_type ( obj_idx , InstanceIdxsList ())). first ;
obj_it -> second . insert ( inst_idx );
}
if ( ! m_valid )
m_type = Invalid ;
else
{
if ( m_list . empty ())
m_type = Empty ;
else if ( m_list . size () == 1 )
{
const GLVolume * first = ( * m_volumes )[ * m_list . begin ()];
if ( first -> is_wipe_tower )
m_type = WipeTower ;
else if ( first -> is_modifier )
m_type = Modifier ;
else
{
const ModelObject * model_object = m_model -> objects [ first -> object_idx ()];
unsigned int volumes_count = ( unsigned int ) model_object -> volumes . size ();
unsigned int instances_count = ( unsigned int ) model_object -> instances . size ();
if ( volumes_count * instances_count == 1 )
m_type = SingleFullObject ;
else if ( volumes_count == 1 ) // instances_count > 1
m_type = SingleFullInstance ;
}
}
else
{
if ( m_cache . content . size () == 1 ) // single object
{
const ModelObject * model_object = m_model -> objects [ m_cache . content . begin () -> first ];
unsigned int volumes_count = ( unsigned int ) model_object -> volumes . size ();
unsigned int instances_count = ( unsigned int ) model_object -> instances . size ();
if ( volumes_count * instances_count == ( unsigned int ) m_list . size ())
m_type = SingleFullObject ;
else if (( m_cache . content . begin () -> second . size () == 1 ) && ( volumes_count == ( unsigned int ) m_list . size ()))
m_type = SingleFullInstance ;
}
}
}
switch ( m_type )
{
case Invalid :
{
std :: cout << "selection type: Invalid" << std :: endl ;
break ;
}
case Empty :
{
std :: cout << "selection type: Empty" << std :: endl ;
break ;
}
case WipeTower :
{
std :: cout << "selection type: WipeTower" << std :: endl ;
break ;
}
case Modifier :
{
std :: cout << "selection type: Modifier" << std :: endl ;
break ;
}
case SingleFullObject :
{
std :: cout << "selection type: SingleFullObject" << std :: endl ;
break ;
}
case SingleFullInstance :
{
std :: cout << "selection type: SingleFullInstance" << std :: endl ;
break ;
}
case Mixed :
{
std :: cout << "selection type: Mixed" << std :: endl ;
break ;
}
}
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _set_caches ()
2018-10-08 14:02:12 +02:00
{
m_cache . volumes_data . clear ();
for ( unsigned int i : m_list )
{
const GLVolume * v = ( * m_volumes )[ i ];
m_cache . volumes_data . emplace ( i , VolumeCache ( v -> get_offset (), v -> get_rotation (), v -> get_scaling_factor ()));
}
m_cache . dragging_center = get_bounding_box (). center ();
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _add_volume ( unsigned int volume_idx )
2018-10-08 14:02:12 +02:00
{
// check if the given idx is already selected
if ( m_list . find ( volume_idx ) != m_list . end ())
return ;
m_list . insert ( volume_idx );
( * m_volumes )[ volume_idx ] -> selected = true ;
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _add_instance ( unsigned int object_idx , unsigned int instance_idx )
2018-10-08 14:02:12 +02:00
{
for ( unsigned int i = 0 ; i < ( unsigned int ) m_volumes -> size (); ++ i )
{
GLVolume * v = ( * m_volumes )[ i ];
if (( v -> object_idx () == object_idx ) && ( v -> instance_idx () == instance_idx ))
2018-10-11 08:26:12 +02:00
_add_volume ( i );
2018-10-08 14:02:12 +02:00
}
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _add_object ( unsigned int object_idx )
2018-10-08 14:02:12 +02:00
{
for ( unsigned int i = 0 ; i < ( unsigned int ) m_volumes -> size (); ++ i )
{
GLVolume * v = ( * m_volumes )[ i ];
if ( v -> object_idx () == object_idx )
2018-10-11 08:26:12 +02:00
_add_volume ( i );
2018-10-08 14:02:12 +02:00
}
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _remove_volume ( unsigned int volume_idx )
2018-10-08 14:02:12 +02:00
{
IndicesList :: iterator v_it = m_list . find ( volume_idx );
if ( v_it == m_list . end ())
return ;
m_list . erase ( v_it );
( * m_volumes )[ volume_idx ] -> selected = false ;
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _remove_instance ( unsigned int object_idx , unsigned int instance_idx )
2018-10-08 14:02:12 +02:00
{
for ( unsigned int i = 0 ; i < ( unsigned int ) m_volumes -> size (); ++ i )
{
GLVolume * v = ( * m_volumes )[ i ];
if (( v -> object_idx () == object_idx ) && ( v -> instance_idx () == instance_idx ))
2018-10-11 08:26:12 +02:00
_remove_volume ( i );
2018-10-08 14:02:12 +02:00
}
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _remove_object ( unsigned int object_idx )
2018-10-08 14:02:12 +02:00
{
for ( unsigned int i = 0 ; i < ( unsigned int ) m_volumes -> size (); ++ i )
{
GLVolume * v = ( * m_volumes )[ i ];
if ( v -> object_idx () == object_idx )
2018-10-11 08:26:12 +02:00
_remove_volume ( i );
2018-10-08 14:02:12 +02:00
}
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _calc_bounding_box () const
2018-10-08 14:02:12 +02:00
{
m_bounding_box = BoundingBoxf3 ();
if ( m_valid )
{
for ( unsigned int i : m_list )
{
2018-10-16 13:49:40 +02:00
m_bounding_box . merge (( * m_volumes )[ i ] -> transformed_convex_hull_bounding_box ());
2018-10-08 14:02:12 +02:00
}
}
m_bounding_box_dirty = false ;
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _render_selected_volumes () const
2018-10-10 11:04:04 +02:00
{
float color [ 3 ] = { 1.0f , 1.0f , 1.0f };
2018-10-11 08:26:12 +02:00
_render_bounding_box ( get_bounding_box (), color );
2018-10-10 11:04:04 +02:00
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _render_unselected_instances () const
2018-10-10 11:04:04 +02:00
{
std :: set < unsigned int > done ; // prevent processing volumes twice
done . insert ( m_list . begin (), m_list . end ());
typedef std :: map < std :: pair < int , int > , BoundingBoxf3 > InstanceToBoxMap ;
InstanceToBoxMap boxes ;
for ( unsigned int i : m_list )
{
if ( done . size () == m_volumes -> size ())
break ;
const GLVolume * volume = ( * m_volumes )[ i ];
int object_idx = volume -> object_idx ();
if ( object_idx >= 1000 )
continue ;
int instance_idx = volume -> instance_idx ();
for ( unsigned int j = 0 ; j < ( unsigned int ) m_volumes -> size (); ++ j )
{
if ( done . size () == m_volumes -> size ())
break ;
if ( done . find ( j ) != done . end ())
continue ;
GLVolume * v = ( * m_volumes )[ j ];
int i_idx = v -> instance_idx ();
if (( v -> object_idx () != object_idx ) || ( i_idx == instance_idx ))
continue ;
std :: pair < int , int > box_id ( object_idx , i_idx );
InstanceToBoxMap :: iterator it = boxes . find ( box_id );
if ( it == boxes . end ())
it = boxes . insert ( InstanceToBoxMap :: value_type ( box_id , BoundingBoxf3 ())). first ;
2018-10-16 13:49:40 +02:00
it -> second . merge ( v -> transformed_convex_hull_bounding_box ());
2018-10-10 11:04:04 +02:00
done . insert ( j );
}
}
float color [ 3 ] = { 1.0f , 1.0f , 0.0f };
for ( const InstanceToBoxMap :: value_type & box : boxes )
{
2018-10-11 08:26:12 +02:00
_render_bounding_box ( box . second , color );
2018-10-10 11:04:04 +02:00
}
}
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _render_bounding_box ( const BoundingBoxf3 & box , float * color ) const
2018-10-08 14:02:12 +02:00
{
if ( color == nullptr )
return ;
Vec3f b_min = box . min . cast < float > ();
Vec3f b_max = box . max . cast < float > ();
Vec3f size = 0.25f * box . size (). cast < float > ();
:: glEnable ( GL_DEPTH_TEST );
:: glColor3fv ( color );
:: glBegin ( GL_LINES );
:: glVertex3f ( b_min ( 0 ), b_min ( 1 ), b_min ( 2 )); :: glVertex3f ( b_min ( 0 ) + size ( 0 ), b_min ( 1 ), b_min ( 2 ));
:: glVertex3f ( b_min ( 0 ), b_min ( 1 ), b_min ( 2 )); :: glVertex3f ( b_min ( 0 ), b_min ( 1 ) + size ( 1 ), b_min ( 2 ));
:: glVertex3f ( b_min ( 0 ), b_min ( 1 ), b_min ( 2 )); :: glVertex3f ( b_min ( 0 ), b_min ( 1 ), b_min ( 2 ) + size ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_min ( 1 ), b_min ( 2 )); :: glVertex3f ( b_max ( 0 ) - size ( 0 ), b_min ( 1 ), b_min ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_min ( 1 ), b_min ( 2 )); :: glVertex3f ( b_max ( 0 ), b_min ( 1 ) + size ( 1 ), b_min ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_min ( 1 ), b_min ( 2 )); :: glVertex3f ( b_max ( 0 ), b_min ( 1 ), b_min ( 2 ) + size ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_max ( 1 ), b_min ( 2 )); :: glVertex3f ( b_max ( 0 ) - size ( 0 ), b_max ( 1 ), b_min ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_max ( 1 ), b_min ( 2 )); :: glVertex3f ( b_max ( 0 ), b_max ( 1 ) - size ( 1 ), b_min ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_max ( 1 ), b_min ( 2 )); :: glVertex3f ( b_max ( 0 ), b_max ( 1 ), b_min ( 2 ) + size ( 2 ));
:: glVertex3f ( b_min ( 0 ), b_max ( 1 ), b_min ( 2 )); :: glVertex3f ( b_min ( 0 ) + size ( 0 ), b_max ( 1 ), b_min ( 2 ));
:: glVertex3f ( b_min ( 0 ), b_max ( 1 ), b_min ( 2 )); :: glVertex3f ( b_min ( 0 ), b_max ( 1 ) - size ( 1 ), b_min ( 2 ));
:: glVertex3f ( b_min ( 0 ), b_max ( 1 ), b_min ( 2 )); :: glVertex3f ( b_min ( 0 ), b_max ( 1 ), b_min ( 2 ) + size ( 2 ));
:: glVertex3f ( b_min ( 0 ), b_min ( 1 ), b_max ( 2 )); :: glVertex3f ( b_min ( 0 ) + size ( 0 ), b_min ( 1 ), b_max ( 2 ));
:: glVertex3f ( b_min ( 0 ), b_min ( 1 ), b_max ( 2 )); :: glVertex3f ( b_min ( 0 ), b_min ( 1 ) + size ( 1 ), b_max ( 2 ));
:: glVertex3f ( b_min ( 0 ), b_min ( 1 ), b_max ( 2 )); :: glVertex3f ( b_min ( 0 ), b_min ( 1 ), b_max ( 2 ) - size ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_min ( 1 ), b_max ( 2 )); :: glVertex3f ( b_max ( 0 ) - size ( 0 ), b_min ( 1 ), b_max ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_min ( 1 ), b_max ( 2 )); :: glVertex3f ( b_max ( 0 ), b_min ( 1 ) + size ( 1 ), b_max ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_min ( 1 ), b_max ( 2 )); :: glVertex3f ( b_max ( 0 ), b_min ( 1 ), b_max ( 2 ) - size ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_max ( 1 ), b_max ( 2 )); :: glVertex3f ( b_max ( 0 ) - size ( 0 ), b_max ( 1 ), b_max ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_max ( 1 ), b_max ( 2 )); :: glVertex3f ( b_max ( 0 ), b_max ( 1 ) - size ( 1 ), b_max ( 2 ));
:: glVertex3f ( b_max ( 0 ), b_max ( 1 ), b_max ( 2 )); :: glVertex3f ( b_max ( 0 ), b_max ( 1 ), b_max ( 2 ) - size ( 2 ));
:: glVertex3f ( b_min ( 0 ), b_max ( 1 ), b_max ( 2 )); :: glVertex3f ( b_min ( 0 ) + size ( 0 ), b_max ( 1 ), b_max ( 2 ));
:: glVertex3f ( b_min ( 0 ), b_max ( 1 ), b_max ( 2 )); :: glVertex3f ( b_min ( 0 ), b_max ( 1 ) - size ( 1 ), b_max ( 2 ));
:: glVertex3f ( b_min ( 0 ), b_max ( 1 ), b_max ( 2 )); :: glVertex3f ( b_min ( 0 ), b_max ( 1 ), b_max ( 2 ) - size ( 2 ));
:: glEnd ();
}
2018-10-10 11:04:04 +02:00
2018-10-11 08:26:12 +02:00
void GLCanvas3D :: Selection :: _synchronize_unselected_instances ()
2018-10-10 11:04:04 +02:00
{
std :: set < unsigned int > done ; // prevent processing volumes twice
done . insert ( m_list . begin (), m_list . end ());
for ( unsigned int i : m_list )
{
if ( done . size () == m_volumes -> size ())
break ;
const GLVolume * volume = ( * m_volumes )[ i ];
int object_idx = volume -> object_idx ();
if ( object_idx >= 1000 )
continue ;
int instance_idx = volume -> instance_idx ();
const Vec3d & rotation = volume -> get_rotation ();
const Vec3d & scaling_factor = volume -> get_scaling_factor ();
// Process unselected instances.
for ( unsigned int j = 0 ; j < ( unsigned int ) m_volumes -> size (); ++ j )
{
if ( done . size () == m_volumes -> size ())
break ;
if ( done . find ( j ) != done . end ())
continue ;
GLVolume * v = ( * m_volumes )[ j ];
if (( v -> object_idx () != object_idx ) || ( v -> instance_idx () == instance_idx ))
continue ;
v -> set_rotation ( rotation );
v -> set_scaling_factor ( scaling_factor );
done . insert ( j );
}
}
}
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-22 09:00:01 +02:00
const float GLCanvas3D :: Gizmos :: OverlayTexturesScale = 0.75f ;
const float GLCanvas3D :: Gizmos :: OverlayOffsetX = 10.0f * OverlayTexturesScale ;
const float GLCanvas3D :: Gizmos :: OverlayGapY = 5.0f * OverlayTexturesScale ;
2018-06-13 09:12:16 +02:00
GLCanvas3D :: Gizmos :: Gizmos ()
: m_enabled ( false )
2018-06-13 09:26:58 +02:00
, m_current ( Undefined )
2018-06-13 09:12:16 +02:00
{
}
GLCanvas3D :: Gizmos ::~ Gizmos ()
{
_reset ();
}
2018-08-24 14:11:41 +02:00
bool GLCanvas3D :: Gizmos :: init ( GLCanvas3D & parent )
2018-06-13 09:12:16 +02:00
{
2018-09-11 09:00:28 +02:00
GLGizmoBase * gizmo = new GLGizmoMove3D ( parent );
if ( gizmo == nullptr )
return false ;
if ( ! gizmo -> init ())
return false ;
m_gizmos . insert ( GizmosMap :: value_type ( Move , gizmo ));
gizmo = new GLGizmoScale3D ( parent );
2018-06-13 09:12:16 +02:00
if ( gizmo == nullptr )
return false ;
if ( ! gizmo -> init ())
return false ;
m_gizmos . insert ( GizmosMap :: value_type ( Scale , gizmo ));
2018-08-24 14:11:41 +02:00
gizmo = new GLGizmoRotate3D ( parent );
2018-06-13 09:12:16 +02:00
if ( gizmo == nullptr )
{
_reset ();
return false ;
}
if ( ! gizmo -> init ())
{
_reset ();
return false ;
}
m_gizmos . insert ( GizmosMap :: value_type ( Rotate , gizmo ));
2018-08-27 14:00:53 +02:00
gizmo = new GLGizmoFlatten ( parent );
2018-08-09 16:55:43 +02:00
if ( gizmo == nullptr )
return false ;
if ( ! gizmo -> init ()) {
_reset ();
return false ;
}
m_gizmos . insert ( GizmosMap :: value_type ( Flatten , gizmo ));
2018-06-13 09:12:16 +02:00
return true ;
}
bool GLCanvas3D :: Gizmos :: is_enabled () const
{
return m_enabled ;
}
void GLCanvas3D :: Gizmos :: set_enabled ( bool enable )
{
m_enabled = enable ;
}
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
void GLCanvas3D :: Gizmos :: update_hover_state ( const GLCanvas3D & canvas , const Vec2d & mouse_pos , const GLCanvas3D :: Selection & selection )
#else
2018-08-21 21:05:24 +02:00
void GLCanvas3D :: Gizmos :: update_hover_state ( const GLCanvas3D & canvas , const Vec2d & mouse_pos )
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-13 10:49:59 +02:00
{
if ( ! m_enabled )
return ;
float cnv_h = ( float ) canvas . get_canvas_size (). get_height ();
float height = _get_total_overlay_height ();
float top_y = 0.5f * ( cnv_h - height );
for ( GizmosMap :: const_iterator it = m_gizmos . begin (); it != m_gizmos . end (); ++ it )
{
2018-06-13 13:14:17 +02:00
if ( it -> second == nullptr )
2018-06-13 10:49:59 +02:00
continue ;
2018-06-22 09:00:01 +02:00
float tex_size = ( float ) it -> second -> get_textures_size () * OverlayTexturesScale ;
2018-06-13 10:49:59 +02:00
float half_tex_size = 0.5f * tex_size ;
// we currently use circular icons for gizmo, so we check the radius
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
bool no_wipe_tower = selection . is_wipe_tower () && ! it -> second -> get_accept_wipe_tower ();
if ( ! no_wipe_tower && ( it -> second -> get_state () != GLGizmoBase :: On ))
#else
2018-06-13 13:14:17 +02:00
if ( it -> second -> get_state () != GLGizmoBase :: On )
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-13 13:14:17 +02:00
{
2018-08-21 21:05:24 +02:00
bool inside = ( mouse_pos - Vec2d ( OverlayOffsetX + half_tex_size , top_y + half_tex_size )). norm () < half_tex_size ;
2018-06-13 13:14:17 +02:00
it -> second -> set_state ( inside ? GLGizmoBase :: Hover : GLGizmoBase :: Off );
}
2018-06-13 10:49:59 +02:00
top_y += ( tex_size + OverlayGapY );
}
}
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
void GLCanvas3D :: Gizmos :: update_on_off_state ( const GLCanvas3D & canvas , const Vec2d & mouse_pos , const GLCanvas3D :: Selection & selection )
#else
2018-08-21 21:05:24 +02:00
void GLCanvas3D :: Gizmos :: update_on_off_state ( const GLCanvas3D & canvas , const Vec2d & mouse_pos )
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-13 13:14:17 +02:00
{
if ( ! m_enabled )
return ;
float cnv_h = ( float ) canvas . get_canvas_size (). get_height ();
float height = _get_total_overlay_height ();
float top_y = 0.5f * ( cnv_h - height );
for ( GizmosMap :: const_iterator it = m_gizmos . begin (); it != m_gizmos . end (); ++ it )
{
if ( it -> second == nullptr )
continue ;
2018-06-22 09:00:01 +02:00
float tex_size = ( float ) it -> second -> get_textures_size () * OverlayTexturesScale ;
2018-06-13 13:14:17 +02:00
float half_tex_size = 0.5f * tex_size ;
// we currently use circular icons for gizmo, so we check the radius
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
bool no_wipe_tower = selection . is_wipe_tower () && ! it -> second -> get_accept_wipe_tower ();
if ( ! no_wipe_tower && (( mouse_pos - Vec2d ( OverlayOffsetX + half_tex_size , top_y + half_tex_size )). norm () < half_tex_size ))
#else
2018-08-21 21:05:24 +02:00
if (( mouse_pos - Vec2d ( OverlayOffsetX + half_tex_size , top_y + half_tex_size )). norm () < half_tex_size )
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-13 15:44:04 +02:00
{
if (( it -> second -> get_state () == GLGizmoBase :: On ))
{
it -> second -> set_state ( GLGizmoBase :: Off );
m_current = Undefined ;
}
else
{
it -> second -> set_state ( GLGizmoBase :: On );
m_current = it -> first ;
}
}
2018-06-13 13:14:17 +02:00
else
it -> second -> set_state ( GLGizmoBase :: Off );
top_y += ( tex_size + OverlayGapY );
}
}
void GLCanvas3D :: Gizmos :: reset_all_states ()
{
2018-06-13 15:44:04 +02:00
if ( ! m_enabled )
return ;
2018-06-13 13:14:17 +02:00
for ( GizmosMap :: const_iterator it = m_gizmos . begin (); it != m_gizmos . end (); ++ it )
{
if ( it -> second != nullptr )
2018-06-15 14:10:28 +02:00
{
2018-06-13 13:14:17 +02:00
it -> second -> set_state ( GLGizmoBase :: Off );
2018-06-15 14:10:28 +02:00
it -> second -> set_hover_id ( - 1 );
}
2018-06-13 13:14:17 +02:00
}
2018-06-13 15:44:04 +02:00
m_current = Undefined ;
2018-06-13 13:14:17 +02:00
}
2018-06-14 15:32:26 +02:00
void GLCanvas3D :: Gizmos :: set_hover_id ( int id )
{
if ( ! m_enabled )
return ;
for ( GizmosMap :: const_iterator it = m_gizmos . begin (); it != m_gizmos . end (); ++ it )
{
if (( it -> second != nullptr ) && ( it -> second -> get_state () == GLGizmoBase :: On ))
it -> second -> set_hover_id ( id );
}
}
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
2018-10-09 15:56:34 +02:00
void GLCanvas3D :: Gizmos :: enable_grabber ( EType type , unsigned int id , bool enable )
2018-10-08 14:02:12 +02:00
{
if ( ! m_enabled )
return ;
GizmosMap :: const_iterator it = m_gizmos . find ( type );
if ( it != m_gizmos . end ())
2018-10-09 15:56:34 +02:00
{
if ( enable )
it -> second -> enable_grabber ( id );
else
it -> second -> disable_grabber ( id );
}
2018-10-08 14:02:12 +02:00
}
#endif // ENABLE_EXTENDED_SELECTION
2018-08-21 21:05:24 +02:00
bool GLCanvas3D :: Gizmos :: overlay_contains_mouse ( const GLCanvas3D & canvas , const Vec2d & mouse_pos ) const
2018-06-13 13:14:17 +02:00
{
2018-06-13 15:44:04 +02:00
if ( ! m_enabled )
return false ;
2018-06-14 12:34:19 +02:00
float cnv_h = ( float ) canvas . get_canvas_size (). get_height ();
float height = _get_total_overlay_height ();
float top_y = 0.5f * ( cnv_h - height );
2018-06-13 13:14:17 +02:00
for ( GizmosMap :: const_iterator it = m_gizmos . begin (); it != m_gizmos . end (); ++ it )
{
2018-06-14 12:34:19 +02:00
if ( it -> second == nullptr )
continue ;
2018-06-22 09:00:01 +02:00
float tex_size = ( float ) it -> second -> get_textures_size () * OverlayTexturesScale ;
2018-06-14 12:34:19 +02:00
float half_tex_size = 0.5f * tex_size ;
// we currently use circular icons for gizmo, so we check the radius
2018-08-21 21:05:24 +02:00
if (( mouse_pos - Vec2d ( OverlayOffsetX + half_tex_size , top_y + half_tex_size )). norm () < half_tex_size )
2018-06-13 13:14:17 +02:00
return true ;
2018-06-14 12:34:19 +02:00
top_y += ( tex_size + OverlayGapY );
2018-06-13 13:14:17 +02:00
}
return false ;
}
2018-06-15 14:10:28 +02:00
bool GLCanvas3D :: Gizmos :: grabber_contains_mouse () const
{
if ( ! m_enabled )
return false ;
GLGizmoBase * curr = _get_current ();
return ( curr != nullptr ) ? ( curr -> get_hover_id () != - 1 ) : false ;
}
2018-08-20 10:23:17 +02:00
void GLCanvas3D :: Gizmos :: update ( const Linef3 & mouse_ray )
2018-06-15 14:10:28 +02:00
{
if ( ! m_enabled )
return ;
GLGizmoBase * curr = _get_current ();
if ( curr != nullptr )
2018-08-20 10:23:17 +02:00
curr -> update ( mouse_ray );
2018-06-15 14:10:28 +02:00
}
2018-09-19 15:39:54 +02:00
#if ENABLE_GIZMOS_RESET
void GLCanvas3D :: Gizmos :: process_double_click ()
{
if ( ! m_enabled )
return ;
GLGizmoBase * curr = _get_current ();
if ( curr != nullptr )
curr -> process_double_click ();
}
#endif // ENABLE_GIZMOS_RESET
2018-06-19 09:46:26 +02:00
GLCanvas3D :: Gizmos :: EType GLCanvas3D :: Gizmos :: get_current_type () const
2018-06-18 15:07:17 +02:00
{
2018-06-19 09:46:26 +02:00
return m_current ;
2018-06-18 15:07:17 +02:00
}
bool GLCanvas3D :: Gizmos :: is_running () const
{
if ( ! m_enabled )
return false ;
GLGizmoBase * curr = _get_current ();
return ( curr != nullptr ) ? ( curr -> get_state () == GLGizmoBase :: On ) : false ;
}
2018-06-15 14:10:28 +02:00
bool GLCanvas3D :: Gizmos :: is_dragging () const
{
2018-10-16 13:49:40 +02:00
if ( ! m_enabled )
return false ;
2018-09-11 09:00:28 +02:00
GLGizmoBase * curr = _get_current ();
return ( curr != nullptr ) ? curr -> is_dragging () : false ;
2018-06-15 14:10:28 +02:00
}
2018-10-15 11:30:50 +02:00
#if ENABLE_EXTENDED_SELECTION
void GLCanvas3D :: Gizmos :: start_dragging ( const GLCanvas3D :: Selection & selection )
{
2018-10-16 13:49:40 +02:00
if ( ! m_enabled )
return ;
2018-10-15 11:30:50 +02:00
GLGizmoBase * curr = _get_current ();
if ( curr != nullptr )
curr -> start_dragging ( selection );
}
#else
2018-09-10 10:01:49 +02:00
void GLCanvas3D :: Gizmos :: start_dragging ( const BoundingBoxf3 & box )
2018-06-15 14:10:28 +02:00
{
2018-10-16 13:49:40 +02:00
if ( ! m_enabled )
return ;
2018-06-15 14:10:28 +02:00
GLGizmoBase * curr = _get_current ();
if ( curr != nullptr )
2018-09-10 10:01:49 +02:00
curr -> start_dragging ( box );
2018-06-15 14:10:28 +02:00
}
2018-10-15 11:30:50 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-15 14:10:28 +02:00
void GLCanvas3D :: Gizmos :: stop_dragging ()
{
2018-10-16 13:49:40 +02:00
if ( ! m_enabled )
return ;
2018-07-12 11:26:13 +02:00
GLGizmoBase * curr = _get_current ();
if ( curr != nullptr )
curr -> stop_dragging ();
2018-06-15 14:10:28 +02:00
}
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
Vec3d GLCanvas3D :: Gizmos :: get_displacement () const
{
if ( ! m_enabled )
return Vec3d :: Zero ();
GizmosMap :: const_iterator it = m_gizmos . find ( Move );
return ( it != m_gizmos . end ()) ? reinterpret_cast < GLGizmoMove3D *> ( it -> second ) -> get_displacement () : Vec3d :: Zero ();
}
#else
2018-09-11 09:00:28 +02:00
Vec3d GLCanvas3D :: Gizmos :: get_position () const
{
if ( ! m_enabled )
return Vec3d :: Zero ();
GizmosMap :: const_iterator it = m_gizmos . find ( Move );
return ( it != m_gizmos . end ()) ? reinterpret_cast < GLGizmoMove3D *> ( it -> second ) -> get_position () : Vec3d :: Zero ();
}
void GLCanvas3D :: Gizmos :: set_position ( const Vec3d & position )
{
if ( ! m_enabled )
return ;
GizmosMap :: const_iterator it = m_gizmos . find ( Move );
if ( it != m_gizmos . end ())
reinterpret_cast < GLGizmoMove3D *> ( it -> second ) -> set_position ( position );
}
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-09-11 09:00:28 +02:00
2018-09-24 15:54:09 +02:00
Vec3d GLCanvas3D :: Gizmos :: get_scale () const
2018-06-18 15:07:17 +02:00
{
if ( ! m_enabled )
2018-09-24 15:54:09 +02:00
return Vec3d :: Ones ();
2018-06-18 15:07:17 +02:00
GizmosMap :: const_iterator it = m_gizmos . find ( Scale );
2018-09-24 15:54:09 +02:00
return ( it != m_gizmos . end ()) ? reinterpret_cast < GLGizmoScale3D *> ( it -> second ) -> get_scale () : Vec3d :: Ones ();
2018-06-18 15:07:17 +02:00
}
2018-09-24 15:54:09 +02:00
void GLCanvas3D :: Gizmos :: set_scale ( const Vec3d & scale )
2018-06-19 09:46:26 +02:00
{
if ( ! m_enabled )
return ;
GizmosMap :: const_iterator it = m_gizmos . find ( Scale );
if ( it != m_gizmos . end ())
2018-08-21 08:50:35 +02:00
reinterpret_cast < GLGizmoScale3D *> ( it -> second ) -> set_scale ( scale );
2018-06-19 09:46:26 +02:00
}
2018-09-20 15:00:40 +02:00
Vec3d GLCanvas3D :: Gizmos :: get_rotation () const
{
if ( ! m_enabled )
return Vec3d :: Zero ();
GizmosMap :: const_iterator it = m_gizmos . find ( Rotate );
return ( it != m_gizmos . end ()) ? reinterpret_cast < GLGizmoRotate3D *> ( it -> second ) -> get_rotation () : Vec3d :: Zero ();
}
void GLCanvas3D :: Gizmos :: set_rotation ( const Vec3d & rotation )
{
if ( ! m_enabled )
return ;
GizmosMap :: const_iterator it = m_gizmos . find ( Rotate );
if ( it != m_gizmos . end ())
reinterpret_cast < GLGizmoRotate3D *> ( it -> second ) -> set_rotation ( rotation );
}
Vec3d GLCanvas3D :: Gizmos :: get_flattening_rotation () const
{
if ( ! m_enabled )
return Vec3d :: Zero ();
GizmosMap :: const_iterator it = m_gizmos . find ( Flatten );
return ( it != m_gizmos . end ()) ? reinterpret_cast < GLGizmoFlatten *> ( it -> second ) -> get_flattening_rotation () : Vec3d :: Zero ();
}
2018-08-09 16:55:43 +02:00
2018-08-17 15:40:47 +02:00
void GLCanvas3D :: Gizmos :: set_flattening_data ( const ModelObject * model_object )
2018-08-09 16:55:43 +02:00
{
if ( ! m_enabled )
return ;
GizmosMap :: const_iterator it = m_gizmos . find ( Flatten );
if ( it != m_gizmos . end ())
2018-08-17 15:40:47 +02:00
reinterpret_cast < GLGizmoFlatten *> ( it -> second ) -> set_flattening_data ( model_object );
2018-08-09 16:55:43 +02:00
}
2018-10-15 11:30:50 +02:00
#if ENABLE_EXTENDED_SELECTION
void GLCanvas3D :: Gizmos :: render_current_gizmo ( const GLCanvas3D :: Selection & selection ) const
#else
2018-08-21 14:27:36 +02:00
void GLCanvas3D :: Gizmos :: render_current_gizmo ( const BoundingBoxf3 & box ) const
2018-10-15 11:30:50 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-13 09:12:16 +02:00
{
if ( ! m_enabled )
return ;
:: glDisable ( GL_DEPTH_TEST );
2018-10-15 11:30:50 +02:00
#if ENABLE_EXTENDED_SELECTION
_render_current_gizmo ( selection );
#else
2018-07-18 14:26:42 +02:00
if ( box . radius () > 0.0 )
_render_current_gizmo ( box );
2018-10-15 11:30:50 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-13 09:12:16 +02:00
}
2018-10-15 11:30:50 +02:00
#if ENABLE_EXTENDED_SELECTION
void GLCanvas3D :: Gizmos :: render_current_gizmo_for_picking_pass ( const GLCanvas3D :: Selection & selection ) const
{
if ( ! m_enabled )
return ;
GLGizmoBase * curr = _get_current ();
if ( curr != nullptr )
curr -> render_for_picking ( selection );
}
#else
2018-06-14 15:32:26 +02:00
void GLCanvas3D :: Gizmos :: render_current_gizmo_for_picking_pass ( const BoundingBoxf3 & box ) const
{
if ( ! m_enabled )
return ;
2018-06-15 14:10:28 +02:00
GLGizmoBase * curr = _get_current ();
if ( curr != nullptr )
curr -> render_for_picking ( box );
2018-06-14 15:32:26 +02:00
}
2018-10-15 11:30:50 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-14 15:32:26 +02:00
2018-08-21 14:27:36 +02:00
void GLCanvas3D :: Gizmos :: render_overlay ( const GLCanvas3D & canvas ) const
{
if ( ! m_enabled )
return ;
:: glDisable ( GL_DEPTH_TEST );
:: glPushMatrix ();
:: glLoadIdentity ();
_render_overlay ( canvas );
:: glPopMatrix ();
}
2018-06-13 09:12:16 +02:00
void GLCanvas3D :: Gizmos :: _reset ()
{
for ( GizmosMap :: value_type & gizmo : m_gizmos )
{
delete gizmo . second ;
gizmo . second = nullptr ;
}
m_gizmos . clear ();
}
void GLCanvas3D :: Gizmos :: _render_overlay ( const GLCanvas3D & canvas ) const
{
if ( m_gizmos . empty ())
return ;
2018-06-13 10:49:59 +02:00
float cnv_w = ( float ) canvas . get_canvas_size (). get_width ();
2018-06-13 09:12:16 +02:00
float zoom = canvas . get_camera_zoom ();
float inv_zoom = ( zoom != 0.0f ) ? 1.0f / zoom : 0.0f ;
2018-06-13 10:49:59 +02:00
float height = _get_total_overlay_height ();
2018-06-13 09:12:16 +02:00
float top_x = ( OverlayOffsetX - 0.5f * cnv_w ) * inv_zoom ;
2018-06-13 10:49:59 +02:00
float top_y = 0.5f * height * inv_zoom ;
2018-06-13 09:12:16 +02:00
float scaled_gap_y = OverlayGapY * inv_zoom ;
for ( GizmosMap :: const_iterator it = m_gizmos . begin (); it != m_gizmos . end (); ++ it )
{
2018-06-22 09:00:01 +02:00
float tex_size = ( float ) it -> second -> get_textures_size () * OverlayTexturesScale * inv_zoom ;
2018-06-22 15:11:04 +02:00
GLTexture :: render_texture ( it -> second -> get_texture_id (), top_x , top_x + tex_size , top_y - tex_size , top_y );
2018-06-13 10:49:59 +02:00
top_y -= ( tex_size + scaled_gap_y );
2018-06-13 09:12:16 +02:00
}
}
2018-10-15 11:30:50 +02:00
#if ENABLE_EXTENDED_SELECTION
void GLCanvas3D :: Gizmos :: _render_current_gizmo ( const GLCanvas3D :: Selection & selection ) const
{
GLGizmoBase * curr = _get_current ();
if ( curr != nullptr )
curr -> render ( selection );
}
#else
2018-06-13 15:44:04 +02:00
void GLCanvas3D :: Gizmos :: _render_current_gizmo ( const BoundingBoxf3 & box ) const
2018-06-13 09:12:16 +02:00
{
2018-06-15 14:10:28 +02:00
GLGizmoBase * curr = _get_current ();
if ( curr != nullptr )
curr -> render ( box );
2018-06-13 09:12:16 +02:00
}
2018-10-15 11:30:50 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-13 10:49:59 +02:00
float GLCanvas3D :: Gizmos :: _get_total_overlay_height () const
{
float height = 0.0f ;
2018-06-15 14:10:28 +02:00
2018-06-13 10:49:59 +02:00
for ( GizmosMap :: const_iterator it = m_gizmos . begin (); it != m_gizmos . end (); ++ it )
{
height += ( float ) it -> second -> get_textures_size ();
if ( std :: distance ( it , m_gizmos . end ()) > 1 )
height += OverlayGapY ;
}
return height ;
}
2018-06-13 09:12:16 +02:00
2018-10-08 14:02:12 +02:00
GLGizmoBase * GLCanvas3D :: Gizmos :: _get_current () const
{
GizmosMap :: const_iterator it = m_gizmos . find ( m_current );
return ( it != m_gizmos . end ()) ? it -> second : nullptr ;
}
2018-07-19 13:18:19 +02:00
const unsigned char GLCanvas3D :: WarningTexture :: Background_Color [ 3 ] = { 9 , 91 , 134 };
const unsigned char GLCanvas3D :: WarningTexture :: Opacity = 255 ;
2018-07-31 14:20:16 +02:00
GLCanvas3D :: WarningTexture :: WarningTexture ()
: GUI :: GLTexture ()
, m_original_width ( 0 )
, m_original_height ( 0 )
{
}
2018-07-19 13:18:19 +02:00
bool GLCanvas3D :: WarningTexture :: generate ( const std :: string & msg )
{
reset ();
if ( msg . empty ())
return false ;
wxMemoryDC memDC ;
// select default font
2018-07-31 14:20:16 +02:00
wxFont font = wxSystemSettings :: GetFont ( wxSYS_DEFAULT_GUI_FONT );
font . MakeLarger ();
2018-07-31 15:32:16 +02:00
font . MakeBold ();
2018-07-31 14:20:16 +02:00
memDC . SetFont ( font );
2018-07-19 13:18:19 +02:00
// calculates texture size
wxCoord w , h ;
memDC . GetTextExtent ( msg , & w , & h );
2018-07-31 14:20:16 +02:00
2018-09-12 11:59:02 +02:00
int pow_of_two_size = next_highest_power_of_2 ( std :: max < unsigned int > ( w , h ));
2018-07-31 14:20:16 +02:00
m_original_width = ( int ) w ;
m_original_height = ( int ) h ;
m_width = pow_of_two_size ;
m_height = pow_of_two_size ;
2018-07-19 13:18:19 +02:00
// generates bitmap
wxBitmap bitmap ( m_width , m_height );
#if defined(__APPLE__) || defined(_MSC_VER)
bitmap . UseAlpha ();
#endif
memDC . SelectObject ( bitmap );
memDC . SetBackground ( wxBrush ( wxColour ( Background_Color [ 0 ], Background_Color [ 1 ], Background_Color [ 2 ])));
memDC . Clear ();
memDC . SetTextForeground ( * wxWHITE );
// draw message
memDC . DrawText ( msg , 0 , 0 );
memDC . SelectObject ( wxNullBitmap );
// Convert the bitmap into a linear data ready to be loaded into the GPU.
wxImage image = bitmap . ConvertToImage ();
image . SetMaskColour ( Background_Color [ 0 ], Background_Color [ 1 ], Background_Color [ 2 ]);
// prepare buffer
std :: vector < unsigned char > data ( 4 * m_width * m_height , 0 );
for ( int h = 0 ; h < m_height ; ++ h )
{
int hh = h * m_width ;
unsigned char * px_ptr = data . data () + 4 * hh ;
for ( int w = 0 ; w < m_width ; ++ w )
{
* px_ptr ++ = image . GetRed ( w , h );
* px_ptr ++ = image . GetGreen ( w , h );
* px_ptr ++ = image . GetBlue ( w , h );
* px_ptr ++ = image . IsTransparent ( w , h ) ? 0 : Opacity ;
}
}
// sends buffer to gpu
:: glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 );
:: glGenTextures ( 1 , & m_id );
:: glBindTexture ( GL_TEXTURE_2D , ( GLuint ) m_id );
:: glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , ( GLsizei ) m_width , ( GLsizei ) m_height , 0 , GL_RGBA , GL_UNSIGNED_BYTE , ( const void * ) data . data ());
:: glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR );
:: glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR );
:: glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAX_LEVEL , 1 );
:: glBindTexture ( GL_TEXTURE_2D , 0 );
return true ;
}
2018-07-31 14:20:16 +02:00
void GLCanvas3D :: WarningTexture :: render ( const GLCanvas3D & canvas ) const
{
2018-07-31 14:32:59 +02:00
if (( m_id > 0 ) && ( m_original_width > 0 ) && ( m_original_height > 0 ) && ( m_width > 0 ) && ( m_height > 0 ))
2018-07-31 14:20:16 +02:00
{
:: glDisable ( GL_DEPTH_TEST );
:: glPushMatrix ();
:: glLoadIdentity ();
const Size & cnv_size = canvas . get_canvas_size ();
float zoom = canvas . get_camera_zoom ();
float inv_zoom = ( zoom != 0.0f ) ? 1.0f / zoom : 0.0f ;
float left = ( - 0.5f * ( float ) m_original_width ) * inv_zoom ;
float top = ( - 0.5f * ( float ) cnv_size . get_height () + ( float ) m_original_height + 2.0f ) * inv_zoom ;
float right = left + ( float ) m_original_width * inv_zoom ;
float bottom = top - ( float ) m_original_height * inv_zoom ;
float uv_left = 0.0f ;
float uv_top = 0.0f ;
float uv_right = ( float ) m_original_width / ( float ) m_width ;
float uv_bottom = ( float ) m_original_height / ( float ) m_height ;
GLTexture :: Quad_UVs uvs ;
uvs . left_top = { uv_left , uv_top };
uvs . left_bottom = { uv_left , uv_bottom };
uvs . right_bottom = { uv_right , uv_bottom };
uvs . right_top = { uv_right , uv_top };
GLTexture :: render_sub_texture ( m_id , left , right , bottom , top , uvs );
:: glPopMatrix ();
:: glEnable ( GL_DEPTH_TEST );
}
}
2018-07-19 13:18:19 +02:00
const unsigned char GLCanvas3D :: LegendTexture :: Squares_Border_Color [ 3 ] = { 64 , 64 , 64 };
const unsigned char GLCanvas3D :: LegendTexture :: Background_Color [ 3 ] = { 9 , 91 , 134 };
const unsigned char GLCanvas3D :: LegendTexture :: Opacity = 255 ;
2018-07-31 14:32:59 +02:00
GLCanvas3D :: LegendTexture :: LegendTexture ()
: GUI :: GLTexture ()
, m_original_width ( 0 )
, m_original_height ( 0 )
{
}
2018-07-19 13:18:19 +02:00
bool GLCanvas3D :: LegendTexture :: generate ( const GCodePreviewData & preview_data , const std :: vector < float >& tool_colors )
{
reset ();
// collects items to render
auto title = _ ( preview_data . get_legend_title ());
const GCodePreviewData :: LegendItemsList & items = preview_data . get_legend_items ( tool_colors );
unsigned int items_count = ( unsigned int ) items . size ();
if ( items_count == 0 )
// nothing to render, return
return false ;
wxMemoryDC memDC ;
// select default font
memDC . SetFont ( wxSystemSettings :: GetFont ( wxSYS_DEFAULT_GUI_FONT ));
// calculates texture size
wxCoord w , h ;
memDC . GetTextExtent ( title , & w , & h );
int title_width = ( int ) w ;
int title_height = ( int ) h ;
int max_text_width = 0 ;
int max_text_height = 0 ;
for ( const GCodePreviewData :: LegendItem & item : items )
{
memDC . GetTextExtent ( GUI :: from_u8 ( item . text ), & w , & h );
max_text_width = std :: max ( max_text_width , ( int ) w );
max_text_height = std :: max ( max_text_height , ( int ) h );
}
2018-07-31 14:32:59 +02:00
m_original_width = std :: max ( 2 * Px_Border + title_width , 2 * ( Px_Border + Px_Square_Contour ) + Px_Square + Px_Text_Offset + max_text_width );
m_original_height = 2 * ( Px_Border + Px_Square_Contour ) + title_height + Px_Title_Offset + items_count * Px_Square ;
2018-07-19 13:18:19 +02:00
if ( items_count > 1 )
2018-07-31 14:32:59 +02:00
m_original_height += ( items_count - 1 ) * Px_Square_Contour ;
2018-09-25 11:53:05 +02:00
int pow_of_two_size = ( int ) next_highest_power_of_2 ( std :: max < uint32_t > ( m_original_width , m_original_height ));
2018-07-31 14:32:59 +02:00
m_width = pow_of_two_size ;
m_height = pow_of_two_size ;
2018-07-19 13:18:19 +02:00
// generates bitmap
wxBitmap bitmap ( m_width , m_height );
#if defined(__APPLE__) || defined(_MSC_VER)
bitmap . UseAlpha ();
#endif
memDC . SelectObject ( bitmap );
memDC . SetBackground ( wxBrush ( wxColour ( Background_Color [ 0 ], Background_Color [ 1 ], Background_Color [ 2 ])));
memDC . Clear ();
memDC . SetTextForeground ( * wxWHITE );
// draw title
int title_x = Px_Border ;
int title_y = Px_Border ;
memDC . DrawText ( title , title_x , title_y );
// draw icons contours as background
int squares_contour_x = Px_Border ;
int squares_contour_y = Px_Border + title_height + Px_Title_Offset ;
int squares_contour_width = Px_Square + 2 * Px_Square_Contour ;
int squares_contour_height = items_count * Px_Square + 2 * Px_Square_Contour ;
if ( items_count > 1 )
squares_contour_height += ( items_count - 1 ) * Px_Square_Contour ;
wxColour color ( Squares_Border_Color [ 0 ], Squares_Border_Color [ 1 ], Squares_Border_Color [ 2 ]);
wxPen pen ( color );
wxBrush brush ( color );
memDC . SetPen ( pen );
memDC . SetBrush ( brush );
memDC . DrawRectangle ( wxRect ( squares_contour_x , squares_contour_y , squares_contour_width , squares_contour_height ));
// draw items (colored icon + text)
int icon_x = squares_contour_x + Px_Square_Contour ;
int icon_x_inner = icon_x + 1 ;
int icon_y = squares_contour_y + Px_Square_Contour ;
int icon_y_step = Px_Square + Px_Square_Contour ;
int text_x = icon_x + Px_Square + Px_Text_Offset ;
int text_y_offset = ( Px_Square - max_text_height ) / 2 ;
int px_inner_square = Px_Square - 2 ;
for ( const GCodePreviewData :: LegendItem & item : items )
{
// draw darker icon perimeter
const std :: vector < unsigned char >& item_color_bytes = item . color . as_bytes ();
wxImage :: HSVValue dark_hsv = wxImage :: RGBtoHSV ( wxImage :: RGBValue ( item_color_bytes [ 0 ], item_color_bytes [ 1 ], item_color_bytes [ 2 ]));
dark_hsv . value *= 0.75 ;
wxImage :: RGBValue dark_rgb = wxImage :: HSVtoRGB ( dark_hsv );
color . Set ( dark_rgb . red , dark_rgb . green , dark_rgb . blue , item_color_bytes [ 3 ]);
pen . SetColour ( color );
brush . SetColour ( color );
memDC . SetPen ( pen );
memDC . SetBrush ( brush );
memDC . DrawRectangle ( wxRect ( icon_x , icon_y , Px_Square , Px_Square ));
// draw icon interior
color . Set ( item_color_bytes [ 0 ], item_color_bytes [ 1 ], item_color_bytes [ 2 ], item_color_bytes [ 3 ]);
pen . SetColour ( color );
brush . SetColour ( color );
memDC . SetPen ( pen );
memDC . SetBrush ( brush );
memDC . DrawRectangle ( wxRect ( icon_x_inner , icon_y + 1 , px_inner_square , px_inner_square ));
// draw text
memDC . DrawText ( GUI :: from_u8 ( item . text ), text_x , icon_y + text_y_offset );
// update y
icon_y += icon_y_step ;
}
memDC . SelectObject ( wxNullBitmap );
// Convert the bitmap into a linear data ready to be loaded into the GPU.
wxImage image = bitmap . ConvertToImage ();
image . SetMaskColour ( Background_Color [ 0 ], Background_Color [ 1 ], Background_Color [ 2 ]);
// prepare buffer
std :: vector < unsigned char > data ( 4 * m_width * m_height , 0 );
for ( int h = 0 ; h < m_height ; ++ h )
{
int hh = h * m_width ;
unsigned char * px_ptr = data . data () + 4 * hh ;
for ( int w = 0 ; w < m_width ; ++ w )
{
* px_ptr ++ = image . GetRed ( w , h );
* px_ptr ++ = image . GetGreen ( w , h );
* px_ptr ++ = image . GetBlue ( w , h );
* px_ptr ++ = image . IsTransparent ( w , h ) ? 0 : Opacity ;
}
}
// sends buffer to gpu
:: glPixelStorei ( GL_UNPACK_ALIGNMENT , 1 );
:: glGenTextures ( 1 , & m_id );
:: glBindTexture ( GL_TEXTURE_2D , ( GLuint ) m_id );
:: glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , ( GLsizei ) m_width , ( GLsizei ) m_height , 0 , GL_RGBA , GL_UNSIGNED_BYTE , ( const void * ) data . data ());
:: glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR );
:: glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR );
:: glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAX_LEVEL , 1 );
:: glBindTexture ( GL_TEXTURE_2D , 0 );
return true ;
}
2018-07-31 14:32:59 +02:00
void GLCanvas3D :: LegendTexture :: render ( const GLCanvas3D & canvas ) const
{
if (( m_id > 0 ) && ( m_original_width > 0 ) && ( m_original_height > 0 ) && ( m_width > 0 ) && ( m_height > 0 ))
{
:: glDisable ( GL_DEPTH_TEST );
:: glPushMatrix ();
:: glLoadIdentity ();
const Size & cnv_size = canvas . get_canvas_size ();
float zoom = canvas . get_camera_zoom ();
float inv_zoom = ( zoom != 0.0f ) ? 1.0f / zoom : 0.0f ;
float left = ( - 0.5f * ( float ) cnv_size . get_width ()) * inv_zoom ;
float top = ( 0.5f * ( float ) cnv_size . get_height ()) * inv_zoom ;
float right = left + ( float ) m_original_width * inv_zoom ;
float bottom = top - ( float ) m_original_height * inv_zoom ;
float uv_left = 0.0f ;
float uv_top = 0.0f ;
float uv_right = ( float ) m_original_width / ( float ) m_width ;
float uv_bottom = ( float ) m_original_height / ( float ) m_height ;
GLTexture :: Quad_UVs uvs ;
uvs . left_top = { uv_left , uv_top };
uvs . left_bottom = { uv_left , uv_bottom };
uvs . right_bottom = { uv_right , uv_bottom };
uvs . right_top = { uv_right , uv_top };
GLTexture :: render_sub_texture ( m_id , left , right , bottom , top , uvs );
:: glPopMatrix ();
:: glEnable ( GL_DEPTH_TEST );
}
}
2018-10-03 11:34:39 +02:00
wxDEFINE_EVENT ( EVT_GLCANVAS_OBJECT_SELECT , ObjectSelectEvent );
wxDEFINE_EVENT ( EVT_GLCANVAS_VIEWPORT_CHANGED , SimpleEvent );
wxDEFINE_EVENT ( EVT_GLCANVAS_DOUBLE_CLICK , SimpleEvent );
wxDEFINE_EVENT ( EVT_GLCANVAS_RIGHT_CLICK , Vec2dEvent );
wxDEFINE_EVENT ( EVT_GLCANVAS_MODEL_UPDATE , SimpleEvent );
wxDEFINE_EVENT ( EVT_GLCANVAS_REMOVE_OBJECT , SimpleEvent );
wxDEFINE_EVENT ( EVT_GLCANVAS_ARRANGE , SimpleEvent );
wxDEFINE_EVENT ( EVT_GLCANVAS_ROTATE_OBJECT , Event < int > );
wxDEFINE_EVENT ( EVT_GLCANVAS_SCALE_UNIFORMLY , SimpleEvent );
wxDEFINE_EVENT ( EVT_GLCANVAS_INCREASE_OBJECTS , Event < int > );
2018-10-09 17:14:59 +02:00
wxDEFINE_EVENT ( EVT_GLCANVAS_INSTANCE_MOVED , SimpleEvent );
2018-10-03 11:34:39 +02:00
wxDEFINE_EVENT ( EVT_GLCANVAS_WIPETOWER_MOVED , Vec3dEvent );
wxDEFINE_EVENT ( EVT_GLCANVAS_ENABLE_ACTION_BUTTONS , Event < bool > );
wxDEFINE_EVENT ( EVT_GLCANVAS_UPDATE_GEOMETRY , Vec3dsEvent < 2 > );
2018-10-09 15:56:34 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-10-12 15:43:29 +02:00
wxDEFINE_EVENT ( EVT_GIZMO_SCALE , Vec3dEvent );
2018-10-03 11:34:39 +02:00
wxDEFINE_EVENT ( EVT_GIZMO_ROTATE , Vec3dEvent );
wxDEFINE_EVENT ( EVT_GIZMO_FLATTEN , Vec3dEvent );
2018-10-16 13:49:40 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-10-03 11:34:39 +02:00
2018-06-27 11:31:11 +02:00
GLCanvas3D :: GLCanvas3D ( wxGLCanvas * canvas )
2018-05-09 10:47:04 +02:00
: m_canvas ( canvas )
2018-06-27 11:31:11 +02:00
, m_context ( nullptr )
2018-05-30 15:18:45 +02:00
, m_timer ( nullptr )
2018-07-27 14:38:19 +02:00
, m_toolbar ( * this )
2018-05-23 11:14:49 +02:00
, m_config ( nullptr )
2018-05-28 15:23:01 +02:00
, m_print ( nullptr )
2018-06-07 11:18:28 +02:00
, m_model ( nullptr )
2018-05-09 10:47:04 +02:00
, m_dirty ( true )
2018-06-05 12:24:26 +02:00
, m_initialized ( false )
2018-06-04 12:26:39 +02:00
, m_use_VBOs ( false )
2018-06-05 10:56:55 +02:00
, m_force_zoom_to_bed_enabled ( false )
2018-05-14 14:14:19 +02:00
, m_apply_zoom_to_volumes_filter ( false )
2018-05-23 15:35:11 +02:00
, m_hover_volume_id ( - 1 )
2018-07-27 12:08:33 +02:00
, m_toolbar_action_running ( false )
2018-05-21 14:40:09 +02:00
, m_warning_texture_enabled ( false )
2018-05-21 14:57:43 +02:00
, m_legend_texture_enabled ( false )
2018-05-22 09:02:42 +02:00
, m_picking_enabled ( false )
2018-05-31 13:51:50 +02:00
, m_moving_enabled ( false )
2018-05-25 14:05:08 +02:00
, m_shader_enabled ( false )
2018-07-27 09:38:39 +02:00
, m_dynamic_background_enabled ( false )
2018-05-23 15:35:11 +02:00
, m_multisample_allowed ( false )
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
, m_regenerate_volumes ( true )
#endif // ENABLE_EXTENDED_SELECTION
2018-06-06 10:16:58 +02:00
, m_color_by ( "volume" )
2018-10-08 14:02:12 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-06-06 10:16:58 +02:00
, m_select_by ( "object" )
, m_drag_by ( "instance" )
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-06-08 09:40:00 +02:00
, m_reload_delayed ( false )
2018-05-09 10:47:04 +02:00
{
2018-05-30 15:18:45 +02:00
if ( m_canvas != nullptr )
2018-06-27 11:31:11 +02:00
{
2018-10-04 10:41:11 +02:00
#if !ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-27 11:31:11 +02:00
m_context = new wxGLContext ( m_canvas );
2018-10-04 10:41:11 +02:00
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
2018-05-30 15:18:45 +02:00
m_timer = new wxTimer ( m_canvas );
2018-06-27 11:31:11 +02:00
}
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
m_selection . set_volumes ( & m_volumes . volumes );
#endif // ENABLE_EXTENDED_SELECTION
2018-05-09 10:47:04 +02:00
}
2018-05-15 09:50:01 +02:00
GLCanvas3D ::~ GLCanvas3D ()
{
2018-06-11 15:13:13 +02:00
reset_volumes ();
2018-06-06 14:19:28 +02:00
2018-05-30 15:18:45 +02:00
if ( m_timer != nullptr )
{
delete m_timer ;
m_timer = nullptr ;
}
2018-10-04 10:41:11 +02:00
#if !ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-27 11:31:11 +02:00
if ( m_context != nullptr )
{
delete m_context ;
m_context = nullptr ;
}
2018-10-04 10:41:11 +02:00
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
2018-10-03 11:34:39 +02:00
}
2018-06-27 11:31:11 +02:00
2018-10-03 14:07:10 +02:00
void GLCanvas3D :: post_event ( wxEvent && event )
2018-10-03 11:34:39 +02:00
{
2018-10-03 14:07:10 +02:00
event . SetEventObject ( m_canvas );
2018-10-03 11:34:39 +02:00
wxPostEvent ( m_canvas , event );
}
void GLCanvas3D :: viewport_changed ()
{
post_event ( SimpleEvent ( EVT_GLCANVAS_VIEWPORT_CHANGED ));
2018-05-23 09:57:44 +02:00
}
2018-05-25 14:05:08 +02:00
bool GLCanvas3D :: init ( bool useVBOs , bool use_legacy_opengl )
2018-05-23 09:57:44 +02:00
{
2018-06-05 12:24:26 +02:00
if ( m_initialized )
return true ;
2018-06-27 11:31:11 +02:00
if (( m_canvas == nullptr ) || ( m_context == nullptr ))
return false ;
2018-05-31 08:44:39 +02:00
:: glClearColor ( 1.0f , 1.0f , 1.0f , 1.0f );
2018-05-23 15:35:11 +02:00
:: glClearDepth ( 1.0f );
2018-06-01 15:54:41 +02:00
2018-05-31 08:44:39 +02:00
:: glDepthFunc ( GL_LESS );
:: glEnable ( GL_DEPTH_TEST );
2018-05-23 15:35:11 +02:00
:: 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 );
// ambient lighting
GLfloat ambient [ 4 ] = { 0.3f , 0.3f , 0.3f , 1.0f };
:: glLightModelfv ( GL_LIGHT_MODEL_AMBIENT , ambient );
:: glEnable ( GL_LIGHT0 );
:: glEnable ( GL_LIGHT1 );
// light from camera
2018-06-11 10:46:32 +02:00
GLfloat specular_cam [ 4 ] = { 0.3f , 0.3f , 0.3f , 1.0f };
:: glLightfv ( GL_LIGHT1 , GL_SPECULAR , specular_cam );
GLfloat diffuse_cam [ 4 ] = { 0.2f , 0.2f , 0.2f , 1.0f };
:: glLightfv ( GL_LIGHT1 , GL_DIFFUSE , diffuse_cam );
2018-05-23 15:35:11 +02:00
2018-05-31 08:44:39 +02:00
// light from above
2018-06-11 10:46:32 +02:00
GLfloat specular_top [ 4 ] = { 0.2f , 0.2f , 0.2f , 1.0f };
:: glLightfv ( GL_LIGHT0 , GL_SPECULAR , specular_top );
GLfloat diffuse_top [ 4 ] = { 0.5f , 0.5f , 0.5f , 1.0f };
:: glLightfv ( GL_LIGHT0 , GL_DIFFUSE , diffuse_top );
2018-05-31 08:44:39 +02:00
2018-05-23 15:35:11 +02:00
// Enables Smooth Color Shading; try GL_FLAT for (lack of) fun.
:: glShadeModel ( GL_SMOOTH );
// A handy trick -- have surface material mirror the color.
:: glColorMaterial ( GL_FRONT_AND_BACK , GL_AMBIENT_AND_DIFFUSE );
:: glEnable ( GL_COLOR_MATERIAL );
2018-06-01 15:54:41 +02:00
if ( m_multisample_allowed )
2018-05-23 15:35:11 +02:00
:: glEnable ( GL_MULTISAMPLE );
2018-05-23 09:57:44 +02:00
if ( useVBOs && ! m_shader . init ( "gouraud.vs" , "gouraud.fs" ))
return false ;
2018-05-25 14:05:08 +02:00
if ( useVBOs && ! m_layers_editing . init ( "variable_layer_height.vs" , "variable_layer_height.fs" ))
return false ;
2018-06-04 12:26:39 +02:00
m_use_VBOs = useVBOs ;
2018-06-04 13:15:28 +02:00
m_layers_editing . set_use_legacy_opengl ( use_legacy_opengl );
2018-05-25 14:05:08 +02:00
2018-06-05 12:24:26 +02:00
// on linux the gl context is not valid until the canvas is not shown on screen
// we defer the geometry finalization of volumes until the first call to render()
2018-06-11 15:13:13 +02:00
if ( ! m_volumes . empty ())
m_volumes . finalize_geometry ( m_use_VBOs );
2018-06-05 12:24:26 +02:00
2018-08-24 14:11:41 +02:00
if ( m_gizmos . is_enabled () && ! m_gizmos . init ( * this ))
2018-06-13 09:12:16 +02:00
return false ;
2018-07-23 13:49:48 +02:00
if ( ! _init_toolbar ())
return false ;
2018-06-05 12:24:26 +02:00
m_initialized = true ;
2018-05-23 09:57:44 +02:00
return true ;
2018-05-15 09:50:01 +02:00
}
2018-10-04 10:41:11 +02:00
#if !ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-27 11:31:11 +02:00
bool GLCanvas3D :: set_current ()
2018-05-09 10:47:04 +02:00
{
2018-06-27 11:31:11 +02:00
if (( m_canvas != nullptr ) && ( m_context != nullptr ))
2018-06-25 15:17:13 +02:00
return m_canvas -> SetCurrent ( * m_context );
2018-05-18 14:08:59 +02:00
return false ;
2018-05-09 10:47:04 +02:00
}
2018-10-04 10:41:11 +02:00
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
2018-05-09 10:47:04 +02:00
2018-06-22 16:06:37 +02:00
void GLCanvas3D :: set_as_dirty ()
{
m_dirty = true ;
}
2018-06-11 13:48:02 +02:00
unsigned int GLCanvas3D :: get_volumes_count () const
2018-05-14 14:14:19 +02:00
{
2018-06-11 15:13:13 +02:00
return ( unsigned int ) m_volumes . volumes . size ();
2018-05-14 14:14:19 +02:00
}
2018-05-18 14:08:59 +02:00
void GLCanvas3D :: reset_volumes ()
{
2018-06-25 15:17:13 +02:00
if ( ! m_volumes . empty ())
2018-05-18 14:08:59 +02:00
{
2018-10-04 10:41:11 +02:00
#if !ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-25 15:17:13 +02:00
// ensures this canvas is current
2018-06-27 11:31:11 +02:00
if ( ! set_current ())
2018-06-25 15:17:13 +02:00
return ;
2018-10-04 10:41:11 +02:00
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-25 15:17:13 +02:00
2018-06-11 15:13:13 +02:00
m_volumes . release_geometry ();
m_volumes . clear ();
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
m_selection . clear ();
#endif // ENABLE_EXTENDED_SELECTION
2018-06-01 15:54:41 +02:00
m_dirty = true ;
2018-05-18 14:08:59 +02:00
}
2018-07-27 08:49:58 +02:00
enable_warning_texture ( false );
_reset_warning_texture ();
2018-05-18 14:08:59 +02:00
}
2018-10-08 15:17:36 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-05-25 09:03:55 +02:00
void GLCanvas3D :: deselect_volumes ()
{
2018-06-11 15:13:13 +02:00
for ( GLVolume * vol : m_volumes . volumes )
2018-05-25 09:03:55 +02:00
{
2018-06-11 15:13:13 +02:00
if ( vol != nullptr )
vol -> selected = false ;
2018-05-25 09:03:55 +02:00
}
}
void GLCanvas3D :: select_volume ( unsigned int id )
{
2018-06-11 15:13:13 +02:00
if ( id < ( unsigned int ) m_volumes . volumes . size ())
2018-05-25 09:03:55 +02:00
{
2018-06-11 15:13:13 +02:00
GLVolume * vol = m_volumes . volumes [ id ];
2018-05-25 09:03:55 +02:00
if ( vol != nullptr )
vol -> selected = true ;
}
}
2018-06-08 09:40:00 +02:00
void GLCanvas3D :: update_volumes_selection ( const std :: vector < int >& selections )
{
if ( m_model == nullptr )
return ;
2018-09-06 16:10:31 +02:00
if ( selections . empty ())
return ;
2018-06-08 09:40:00 +02:00
for ( unsigned int obj_idx = 0 ; obj_idx < ( unsigned int ) m_model -> objects . size (); ++ obj_idx )
{
2018-06-11 15:49:04 +02:00
if (( selections [ obj_idx ] == 1 ) && ( obj_idx < ( unsigned int ) m_objects_volumes_idxs . size ()))
2018-06-08 09:40:00 +02:00
{
const std :: vector < int >& volume_idxs = m_objects_volumes_idxs [ obj_idx ];
for ( int v : volume_idxs )
{
select_volume ( v );
}
}
}
}
2018-10-08 15:17:36 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-06-08 09:40:00 +02:00
2018-07-23 10:16:56 +02:00
int GLCanvas3D :: check_volumes_outside_state ( const DynamicPrintConfig * config ) const
2018-06-11 13:48:02 +02:00
{
2018-07-23 10:16:56 +02:00
ModelInstance :: EPrintVolumeState state ;
m_volumes . check_outside_state ( config , & state );
return ( int ) state ;
2018-06-11 13:48:02 +02:00
}
bool GLCanvas3D :: move_volume_up ( unsigned int id )
{
2018-06-11 15:13:13 +02:00
if (( id > 0 ) && ( id < ( unsigned int ) m_volumes . volumes . size ()))
2018-06-11 13:48:02 +02:00
{
2018-06-11 15:13:13 +02:00
std :: swap ( m_volumes . volumes [ id - 1 ], m_volumes . volumes [ id ]);
std :: swap ( m_volumes . volumes [ id - 1 ] -> composite_id , m_volumes . volumes [ id ] -> composite_id );
2018-10-08 14:02:12 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-06-11 15:13:13 +02:00
std :: swap ( m_volumes . volumes [ id - 1 ] -> select_group_id , m_volumes . volumes [ id ] -> select_group_id );
std :: swap ( m_volumes . volumes [ id - 1 ] -> drag_group_id , m_volumes . volumes [ id ] -> drag_group_id );
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-06-11 15:13:13 +02:00
return true ;
2018-06-11 13:48:02 +02:00
}
2018-06-11 15:13:13 +02:00
2018-06-11 13:48:02 +02:00
return false ;
}
bool GLCanvas3D :: move_volume_down ( unsigned int id )
{
2018-06-11 15:13:13 +02:00
if (( id >= 0 ) && ( id + 1 < ( unsigned int ) m_volumes . volumes . size ()))
2018-06-11 13:48:02 +02:00
{
2018-06-11 15:13:13 +02:00
std :: swap ( m_volumes . volumes [ id + 1 ], m_volumes . volumes [ id ]);
std :: swap ( m_volumes . volumes [ id + 1 ] -> composite_id , m_volumes . volumes [ id ] -> composite_id );
2018-10-08 14:02:12 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-06-11 15:13:13 +02:00
std :: swap ( m_volumes . volumes [ id + 1 ] -> select_group_id , m_volumes . volumes [ id ] -> select_group_id );
std :: swap ( m_volumes . volumes [ id + 1 ] -> drag_group_id , m_volumes . volumes [ id ] -> drag_group_id );
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-06-11 15:13:13 +02:00
return true ;
2018-06-11 13:48:02 +02:00
}
return false ;
}
2018-10-08 14:02:12 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-06-08 09:40:00 +02:00
void GLCanvas3D :: set_objects_selections ( const std :: vector < int >& selections )
{
m_objects_selections = selections ;
}
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-06-08 09:40:00 +02:00
2018-05-23 11:14:49 +02:00
void GLCanvas3D :: set_config ( DynamicPrintConfig * config )
{
m_config = config ;
}
2018-05-28 15:23:01 +02:00
void GLCanvas3D :: set_print ( Print * print )
{
m_print = print ;
}
2018-06-07 11:18:28 +02:00
void GLCanvas3D :: set_model ( Model * model )
{
m_model = model ;
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
m_selection . set_model ( m_model );
#endif // ENABLE_EXTENDED_SELECTION
2018-06-07 11:18:28 +02:00
}
2018-05-14 14:14:19 +02:00
void GLCanvas3D :: set_bed_shape ( const Pointfs & shape )
{
2018-08-05 22:52:38 +02:00
bool new_shape = m_bed . set_shape ( shape );
2018-05-18 13:02:47 +02:00
// Set the origin and size for painting of the coordinate system axes.
2018-08-24 10:20:00 +02:00
m_axes . origin = Vec3d ( 0.0 , 0.0 , ( double ) GROUND_Z );
2018-06-01 15:54:41 +02:00
set_axes_length ( 0.3f * ( float ) m_bed . get_bounding_box (). max_size ());
2018-06-27 12:05:23 +02:00
2018-07-30 13:57:05 +02:00
if ( new_shape )
{
// forces the selection of the proper camera target
if ( m_volumes . volumes . empty ())
zoom_to_bed ();
else
zoom_to_volumes ();
}
2018-07-30 10:35:08 +02:00
2018-06-27 12:05:23 +02:00
m_dirty = true ;
2018-05-14 14:14:19 +02:00
}
2018-05-15 15:38:25 +02:00
void GLCanvas3D :: set_auto_bed_shape ()
{
// draw a default square bed around object center
const BoundingBoxf3 & bbox = volumes_bounding_box ();
2018-08-24 10:20:00 +02:00
double max_size = bbox . max_size ();
2018-08-21 17:43:05 +02:00
const Vec3d center = bbox . center ();
2018-05-15 15:38:25 +02:00
Pointfs bed_shape ;
bed_shape . reserve ( 4 );
2018-08-17 15:53:43 +02:00
bed_shape . emplace_back ( center ( 0 ) - max_size , center ( 1 ) - max_size );
bed_shape . emplace_back ( center ( 0 ) + max_size , center ( 1 ) - max_size );
bed_shape . emplace_back ( center ( 0 ) + max_size , center ( 1 ) + max_size );
bed_shape . emplace_back ( center ( 0 ) - max_size , center ( 1 ) + max_size );
2018-05-15 15:38:25 +02:00
set_bed_shape ( bed_shape );
// Set the origin for painting of the coordinate system axes.
2018-08-24 10:20:00 +02:00
m_axes . origin = Vec3d ( center ( 0 ), center ( 1 ), ( double ) GROUND_Z );
2018-05-18 13:02:47 +02:00
}
void GLCanvas3D :: set_axes_length ( float length )
{
2018-06-01 15:54:41 +02:00
m_axes . length = length ;
2018-05-15 11:07:32 +02:00
}
2018-05-18 11:05:48 +02:00
void GLCanvas3D :: set_cutting_plane ( float z , const ExPolygons & polygons )
{
m_cutting_plane . set ( z , polygons );
}
2018-06-06 12:36:52 +02:00
void GLCanvas3D :: set_color_by ( const std :: string & value )
{
m_color_by = value ;
}
2018-10-08 14:02:12 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-06-06 12:36:52 +02:00
void GLCanvas3D :: set_select_by ( const std :: string & value )
{
m_select_by = value ;
2018-09-06 16:10:31 +02:00
m_volumes . set_select_by ( value );
2018-06-06 12:36:52 +02:00
}
void GLCanvas3D :: set_drag_by ( const std :: string & value )
{
m_drag_by = value ;
2018-09-11 09:00:28 +02:00
m_volumes . set_drag_by ( value );
2018-06-06 12:36:52 +02:00
}
2018-09-06 16:10:31 +02:00
const std :: string & GLCanvas3D :: get_select_by () const
{
return m_select_by ;
}
2018-09-11 09:00:28 +02:00
const std :: string & GLCanvas3D :: get_drag_by () const
{
return m_drag_by ;
}
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-09-11 09:00:28 +02:00
2018-05-09 10:47:04 +02:00
float GLCanvas3D :: get_camera_zoom () const
{
2018-06-01 15:54:41 +02:00
return m_camera . zoom ;
2018-05-14 14:14:19 +02:00
}
BoundingBoxf3 GLCanvas3D :: volumes_bounding_box () const
{
BoundingBoxf3 bb ;
2018-06-11 15:13:13 +02:00
for ( const GLVolume * volume : m_volumes . volumes )
2018-05-14 14:14:19 +02:00
{
2018-06-11 15:13:13 +02:00
if ( ! m_apply_zoom_to_volumes_filter || (( volume != nullptr ) && volume -> zoom_to_volumes ))
bb . merge ( volume -> transformed_bounding_box ());
2018-05-14 14:14:19 +02:00
}
return bb ;
}
2018-05-18 14:08:59 +02:00
bool GLCanvas3D :: is_layers_editing_enabled () const
{
return m_layers_editing . is_enabled ();
}
2018-05-25 16:28:24 +02:00
bool GLCanvas3D :: is_layers_editing_allowed () const
{
return m_layers_editing . is_allowed ();
}
2018-06-04 14:28:59 +02:00
bool GLCanvas3D :: is_shader_enabled () const
{
return m_shader_enabled ;
}
2018-06-08 09:40:00 +02:00
bool GLCanvas3D :: is_reload_delayed () const
{
return m_reload_delayed ;
}
2018-05-25 14:05:08 +02:00
void GLCanvas3D :: enable_layers_editing ( bool enable )
{
m_layers_editing . set_enabled ( enable );
}
2018-05-21 14:40:09 +02:00
void GLCanvas3D :: enable_warning_texture ( bool enable )
{
m_warning_texture_enabled = enable ;
}
2018-05-21 14:57:43 +02:00
void GLCanvas3D :: enable_legend_texture ( bool enable )
{
m_legend_texture_enabled = enable ;
}
2018-05-22 09:02:42 +02:00
void GLCanvas3D :: enable_picking ( bool enable )
{
m_picking_enabled = enable ;
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
m_selection . set_mode ( Selection :: Instance );
#endif // ENABLE_EXTENDED_SELECTION
2018-05-22 09:02:42 +02:00
}
2018-05-31 13:51:50 +02:00
void GLCanvas3D :: enable_moving ( bool enable )
{
m_moving_enabled = enable ;
}
2018-06-13 09:12:16 +02:00
void GLCanvas3D :: enable_gizmos ( bool enable )
{
m_gizmos . set_enabled ( enable );
}
2018-07-23 13:49:48 +02:00
void GLCanvas3D :: enable_toolbar ( bool enable )
{
m_toolbar . set_enabled ( enable );
}
2018-05-23 09:57:44 +02:00
void GLCanvas3D :: enable_shader ( bool enable )
{
2018-05-25 14:05:08 +02:00
m_shader_enabled = enable ;
2018-05-23 09:57:44 +02:00
}
2018-06-05 10:56:55 +02:00
void GLCanvas3D :: enable_force_zoom_to_bed ( bool enable )
{
m_force_zoom_to_bed_enabled = enable ;
}
2018-07-27 09:38:39 +02:00
void GLCanvas3D :: enable_dynamic_background ( bool enable )
{
m_dynamic_background_enabled = enable ;
}
2018-05-23 15:35:11 +02:00
void GLCanvas3D :: allow_multisample ( bool allow )
{
m_multisample_allowed = allow ;
}
2018-07-23 13:49:48 +02:00
void GLCanvas3D :: enable_toolbar_item ( const std :: string & name , bool enable )
{
if ( enable )
m_toolbar . enable_item ( name );
else
m_toolbar . disable_item ( name );
}
2018-07-27 12:08:33 +02:00
bool GLCanvas3D :: is_toolbar_item_pressed ( const std :: string & name ) const
{
return m_toolbar . is_item_pressed ( name );
}
2018-07-23 13:49:48 +02:00
2018-05-15 10:32:38 +02:00
void GLCanvas3D :: zoom_to_bed ()
{
2018-06-01 15:54:41 +02:00
_zoom_to_bounding_box ( m_bed . get_bounding_box ());
2018-05-15 10:32:38 +02:00
}
void GLCanvas3D :: zoom_to_volumes ()
{
m_apply_zoom_to_volumes_filter = true ;
_zoom_to_bounding_box ( volumes_bounding_box ());
m_apply_zoom_to_volumes_filter = false ;
}
2018-05-15 11:30:11 +02:00
void GLCanvas3D :: select_view ( const std :: string & direction )
{
const float * dir_vec = nullptr ;
if ( direction == "iso" )
dir_vec = VIEW_DEFAULT ;
else if ( direction == "left" )
dir_vec = VIEW_LEFT ;
else if ( direction == "right" )
dir_vec = VIEW_RIGHT ;
else if ( direction == "top" )
dir_vec = VIEW_TOP ;
else if ( direction == "bottom" )
dir_vec = VIEW_BOTTOM ;
else if ( direction == "front" )
dir_vec = VIEW_FRONT ;
else if ( direction == "rear" )
dir_vec = VIEW_REAR ;
if (( dir_vec != nullptr ) && ! empty ( volumes_bounding_box ()))
{
2018-06-01 15:54:41 +02:00
m_camera . phi = dir_vec [ 0 ];
2018-05-15 11:30:11 +02:00
m_camera . set_theta ( dir_vec [ 1 ]);
2018-10-03 11:34:39 +02:00
viewport_changed ();
2018-05-15 11:30:11 +02:00
if ( m_canvas != nullptr )
m_canvas -> Refresh ();
}
}
2018-05-29 15:36:09 +02:00
void GLCanvas3D :: set_viewport_from_scene ( const GLCanvas3D & other )
{
2018-06-01 15:54:41 +02:00
m_camera . phi = other . m_camera . phi ;
m_camera . set_theta ( other . m_camera . get_theta ());
m_camera . target = other . m_camera . target ;
m_camera . zoom = other . m_camera . zoom ;
m_dirty = true ;
2018-05-29 15:36:09 +02:00
}
2018-05-29 15:07:06 +02:00
void GLCanvas3D :: update_volumes_colors_by_extruder ()
{
2018-06-11 15:13:13 +02:00
if ( m_config != nullptr )
m_volumes . update_colors_by_extruder ( m_config );
2018-05-29 15:07:06 +02:00
}
2018-06-19 09:46:26 +02:00
void GLCanvas3D :: update_gizmos_data ()
{
2018-07-19 16:06:46 +02:00
if ( ! m_gizmos . is_enabled ())
2018-06-19 09:46:26 +02:00
return ;
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
2018-10-09 15:56:34 +02:00
bool enable_move_z = ! m_selection . is_wipe_tower ();
m_gizmos . enable_grabber ( Gizmos :: Move , 2 , enable_move_z );
2018-10-12 15:43:29 +02:00
bool enable_scale_xyz = m_selection . is_single_full_instance ();
for ( int i = 0 ; i < 6 ; ++ i )
{
m_gizmos . enable_grabber ( Gizmos :: Scale , i , enable_scale_xyz );
}
2018-10-08 14:02:12 +02:00
2018-10-10 13:51:11 +02:00
if ( m_selection . is_single_full_instance ())
2018-10-08 14:02:12 +02:00
{
2018-10-09 15:56:34 +02:00
ModelObject * model_object = m_model -> objects [ m_selection . get_object_idx ()];
ModelInstance * model_instance = model_object -> instances [ m_selection . get_instance_idx ()];
2018-10-08 14:02:12 +02:00
m_gizmos . set_scale ( model_instance -> get_scaling_factor ());
m_gizmos . set_rotation ( model_instance -> get_rotation ());
m_gizmos . set_flattening_data ( model_object );
}
else
{
m_gizmos . set_scale ( Vec3d :: Ones ());
m_gizmos . set_rotation ( Vec3d :: Zero ());
m_gizmos . set_flattening_data ( nullptr );
}
#else
2018-06-19 09:46:26 +02:00
int id = _get_first_selected_object_id ();
if (( id != - 1 ) && ( m_model != nullptr ))
{
ModelObject * model_object = m_model -> objects [ id ];
if ( model_object != nullptr )
{
ModelInstance * model_instance = model_object -> instances [ 0 ];
if ( model_instance != nullptr )
{
2018-09-17 15:12:13 +02:00
m_gizmos . set_position ( model_instance -> get_offset ());
2018-09-24 15:54:09 +02:00
m_gizmos . set_scale ( model_instance -> get_scaling_factor ());
2018-09-20 15:00:40 +02:00
m_gizmos . set_rotation ( model_instance -> get_rotation ());
2018-08-17 15:40:47 +02:00
m_gizmos . set_flattening_data ( model_object );
2018-06-19 09:46:26 +02:00
}
}
}
2018-07-19 11:39:51 +02:00
else
{
2018-09-11 09:00:28 +02:00
m_gizmos . set_position ( Vec3d :: Zero ());
2018-09-24 15:54:09 +02:00
m_gizmos . set_scale ( Vec3d :: Ones ());
2018-09-20 15:00:40 +02:00
m_gizmos . set_rotation ( Vec3d :: Zero ());
2018-08-17 15:40:47 +02:00
m_gizmos . set_flattening_data ( nullptr );
2018-07-19 11:39:51 +02:00
}
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-19 09:46:26 +02:00
}
2018-06-04 12:26:39 +02:00
void GLCanvas3D :: render ()
2018-05-23 15:35:11 +02:00
{
2018-05-29 14:34:45 +02:00
if ( m_canvas == nullptr )
return ;
2018-06-12 09:18:25 +02:00
if ( ! _is_shown_on_screen ())
2018-06-04 12:26:39 +02:00
return ;
2018-06-25 15:17:13 +02:00
// ensures this canvas is current and initialized
2018-10-04 10:41:11 +02:00
#if ENABLE_USE_UNIQUE_GLCONTEXT
if ( ! _set_current () || ! _3DScene :: init ( m_canvas ))
#else
2018-06-27 11:31:11 +02:00
if ( ! set_current () || ! _3DScene :: init ( m_canvas ))
2018-10-04 10:41:11 +02:00
#endif // ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-04 12:26:39 +02:00
return ;
2018-06-05 10:56:55 +02:00
if ( m_force_zoom_to_bed_enabled )
_force_zoom_to_bed ();
2018-06-04 12:26:39 +02:00
2018-05-31 08:44:39 +02:00
_camera_tranform ();
2018-06-04 12:26:39 +02:00
2018-06-11 10:46:32 +02:00
GLfloat position_cam [ 4 ] = { 1.0f , 0.0f , 1.0f , 0.0f };
:: glLightfv ( GL_LIGHT1 , GL_POSITION , position_cam );
GLfloat position_top [ 4 ] = { - 0.5f , - 0.5f , 1.0f , 0.0f };
:: glLightfv ( GL_LIGHT0 , GL_POSITION , position_top );
2018-06-04 12:26:39 +02:00
2018-06-11 11:40:11 +02:00
float theta = m_camera . get_theta ();
bool is_custom_bed = m_bed . is_custom ();
2018-08-24 11:17:53 +02:00
// picking pass
2018-05-29 13:54:34 +02:00
_picking_pass ();
2018-08-21 14:27:36 +02:00
2018-08-24 11:17:53 +02:00
// draw scene
_render_background ();
2018-08-21 14:27:36 +02:00
2018-08-24 11:17:53 +02:00
if ( is_custom_bed ) // untextured bed needs to be rendered before objects
2018-06-11 11:40:11 +02:00
{
_render_bed ( theta );
2018-06-13 13:14:17 +02:00
// disable depth testing so that axes are not covered by ground
_render_axes ( false );
2018-06-11 11:40:11 +02:00
}
2018-06-04 12:26:39 +02:00
_render_objects ();
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
_render_selection ();
#endif // ENABLE_EXTENDED_SELECTION
2018-08-24 11:17:53 +02:00
if ( ! is_custom_bed ) // textured bed needs to be rendered after objects
2018-06-11 11:40:11 +02:00
{
2018-06-13 13:14:17 +02:00
_render_axes ( true );
2018-06-11 11:40:11 +02:00
_render_bed ( theta );
}
2018-08-24 11:17:53 +02:00
2018-08-24 12:06:05 +02:00
_render_current_gizmo ();
2018-05-29 13:54:34 +02:00
_render_cutting_plane ();
2018-08-24 11:17:53 +02:00
// draw overlays
2018-08-21 14:27:36 +02:00
_render_gizmos_overlay ();
2018-05-29 13:54:34 +02:00
_render_warning_texture ();
_render_legend_texture ();
2018-07-23 13:49:48 +02:00
_render_toolbar ();
2018-06-14 12:34:19 +02:00
_render_layer_editing_overlay ();
2018-05-29 14:34:45 +02:00
m_canvas -> SwapBuffers ();
2018-05-21 15:24:52 +02:00
}
2018-06-04 15:42:34 +02:00
std :: vector < double > GLCanvas3D :: get_current_print_zs ( bool active_only ) const
{
2018-06-11 15:13:13 +02:00
return m_volumes . get_current_print_zs ( active_only );
2018-06-04 15:42:34 +02:00
}
void GLCanvas3D :: set_toolpaths_range ( double low , double high )
{
2018-06-11 15:13:13 +02:00
m_volumes . set_range ( low , high );
2018-06-04 15:42:34 +02:00
}
2018-06-06 10:16:58 +02:00
std :: vector < int > GLCanvas3D :: load_object ( const ModelObject & model_object , int obj_idx , std :: vector < int > instance_idxs )
{
if ( instance_idxs . empty ())
{
for ( unsigned int i = 0 ; i < model_object . instances . size (); ++ i )
{
instance_idxs . push_back ( i );
}
}
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
2018-10-08 16:05:55 +02:00
return m_volumes . load_object ( & model_object , obj_idx , instance_idxs , m_color_by , m_use_VBOs && m_initialized );
2018-10-08 14:02:12 +02:00
#else
2018-06-11 15:13:13 +02:00
return m_volumes . load_object ( & model_object , obj_idx , instance_idxs , m_color_by , m_select_by , m_drag_by , m_use_VBOs && m_initialized );
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-06 10:16:58 +02:00
}
2018-06-07 09:22:19 +02:00
std :: vector < int > GLCanvas3D :: load_object ( const Model & model , int obj_idx )
2018-06-06 10:16:58 +02:00
{
if (( 0 <= obj_idx ) && ( obj_idx < ( int ) model . objects . size ()))
{
const ModelObject * model_object = model . objects [ obj_idx ];
if ( model_object != nullptr )
2018-06-07 09:22:19 +02:00
return load_object ( * model_object , obj_idx , std :: vector < int > ());
2018-06-06 10:16:58 +02:00
}
return std :: vector < int > ();
}
2018-09-06 16:10:31 +02:00
int GLCanvas3D :: get_first_volume_id ( int obj_idx ) const
{
for ( int i = 0 ; i < ( int ) m_volumes . volumes . size (); ++ i )
{
if (( m_volumes . volumes [ i ] != nullptr ) && ( m_volumes . volumes [ i ] -> object_idx () == obj_idx ))
return i ;
}
return - 1 ;
}
2018-09-17 15:12:13 +02:00
int GLCanvas3D :: get_in_object_volume_id ( int scene_vol_idx ) const
{
return (( 0 <= scene_vol_idx ) && ( scene_vol_idx < ( int ) m_volumes . volumes . size ())) ? m_volumes . volumes [ scene_vol_idx ] -> volume_idx () : - 1 ;
}
2018-06-08 09:40:00 +02:00
void GLCanvas3D :: reload_scene ( bool force )
{
2018-06-11 15:13:13 +02:00
if (( m_canvas == nullptr ) || ( m_config == nullptr ) || ( m_model == nullptr ))
2018-06-08 09:40:00 +02:00
return ;
2018-10-08 14:02:12 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-06-08 09:40:00 +02:00
reset_volumes ();
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-06-25 15:17:13 +02:00
2018-10-04 10:41:11 +02:00
#if !ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-25 15:17:13 +02:00
// ensures this canvas is current
2018-06-27 11:31:11 +02:00
if ( ! set_current ())
2018-06-25 15:17:13 +02:00
return ;
2018-10-04 10:41:11 +02:00
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-25 15:17:13 +02:00
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
if ( m_regenerate_volumes )
reset_volumes ();
#endif // ENABLE_EXTENDED_SELECTION
2018-06-08 09:40:00 +02:00
set_bed_shape ( dynamic_cast < const ConfigOptionPoints *> ( m_config -> option ( "bed_shape" )) -> values );
if ( ! m_canvas -> IsShown () && ! force )
{
m_reload_delayed = true ;
return ;
}
m_reload_delayed = false ;
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
if ( m_regenerate_volumes )
{
for ( unsigned int obj_idx = 0 ; obj_idx < ( unsigned int ) m_model -> objects . size (); ++ obj_idx )
{
load_object ( * m_model , obj_idx );
}
}
update_gizmos_data ();
#else
2018-06-08 09:40:00 +02:00
m_objects_volumes_idxs . clear ();
for ( unsigned int obj_idx = 0 ; obj_idx < ( unsigned int ) m_model -> objects . size (); ++ obj_idx )
{
m_objects_volumes_idxs . push_back ( load_object ( * m_model , obj_idx ));
}
2018-07-19 13:43:33 +02:00
// 1st call to reset if no objects left
2018-06-19 09:46:26 +02:00
update_gizmos_data ();
2018-06-08 09:40:00 +02:00
update_volumes_selection ( m_objects_selections );
2018-08-28 12:05:16 +02:00
// 2nd call to restore selection, if any
2018-07-19 13:43:33 +02:00
if ( ! m_objects_selections . empty ())
update_gizmos_data ();
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-08 09:40:00 +02:00
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
if ( m_regenerate_volumes )
2018-06-08 09:40:00 +02:00
{
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
if ( m_config -> has ( "nozzle_diameter" ))
2018-06-08 09:40:00 +02:00
{
2018-10-08 14:02:12 +02:00
// Should the wipe tower be visualized ?
unsigned int extruders_count = ( unsigned int ) dynamic_cast < const ConfigOptionFloats *> ( m_config -> option ( "nozzle_diameter" )) -> values . size ();
2018-06-08 09:40:00 +02:00
2018-10-08 14:02:12 +02:00
bool semm = dynamic_cast < const ConfigOptionBool *> ( m_config -> option ( "single_extruder_multi_material" )) -> value ;
bool wt = dynamic_cast < const ConfigOptionBool *> ( m_config -> option ( "wipe_tower" )) -> value ;
bool co = dynamic_cast < const ConfigOptionBool *> ( m_config -> option ( "complete_objects" )) -> value ;
2018-06-08 09:40:00 +02:00
2018-10-08 14:02:12 +02:00
if (( extruders_count > 1 ) && semm && wt && ! co )
{
// Height of a print (Show at least a slab)
double height = std :: max ( m_model -> bounding_box (). max ( 2 ), 10.0 );
2018-07-27 15:56:27 +02:00
2018-10-08 14:02:12 +02:00
float x = dynamic_cast < const ConfigOptionFloat *> ( m_config -> option ( "wipe_tower_x" )) -> value ;
float y = dynamic_cast < const ConfigOptionFloat *> ( m_config -> option ( "wipe_tower_y" )) -> value ;
float w = dynamic_cast < const ConfigOptionFloat *> ( m_config -> option ( "wipe_tower_width" )) -> value ;
float a = dynamic_cast < const ConfigOptionFloat *> ( m_config -> option ( "wipe_tower_rotation_angle" )) -> value ;
float depth = m_print -> get_wipe_tower_depth ();
if ( ! m_print -> is_step_done ( psWipeTower ))
depth = ( 900.f / w ) * ( float )( extruders_count - 1 ) ;
m_volumes . load_wipe_tower_preview ( 1000 , x , y , w , depth , ( float ) height , a , m_use_VBOs && m_initialized , ! m_print -> is_step_done ( psWipeTower ),
m_print -> config (). nozzle_diameter . values [ 0 ] * 1.25f * 4.5f );
}
2018-06-08 09:40:00 +02:00
}
2018-10-08 14:02:12 +02:00
update_volumes_colors_by_extruder ();
#if ENABLE_EXTENDED_SELECTION
}
#endif // ENABLE_EXTENDED_SELECTION
2018-06-08 09:40:00 +02:00
// checks for geometry outside the print volume to render it accordingly
2018-06-11 15:13:13 +02:00
if ( ! m_volumes . empty ())
2018-06-08 09:40:00 +02:00
{
2018-07-18 14:26:42 +02:00
ModelInstance :: EPrintVolumeState state ;
bool contained = m_volumes . check_outside_state ( m_config , & state );
2018-06-08 09:40:00 +02:00
if ( ! contained )
{
enable_warning_texture ( true );
2018-07-19 13:18:19 +02:00
_generate_warning_texture ( L ( "Detected object outside print volume" ));
2018-10-03 11:34:39 +02:00
post_event ( Event < bool > ( EVT_GLCANVAS_ENABLE_ACTION_BUTTONS , state == ModelInstance :: PVS_Fully_Outside ));
2018-06-08 09:40:00 +02:00
}
else
{
enable_warning_texture ( false );
2018-06-11 15:13:13 +02:00
m_volumes . reset_outside_state ();
2018-07-19 13:18:19 +02:00
_reset_warning_texture ();
2018-10-03 11:34:39 +02:00
post_event ( Event < bool > ( EVT_GLCANVAS_ENABLE_ACTION_BUTTONS , ! m_model -> objects . empty ()));
2018-06-08 09:40:00 +02:00
}
}
else
{
enable_warning_texture ( false );
2018-07-19 13:18:19 +02:00
_reset_warning_texture ();
2018-10-03 11:34:39 +02:00
post_event ( Event < bool > ( EVT_GLCANVAS_ENABLE_ACTION_BUTTONS , false ));
2018-06-08 09:40:00 +02:00
}
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
// restore to default value
m_regenerate_volumes = true ;
#endif // ENABLE_EXTENDED_SELECTION
2018-06-08 09:40:00 +02:00
}
2018-06-05 10:56:55 +02:00
void GLCanvas3D :: load_gcode_preview ( const GCodePreviewData & preview_data , const std :: vector < std :: string >& str_tool_colors )
{
2018-06-11 15:13:13 +02:00
if (( m_canvas != nullptr ) && ( m_print != nullptr ))
2018-06-05 10:56:55 +02:00
{
2018-10-04 10:41:11 +02:00
#if !ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-25 15:17:13 +02:00
// ensures that this canvas is current
2018-06-27 11:31:11 +02:00
if ( ! set_current ())
2018-06-05 10:56:55 +02:00
return ;
2018-10-04 10:41:11 +02:00
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-05 10:56:55 +02:00
2018-06-11 15:13:13 +02:00
if ( m_volumes . empty ())
2018-06-05 10:56:55 +02:00
{
std :: vector < float > tool_colors = _parse_colors ( str_tool_colors );
m_gcode_preview_volume_index . reset ();
_load_gcode_extrusion_paths ( preview_data , tool_colors );
_load_gcode_travel_paths ( preview_data , tool_colors );
_load_gcode_retractions ( preview_data );
_load_gcode_unretractions ( preview_data );
2018-06-11 15:13:13 +02:00
if ( m_volumes . empty ())
2018-07-19 13:18:19 +02:00
reset_legend_texture ();
2018-06-05 10:56:55 +02:00
else
{
2018-07-19 13:18:19 +02:00
_generate_legend_texture ( preview_data , tool_colors );
2018-06-05 10:56:55 +02:00
// removes empty volumes
2018-06-11 15:13:13 +02:00
m_volumes . volumes . erase ( std :: remove_if ( m_volumes . volumes . begin (), m_volumes . volumes . end (),
[]( const GLVolume * volume ) { return volume -> print_zs . empty (); }), m_volumes . volumes . end ());
2018-06-05 10:56:55 +02:00
_load_shells ();
}
2018-07-24 13:39:17 +02:00
_update_toolpath_volumes_outside_state ();
2018-06-05 10:56:55 +02:00
}
_update_gcode_volumes_visibility ( preview_data );
2018-07-24 13:39:17 +02:00
_show_warning_texture_if_needed ();
2018-06-05 10:56:55 +02:00
}
}
2018-07-24 13:39:17 +02:00
void GLCanvas3D :: load_preview ( const std :: vector < std :: string >& str_tool_colors )
{
if ( m_print == nullptr )
return ;
_load_print_toolpaths ();
_load_wipe_tower_toolpaths ( str_tool_colors );
2018-09-12 11:59:02 +02:00
for ( const PrintObject * object : m_print -> objects ())
2018-07-24 13:39:17 +02:00
{
if ( object != nullptr )
_load_print_object_toolpaths ( * object , str_tool_colors );
}
2018-07-27 08:49:58 +02:00
for ( GLVolume * volume : m_volumes . volumes )
{
volume -> is_extrusion_path = true ;
}
2018-07-24 13:39:17 +02:00
_update_toolpath_volumes_outside_state ();
_show_warning_texture_if_needed ();
reset_legend_texture ();
}
2018-06-06 14:19:28 +02:00
void GLCanvas3D :: bind_event_handlers ()
{
if ( m_canvas != nullptr )
{
m_canvas -> Bind ( wxEVT_SIZE , & GLCanvas3D :: on_size , this );
m_canvas -> Bind ( wxEVT_IDLE , & GLCanvas3D :: on_idle , this );
m_canvas -> Bind ( wxEVT_CHAR , & GLCanvas3D :: on_char , this );
m_canvas -> Bind ( wxEVT_MOUSEWHEEL , & GLCanvas3D :: on_mouse_wheel , this );
m_canvas -> Bind ( wxEVT_TIMER , & GLCanvas3D :: on_timer , this );
m_canvas -> Bind ( wxEVT_LEFT_DOWN , & GLCanvas3D :: on_mouse , this );
m_canvas -> Bind ( wxEVT_LEFT_UP , & GLCanvas3D :: on_mouse , this );
m_canvas -> Bind ( wxEVT_MIDDLE_DOWN , & GLCanvas3D :: on_mouse , this );
m_canvas -> Bind ( wxEVT_MIDDLE_UP , & GLCanvas3D :: on_mouse , this );
m_canvas -> Bind ( wxEVT_RIGHT_DOWN , & GLCanvas3D :: on_mouse , this );
m_canvas -> Bind ( wxEVT_RIGHT_UP , & GLCanvas3D :: on_mouse , this );
m_canvas -> Bind ( wxEVT_MOTION , & GLCanvas3D :: on_mouse , this );
m_canvas -> Bind ( wxEVT_ENTER_WINDOW , & GLCanvas3D :: on_mouse , this );
m_canvas -> Bind ( wxEVT_LEAVE_WINDOW , & GLCanvas3D :: on_mouse , this );
m_canvas -> Bind ( wxEVT_LEFT_DCLICK , & GLCanvas3D :: on_mouse , this );
m_canvas -> Bind ( wxEVT_MIDDLE_DCLICK , & GLCanvas3D :: on_mouse , this );
m_canvas -> Bind ( wxEVT_RIGHT_DCLICK , & GLCanvas3D :: on_mouse , this );
m_canvas -> Bind ( wxEVT_PAINT , & GLCanvas3D :: on_paint , this );
2018-06-07 09:22:19 +02:00
m_canvas -> Bind ( wxEVT_KEY_DOWN , & GLCanvas3D :: on_key_down , this );
2018-06-06 14:19:28 +02:00
}
}
void GLCanvas3D :: unbind_event_handlers ()
{
if ( m_canvas != nullptr )
{
m_canvas -> Unbind ( wxEVT_SIZE , & GLCanvas3D :: on_size , this );
m_canvas -> Unbind ( wxEVT_IDLE , & GLCanvas3D :: on_idle , this );
m_canvas -> Unbind ( wxEVT_CHAR , & GLCanvas3D :: on_char , this );
m_canvas -> Unbind ( wxEVT_MOUSEWHEEL , & GLCanvas3D :: on_mouse_wheel , this );
m_canvas -> Unbind ( wxEVT_TIMER , & GLCanvas3D :: on_timer , this );
m_canvas -> Unbind ( wxEVT_LEFT_DOWN , & GLCanvas3D :: on_mouse , this );
m_canvas -> Unbind ( wxEVT_LEFT_UP , & GLCanvas3D :: on_mouse , this );
m_canvas -> Unbind ( wxEVT_MIDDLE_DOWN , & GLCanvas3D :: on_mouse , this );
m_canvas -> Unbind ( wxEVT_MIDDLE_UP , & GLCanvas3D :: on_mouse , this );
m_canvas -> Unbind ( wxEVT_RIGHT_DOWN , & GLCanvas3D :: on_mouse , this );
m_canvas -> Unbind ( wxEVT_RIGHT_UP , & GLCanvas3D :: on_mouse , this );
m_canvas -> Unbind ( wxEVT_MOTION , & GLCanvas3D :: on_mouse , this );
m_canvas -> Unbind ( wxEVT_ENTER_WINDOW , & GLCanvas3D :: on_mouse , this );
m_canvas -> Unbind ( wxEVT_LEAVE_WINDOW , & GLCanvas3D :: on_mouse , this );
m_canvas -> Unbind ( wxEVT_LEFT_DCLICK , & GLCanvas3D :: on_mouse , this );
m_canvas -> Unbind ( wxEVT_MIDDLE_DCLICK , & GLCanvas3D :: on_mouse , this );
m_canvas -> Unbind ( wxEVT_RIGHT_DCLICK , & GLCanvas3D :: on_mouse , this );
m_canvas -> Unbind ( wxEVT_PAINT , & GLCanvas3D :: on_paint , this );
2018-06-07 09:22:19 +02:00
m_canvas -> Unbind ( wxEVT_KEY_DOWN , & GLCanvas3D :: on_key_down , this );
2018-06-06 14:19:28 +02:00
}
}
2018-05-14 14:47:13 +02:00
void GLCanvas3D :: on_size ( wxSizeEvent & evt )
{
2018-06-01 15:54:41 +02:00
m_dirty = true ;
2018-05-14 14:47:13 +02:00
}
void GLCanvas3D :: on_idle ( wxIdleEvent & evt )
{
2018-06-01 15:54:41 +02:00
if ( ! m_dirty )
2018-05-14 14:47:13 +02:00
return ;
2018-05-28 15:23:01 +02:00
_refresh_if_shown_on_screen ();
2018-05-14 14:47:13 +02:00
}
2018-05-15 16:09:04 +02:00
void GLCanvas3D :: on_char ( wxKeyEvent & evt )
{
if ( evt . HasModifiers ())
evt . Skip ();
else
{
int keyCode = evt . GetKeyCode ();
switch ( keyCode - 48 )
{
// numerical input
case 0 : { select_view ( "iso" ); break ; }
case 1 : { select_view ( "top" ); break ; }
case 2 : { select_view ( "bottom" ); break ; }
case 3 : { select_view ( "front" ); break ; }
case 4 : { select_view ( "rear" ); break ; }
case 5 : { select_view ( "left" ); break ; }
case 6 : { select_view ( "right" ); break ; }
default :
{
// text input
switch ( keyCode )
{
2018-06-07 09:22:19 +02:00
// key +
2018-10-03 11:34:39 +02:00
case 43 : { post_event ( Event < int > ( EVT_GLCANVAS_INCREASE_OBJECTS , + 1 )); break ; }
2018-06-07 09:22:19 +02:00
// key -
2018-10-03 11:34:39 +02:00
case 45 : { post_event ( Event < int > ( EVT_GLCANVAS_INCREASE_OBJECTS , - 1 )); break ; }
2018-06-07 09:22:19 +02:00
// key A/a
case 65 :
2018-10-03 11:34:39 +02:00
case 97 : { post_event ( SimpleEvent ( EVT_GLCANVAS_ARRANGE )); break ; }
2018-05-15 16:09:04 +02:00
// key B/b
case 66 :
2018-05-31 13:51:50 +02:00
case 98 : { zoom_to_bed (); break ; }
2018-06-07 09:22:19 +02:00
// key L/l
case 76 :
2018-10-03 11:34:39 +02:00
case 108 : { post_event ( Event < int > ( EVT_GLCANVAS_ROTATE_OBJECT , - 1 )); break ; }
2018-06-07 09:22:19 +02:00
// key R/r
case 82 :
2018-10-03 11:34:39 +02:00
case 114 : { post_event ( Event < int > ( EVT_GLCANVAS_ROTATE_OBJECT , + 1 )); break ; }
2018-06-07 09:22:19 +02:00
// key S/s
case 83 :
2018-10-03 11:34:39 +02:00
case 115 : { post_event ( SimpleEvent ( EVT_GLCANVAS_SCALE_UNIFORMLY )); break ; }
2018-05-15 16:09:04 +02:00
// key Z/z
case 90 :
case 122 : { zoom_to_volumes (); break ; }
2018-06-07 09:22:19 +02:00
default :
{
evt . Skip ();
break ;
}
2018-05-15 16:09:04 +02:00
}
}
}
}
}
2018-05-28 15:23:01 +02:00
void GLCanvas3D :: on_mouse_wheel ( wxMouseEvent & evt )
{
// Ignore the wheel events if the middle button is pressed.
if ( evt . MiddleIsDown ())
return ;
// Performs layers editing updates, if enabled
2018-06-13 13:14:17 +02:00
if ( is_layers_editing_enabled ())
2018-05-28 15:23:01 +02:00
{
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
2018-10-09 15:56:34 +02:00
int object_idx_selected = m_selection . get_object_idx ();
2018-10-08 14:02:12 +02:00
#else
2018-06-13 13:14:17 +02:00
int object_idx_selected = _get_first_selected_object_id ();
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-05-28 15:23:01 +02:00
if ( object_idx_selected != - 1 )
{
// A volume is selected. Test, whether hovering over a layer thickness bar.
2018-06-13 13:14:17 +02:00
if ( m_layers_editing . bar_rect_contains ( * this , ( float ) evt . GetX (), ( float ) evt . GetY ()))
2018-05-28 15:23:01 +02:00
{
// Adjust the width of the selection.
2018-06-01 15:54:41 +02:00
m_layers_editing . band_width = std :: max ( std :: min ( m_layers_editing . band_width * ( 1.0f + 0.1f * ( float ) evt . GetWheelRotation () / ( float ) evt . GetWheelDelta ()), 10.0f ), 1.5f );
2018-05-28 15:23:01 +02:00
if ( m_canvas != nullptr )
m_canvas -> Refresh ();
return ;
}
}
}
// Calculate the zoom delta and apply it to the current zoom factor
float zoom = ( float ) evt . GetWheelRotation () / ( float ) evt . GetWheelDelta ();
zoom = std :: max ( std :: min ( zoom , 4.0f ), - 4.0f ) / 10.0f ;
zoom = get_camera_zoom () / ( 1.0f - zoom );
// Don't allow to zoom too far outside the scene.
2018-06-01 15:54:41 +02:00
float zoom_min = _get_zoom_to_bounding_box_factor ( _max_bounding_box ());
2018-05-28 15:23:01 +02:00
if ( zoom_min > 0.0f )
2018-06-11 11:40:11 +02:00
zoom = std :: max ( zoom , zoom_min * 0.8f );
2018-05-28 15:23:01 +02:00
2018-06-01 15:54:41 +02:00
m_camera . zoom = zoom ;
2018-10-03 11:34:39 +02:00
viewport_changed ();
2018-05-28 15:23:01 +02:00
_refresh_if_shown_on_screen ();
}
2018-05-30 15:18:45 +02:00
void GLCanvas3D :: on_timer ( wxTimerEvent & evt )
{
2018-06-01 15:54:41 +02:00
if ( m_layers_editing . state != LayersEditing :: Editing )
2018-05-30 15:18:45 +02:00
return ;
_perform_layer_editing_action ();
}
2018-05-31 13:51:50 +02:00
void GLCanvas3D :: on_mouse ( wxMouseEvent & evt )
{
Point pos ( evt . GetX (), evt . GetY ());
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
2018-10-09 15:56:34 +02:00
int selected_object_idx = m_selection . get_object_idx ();
2018-10-08 14:02:12 +02:00
int layer_editing_object_idx = is_layers_editing_enabled () ? selected_object_idx : - 1 ;
#else
2018-06-13 13:14:17 +02:00
int selected_object_idx = _get_first_selected_object_id ();
int layer_editing_object_idx = is_layers_editing_enabled () ? selected_object_idx : - 1 ;
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-13 13:14:17 +02:00
m_layers_editing . last_object_id = layer_editing_object_idx ;
2018-06-14 12:34:19 +02:00
bool gizmos_overlay_contains_mouse = m_gizmos . overlay_contains_mouse ( * this , m_mouse . position );
2018-07-27 14:38:19 +02:00
int toolbar_contains_mouse = m_toolbar . contains_mouse ( m_mouse . position );
2018-05-31 13:51:50 +02:00
if ( evt . Entering ())
{
2018-06-01 09:18:10 +02:00
#if defined(__WXMSW__) || defined(__linux__)
2018-05-31 13:51:50 +02:00
// On Windows and Linux needs focus in order to catch key events
2018-06-01 15:54:41 +02:00
if ( m_canvas != nullptr )
m_canvas -> SetFocus ();
m_mouse . set_start_position_2D_as_invalid ();
2018-05-31 13:51:50 +02:00
#endif
2018-07-26 12:10:45 +02:00
}
2018-07-26 12:51:31 +02:00
else if ( evt . Leaving ())
{
2018-07-30 09:09:14 +02:00
// to remove hover on objects when the mouse goes out of this canvas
2018-08-21 21:05:24 +02:00
m_mouse . position = Vec2d ( - 1.0 , - 1.0 );
2018-07-30 09:09:14 +02:00
m_dirty = true ;
2018-07-26 12:51:31 +02:00
}
2018-09-07 12:00:04 +02:00
else if ( evt . LeftDClick () && ( m_hover_volume_id != - 1 ) && ! gizmos_overlay_contains_mouse && ( toolbar_contains_mouse == - 1 ))
2018-10-03 11:34:39 +02:00
post_event ( SimpleEvent ( EVT_GLCANVAS_DOUBLE_CLICK ));
2018-07-27 12:08:33 +02:00
else if ( evt . LeftDClick () && ( toolbar_contains_mouse != - 1 ))
{
m_toolbar_action_running = true ;
2018-07-27 14:38:19 +02:00
m_toolbar . do_action (( unsigned int ) toolbar_contains_mouse );
2018-07-27 12:08:33 +02:00
}
2018-09-19 15:39:54 +02:00
#if ENABLE_GIZMOS_RESET
else if ( evt . LeftDClick () && m_gizmos . grabber_contains_mouse ())
{
2018-09-20 09:50:49 +02:00
m_mouse . ignore_up_event = true ;
2018-09-19 15:39:54 +02:00
m_gizmos . process_double_click ();
switch ( m_gizmos . get_current_type ())
{
case Gizmos :: Scale :
{
2018-10-12 15:43:29 +02:00
#if ENABLE_EXTENDED_SELECTION
m_regenerate_volumes = false ;
m_selection . scale ( m_gizmos . get_scale ());
_on_scale ();
2018-09-24 15:54:09 +02:00
#else
2018-10-16 13:49:40 +02:00
post_event ( Vec3dEvent ( EVT_GIZMO_SCALE , m_gizmos . get_scale ()));
2018-10-16 09:51:30 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-10-12 15:43:29 +02:00
#if ENABLE_EXTENDED_SELECTION
wxGetApp (). obj_manipul () -> update_settings_value ( m_selection );
#else
2018-10-04 16:43:10 +02:00
wxGetApp (). obj_manipul () -> update_scale_values ();
2018-10-12 15:43:29 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-09-19 15:39:54 +02:00
m_dirty = true ;
break ;
}
case Gizmos :: Rotate :
{
2018-10-09 15:56:34 +02:00
#if ENABLE_EXTENDED_SELECTION
m_regenerate_volumes = false ;
2018-10-12 15:43:29 +02:00
m_selection . rotate ( m_gizmos . get_rotation ());
2018-10-09 15:56:34 +02:00
_on_rotate ();
#else
2018-10-03 11:34:39 +02:00
post_event ( Vec3dEvent ( EVT_GIZMO_ROTATE , std :: move ( m_gizmos . get_rotation ())));
2018-10-09 15:56:34 +02:00
#endif // ENABLE_EXTENDED_SELECTION
#if ENABLE_EXTENDED_SELECTION
wxGetApp (). obj_manipul () -> update_settings_value ( m_selection );
#else
2018-10-04 16:43:10 +02:00
wxGetApp (). obj_manipul () -> update_rotation_values ();
2018-10-09 15:56:34 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-09-19 15:39:54 +02:00
m_dirty = true ;
break ;
}
default :
{
break ;
}
}
}
#endif // ENABLE_GIZMOS_RESET
2018-05-31 13:51:50 +02:00
else if ( evt . LeftDown () || evt . RightDown ())
{
// If user pressed left or right button we first check whether this happened
// on a volume or not.
2018-10-08 14:02:12 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-06-01 15:54:41 +02:00
int volume_idx = m_hover_volume_id ;
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-06-01 15:54:41 +02:00
m_layers_editing . state = LayersEditing :: Unknown ;
2018-08-17 15:53:43 +02:00
if (( layer_editing_object_idx != - 1 ) && m_layers_editing . bar_rect_contains ( * this , pos ( 0 ), pos ( 1 )))
2018-05-31 13:51:50 +02:00
{
// A volume is selected and the mouse is inside the layer thickness bar.
// Start editing the layer height.
2018-06-01 15:54:41 +02:00
m_layers_editing . state = LayersEditing :: Editing ;
_perform_layer_editing_action ( & evt );
2018-05-31 13:51:50 +02:00
}
2018-08-17 15:53:43 +02:00
else if (( layer_editing_object_idx != - 1 ) && m_layers_editing . reset_rect_contains ( * this , pos ( 0 ), pos ( 1 )))
2018-05-31 13:51:50 +02:00
{
if ( evt . LeftDown ())
{
// A volume is selected and the mouse is inside the reset button.
2018-09-12 11:59:02 +02:00
// The PrintObject::adjust_layer_height_profile() call adjusts the profile of its associated ModelObject, it does not modify the profile of the PrintObject itself,
// therefore it is safe to call it while the background processing is running.
const_cast < PrintObject *> ( m_print -> get_object ( layer_editing_object_idx )) -> reset_layer_height_profile ();
2018-05-31 13:51:50 +02:00
// Index 2 means no editing, just wait for mouse up event.
2018-06-01 15:54:41 +02:00
m_layers_editing . state = LayersEditing :: Completed ;
m_dirty = true ;
2018-05-31 13:51:50 +02:00
}
}
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
else if ( ! m_selection . is_empty () && gizmos_overlay_contains_mouse )
{
m_gizmos . update_on_off_state ( * this , m_mouse . position , m_selection );
update_gizmos_data ();
m_dirty = true ;
}
#else
2018-06-14 12:34:19 +02:00
else if (( selected_object_idx != - 1 ) && gizmos_overlay_contains_mouse )
2018-06-13 13:14:17 +02:00
{
2018-06-19 09:46:26 +02:00
update_gizmos_data ();
2018-06-13 13:14:17 +02:00
m_gizmos . update_on_off_state ( * this , m_mouse . position );
m_dirty = true ;
}
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
#if ENABLE_EXTENDED_SELECTION
else if ( ! m_selection . is_empty () && m_gizmos . grabber_contains_mouse ())
#else
2018-06-15 14:10:28 +02:00
else if (( selected_object_idx != - 1 ) && m_gizmos . grabber_contains_mouse ())
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-19 09:46:26 +02:00
{
update_gizmos_data ();
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
m_selection . start_dragging ();
2018-10-15 11:30:50 +02:00
m_gizmos . start_dragging ( m_selection );
2018-10-08 14:02:12 +02:00
#else
2018-09-10 10:01:49 +02:00
m_gizmos . start_dragging ( _selected_volumes_bounding_box ());
2018-07-24 15:32:44 +02:00
m_mouse . drag . gizmo_volume_idx = _get_first_selected_volume_id ( selected_object_idx );
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-08-09 16:55:43 +02:00
if ( m_gizmos . get_current_type () == Gizmos :: Flatten ) {
2018-09-20 15:00:40 +02:00
// Rotate the object so the normal points downward:
2018-10-16 13:49:40 +02:00
#if ENABLE_EXTENDED_SELECTION
m_regenerate_volumes = false ;
m_selection . rotate ( m_gizmos . get_flattening_rotation ());
_on_flatten ();
wxGetApp (). obj_manipul () -> update_settings_value ( m_selection );
#else
2018-10-03 11:34:39 +02:00
post_event ( Vec3dEvent ( EVT_GIZMO_FLATTEN , m_gizmos . get_flattening_rotation ()));
2018-10-16 13:49:40 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-08-09 16:55:43 +02:00
}
2018-08-27 14:00:53 +02:00
2018-06-18 15:07:17 +02:00
m_dirty = true ;
2018-06-15 14:10:28 +02:00
}
2018-07-27 12:08:33 +02:00
else if ( toolbar_contains_mouse != - 1 )
{
m_toolbar_action_running = true ;
2018-07-27 14:38:19 +02:00
m_toolbar . do_action (( unsigned int ) toolbar_contains_mouse );
2018-07-27 12:08:33 +02:00
}
2018-05-31 13:51:50 +02:00
else
{
// Select volume in this 3D canvas.
// Don't deselect a volume if layer editing is enabled. We want the object to stay selected
// during the scene manipulation.
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
if ( m_picking_enabled && (( m_hover_volume_id != - 1 ) || ! is_layers_editing_enabled ()))
#else
2018-06-01 15:54:41 +02:00
if ( m_picking_enabled && (( volume_idx != - 1 ) || ! is_layers_editing_enabled ()))
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-05-31 13:51:50 +02:00
{
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
if ( m_hover_volume_id != - 1 )
{
if ( evt . ControlDown ())
m_selection . remove ( m_hover_volume_id );
else
m_selection . add ( m_hover_volume_id , ! evt . ShiftDown ());
update_gizmos_data ();
wxGetApp (). obj_manipul () -> update_settings_value ( m_selection );
m_dirty = true ;
}
#else
2018-05-31 13:51:50 +02:00
if ( volume_idx != - 1 )
{
2018-06-14 12:34:19 +02:00
deselect_volumes ();
select_volume ( volume_idx );
2018-06-11 15:13:13 +02:00
int group_id = m_volumes . volumes [ volume_idx ] -> select_group_id ;
2018-05-31 13:51:50 +02:00
if ( group_id != - 1 )
{
2018-06-11 15:13:13 +02:00
for ( GLVolume * vol : m_volumes . volumes )
2018-05-31 13:51:50 +02:00
{
if (( vol != nullptr ) && ( vol -> select_group_id == group_id ))
vol -> selected = true ;
}
}
2018-06-18 15:07:17 +02:00
2018-06-19 09:46:26 +02:00
update_gizmos_data ();
2018-06-14 12:34:19 +02:00
m_dirty = true ;
2018-05-31 13:51:50 +02:00
}
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-05-31 13:51:50 +02:00
}
// propagate event through callback
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
if ( m_picking_enabled && ( m_hover_volume_id != - 1 ))
{
2018-10-09 15:56:34 +02:00
int object_idx = m_selection . get_object_idx ();
2018-10-08 14:02:12 +02:00
_on_select ( m_hover_volume_id , object_idx );
}
#else
2018-06-14 12:34:19 +02:00
if ( m_picking_enabled && ( volume_idx != - 1 ))
2018-09-06 16:10:31 +02:00
_on_select ( volume_idx , selected_object_idx );
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-05-31 13:51:50 +02:00
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
if ( m_hover_volume_id != - 1 )
#else
2018-05-31 13:51:50 +02:00
if ( volume_idx != - 1 )
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-05-31 13:51:50 +02:00
{
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
if ( evt . LeftDown () && m_moving_enabled && ( m_mouse . drag . move_volume_idx == - 1 ))
#else
2018-06-01 15:54:41 +02:00
if ( evt . LeftDown () && m_moving_enabled )
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-05-31 13:51:50 +02:00
{
2018-06-13 15:44:04 +02:00
// The mouse_to_3d gets the Z coordinate from the Z buffer at the screen coordinate pos x, y,
// an converts the screen space coordinate to unscaled object space.
2018-10-08 14:02:12 +02:00
Vec3d pos3d = _mouse_to_3d ( pos );
2018-06-13 15:44:04 +02:00
2018-05-31 13:51:50 +02:00
// Only accept the initial position, if it is inside the volume bounding box.
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
BoundingBoxf3 volume_bbox = m_volumes . volumes [ m_hover_volume_id ] -> transformed_bounding_box ();
#else
2018-06-11 15:13:13 +02:00
BoundingBoxf3 volume_bbox = m_volumes . volumes [ volume_idx ] -> transformed_bounding_box ();
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-05-31 13:51:50 +02:00
volume_bbox . offset ( 1.0 );
if ( volume_bbox . contains ( pos3d ))
{
// The dragging operation is initiated.
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
m_mouse . drag . move_volume_idx = m_hover_volume_id ;
m_selection . start_dragging ();
#else
2018-06-21 11:14:17 +02:00
m_mouse . drag . move_with_shift = evt . ShiftDown ();
2018-06-21 08:37:04 +02:00
m_mouse . drag . move_volume_idx = volume_idx ;
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-01 15:54:41 +02:00
m_mouse . drag . start_position_3D = pos3d ;
2018-10-08 14:02:12 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-05-31 13:51:50 +02:00
// Remember the shift to to the object center.The object center will later be used
// to limit the object placement close to the bed.
2018-08-17 14:14:24 +02:00
m_mouse . drag . volume_center_offset = volume_bbox . center () - pos3d ;
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-05-31 13:51:50 +02:00
}
}
else if ( evt . RightDown ())
{
2018-07-27 14:38:19 +02:00
// forces a frame render to ensure that m_hover_volume_id is updated even when the user right clicks while
// the context menu is already shown, ensuring it to disappear if the mouse is outside any volume
2018-08-23 15:37:38 +02:00
m_mouse . position = Vec2d (( double ) pos ( 0 ), ( double ) pos ( 1 ));
2018-07-27 14:38:19 +02:00
render ();
if ( m_hover_volume_id != - 1 )
{
// if right clicking on volume, propagate event through callback (shows context menu)
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
if ( m_volumes . volumes [ m_hover_volume_id ] -> hover )
#else
2018-07-27 14:38:19 +02:00
if ( m_volumes . volumes [ volume_idx ] -> hover )
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-10-03 11:34:39 +02:00
post_event ( Vec2dEvent ( EVT_GLCANVAS_RIGHT_CLICK , pos . cast < double > ()));
2018-07-27 14:38:19 +02:00
}
2018-05-31 13:51:50 +02:00
}
}
}
}
2018-06-21 08:37:04 +02:00
else if ( evt . Dragging () && evt . LeftIsDown () && ! gizmos_overlay_contains_mouse && ( m_layers_editing . state == LayersEditing :: Unknown ) && ( m_mouse . drag . move_volume_idx != - 1 ))
2018-05-31 16:04:59 +02:00
{
2018-06-01 15:54:41 +02:00
m_mouse . dragging = true ;
2018-05-31 16:04:59 +02:00
// Get new position at the same Z of the initial click point.
float z0 = 0.0f ;
float z1 = 1.0f ;
2018-08-21 17:43:05 +02:00
Vec3d cur_pos = Linef3 ( _mouse_to_3d ( pos , & z0 ), _mouse_to_3d ( pos , & z1 )). intersect_plane ( m_mouse . drag . start_position_3D ( 2 ));
2018-05-31 16:04:59 +02:00
2018-10-08 14:02:12 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-05-31 16:04:59 +02:00
// Clip the new position, so the object center remains close to the bed.
2018-08-17 14:14:24 +02:00
cur_pos += m_mouse . drag . volume_center_offset ;
2018-08-17 15:53:43 +02:00
Point cur_pos2 ( scale_ ( cur_pos ( 0 )), scale_ ( cur_pos ( 1 )));
2018-05-31 16:04:59 +02:00
if ( ! m_bed . contains ( cur_pos2 ))
{
Point ip = m_bed . point_projection ( cur_pos2 );
2018-08-21 17:43:05 +02:00
cur_pos ( 0 ) = unscale < double > ( ip ( 0 ));
cur_pos ( 1 ) = unscale < double > ( ip ( 1 ));
2018-05-31 16:04:59 +02:00
}
2018-08-17 14:14:24 +02:00
cur_pos -= m_mouse . drag . volume_center_offset ;
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-05-31 16:04:59 +02:00
// Calculate the translation vector.
2018-10-08 14:02:12 +02:00
Vec3d displacement = cur_pos - m_mouse . drag . start_position_3D ;
#if ENABLE_EXTENDED_SELECTION
m_selection . translate ( displacement );
wxGetApp (). obj_manipul () -> update_settings_value ( m_selection );
#else
2018-05-31 16:04:59 +02:00
// Get the volume being dragged.
2018-06-21 08:37:04 +02:00
GLVolume * volume = m_volumes . volumes [ m_mouse . drag . move_volume_idx ];
2018-05-31 16:04:59 +02:00
// Get all volumes belonging to the same group, if any.
std :: vector < GLVolume *> volumes ;
2018-06-21 11:14:17 +02:00
int group_id = m_mouse . drag . move_with_shift ? volume -> select_group_id : volume -> drag_group_id ;
2018-06-21 08:37:04 +02:00
if ( group_id == - 1 )
2018-05-31 16:04:59 +02:00
volumes . push_back ( volume );
else
{
2018-06-11 15:13:13 +02:00
for ( GLVolume * v : m_volumes . volumes )
2018-05-31 16:04:59 +02:00
{
2018-06-21 08:37:04 +02:00
if ( v != nullptr )
{
2018-07-25 08:40:34 +02:00
if (( m_mouse . drag . move_with_shift && ( v -> select_group_id == group_id )) || ( ! m_mouse . drag . move_with_shift && ( v -> drag_group_id == group_id )))
2018-06-21 08:37:04 +02:00
volumes . push_back ( v );
}
2018-05-31 16:04:59 +02:00
}
}
// Apply new temporary volume origin and ignore Z.
for ( GLVolume * v : volumes )
2018-09-05 09:11:58 +02:00
{
2018-10-08 14:02:12 +02:00
v -> set_offset ( v -> get_offset () + Vec3d ( displacement ( 0 ), displacement ( 1 ), 0.0 ));
2018-09-05 09:11:58 +02:00
}
2018-05-31 16:04:59 +02:00
2018-10-09 15:56:34 +02:00
wxGetApp (). obj_manipul () -> update_position_value ( volume -> get_offset ());
2018-06-01 15:54:41 +02:00
m_mouse . drag . start_position_3D = cur_pos ;
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-01 15:54:41 +02:00
m_dirty = true ;
2018-05-31 16:04:59 +02:00
}
2018-06-15 14:10:28 +02:00
else if ( evt . Dragging () && m_gizmos . is_dragging ())
{
2018-08-28 09:31:23 +02:00
if ( ! m_canvas -> HasCapture ())
m_canvas -> CaptureMouse ();
2018-08-24 15:49:57 +02:00
2018-06-15 14:10:28 +02:00
m_mouse . dragging = true ;
2018-08-20 10:23:17 +02:00
m_gizmos . update ( mouse_ray ( pos ));
2018-06-18 15:07:17 +02:00
2018-10-08 14:02:12 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-06-21 08:37:04 +02:00
std :: vector < GLVolume *> volumes ;
if ( m_mouse . drag . gizmo_volume_idx != - 1 )
{
GLVolume * volume = m_volumes . volumes [ m_mouse . drag . gizmo_volume_idx ];
// Get all volumes belonging to the same group, if any.
if ( volume -> select_group_id == - 1 )
volumes . push_back ( volume );
else
{
for ( GLVolume * v : m_volumes . volumes )
{
if (( v != nullptr ) && ( v -> select_group_id == volume -> select_group_id ))
volumes . push_back ( v );
}
}
}
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-06-21 08:37:04 +02:00
2018-06-19 09:46:26 +02:00
switch ( m_gizmos . get_current_type ())
{
2018-09-11 09:00:28 +02:00
case Gizmos :: Move :
{
// Apply new temporary offset
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
m_selection . translate ( m_gizmos . get_displacement ());
wxGetApp (). obj_manipul () -> update_settings_value ( m_selection );
#else
2018-09-11 09:00:28 +02:00
GLVolume * volume = m_volumes . volumes [ m_mouse . drag . gizmo_volume_idx ];
Vec3d offset = m_gizmos . get_position () - volume -> get_offset ();
for ( GLVolume * v : volumes )
{
v -> set_offset ( v -> get_offset () + offset );
}
2018-10-09 15:56:34 +02:00
wxGetApp (). obj_manipul () -> update_position_value ( volume -> get_offset ());
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-09-11 09:00:28 +02:00
break ;
}
2018-06-19 09:46:26 +02:00
case Gizmos :: Scale :
{
2018-09-24 15:54:09 +02:00
// Apply new temporary scale factors
2018-10-12 15:43:29 +02:00
#if ENABLE_EXTENDED_SELECTION
m_selection . scale ( m_gizmos . get_scale ());
wxGetApp (). obj_manipul () -> update_settings_value ( m_selection );
#else
2018-09-24 15:54:09 +02:00
const Vec3d & scale = m_gizmos . get_scale ();
for ( GLVolume * v : volumes )
{
v -> set_scaling_factor ( scale );
}
2018-10-12 15:43:29 +02:00
wxGetApp (). obj_manipul () -> update_scale_value ( scale );
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-19 09:46:26 +02:00
break ;
}
case Gizmos :: Rotate :
{
2018-10-12 15:43:29 +02:00
// Apply new temporary rotations
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
2018-10-09 15:56:34 +02:00
m_selection . rotate ( m_gizmos . get_rotation ());
wxGetApp (). obj_manipul () -> update_settings_value ( m_selection );
2018-10-08 14:02:12 +02:00
#else
2018-09-20 15:00:40 +02:00
// Apply new temporary rotation
2018-09-24 15:54:09 +02:00
const Vec3d & rotation = m_gizmos . get_rotation ();
2018-09-20 15:00:40 +02:00
for ( GLVolume * v : volumes )
{
v -> set_rotation ( rotation );
}
2018-10-04 16:43:10 +02:00
wxGetApp (). obj_manipul () -> update_rotation_value ( rotation );
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-19 09:46:26 +02:00
break ;
}
default :
break ;
}
2018-06-22 11:19:38 +02:00
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
#else
2018-06-22 11:19:38 +02:00
if ( ! volumes . empty ())
{
2018-07-25 11:49:38 +02:00
BoundingBoxf3 bb ;
for ( const GLVolume * volume : volumes )
{
bb . merge ( volume -> transformed_bounding_box ());
}
2018-08-21 17:43:05 +02:00
const Vec3d & size = bb . size ();
2018-09-24 15:54:09 +02:00
const Vec3d & scale = m_gizmos . get_scale ();
2018-10-03 11:34:39 +02:00
post_event ( Vec3dsEvent < 2 > ( EVT_GLCANVAS_UPDATE_GEOMETRY , { size , scale }));
2018-06-22 11:19:38 +02:00
}
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-22 11:19:38 +02:00
2018-06-15 14:10:28 +02:00
m_dirty = true ;
}
2018-06-14 12:34:19 +02:00
else if ( evt . Dragging () && ! gizmos_overlay_contains_mouse )
2018-05-31 16:04:59 +02:00
{
2018-06-01 15:54:41 +02:00
m_mouse . dragging = true ;
2018-06-13 13:14:17 +02:00
if (( m_layers_editing . state != LayersEditing :: Unknown ) && ( layer_editing_object_idx != - 1 ))
2018-05-31 16:04:59 +02:00
{
2018-06-01 15:54:41 +02:00
if ( m_layers_editing . state == LayersEditing :: Editing )
_perform_layer_editing_action ( & evt );
2018-05-31 16:04:59 +02:00
}
2018-06-01 09:00:30 +02:00
else if ( evt . LeftIsDown ())
{
// if dragging over blank area with left button, rotate
2018-06-01 15:54:41 +02:00
if ( m_mouse . is_start_position_3D_defined ())
2018-06-01 09:00:30 +02:00
{
2018-08-21 17:43:05 +02:00
const Vec3d & orig = m_mouse . drag . start_position_3D ;
2018-08-17 15:53:43 +02:00
m_camera . phi += ((( float ) pos ( 0 ) - ( float ) orig ( 0 )) * TRACKBALLSIZE );
m_camera . set_theta ( m_camera . get_theta () - (( float ) pos ( 1 ) - ( float ) orig ( 1 )) * TRACKBALLSIZE );
2018-06-01 09:00:30 +02:00
2018-10-03 11:34:39 +02:00
viewport_changed ();
2018-06-01 09:00:30 +02:00
2018-06-01 15:54:41 +02:00
m_dirty = true ;
2018-06-01 09:00:30 +02:00
}
2018-08-24 10:20:00 +02:00
m_mouse . drag . start_position_3D = Vec3d (( double ) pos ( 0 ), ( double ) pos ( 1 ), 0.0 );
2018-06-01 09:00:30 +02:00
}
else if ( evt . MiddleIsDown () || evt . RightIsDown ())
{
// If dragging over blank area with right button, pan.
2018-06-01 15:54:41 +02:00
if ( m_mouse . is_start_position_2D_defined ())
2018-06-01 09:00:30 +02:00
{
// get point in model space at Z = 0
float z = 0.0f ;
2018-08-21 17:43:05 +02:00
const Vec3d & cur_pos = _mouse_to_3d ( pos , & z );
Vec3d orig = _mouse_to_3d ( m_mouse . drag . start_position_2D , & z );
2018-08-17 14:14:24 +02:00
m_camera . target += orig - cur_pos ;
2018-06-01 09:00:30 +02:00
2018-10-03 11:34:39 +02:00
viewport_changed ();
2018-06-01 09:00:30 +02:00
2018-06-01 15:54:41 +02:00
m_dirty = true ;
2018-06-01 09:00:30 +02:00
}
2018-06-01 15:54:41 +02:00
m_mouse . drag . start_position_2D = pos ;
2018-06-01 09:00:30 +02:00
}
2018-05-31 16:04:59 +02:00
}
else if ( evt . LeftUp () || evt . MiddleUp () || evt . RightUp ())
{
2018-06-01 15:54:41 +02:00
if ( m_layers_editing . state != LayersEditing :: Unknown )
2018-05-31 16:04:59 +02:00
{
2018-06-01 15:54:41 +02:00
m_layers_editing . state = LayersEditing :: Unknown ;
_stop_timer ();
2018-05-31 16:04:59 +02:00
2018-06-13 13:14:17 +02:00
if ( layer_editing_object_idx != - 1 )
2018-10-03 11:34:39 +02:00
post_event ( SimpleEvent ( EVT_GLCANVAS_MODEL_UPDATE ));
2018-05-31 16:04:59 +02:00
}
2018-06-21 08:37:04 +02:00
else if (( m_mouse . drag . move_volume_idx != - 1 ) && m_mouse . dragging )
2018-05-31 16:04:59 +02:00
{
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
m_regenerate_volumes = false ;
_on_move ();
wxGetApp (). obj_manipul () -> update_settings_value ( m_selection );
#else
2018-05-31 16:04:59 +02:00
// get all volumes belonging to the same group, if any
std :: vector < int > volume_idxs ;
2018-06-21 08:37:04 +02:00
int vol_id = m_mouse . drag . move_volume_idx ;
2018-06-21 11:14:17 +02:00
int group_id = m_mouse . drag . move_with_shift ? m_volumes . volumes [ vol_id ] -> select_group_id : m_volumes . volumes [ vol_id ] -> drag_group_id ;
2018-05-31 16:04:59 +02:00
if ( group_id == - 1 )
volume_idxs . push_back ( vol_id );
else
{
2018-06-13 09:26:58 +02:00
for ( int i = 0 ; i < ( int ) m_volumes . volumes . size (); ++ i )
2018-05-31 16:04:59 +02:00
{
2018-06-21 11:14:17 +02:00
if (( m_mouse . drag . move_with_shift && ( m_volumes . volumes [ i ] -> select_group_id == group_id )) || ( m_volumes . volumes [ i ] -> drag_group_id == group_id ))
2018-05-31 16:04:59 +02:00
volume_idxs . push_back ( i );
}
}
2018-06-07 11:18:28 +02:00
_on_move ( volume_idxs );
2018-08-16 13:22:02 +02:00
// force re-selection of the wipe tower, if needed
if (( volume_idxs . size () == 1 ) && m_volumes . volumes [ volume_idxs [ 0 ]] -> is_wipe_tower )
select_volume ( volume_idxs [ 0 ]);
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-05-31 16:04:59 +02:00
}
2018-07-27 14:38:19 +02:00
else if ( evt . LeftUp () && ! m_mouse . dragging && ( m_hover_volume_id == - 1 ) && ! gizmos_overlay_contains_mouse && ! m_gizmos . is_dragging () && ! is_layers_editing_enabled ())
2018-06-14 12:34:19 +02:00
{
// deselect and propagate event through callback
2018-09-20 09:50:49 +02:00
#if ENABLE_GIZMOS_RESET
if ( ! m_mouse . ignore_up_event && m_picking_enabled && ! m_toolbar_action_running )
#else
2018-07-27 12:08:33 +02:00
if ( m_picking_enabled && ! m_toolbar_action_running )
2018-09-20 09:50:49 +02:00
#endif // ENABLE_GIZMOS_RESET
2018-06-14 12:34:19 +02:00
{
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
m_selection . clear ();
wxGetApp (). obj_manipul () -> update_settings_value ( m_selection );
#else
2018-06-14 12:34:19 +02:00
deselect_volumes ();
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-09-06 16:10:31 +02:00
_on_select ( - 1 , - 1 );
2018-07-19 16:06:46 +02:00
update_gizmos_data ();
2018-06-14 12:34:19 +02:00
}
2018-09-20 09:50:49 +02:00
#if ENABLE_GIZMOS_RESET
else if ( m_mouse . ignore_up_event )
m_mouse . ignore_up_event = false ;
#endif // ENABLE_GIZMOS_RESET
2018-06-14 12:34:19 +02:00
}
2018-06-15 14:10:28 +02:00
else if ( evt . LeftUp () && m_gizmos . is_dragging ())
{
2018-06-21 08:37:04 +02:00
switch ( m_gizmos . get_current_type ())
{
2018-09-11 09:00:28 +02:00
case Gizmos :: Move :
{
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
m_regenerate_volumes = false ;
_on_move ();
#else
2018-09-11 09:00:28 +02:00
// get all volumes belonging to the same group, if any
std :: vector < int > volume_idxs ;
int vol_id = m_mouse . drag . gizmo_volume_idx ;
int group_id = m_volumes . volumes [ vol_id ] -> select_group_id ;
if ( group_id == - 1 )
volume_idxs . push_back ( vol_id );
else
{
for ( int i = 0 ; i < ( int ) m_volumes . volumes . size (); ++ i )
{
if ( m_volumes . volumes [ i ] -> select_group_id == group_id )
volume_idxs . push_back ( i );
}
}
_on_move ( volume_idxs );
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-09-11 09:00:28 +02:00
break ;
}
2018-06-21 08:37:04 +02:00
case Gizmos :: Scale :
{
2018-10-12 15:43:29 +02:00
#if ENABLE_EXTENDED_SELECTION
m_regenerate_volumes = false ;
_on_scale ();
#endif // ENABLE_EXTENDED_SELECTION
2018-06-21 08:37:04 +02:00
break ;
}
case Gizmos :: Rotate :
{
2018-10-09 15:56:34 +02:00
#if ENABLE_EXTENDED_SELECTION
m_regenerate_volumes = false ;
_on_rotate ();
#else
2018-10-03 11:34:39 +02:00
post_event ( Vec3dEvent ( EVT_GIZMO_ROTATE , m_gizmos . get_rotation ()));
2018-10-09 15:56:34 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-21 08:37:04 +02:00
break ;
}
default :
break ;
}
2018-06-15 14:10:28 +02:00
m_gizmos . stop_dragging ();
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
wxGetApp (). obj_manipul () -> update_settings_value ( m_selection );
#else
2018-10-04 16:43:10 +02:00
wxGetApp (). obj_manipul () -> update_values ();
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-15 14:10:28 +02:00
}
2018-06-14 12:34:19 +02:00
2018-06-21 08:37:04 +02:00
m_mouse . drag . move_volume_idx = - 1 ;
2018-10-08 14:02:12 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-06-21 08:37:04 +02:00
m_mouse . drag . gizmo_volume_idx = - 1 ;
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-06-01 15:54:41 +02:00
m_mouse . set_start_position_3D_as_invalid ();
m_mouse . set_start_position_2D_as_invalid ();
m_mouse . dragging = false ;
2018-07-27 12:08:33 +02:00
m_toolbar_action_running = false ;
2018-07-18 14:26:42 +02:00
m_dirty = true ;
2018-08-24 15:49:57 +02:00
if ( m_canvas -> HasCapture ())
m_canvas -> ReleaseMouse ();
2018-05-31 16:04:59 +02:00
}
2018-05-31 13:51:50 +02:00
else if ( evt . Moving ())
{
2018-08-24 10:20:00 +02:00
m_mouse . position = Vec2d (( double ) pos ( 0 ), ( double ) pos ( 1 ));
2018-05-31 13:51:50 +02:00
// Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor hovers over.
2018-06-01 15:54:41 +02:00
if ( m_picking_enabled )
m_dirty = true ;
2018-05-31 13:51:50 +02:00
}
else
evt . Skip ();
}
2018-06-04 12:26:39 +02:00
void GLCanvas3D :: on_paint ( wxPaintEvent & evt )
{
render ();
}
2018-06-07 09:22:19 +02:00
void GLCanvas3D :: on_key_down ( wxKeyEvent & evt )
{
if ( evt . HasModifiers ())
evt . Skip ();
else
{
int key = evt . GetKeyCode ();
if ( key == WXK_DELETE )
2018-10-03 11:34:39 +02:00
post_event ( SimpleEvent ( EVT_GLCANVAS_REMOVE_OBJECT ));
2018-06-07 09:22:19 +02:00
else
2018-07-23 09:59:04 +02:00
{
#ifdef __WXOSX__
if ( key == WXK_BACK )
2018-10-03 11:34:39 +02:00
post_event ( SimpleEvent ( EVT_GLCANVAS_REMOVE_OBJECT ));
2018-07-23 09:59:04 +02:00
#endif
evt . Skip ();
}
2018-06-07 09:22:19 +02:00
}
}
2018-05-24 13:46:17 +02:00
Size GLCanvas3D :: get_canvas_size () const
{
int w = 0 ;
int h = 0 ;
if ( m_canvas != nullptr )
m_canvas -> GetSize ( & w , & h );
return Size ( w , h );
}
2018-05-25 15:56:14 +02:00
Point GLCanvas3D :: get_local_mouse_position () const
{
if ( m_canvas == nullptr )
return Point ();
wxPoint mouse_pos = m_canvas -> ScreenToClient ( wxGetMousePosition ());
2018-05-28 13:43:29 +02:00
return Point ( mouse_pos . x , mouse_pos . y );
2018-05-25 15:56:14 +02:00
}
2018-07-19 13:18:19 +02:00
void GLCanvas3D :: reset_legend_texture ()
{
2018-10-04 10:41:11 +02:00
#if !ENABLE_USE_UNIQUE_GLCONTEXT
2018-07-19 13:18:19 +02:00
if ( ! set_current ())
return ;
2018-10-04 10:41:11 +02:00
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
2018-07-19 13:18:19 +02:00
m_legend_texture . reset ();
}
2018-07-25 10:01:17 +02:00
void GLCanvas3D :: set_tooltip ( const std :: string & tooltip )
{
if ( m_canvas != nullptr )
m_canvas -> SetToolTip ( tooltip );
}
2018-06-12 09:18:25 +02:00
bool GLCanvas3D :: _is_shown_on_screen () const
{
2018-06-27 12:05:23 +02:00
return ( m_canvas != nullptr ) ? m_canvas -> IsShownOnScreen () : false ;
2018-06-12 09:18:25 +02:00
}
2018-06-05 10:56:55 +02:00
void GLCanvas3D :: _force_zoom_to_bed ()
2018-06-04 12:26:39 +02:00
{
zoom_to_bed ();
2018-06-05 10:56:55 +02:00
m_force_zoom_to_bed_enabled = false ;
2018-06-04 12:26:39 +02:00
}
2018-07-23 13:49:48 +02:00
bool GLCanvas3D :: _init_toolbar ()
{
if ( ! m_toolbar . is_enabled ())
return true ;
2018-07-31 12:25:00 +02:00
if ( ! m_toolbar . init ( "toolbar.png" , 36 , 1 , 1 ))
{
// unable to init the toolbar texture, disable it
m_toolbar . set_enabled ( false );
return true ;
}
// m_toolbar.set_layout_type(GLToolbar::Layout::Vertical);
m_toolbar . set_layout_type ( GLToolbar :: Layout :: Horizontal );
m_toolbar . set_separator_size ( 5 );
m_toolbar . set_gap_size ( 2 );
GLToolbarItem :: Data item ;
2018-07-23 13:49:48 +02:00
item . name = "add" ;
item . tooltip = GUI :: L_str ( "Add..." );
2018-07-31 12:25:00 +02:00
item . sprite_id = 0 ;
2018-07-27 12:08:33 +02:00
item . is_toggable = false ;
2018-09-17 12:15:11 +02:00
item . action_event = EVT_GLTOOLBAR_ADD ;
2018-07-23 13:49:48 +02:00
if ( ! m_toolbar . add_item ( item ))
return false ;
item . name = "delete" ;
item . tooltip = GUI :: L_str ( "Delete" );
2018-07-31 12:25:00 +02:00
item . sprite_id = 1 ;
2018-07-27 12:08:33 +02:00
item . is_toggable = false ;
2018-09-17 12:15:11 +02:00
item . action_event = EVT_GLTOOLBAR_DELETE ;
2018-07-23 13:49:48 +02:00
if ( ! m_toolbar . add_item ( item ))
return false ;
item . name = "deleteall" ;
item . tooltip = GUI :: L_str ( "Delete all" );
2018-07-31 12:25:00 +02:00
item . sprite_id = 2 ;
2018-07-27 12:08:33 +02:00
item . is_toggable = false ;
2018-10-03 11:34:39 +02:00
item . action_event = EVT_GLTOOLBAR_DELETE_ALL ;
2018-07-23 13:49:48 +02:00
if ( ! m_toolbar . add_item ( item ))
return false ;
item . name = "arrange" ;
item . tooltip = GUI :: L_str ( "Arrange" );
2018-07-31 12:25:00 +02:00
item . sprite_id = 3 ;
2018-07-27 12:08:33 +02:00
item . is_toggable = false ;
2018-10-03 11:34:39 +02:00
item . action_event = EVT_GLTOOLBAR_ARRANGE ;
2018-07-23 13:49:48 +02:00
if ( ! m_toolbar . add_item ( item ))
return false ;
if ( ! m_toolbar . add_separator ())
return false ;
item . name = "more" ;
item . tooltip = GUI :: L_str ( "Add instance" );
2018-07-31 12:25:00 +02:00
item . sprite_id = 4 ;
2018-07-27 12:08:33 +02:00
item . is_toggable = false ;
2018-10-03 11:34:39 +02:00
item . action_event = EVT_GLTOOLBAR_MORE ;
2018-07-23 13:49:48 +02:00
if ( ! m_toolbar . add_item ( item ))
return false ;
item . name = "fewer" ;
item . tooltip = GUI :: L_str ( "Remove instance" );
2018-07-31 12:25:00 +02:00
item . sprite_id = 5 ;
2018-07-27 12:08:33 +02:00
item . is_toggable = false ;
2018-10-03 11:34:39 +02:00
item . action_event = EVT_GLTOOLBAR_FEWER ;
2018-07-23 13:49:48 +02:00
if ( ! m_toolbar . add_item ( item ))
return false ;
if ( ! m_toolbar . add_separator ())
return false ;
item . name = "split" ;
item . tooltip = GUI :: L_str ( "Split" );
2018-08-24 15:08:19 +02:00
item . sprite_id = 6 ;
2018-07-27 12:08:33 +02:00
item . is_toggable = false ;
2018-10-03 11:34:39 +02:00
item . action_event = EVT_GLTOOLBAR_SPLIT ;
2018-07-23 13:49:48 +02:00
if ( ! m_toolbar . add_item ( item ))
return false ;
item . name = "cut" ;
item . tooltip = GUI :: L_str ( "Cut..." );
2018-08-24 15:08:19 +02:00
item . sprite_id = 7 ;
2018-07-27 12:08:33 +02:00
item . is_toggable = false ;
2018-10-03 11:34:39 +02:00
item . action_event = EVT_GLTOOLBAR_CUT ;
2018-07-23 13:49:48 +02:00
if ( ! m_toolbar . add_item ( item ))
return false ;
if ( ! m_toolbar . add_separator ())
return false ;
item . name = "settings" ;
item . tooltip = GUI :: L_str ( "Settings..." );
2018-08-24 15:08:19 +02:00
item . sprite_id = 8 ;
2018-07-27 12:08:33 +02:00
item . is_toggable = false ;
2018-10-03 11:34:39 +02:00
item . action_event = EVT_GLTOOLBAR_SETTINGS ;
2018-07-23 13:49:48 +02:00
if ( ! m_toolbar . add_item ( item ))
return false ;
item . name = "layersediting" ;
item . tooltip = GUI :: L_str ( "Layers editing" );
2018-08-24 15:08:19 +02:00
item . sprite_id = 9 ;
2018-07-27 12:08:33 +02:00
item . is_toggable = true ;
2018-10-03 11:34:39 +02:00
item . action_event = EVT_GLTOOLBAR_LAYERSEDITING ;
2018-07-23 13:49:48 +02:00
if ( ! m_toolbar . add_item ( item ))
return false ;
2018-09-06 16:10:31 +02:00
if ( ! m_toolbar . add_separator ())
return false ;
2018-10-08 14:02:12 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-09-06 16:10:31 +02:00
item . name = "selectbyparts" ;
item . tooltip = GUI :: L_str ( "Select by parts" );
item . sprite_id = 10 ;
item . is_toggable = true ;
2018-10-03 11:34:39 +02:00
item . action_event = EVT_GLTOOLBAR_SELECTBYPARTS ;
2018-09-06 16:10:31 +02:00
if ( ! m_toolbar . add_item ( item ))
return false ;
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-09-06 16:10:31 +02:00
2018-07-23 13:49:48 +02:00
enable_toolbar_item ( "add" , true );
return true ;
}
2018-10-04 10:41:11 +02:00
#if ENABLE_USE_UNIQUE_GLCONTEXT
bool GLCanvas3D :: _set_current ()
{
if (( m_canvas != nullptr ) && ( m_context != nullptr ))
return m_canvas -> SetCurrent ( * m_context );
return false ;
}
#endif ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-01 15:54:41 +02:00
void GLCanvas3D :: _resize ( unsigned int w , unsigned int h )
2018-05-30 15:18:45 +02:00
{
2018-06-25 15:17:13 +02:00
if (( m_canvas == nullptr ) && ( m_context == nullptr ))
2018-06-01 15:54:41 +02:00
return ;
2018-06-25 15:17:13 +02:00
// ensures that this canvas is current
2018-10-04 10:41:11 +02:00
#if ENABLE_USE_UNIQUE_GLCONTEXT
_set_current ();
#else
2018-06-27 11:31:11 +02:00
set_current ();
2018-10-04 10:41:11 +02:00
#endif // ENABLE_USE_UNIQUE_GLCONTEXT
2018-06-01 15:54:41 +02:00
:: glViewport ( 0 , 0 , w , h );
:: glMatrixMode ( GL_PROJECTION );
:: glLoadIdentity ();
const BoundingBoxf3 & bbox = _max_bounding_box ();
switch ( m_camera . type )
{
case Camera :: Ortho :
{
float w2 = w ;
float h2 = h ;
float two_zoom = 2.0f * get_camera_zoom ();
if ( two_zoom != 0.0f )
{
float inv_two_zoom = 1.0f / two_zoom ;
w2 *= inv_two_zoom ;
h2 *= inv_two_zoom ;
}
// FIXME: calculate a tighter value for depth will improve z-fighting
float depth = 5.0f * ( float ) bbox . max_size ();
:: glOrtho ( - w2 , w2 , - h2 , h2 , - depth , depth );
break ;
}
2018-06-04 12:26:39 +02:00
// case Camera::Perspective:
// {
// float bbox_r = (float)bbox.radius();
// float fov = PI * 45.0f / 180.0f;
// float fov_tan = tan(0.5f * fov);
// float cam_distance = 0.5f * bbox_r / fov_tan;
// m_camera.distance = cam_distance;
//
// float nr = cam_distance - bbox_r * 1.1f;
// float fr = cam_distance + bbox_r * 1.1f;
// if (nr < 1.0f)
// nr = 1.0f;
//
// if (fr < nr + 1.0f)
// fr = nr + 1.0f;
//
// float h2 = fov_tan * nr;
// float w2 = h2 * w / h;
// ::glFrustum(-w2, w2, -h2, h2, nr, fr);
//
// break;
// }
2018-06-01 15:54:41 +02:00
default :
{
throw std :: runtime_error ( "Invalid camera type." );
break ;
}
}
:: glMatrixMode ( GL_MODELVIEW );
m_dirty = false ;
2018-05-30 15:18:45 +02:00
}
2018-06-01 15:54:41 +02:00
BoundingBoxf3 GLCanvas3D :: _max_bounding_box () const
2018-05-30 15:18:45 +02:00
{
2018-06-01 15:54:41 +02:00
BoundingBoxf3 bb = m_bed . get_bounding_box ();
bb . merge ( volumes_bounding_box ());
return bb ;
2018-05-30 15:18:45 +02:00
}
2018-10-08 14:02:12 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-06-13 15:44:04 +02:00
BoundingBoxf3 GLCanvas3D :: _selected_volumes_bounding_box () const
{
BoundingBoxf3 bb ;
2018-07-25 11:49:38 +02:00
std :: vector < const GLVolume *> selected_volumes ;
2018-06-13 15:44:04 +02:00
for ( const GLVolume * volume : m_volumes . volumes )
{
2018-07-18 14:26:42 +02:00
if (( volume != nullptr ) && ! volume -> is_wipe_tower && volume -> selected )
2018-07-25 11:49:38 +02:00
selected_volumes . push_back ( volume );
2018-06-13 15:44:04 +02:00
}
2018-07-25 11:49:38 +02:00
bool use_drag_group_id = selected_volumes . size () > 1 ;
if ( use_drag_group_id )
{
int drag_group_id = selected_volumes [ 0 ] -> drag_group_id ;
for ( const GLVolume * volume : selected_volumes )
{
if ( drag_group_id != volume -> drag_group_id )
{
use_drag_group_id = false ;
break ;
}
}
}
if ( use_drag_group_id )
{
for ( const GLVolume * volume : selected_volumes )
{
bb . merge ( volume -> bounding_box );
}
2018-08-24 10:03:34 +02:00
bb = bb . transformed ( selected_volumes [ 0 ] -> world_matrix (). cast < double > ());
2018-07-25 11:49:38 +02:00
}
else
{
for ( const GLVolume * volume : selected_volumes )
{
2018-08-27 14:54:20 +02:00
bb . merge ( volume -> transformed_bounding_box ());
2018-07-25 11:49:38 +02:00
}
}
2018-06-13 15:44:04 +02:00
return bb ;
}
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-06-13 15:44:04 +02:00
2018-05-14 14:14:19 +02:00
void GLCanvas3D :: _zoom_to_bounding_box ( const BoundingBoxf3 & bbox )
{
2018-05-15 09:50:01 +02:00
// Calculate the zoom factor needed to adjust viewport to bounding box.
float zoom = _get_zoom_to_bounding_box_factor ( bbox );
if ( zoom > 0.0f )
{
2018-06-01 15:54:41 +02:00
m_camera . zoom = zoom ;
2018-05-15 09:50:01 +02:00
// center view around bounding box center
2018-06-01 15:54:41 +02:00
m_camera . target = bbox . center ();
2018-05-15 09:50:01 +02:00
2018-10-03 11:34:39 +02:00
viewport_changed ();
2018-05-15 09:50:01 +02:00
2018-05-28 15:23:01 +02:00
_refresh_if_shown_on_screen ();
2018-05-15 09:50:01 +02:00
}
2018-05-14 14:14:19 +02:00
}
2018-05-15 09:50:01 +02:00
float GLCanvas3D :: _get_zoom_to_bounding_box_factor ( const BoundingBoxf3 & bbox ) const
{
float max_bb_size = bbox . max_size ();
if ( max_bb_size == 0.0f )
return - 1.0f ;
// project the bbox vertices on a plane perpendicular to the camera forward axis
// then calculates the vertices coordinate on this plane along the camera xy axes
2018-05-31 08:44:39 +02:00
// we need the view matrix, we let opengl calculate it (same as done in render())
_camera_tranform ();
2018-05-15 09:50:01 +02:00
// get the view matrix back from opengl
GLfloat matrix [ 16 ];
:: glGetFloatv ( GL_MODELVIEW_MATRIX , matrix );
// camera axes
2018-08-24 10:20:00 +02:00
Vec3d right (( double ) matrix [ 0 ], ( double ) matrix [ 4 ], ( double ) matrix [ 8 ]);
Vec3d up (( double ) matrix [ 1 ], ( double ) matrix [ 5 ], ( double ) matrix [ 9 ]);
Vec3d forward (( double ) matrix [ 2 ], ( double ) matrix [ 6 ], ( double ) matrix [ 10 ]);
2018-05-15 09:50:01 +02:00
2018-08-21 17:43:05 +02:00
Vec3d bb_min = bbox . min ;
Vec3d bb_max = bbox . max ;
Vec3d bb_center = bbox . center ();
2018-05-15 09:50:01 +02:00
// bbox vertices in world space
2018-08-21 17:43:05 +02:00
std :: vector < Vec3d > vertices ;
2018-05-15 09:50:01 +02:00
vertices . reserve ( 8 );
vertices . push_back ( bb_min );
2018-08-17 15:53:43 +02:00
vertices . emplace_back ( bb_max ( 0 ), bb_min ( 1 ), bb_min ( 2 ));
vertices . emplace_back ( bb_max ( 0 ), bb_max ( 1 ), bb_min ( 2 ));
vertices . emplace_back ( bb_min ( 0 ), bb_max ( 1 ), bb_min ( 2 ));
vertices . emplace_back ( bb_min ( 0 ), bb_min ( 1 ), bb_max ( 2 ));
vertices . emplace_back ( bb_max ( 0 ), bb_min ( 1 ), bb_max ( 2 ));
2018-05-15 09:50:01 +02:00
vertices . push_back ( bb_max );
2018-08-17 15:53:43 +02:00
vertices . emplace_back ( bb_min ( 0 ), bb_max ( 1 ), bb_max ( 2 ));
2018-05-15 09:50:01 +02:00
2018-08-24 10:20:00 +02:00
double max_x = 0.0 ;
double max_y = 0.0 ;
2018-05-15 09:50:01 +02:00
// margin factor to give some empty space around the bbox
2018-08-24 10:20:00 +02:00
double margin_factor = 1.25 ;
2018-05-15 09:50:01 +02:00
2018-08-21 17:43:05 +02:00
for ( const Vec3d v : vertices )
2018-05-15 09:50:01 +02:00
{
// project vertex on the plane perpendicular to camera forward axis
2018-08-21 17:43:05 +02:00
Vec3d pos ( v ( 0 ) - bb_center ( 0 ), v ( 1 ) - bb_center ( 1 ), v ( 2 ) - bb_center ( 2 ));
Vec3d proj_on_plane = pos - pos . dot ( forward ) * forward ;
2018-05-15 09:50:01 +02:00
// calculates vertex coordinate along camera xy axes
2018-08-24 10:20:00 +02:00
double x_on_plane = proj_on_plane . dot ( right );
double y_on_plane = proj_on_plane . dot ( up );
2018-05-15 09:50:01 +02:00
max_x = std :: max ( max_x , margin_factor * std :: abs ( x_on_plane ));
max_y = std :: max ( max_y , margin_factor * std :: abs ( y_on_plane ));
}
if (( max_x == 0.0 ) || ( max_y == 0.0 ))
return - 1.0f ;
max_x *= 2.0 ;
max_y *= 2.0 ;
2018-05-24 13:46:17 +02:00
const Size & cnv_size = get_canvas_size ();
2018-08-24 10:20:00 +02:00
return ( float ) std :: min (( double ) cnv_size . get_width () / max_x , ( double ) cnv_size . get_height () / max_y );
2018-05-15 09:50:01 +02:00
}
2018-05-29 14:34:45 +02:00
void GLCanvas3D :: _mark_volumes_for_layer_height () const
{
2018-06-11 15:13:13 +02:00
if ( m_print == nullptr )
2018-05-29 14:34:45 +02:00
return ;
2018-06-11 15:13:13 +02:00
for ( GLVolume * vol : m_volumes . volumes )
2018-05-29 14:34:45 +02:00
{
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
int object_id = vol -> object_idx ();
#else
2018-05-29 14:34:45 +02:00
int object_id = int ( vol -> select_group_id / 1000000 );
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-01 15:54:41 +02:00
int shader_id = m_layers_editing . get_shader_program_id ();
2018-05-29 14:34:45 +02:00
2018-06-01 15:54:41 +02:00
if ( is_layers_editing_enabled () && ( shader_id != - 1 ) && vol -> selected &&
2018-09-12 11:59:02 +02:00
vol -> has_layer_height_texture () && ( object_id < ( int ) m_print -> objects (). size ()))
2018-05-29 14:34:45 +02:00
{
2018-06-01 15:54:41 +02:00
vol -> set_layer_height_texture_data ( m_layers_editing . get_z_texture_id (), shader_id ,
m_print -> get_object ( object_id ), _get_layers_editing_cursor_z_relative (), m_layers_editing . band_width );
2018-05-29 14:34:45 +02:00
}
else
vol -> reset_layer_height_texture_data ();
}
2018-05-15 09:50:01 +02:00
}
2018-05-28 15:23:01 +02:00
void GLCanvas3D :: _refresh_if_shown_on_screen ()
{
2018-06-12 09:18:25 +02:00
if ( _is_shown_on_screen ())
2018-05-28 15:23:01 +02:00
{
const Size & cnv_size = get_canvas_size ();
2018-06-01 15:54:41 +02:00
_resize (( unsigned int ) cnv_size . get_width (), ( unsigned int ) cnv_size . get_height ());
2018-05-28 15:23:01 +02:00
if ( m_canvas != nullptr )
m_canvas -> Refresh ();
}
}
2018-05-31 08:44:39 +02:00
void GLCanvas3D :: _camera_tranform () const
{
:: glMatrixMode ( GL_MODELVIEW );
:: glLoadIdentity ();
2018-08-06 16:37:41 +02:00
:: glRotatef ( - m_camera . get_theta (), 1.0f , 0.0f , 0.0f ); // pitch
2018-06-12 09:18:25 +02:00
:: glRotatef ( m_camera . phi , 0.0f , 0.0f , 1.0f ); // yaw
2018-05-31 08:44:39 +02:00
2018-08-21 17:43:05 +02:00
Vec3d neg_target = - m_camera . target ;
2018-10-11 10:52:50 +02:00
:: glTranslated ( neg_target ( 0 ), neg_target ( 1 ), neg_target ( 2 ));
2018-05-31 08:44:39 +02:00
}
2018-05-29 13:54:34 +02:00
void GLCanvas3D :: _picking_pass () const
{
2018-08-21 21:05:24 +02:00
const Vec2d & pos = m_mouse . position ;
2018-06-01 15:54:41 +02:00
2018-08-21 21:05:24 +02:00
if ( m_picking_enabled && ! m_mouse . dragging && ( pos != Vec2d ( DBL_MAX , DBL_MAX )))
2018-05-29 13:54:34 +02:00
{
// Render the object for picking.
// FIXME This cannot possibly work in a multi - sampled context as the color gets mangled by the anti - aliasing.
// Better to use software ray - casting on a bounding - box hierarchy.
2018-06-01 15:54:41 +02:00
if ( m_multisample_allowed )
2018-05-29 13:54:34 +02:00
:: glDisable ( GL_MULTISAMPLE );
:: glDisable ( GL_BLEND );
2018-07-18 15:09:26 +02:00
:: glEnable ( GL_DEPTH_TEST );
2018-05-29 13:54:34 +02:00
:: glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
2018-08-21 14:27:36 +02:00
_render_volumes ( true );
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
2018-10-15 11:30:50 +02:00
m_gizmos . render_current_gizmo_for_picking_pass ( m_selection );
2018-10-08 14:02:12 +02:00
#else
2018-08-24 11:17:53 +02:00
m_gizmos . render_current_gizmo_for_picking_pass ( _selected_volumes_bounding_box ());
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-05-29 13:54:34 +02:00
2018-06-01 15:54:41 +02:00
if ( m_multisample_allowed )
2018-05-29 13:54:34 +02:00
:: glEnable ( GL_MULTISAMPLE );
2018-07-30 09:09:14 +02:00
int volume_id = - 1 ;
2018-10-08 14:02:12 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-06-11 15:13:13 +02:00
for ( GLVolume * vol : m_volumes . volumes )
2018-05-29 13:54:34 +02:00
{
vol -> hover = false ;
}
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-05-29 13:54:34 +02:00
2018-07-30 09:09:14 +02:00
GLubyte color [ 4 ] = { 0 , 0 , 0 , 0 };
const Size & cnv_size = get_canvas_size ();
2018-08-17 18:07:45 +02:00
bool inside = ( 0 <= pos ( 0 )) && ( pos ( 0 ) < cnv_size . get_width ()) && ( 0 <= pos ( 1 )) && ( pos ( 1 ) < cnv_size . get_height ());
2018-07-30 09:09:14 +02:00
if ( inside )
{
2018-08-17 18:07:45 +02:00
:: glReadPixels ( pos ( 0 ), cnv_size . get_height () - pos ( 1 ) - 1 , 1 , 1 , GL_RGBA , GL_UNSIGNED_BYTE , ( void * ) color );
2018-07-30 09:09:14 +02:00
volume_id = color [ 0 ] + color [ 1 ] * 256 + color [ 2 ] * 256 * 256 ;
}
if (( 0 <= volume_id ) && ( volume_id < ( int ) m_volumes . volumes . size ()))
2018-05-29 13:54:34 +02:00
{
m_hover_volume_id = volume_id ;
2018-10-08 14:02:12 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-06-11 15:13:13 +02:00
m_volumes . volumes [ volume_id ] -> hover = true ;
int group_id = m_volumes . volumes [ volume_id ] -> select_group_id ;
2018-05-29 13:54:34 +02:00
if ( group_id != - 1 )
{
2018-06-11 15:13:13 +02:00
for ( GLVolume * vol : m_volumes . volumes )
2018-05-29 13:54:34 +02:00
{
if ( vol -> select_group_id == group_id )
vol -> hover = true ;
}
}
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-06-14 15:32:26 +02:00
m_gizmos . set_hover_id ( - 1 );
2018-05-29 13:54:34 +02:00
}
2018-06-14 15:32:26 +02:00
else
2018-07-30 09:09:14 +02:00
{
m_hover_volume_id = - 1 ;
m_gizmos . set_hover_id ( inside ? ( 254 - ( int ) color [ 2 ]) : - 1 );
}
2018-06-13 10:49:59 +02:00
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
_update_volumes_hover_state ();
#endif // ENABLE_EXTENDED_SELECTION
2018-06-13 10:49:59 +02:00
// updates gizmos overlay
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
if ( ! m_selection . is_empty ())
m_gizmos . update_hover_state ( * this , pos , m_selection );
#else
2018-06-13 13:14:17 +02:00
if ( _get_first_selected_object_id () != - 1 )
2018-06-13 10:49:59 +02:00
m_gizmos . update_hover_state ( * this , pos );
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-13 13:14:17 +02:00
else
m_gizmos . reset_all_states ();
2018-07-23 13:49:48 +02:00
2018-07-27 14:38:19 +02:00
m_toolbar . update_hover_state ( pos );
2018-05-29 13:54:34 +02:00
}
}
void GLCanvas3D :: _render_background () const
{
:: glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
:: glPushMatrix ();
:: glLoadIdentity ();
:: glMatrixMode ( GL_PROJECTION );
:: glPushMatrix ();
:: glLoadIdentity ();
// Draws a bluish bottom to top gradient over the complete screen.
:: glDisable ( GL_DEPTH_TEST );
:: glBegin ( GL_QUADS );
:: glColor3f ( 0.0f , 0.0f , 0.0f );
2018-07-27 09:38:39 +02:00
:: glVertex2f ( - 1.0f , - 1.0f );
:: glVertex2f ( 1.0f , - 1.0f );
if ( m_dynamic_background_enabled && _is_any_volume_outside ())
2018-10-11 10:52:50 +02:00
:: glColor3fv ( ERROR_BG_COLOR );
2018-07-27 09:38:39 +02:00
else
2018-10-11 10:52:50 +02:00
:: glColor3fv ( DEFAULT_BG_COLOR );
2018-07-27 09:38:39 +02:00
:: glVertex2f ( 1.0f , 1.0f );
:: glVertex2f ( - 1.0f , 1.0f );
2018-05-29 13:54:34 +02:00
:: glEnd ();
:: glEnable ( GL_DEPTH_TEST );
:: glPopMatrix ();
:: glMatrixMode ( GL_MODELVIEW );
:: glPopMatrix ();
}
2018-06-11 10:46:32 +02:00
void GLCanvas3D :: _render_bed ( float theta ) const
2018-05-29 13:54:34 +02:00
{
2018-06-11 10:46:32 +02:00
m_bed . render ( theta );
2018-05-29 13:54:34 +02:00
}
2018-06-13 13:14:17 +02:00
void GLCanvas3D :: _render_axes ( bool depth_test ) const
2018-05-29 13:54:34 +02:00
{
2018-06-13 13:14:17 +02:00
m_axes . render ( depth_test );
2018-05-29 13:54:34 +02:00
}
2018-06-04 12:26:39 +02:00
void GLCanvas3D :: _render_objects () const
2018-05-29 13:54:34 +02:00
{
2018-06-11 15:13:13 +02:00
if ( m_volumes . empty ())
2018-05-29 13:54:34 +02:00
return ;
:: glEnable ( GL_LIGHTING );
2018-08-21 14:27:36 +02:00
:: glEnable ( GL_DEPTH_TEST );
2018-05-29 13:54:34 +02:00
if ( ! m_shader_enabled )
2018-06-01 15:54:41 +02:00
_render_volumes ( false );
2018-06-04 12:26:39 +02:00
else if ( m_use_VBOs )
2018-05-29 13:54:34 +02:00
{
2018-06-01 15:54:41 +02:00
if ( m_picking_enabled )
2018-05-29 13:54:34 +02:00
{
2018-05-29 14:34:45 +02:00
_mark_volumes_for_layer_height ();
2018-05-29 13:54:34 +02:00
if ( m_config != nullptr )
{
2018-06-01 15:54:41 +02:00
const BoundingBoxf3 & bed_bb = m_bed . get_bounding_box ();
2018-08-17 15:53:43 +02:00
m_volumes . set_print_box (( float ) bed_bb . min ( 0 ), ( float ) bed_bb . min ( 1 ), 0.0f , ( float ) bed_bb . max ( 0 ), ( float ) bed_bb . max ( 1 ), ( float ) m_config -> opt_float ( "max_print_height" ));
2018-07-18 14:26:42 +02:00
m_volumes . check_outside_state ( m_config , nullptr );
2018-05-29 13:54:34 +02:00
}
// do not cull backfaces to show broken geometry, if any
:: glDisable ( GL_CULL_FACE );
}
2018-06-01 15:54:41 +02:00
m_shader . start_using ();
2018-06-11 15:13:13 +02:00
m_volumes . render_VBOs ();
2018-06-01 15:54:41 +02:00
m_shader . stop_using ();
2018-05-29 13:54:34 +02:00
2018-06-01 15:54:41 +02:00
if ( m_picking_enabled )
2018-05-29 13:54:34 +02:00
:: glEnable ( GL_CULL_FACE );
}
else
{
// do not cull backfaces to show broken geometry, if any
2018-06-01 15:54:41 +02:00
if ( m_picking_enabled )
2018-05-29 13:54:34 +02:00
:: glDisable ( GL_CULL_FACE );
2018-06-11 15:13:13 +02:00
m_volumes . render_legacy ();
2018-05-29 13:54:34 +02:00
2018-06-01 15:54:41 +02:00
if ( m_picking_enabled )
2018-05-29 13:54:34 +02:00
:: glEnable ( GL_CULL_FACE );
}
2018-06-22 12:21:43 +02:00
:: glDisable ( GL_LIGHTING );
2018-05-29 13:54:34 +02:00
}
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
void GLCanvas3D :: _render_selection () const
{
2018-10-10 11:04:04 +02:00
Gizmos :: EType type = m_gizmos . get_current_type ();
2018-10-16 13:49:40 +02:00
bool show_indirect_selection = m_gizmos . is_running () && (( type == Gizmos :: Rotate ) || ( type == Gizmos :: Scale ) || ( type == Gizmos :: Flatten ));
2018-10-10 11:04:04 +02:00
m_selection . render ( show_indirect_selection );
2018-10-08 14:02:12 +02:00
}
#endif // ENABLE_EXTENDED_SELECTION
2018-05-29 13:54:34 +02:00
void GLCanvas3D :: _render_cutting_plane () const
{
m_cutting_plane . render ( volumes_bounding_box ());
}
void GLCanvas3D :: _render_warning_texture () const
{
if ( ! m_warning_texture_enabled )
return ;
2018-07-31 14:20:16 +02:00
m_warning_texture . render ( * this );
2018-05-29 13:54:34 +02:00
}
void GLCanvas3D :: _render_legend_texture () const
{
if ( ! m_legend_texture_enabled )
return ;
2018-07-31 14:32:59 +02:00
m_legend_texture . render ( * this );
2018-05-29 13:54:34 +02:00
}
void GLCanvas3D :: _render_layer_editing_overlay () const
{
2018-10-08 09:08:42 +02:00
if (( m_print == nullptr ) || m_print -> objects (). empty ())
2018-05-29 13:54:34 +02:00
return ;
GLVolume * volume = nullptr ;
2018-06-11 15:13:13 +02:00
for ( GLVolume * vol : m_volumes . volumes )
2018-05-29 13:54:34 +02:00
{
if (( vol != nullptr ) && vol -> selected && vol -> has_layer_height_texture ())
{
volume = vol ;
break ;
}
}
if ( volume == nullptr )
return ;
// If the active object was not allocated at the Print, go away.This should only be a momentary case between an object addition / deletion
// and an update by Platter::async_apply_config.
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
int object_idx = volume -> object_idx ();
#else
2018-05-29 13:54:34 +02:00
int object_idx = int ( volume -> select_group_id / 1000000 );
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-09-12 11:59:02 +02:00
if (( int ) m_print -> objects (). size () < object_idx )
2018-05-29 13:54:34 +02:00
return ;
const PrintObject * print_object = m_print -> get_object ( object_idx );
if ( print_object == nullptr )
return ;
m_layers_editing . render ( * this , * print_object , * volume );
}
2018-06-01 15:54:41 +02:00
void GLCanvas3D :: _render_volumes ( bool fake_colors ) const
{
2018-06-14 15:32:26 +02:00
static const GLfloat INV_255 = 1.0f / 255.0f ;
2018-06-01 15:54:41 +02:00
2018-06-22 12:21:43 +02:00
if ( ! fake_colors )
2018-06-01 15:54:41 +02:00
:: glEnable ( GL_LIGHTING );
// do not cull backfaces to show broken geometry, if any
:: glDisable ( GL_CULL_FACE );
:: glEnable ( GL_BLEND );
:: glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA );
:: glEnableClientState ( GL_VERTEX_ARRAY );
:: glEnableClientState ( GL_NORMAL_ARRAY );
unsigned int volume_id = 0 ;
2018-06-11 15:13:13 +02:00
for ( GLVolume * vol : m_volumes . volumes )
2018-06-01 15:54:41 +02:00
{
if ( fake_colors )
{
// Object picking mode. Render the object with a color encoding the object index.
unsigned int r = ( volume_id & 0x000000FF ) >> 0 ;
unsigned int g = ( volume_id & 0x0000FF00 ) >> 8 ;
unsigned int b = ( volume_id & 0x00FF0000 ) >> 16 ;
2018-06-14 15:32:26 +02:00
:: glColor3f (( GLfloat ) r * INV_255 , ( GLfloat ) g * INV_255 , ( GLfloat ) b * INV_255 );
2018-06-01 15:54:41 +02:00
}
else
{
vol -> set_render_color ();
2018-10-11 10:52:50 +02:00
:: glColor4fv ( vol -> render_color );
2018-06-01 15:54:41 +02:00
}
vol -> render ();
++ volume_id ;
}
:: glDisableClientState ( GL_NORMAL_ARRAY );
:: glDisableClientState ( GL_VERTEX_ARRAY );
:: glDisable ( GL_BLEND );
:: glEnable ( GL_CULL_FACE );
2018-06-22 12:21:43 +02:00
if ( ! fake_colors )
:: glDisable ( GL_LIGHTING );
2018-06-01 15:54:41 +02:00
}
2018-08-21 14:27:36 +02:00
void GLCanvas3D :: _render_current_gizmo () const
2018-06-13 09:12:16 +02:00
{
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
2018-10-15 11:30:50 +02:00
m_gizmos . render_current_gizmo ( m_selection );
2018-10-08 14:02:12 +02:00
#else
2018-08-21 14:27:36 +02:00
m_gizmos . render_current_gizmo ( _selected_volumes_bounding_box ());
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-08-21 14:27:36 +02:00
}
void GLCanvas3D :: _render_gizmos_overlay () const
{
m_gizmos . render_overlay ( * this );
2018-06-13 09:12:16 +02:00
}
2018-07-23 13:49:48 +02:00
void GLCanvas3D :: _render_toolbar () const
{
2018-07-31 12:25:00 +02:00
_resize_toolbar ();
m_toolbar . render ();
2018-07-23 13:49:48 +02:00
}
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
void GLCanvas3D :: _update_volumes_hover_state () const
{
for ( GLVolume * v : m_volumes . volumes )
{
v -> hover = false ;
}
if ( m_hover_volume_id == - 1 )
return ;
GLVolume * volume = m_volumes . volumes [ m_hover_volume_id ];
switch ( m_selection . get_mode ())
{
case Selection :: Volume :
{
volume -> hover = true ;
break ;
}
case Selection :: Instance :
{
int object_idx = volume -> object_idx ();
int instance_idx = volume -> instance_idx ();
for ( GLVolume * v : m_volumes . volumes )
{
if (( v -> object_idx () == object_idx ) && ( v -> instance_idx () == instance_idx ))
v -> hover = true ;
}
break ;
}
}
}
#endif // ENABLE_EXTENDED_SELECTION
2018-06-01 15:54:41 +02:00
float GLCanvas3D :: _get_layers_editing_cursor_z_relative () const
{
return m_layers_editing . get_cursor_z_relative ( * this );
}
2018-05-30 15:18:45 +02:00
void GLCanvas3D :: _perform_layer_editing_action ( wxMouseEvent * evt )
{
2018-06-01 15:54:41 +02:00
int object_idx_selected = m_layers_editing . last_object_id ;
2018-05-30 15:18:45 +02:00
if ( object_idx_selected == - 1 )
return ;
2018-06-11 15:13:13 +02:00
if ( m_print == nullptr )
2018-05-30 15:18:45 +02:00
return ;
2018-09-12 11:59:02 +02:00
const PrintObject * selected_obj = m_print -> get_object ( object_idx_selected );
2018-05-30 15:18:45 +02:00
if ( selected_obj == nullptr )
return ;
// A volume is selected. Test, whether hovering over a layer thickness bar.
if ( evt != nullptr )
{
const Rect & rect = LayersEditing :: get_bar_rect_screen ( * this );
float b = rect . get_bottom ();
2018-08-21 17:43:05 +02:00
m_layers_editing . last_z = unscale < double > ( selected_obj -> size ( 2 )) * ( b - evt -> GetY () - 1.0f ) / ( b - rect . get_top ());
2018-06-01 15:54:41 +02:00
m_layers_editing . last_action = evt -> ShiftDown () ? ( evt -> RightIsDown () ? 3 : 2 ) : ( evt -> RightIsDown () ? 0 : 1 );
2018-05-30 15:18:45 +02:00
}
// Mark the volume as modified, so Print will pick its layer height profile ? Where to mark it ?
// Start a timer to refresh the print ? schedule_background_process() ?
2018-09-12 11:59:02 +02:00
// The PrintObject::adjust_layer_height_profile() call adjusts the profile of its associated ModelObject, it does not modify the profile of the PrintObject itself,
// therefore it is safe to call it while the background processing is running.
const_cast < PrintObject *> ( selected_obj ) -> adjust_layer_height_profile ( m_layers_editing . last_z , m_layers_editing . strength , m_layers_editing . band_width , m_layers_editing . last_action );
2018-05-30 15:18:45 +02:00
// searches the id of the first volume of the selected object
int volume_idx = 0 ;
for ( int i = 0 ; i < object_idx_selected ; ++ i )
{
2018-09-12 11:59:02 +02:00
const PrintObject * obj = m_print -> get_object ( i );
2018-05-30 15:18:45 +02:00
if ( obj != nullptr )
{
for ( int j = 0 ; j < ( int ) obj -> region_volumes . size (); ++ j )
{
volume_idx += ( int ) obj -> region_volumes [ j ]. size ();
}
}
}
2018-06-11 15:13:13 +02:00
m_volumes . volumes [ volume_idx ] -> generate_layer_height_texture ( selected_obj , 1 );
2018-05-30 15:18:45 +02:00
_refresh_if_shown_on_screen ();
// Automatic action on mouse down with the same coordinate.
2018-06-01 15:54:41 +02:00
_start_timer ();
}
2018-08-21 17:43:05 +02:00
Vec3d GLCanvas3D :: _mouse_to_3d ( const Point & mouse_pos , float * z )
2018-05-31 13:51:50 +02:00
{
2018-06-25 15:17:13 +02:00
if ( m_canvas == nullptr )
2018-08-21 17:43:05 +02:00
return Vec3d ( DBL_MAX , DBL_MAX , DBL_MAX );
2018-05-31 13:51:50 +02:00
2018-06-25 15:17:13 +02:00
_camera_tranform ();
2018-05-31 13:51:50 +02:00
GLint viewport [ 4 ];
:: glGetIntegerv ( GL_VIEWPORT , viewport );
GLdouble modelview_matrix [ 16 ];
:: glGetDoublev ( GL_MODELVIEW_MATRIX , modelview_matrix );
GLdouble projection_matrix [ 16 ];
:: glGetDoublev ( GL_PROJECTION_MATRIX , projection_matrix );
2018-08-17 15:53:43 +02:00
GLint y = viewport [ 3 ] - ( GLint ) mouse_pos ( 1 );
2018-05-31 13:51:50 +02:00
GLfloat mouse_z ;
if ( z == nullptr )
2018-08-17 15:53:43 +02:00
:: glReadPixels (( GLint ) mouse_pos ( 0 ), y , 1 , 1 , GL_DEPTH_COMPONENT , GL_FLOAT , ( void * ) & mouse_z );
2018-05-31 13:51:50 +02:00
else
mouse_z = * z ;
GLdouble out_x , out_y , out_z ;
2018-08-17 15:53:43 +02:00
:: gluUnProject (( GLdouble ) mouse_pos ( 0 ), ( GLdouble ) y , ( GLdouble ) mouse_z , modelview_matrix , projection_matrix , viewport , & out_x , & out_y , & out_z );
2018-08-24 10:20:00 +02:00
return Vec3d (( double ) out_x , ( double ) out_y , ( double ) out_z );
2018-05-31 13:51:50 +02:00
}
2018-08-21 17:43:05 +02:00
Vec3d GLCanvas3D :: _mouse_to_bed_3d ( const Point & mouse_pos )
2018-08-20 10:23:17 +02:00
{
return mouse_ray ( mouse_pos ). intersect_plane ( 0.0 );
}
Linef3 GLCanvas3D :: mouse_ray ( const Point & mouse_pos )
2018-06-15 14:10:28 +02:00
{
2018-06-18 15:07:17 +02:00
float z0 = 0.0f ;
float z1 = 1.0f ;
2018-08-20 10:23:17 +02:00
return Linef3 ( _mouse_to_3d ( mouse_pos , & z0 ), _mouse_to_3d ( mouse_pos , & z1 ));
2018-06-15 14:10:28 +02:00
}
2018-06-01 15:54:41 +02:00
void GLCanvas3D :: _start_timer ()
{
if ( m_timer != nullptr )
m_timer -> Start ( 100 , wxTIMER_CONTINUOUS );
}
void GLCanvas3D :: _stop_timer ()
{
if ( m_timer != nullptr )
m_timer -> Stop ();
}
2018-10-08 14:02:12 +02:00
#if !ENABLE_EXTENDED_SELECTION
2018-06-13 13:14:17 +02:00
int GLCanvas3D :: _get_first_selected_object_id () const
{
if ( m_print != nullptr )
{
2018-09-12 11:59:02 +02:00
int objects_count = ( int ) m_print -> objects (). size ();
2018-06-13 13:14:17 +02:00
for ( const GLVolume * vol : m_volumes . volumes )
{
if (( vol != nullptr ) && vol -> selected )
{
int object_id = vol -> select_group_id / 1000000 ;
// Objects with object_id >= 1000 have a specific meaning, for example the wipe tower proxy.
if ( object_id < 10000 )
return ( object_id >= objects_count ) ? - 1 : object_id ;
}
}
}
return - 1 ;
}
2018-07-24 15:32:44 +02:00
int GLCanvas3D :: _get_first_selected_volume_id ( int object_id ) const
2018-06-21 08:37:04 +02:00
{
2018-07-24 15:32:44 +02:00
int volume_id = - 1 ;
2018-06-21 08:37:04 +02:00
2018-07-24 15:32:44 +02:00
for ( const GLVolume * vol : m_volumes . volumes )
{
++ volume_id ;
if (( vol != nullptr ) && vol -> selected && ( object_id == vol -> select_group_id / 1000000 ))
return volume_id ;
2018-06-21 08:37:04 +02:00
}
2018-07-24 15:32:44 +02:00
2018-06-21 08:37:04 +02:00
return - 1 ;
}
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-06-21 08:37:04 +02:00
2018-07-24 13:39:17 +02:00
void GLCanvas3D :: _load_print_toolpaths ()
{
2018-10-04 10:41:11 +02:00
#if !ENABLE_USE_UNIQUE_GLCONTEXT
2018-07-24 13:39:17 +02:00
// ensures this canvas is current
if ( ! set_current ())
return ;
2018-10-04 10:41:11 +02:00
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
2018-07-24 13:39:17 +02:00
if ( m_print == nullptr )
return ;
2018-09-12 11:59:02 +02:00
if ( ! m_print -> is_step_done ( psSkirt ) || ! m_print -> is_step_done ( psBrim ))
2018-07-24 13:39:17 +02:00
return ;
2018-09-12 11:59:02 +02:00
if ( ! m_print -> has_skirt () && ( m_print -> config (). brim_width . value == 0 ))
2018-07-24 13:39:17 +02:00
return ;
const float color [] = { 0.5f , 1.0f , 0.5f , 1.0f }; // greenish
// number of skirt layers
size_t total_layer_count = 0 ;
2018-09-12 11:59:02 +02:00
for ( const PrintObject * print_object : m_print -> objects ())
2018-07-24 13:39:17 +02:00
{
total_layer_count = std :: max ( total_layer_count , print_object -> total_layer_count ());
}
2018-09-12 11:59:02 +02:00
size_t skirt_height = m_print -> has_infinite_skirt () ? total_layer_count : std :: min < size_t > ( m_print -> config (). skirt_height . value , total_layer_count );
if (( skirt_height == 0 ) && ( m_print -> config (). brim_width . value > 0 ))
2018-07-24 13:39:17 +02:00
skirt_height = 1 ;
// get first skirt_height layers (maybe this should be moved to a PrintObject method?)
2018-09-12 11:59:02 +02:00
const PrintObject * object0 = m_print -> objects (). front ();
2018-07-24 13:39:17 +02:00
std :: vector < float > print_zs ;
print_zs . reserve ( skirt_height * 2 );
2018-09-12 11:59:02 +02:00
for ( size_t i = 0 ; i < std :: min ( skirt_height , object0 -> layers (). size ()); ++ i )
2018-07-24 13:39:17 +02:00
{
2018-09-12 11:59:02 +02:00
print_zs . push_back ( float ( object0 -> layers ()[ i ] -> print_z ));
2018-07-24 13:39:17 +02:00
}
//FIXME why there are support layers?
2018-09-12 11:59:02 +02:00
for ( size_t i = 0 ; i < std :: min ( skirt_height , object0 -> support_layers (). size ()); ++ i )
2018-07-24 13:39:17 +02:00
{
2018-09-12 11:59:02 +02:00
print_zs . push_back ( float ( object0 -> support_layers ()[ i ] -> print_z ));
2018-07-24 13:39:17 +02:00
}
sort_remove_duplicates ( print_zs );
if ( print_zs . size () > skirt_height )
print_zs . erase ( print_zs . begin () + skirt_height , print_zs . end ());
m_volumes . volumes . emplace_back ( new GLVolume ( color ));
GLVolume & volume = * m_volumes . volumes . back ();
for ( size_t i = 0 ; i < skirt_height ; ++ i ) {
volume . print_zs . push_back ( print_zs [ i ]);
volume . offsets . push_back ( volume . indexed_vertex_array . quad_indices . size ());
volume . offsets . push_back ( volume . indexed_vertex_array . triangle_indices . size ());
if ( i == 0 )
2018-09-12 11:59:02 +02:00
_3DScene :: extrusionentity_to_verts ( m_print -> brim (), print_zs [ i ], Point ( 0 , 0 ), volume );
2018-07-24 13:39:17 +02:00
2018-09-12 11:59:02 +02:00
_3DScene :: extrusionentity_to_verts ( m_print -> skirt (), print_zs [ i ], Point ( 0 , 0 ), volume );
2018-07-24 13:39:17 +02:00
}
volume . bounding_box = volume . indexed_vertex_array . bounding_box ();
volume . indexed_vertex_array . finalize_geometry ( m_use_VBOs && m_initialized );
}
void GLCanvas3D :: _load_print_object_toolpaths ( const PrintObject & print_object , const std :: vector < std :: string >& str_tool_colors )
{
std :: vector < float > tool_colors = _parse_colors ( str_tool_colors );
struct Ctxt
{
const Points * shifted_copies ;
std :: vector < const Layer *> layers ;
bool has_perimeters ;
bool has_infill ;
bool has_support ;
const std :: vector < float >* tool_colors ;
// Number of vertices (each vertex is 6x4=24 bytes long)
static const size_t alloc_size_max () { return 131072 ; } // 3.15MB
// static const size_t alloc_size_max () { return 65536; } // 1.57MB
// static const size_t alloc_size_max () { return 32768; } // 786kB
static const size_t alloc_size_reserve () { return alloc_size_max () * 2 ; }
static const float * color_perimeters () { static float color [ 4 ] = { 1.0f , 1.0f , 0.0f , 1.f }; return color ; } // yellow
static const float * color_infill () { static float color [ 4 ] = { 1.0f , 0.5f , 0.5f , 1.f }; return color ; } // redish
static const float * color_support () { static float color [ 4 ] = { 0.5f , 1.0f , 0.5f , 1.f }; return color ; } // greenish
// For cloring by a tool, return a parsed color.
bool color_by_tool () const { return tool_colors != nullptr ; }
size_t number_tools () const { return this -> color_by_tool () ? tool_colors -> size () / 4 : 0 ; }
const float * color_tool ( size_t tool ) const { return tool_colors -> data () + tool * 4 ; }
int volume_idx ( int extruder , int feature ) const
{
return this -> color_by_tool () ? std :: min < int > ( this -> number_tools () - 1 , std :: max < int > ( extruder - 1 , 0 )) : feature ;
}
} ctxt ;
2018-09-12 11:59:02 +02:00
ctxt . shifted_copies = & print_object . copies ();
2018-07-24 13:39:17 +02:00
// order layers by print_z
2018-09-12 11:59:02 +02:00
ctxt . layers . reserve ( print_object . layers (). size () + print_object . support_layers (). size ());
for ( const Layer * layer : print_object . layers ())
2018-07-24 13:39:17 +02:00
ctxt . layers . push_back ( layer );
2018-09-12 11:59:02 +02:00
for ( const Layer * layer : print_object . support_layers ())
2018-07-24 13:39:17 +02:00
ctxt . layers . push_back ( layer );
std :: sort ( ctxt . layers . begin (), ctxt . layers . end (), []( const Layer * l1 , const Layer * l2 ) { return l1 -> print_z < l2 -> print_z ; });
// Maximum size of an allocation block: 32MB / sizeof(float)
2018-09-12 11:59:02 +02:00
ctxt . has_perimeters = print_object . is_step_done ( posPerimeters );
ctxt . has_infill = print_object . is_step_done ( posInfill );
ctxt . has_support = print_object . is_step_done ( posSupportMaterial );
2018-07-24 13:39:17 +02:00
ctxt . tool_colors = tool_colors . empty () ? nullptr : & tool_colors ;
BOOST_LOG_TRIVIAL ( debug ) << "Loading print object toolpaths in parallel - start" ;
//FIXME Improve the heuristics for a grain size.
size_t grain_size = std :: max ( ctxt . layers . size () / 16 , size_t ( 1 ));
tbb :: spin_mutex new_volume_mutex ;
auto new_volume = [ this , & new_volume_mutex ]( const float * color ) -> GLVolume * {
auto * volume = new GLVolume ( color );
new_volume_mutex . lock ();
m_volumes . volumes . emplace_back ( volume );
new_volume_mutex . unlock ();
return volume ;
};
const size_t volumes_cnt_initial = m_volumes . volumes . size ();
std :: vector < GLVolumeCollection > volumes_per_thread ( ctxt . layers . size ());
tbb :: parallel_for (
tbb :: blocked_range < size_t > ( 0 , ctxt . layers . size (), grain_size ),
[ & ctxt , & new_volume ]( const tbb :: blocked_range < size_t >& range ) {
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
GLVolumePtrs vols ;
#else
2018-07-24 13:39:17 +02:00
std :: vector < GLVolume *> vols ;
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-07-24 13:39:17 +02:00
if ( ctxt . color_by_tool ()) {
for ( size_t i = 0 ; i < ctxt . number_tools (); ++ i )
vols . emplace_back ( new_volume ( ctxt . color_tool ( i )));
}
else
vols = { new_volume ( ctxt . color_perimeters ()), new_volume ( ctxt . color_infill ()), new_volume ( ctxt . color_support ()) };
for ( GLVolume * vol : vols )
vol -> indexed_vertex_array . reserve ( ctxt . alloc_size_reserve ());
for ( size_t idx_layer = range . begin (); idx_layer < range . end (); ++ idx_layer ) {
const Layer * layer = ctxt . layers [ idx_layer ];
for ( size_t i = 0 ; i < vols . size (); ++ i ) {
GLVolume & vol = * vols [ i ];
if ( vol . print_zs . empty () || vol . print_zs . back () != layer -> print_z ) {
vol . print_zs . push_back ( layer -> print_z );
vol . offsets . push_back ( vol . indexed_vertex_array . quad_indices . size ());
vol . offsets . push_back ( vol . indexed_vertex_array . triangle_indices . size ());
}
}
for ( const Point & copy : * ctxt . shifted_copies ) {
2018-09-12 11:59:02 +02:00
for ( const LayerRegion * layerm : layer -> regions ()) {
2018-07-24 13:39:17 +02:00
if ( ctxt . has_perimeters )
_3DScene :: extrusionentity_to_verts ( layerm -> perimeters , float ( layer -> print_z ), copy ,
2018-09-12 11:59:02 +02:00
* vols [ ctxt . volume_idx ( layerm -> region () -> config (). perimeter_extruder . value , 0 )]);
2018-07-24 13:39:17 +02:00
if ( ctxt . has_infill ) {
for ( const ExtrusionEntity * ee : layerm -> fills . entities ) {
// fill represents infill extrusions of a single island.
const auto * fill = dynamic_cast < const ExtrusionEntityCollection *> ( ee );
if ( ! fill -> entities . empty ())
_3DScene :: extrusionentity_to_verts ( * fill , float ( layer -> print_z ), copy ,
* vols [ ctxt . volume_idx (
is_solid_infill ( fill -> entities . front () -> role ()) ?
2018-09-12 11:59:02 +02:00
layerm -> region () -> config (). solid_infill_extruder :
layerm -> region () -> config (). infill_extruder ,
2018-07-24 13:39:17 +02:00
1 )]);
}
}
}
if ( ctxt . has_support ) {
const SupportLayer * support_layer = dynamic_cast < const SupportLayer *> ( layer );
if ( support_layer ) {
for ( const ExtrusionEntity * extrusion_entity : support_layer -> support_fills . entities )
_3DScene :: extrusionentity_to_verts ( extrusion_entity , float ( layer -> print_z ), copy ,
* vols [ ctxt . volume_idx (
( extrusion_entity -> role () == erSupportMaterial ) ?
2018-09-12 11:59:02 +02:00
support_layer -> object () -> config (). support_material_extruder :
support_layer -> object () -> config (). support_material_interface_extruder ,
2018-07-24 13:39:17 +02:00
2 )]);
}
}
}
for ( size_t i = 0 ; i < vols . size (); ++ i ) {
GLVolume & vol = * vols [ i ];
if ( vol . indexed_vertex_array . vertices_and_normals_interleaved . size () / 6 > ctxt . alloc_size_max ()) {
// Store the vertex arrays and restart their containers,
vols [ i ] = new_volume ( vol . color );
GLVolume & vol_new = * vols [ i ];
// Assign the large pre-allocated buffers to the new GLVolume.
vol_new . indexed_vertex_array = std :: move ( vol . indexed_vertex_array );
// Copy the content back to the old GLVolume.
vol . indexed_vertex_array = vol_new . indexed_vertex_array ;
// Finalize a bounding box of the old GLVolume.
vol . bounding_box = vol . indexed_vertex_array . bounding_box ();
// Clear the buffers, but keep them pre-allocated.
vol_new . indexed_vertex_array . clear ();
// Just make sure that clear did not clear the reserved memory.
vol_new . indexed_vertex_array . reserve ( ctxt . alloc_size_reserve ());
}
}
}
for ( GLVolume * vol : vols ) {
vol -> bounding_box = vol -> indexed_vertex_array . bounding_box ();
vol -> indexed_vertex_array . shrink_to_fit ();
}
});
BOOST_LOG_TRIVIAL ( debug ) << "Loading print object toolpaths in parallel - finalizing results" ;
// Remove empty volumes from the newly added volumes.
m_volumes . volumes . erase (
std :: remove_if ( m_volumes . volumes . begin () + volumes_cnt_initial , m_volumes . volumes . end (),
[]( const GLVolume * volume ) { return volume -> empty (); }),
m_volumes . volumes . end ());
for ( size_t i = volumes_cnt_initial ; i < m_volumes . volumes . size (); ++ i )
m_volumes . volumes [ i ] -> indexed_vertex_array . finalize_geometry ( m_use_VBOs && m_initialized );
BOOST_LOG_TRIVIAL ( debug ) << "Loading print object toolpaths in parallel - end" ;
}
void GLCanvas3D :: _load_wipe_tower_toolpaths ( const std :: vector < std :: string >& str_tool_colors )
{
2018-09-12 11:59:02 +02:00
if (( m_print == nullptr ) || m_print -> wipe_tower_data (). tool_changes . empty ())
2018-07-24 13:39:17 +02:00
return ;
2018-09-12 11:59:02 +02:00
if ( ! m_print -> is_step_done ( psWipeTower ))
2018-07-24 13:39:17 +02:00
return ;
std :: vector < float > tool_colors = _parse_colors ( str_tool_colors );
struct Ctxt
{
const Print * print ;
const std :: vector < float > * tool_colors ;
2018-08-02 13:29:39 +02:00
WipeTower :: xy wipe_tower_pos ;
float wipe_tower_angle ;
2018-07-24 13:39:17 +02:00
// Number of vertices (each vertex is 6x4=24 bytes long)
static const size_t alloc_size_max () { return 131072 ; } // 3.15MB
static const size_t alloc_size_reserve () { return alloc_size_max () * 2 ; }
static const float * color_support () { static float color [ 4 ] = { 0.5f , 1.0f , 0.5f , 1.f }; return color ; } // greenish
// For cloring by a tool, return a parsed color.
bool color_by_tool () const { return tool_colors != nullptr ; }
size_t number_tools () const { return this -> color_by_tool () ? tool_colors -> size () / 4 : 0 ; }
const float * color_tool ( size_t tool ) const { return tool_colors -> data () + tool * 4 ; }
int volume_idx ( int tool , int feature ) const
{
return this -> color_by_tool () ? std :: min < int > ( this -> number_tools () - 1 , std :: max < int > ( tool , 0 )) : feature ;
}
const std :: vector < WipeTower :: ToolChangeResult >& tool_change ( size_t idx ) {
2018-09-12 11:59:02 +02:00
const auto & tool_changes = print -> wipe_tower_data (). tool_changes ;
2018-07-24 13:39:17 +02:00
return priming . empty () ?
2018-09-12 11:59:02 +02:00
(( idx == tool_changes . size ()) ? final : tool_changes [ idx ]) :
(( idx == 0 ) ? priming : ( idx == tool_changes . size () + 1 ) ? final : tool_changes [ idx - 1 ]);
2018-07-24 13:39:17 +02:00
}
std :: vector < WipeTower :: ToolChangeResult > priming ;
std :: vector < WipeTower :: ToolChangeResult > final ;
} ctxt ;
ctxt . print = m_print ;
ctxt . tool_colors = tool_colors . empty () ? nullptr : & tool_colors ;
2018-09-12 11:59:02 +02:00
if ( m_print -> wipe_tower_data (). priming && m_print -> config (). single_extruder_multi_material_priming )
ctxt . priming . emplace_back ( * m_print -> wipe_tower_data (). priming . get ());
if ( m_print -> wipe_tower_data (). final_purge )
ctxt . final . emplace_back ( * m_print -> wipe_tower_data (). final_purge . get ());
2018-07-24 13:39:17 +02:00
2018-09-12 11:59:02 +02:00
ctxt . wipe_tower_angle = ctxt . print -> config (). wipe_tower_rotation_angle . value / 180.f * PI ;
ctxt . wipe_tower_pos = WipeTower :: xy ( ctxt . print -> config (). wipe_tower_x . value , ctxt . print -> config (). wipe_tower_y . value );
2018-08-02 13:29:39 +02:00
2018-07-24 13:39:17 +02:00
BOOST_LOG_TRIVIAL ( debug ) << "Loading wipe tower toolpaths in parallel - start" ;
//FIXME Improve the heuristics for a grain size.
2018-09-12 11:59:02 +02:00
size_t n_items = m_print -> wipe_tower_data (). tool_changes . size () + ( ctxt . priming . empty () ? 0 : 1 );
2018-07-24 13:39:17 +02:00
size_t grain_size = std :: max ( n_items / 128 , size_t ( 1 ));
tbb :: spin_mutex new_volume_mutex ;
auto new_volume = [ this , & new_volume_mutex ]( const float * color ) -> GLVolume * {
auto * volume = new GLVolume ( color );
new_volume_mutex . lock ();
m_volumes . volumes . emplace_back ( volume );
new_volume_mutex . unlock ();
return volume ;
};
const size_t volumes_cnt_initial = m_volumes . volumes . size ();
std :: vector < GLVolumeCollection > volumes_per_thread ( n_items );
tbb :: parallel_for (
tbb :: blocked_range < size_t > ( 0 , n_items , grain_size ),
[ & ctxt , & new_volume ]( const tbb :: blocked_range < size_t >& range ) {
// Bounding box of this slab of a wipe tower.
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
GLVolumePtrs vols ;
#else
2018-07-24 13:39:17 +02:00
std :: vector < GLVolume *> vols ;
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-07-24 13:39:17 +02:00
if ( ctxt . color_by_tool ()) {
for ( size_t i = 0 ; i < ctxt . number_tools (); ++ i )
vols . emplace_back ( new_volume ( ctxt . color_tool ( i )));
}
else
vols = { new_volume ( ctxt . color_support ()) };
for ( GLVolume * volume : vols )
volume -> indexed_vertex_array . reserve ( ctxt . alloc_size_reserve ());
for ( size_t idx_layer = range . begin (); idx_layer < range . end (); ++ idx_layer ) {
const std :: vector < WipeTower :: ToolChangeResult > & layer = ctxt . tool_change ( idx_layer );
for ( size_t i = 0 ; i < vols . size (); ++ i ) {
GLVolume & vol = * vols [ i ];
if ( vol . print_zs . empty () || vol . print_zs . back () != layer . front (). print_z ) {
vol . print_zs . push_back ( layer . front (). print_z );
vol . offsets . push_back ( vol . indexed_vertex_array . quad_indices . size ());
vol . offsets . push_back ( vol . indexed_vertex_array . triangle_indices . size ());
}
}
for ( const WipeTower :: ToolChangeResult & extrusions : layer ) {
for ( size_t i = 1 ; i < extrusions . extrusions . size ();) {
const WipeTower :: Extrusion & e = extrusions . extrusions [ i ];
if ( e . width == 0. ) {
++ i ;
continue ;
}
size_t j = i + 1 ;
if ( ctxt . color_by_tool ())
for (; j < extrusions . extrusions . size () && extrusions . extrusions [ j ]. tool == e . tool && extrusions . extrusions [ j ]. width > 0.f ; ++ j );
else
for (; j < extrusions . extrusions . size () && extrusions . extrusions [ j ]. width > 0.f ; ++ j );
size_t n_lines = j - i ;
Lines lines ;
std :: vector < double > widths ;
std :: vector < double > heights ;
lines . reserve ( n_lines );
widths . reserve ( n_lines );
heights . assign ( n_lines , extrusions . layer_height );
2018-08-02 13:29:39 +02:00
WipeTower :: Extrusion e_prev = extrusions . extrusions [ i - 1 ];
if ( ! extrusions . priming ) { // wipe tower extrusions describe the wipe tower at the origin with no rotation
e_prev . pos . rotate ( ctxt . wipe_tower_angle );
e_prev . pos . translate ( ctxt . wipe_tower_pos );
}
2018-07-24 13:39:17 +02:00
for (; i < j ; ++ i ) {
2018-08-02 13:29:39 +02:00
WipeTower :: Extrusion e = extrusions . extrusions [ i ];
2018-07-24 13:39:17 +02:00
assert ( e . width > 0.f );
2018-08-02 13:29:39 +02:00
if ( ! extrusions . priming ) {
e . pos . rotate ( ctxt . wipe_tower_angle );
e . pos . translate ( ctxt . wipe_tower_pos );
}
2018-07-24 13:39:17 +02:00
lines . emplace_back ( Point :: new_scale ( e_prev . pos . x , e_prev . pos . y ), Point :: new_scale ( e . pos . x , e . pos . y ));
widths . emplace_back ( e . width );
2018-08-02 13:29:39 +02:00
e_prev = e ;
2018-07-24 13:39:17 +02:00
}
_3DScene :: thick_lines_to_verts ( lines , widths , heights , lines . front (). a == lines . back (). b , extrusions . print_z ,
* vols [ ctxt . volume_idx ( e . tool , 0 )]);
}
}
}
for ( size_t i = 0 ; i < vols . size (); ++ i ) {
GLVolume & vol = * vols [ i ];
if ( vol . indexed_vertex_array . vertices_and_normals_interleaved . size () / 6 > ctxt . alloc_size_max ()) {
// Store the vertex arrays and restart their containers,
vols [ i ] = new_volume ( vol . color );
GLVolume & vol_new = * vols [ i ];
// Assign the large pre-allocated buffers to the new GLVolume.
vol_new . indexed_vertex_array = std :: move ( vol . indexed_vertex_array );
// Copy the content back to the old GLVolume.
vol . indexed_vertex_array = vol_new . indexed_vertex_array ;
// Finalize a bounding box of the old GLVolume.
vol . bounding_box = vol . indexed_vertex_array . bounding_box ();
// Clear the buffers, but keep them pre-allocated.
vol_new . indexed_vertex_array . clear ();
// Just make sure that clear did not clear the reserved memory.
vol_new . indexed_vertex_array . reserve ( ctxt . alloc_size_reserve ());
}
}
for ( GLVolume * vol : vols ) {
vol -> bounding_box = vol -> indexed_vertex_array . bounding_box ();
vol -> indexed_vertex_array . shrink_to_fit ();
}
});
BOOST_LOG_TRIVIAL ( debug ) << "Loading wipe tower toolpaths in parallel - finalizing results" ;
// Remove empty volumes from the newly added volumes.
m_volumes . volumes . erase (
std :: remove_if ( m_volumes . volumes . begin () + volumes_cnt_initial , m_volumes . volumes . end (),
[]( const GLVolume * volume ) { return volume -> empty (); }),
m_volumes . volumes . end ());
for ( size_t i = volumes_cnt_initial ; i < m_volumes . volumes . size (); ++ i )
m_volumes . volumes [ i ] -> indexed_vertex_array . finalize_geometry ( m_use_VBOs && m_initialized );
BOOST_LOG_TRIVIAL ( debug ) << "Loading wipe tower toolpaths in parallel - end" ;
}
2018-06-05 10:56:55 +02:00
static inline int hex_digit_to_int ( const char c )
{
return
( c >= '0' && c <= '9' ) ? int ( c - '0' ) :
( c >= 'A' && c <= 'F' ) ? int ( c - 'A' ) + 10 :
( c >= 'a' && c <= 'f' ) ? int ( c - 'a' ) + 10 : - 1 ;
}
void GLCanvas3D :: _load_gcode_extrusion_paths ( const GCodePreviewData & preview_data , const std :: vector < float >& tool_colors )
{
// helper functions to select data in dependence of the extrusion view type
struct Helper
{
static float path_filter ( GCodePreviewData :: Extrusion :: EViewType type , const ExtrusionPath & path )
{
switch ( type )
{
case GCodePreviewData :: Extrusion :: FeatureType :
return ( float ) path . role ();
case GCodePreviewData :: Extrusion :: Height :
return path . height ;
case GCodePreviewData :: Extrusion :: Width :
return path . width ;
case GCodePreviewData :: Extrusion :: Feedrate :
return path . feedrate ;
case GCodePreviewData :: Extrusion :: VolumetricRate :
return path . feedrate * ( float ) path . mm3_per_mm ;
case GCodePreviewData :: Extrusion :: Tool :
return ( float ) path . extruder_id ;
2018-06-13 09:26:58 +02:00
default :
return 0.0f ;
2018-06-05 10:56:55 +02:00
}
return 0.0f ;
}
static GCodePreviewData :: Color path_color ( const GCodePreviewData & data , const std :: vector < float >& tool_colors , float value )
{
switch ( data . extrusion . view_type )
{
case GCodePreviewData :: Extrusion :: FeatureType :
return data . get_extrusion_role_color (( ExtrusionRole )( int ) value );
case GCodePreviewData :: Extrusion :: Height :
return data . get_height_color ( value );
case GCodePreviewData :: Extrusion :: Width :
return data . get_width_color ( value );
case GCodePreviewData :: Extrusion :: Feedrate :
return data . get_feedrate_color ( value );
case GCodePreviewData :: Extrusion :: VolumetricRate :
return data . get_volumetric_rate_color ( value );
case GCodePreviewData :: Extrusion :: Tool :
{
GCodePreviewData :: Color color ;
:: memcpy (( void * ) color . rgba , ( const void * )( tool_colors . data () + ( unsigned int ) value * 4 ), 4 * sizeof ( float ));
return color ;
}
2018-06-13 09:26:58 +02:00
default :
return GCodePreviewData :: Color :: Dummy ;
2018-06-05 10:56:55 +02:00
}
return GCodePreviewData :: Color :: Dummy ;
}
};
// Helper structure for filters
struct Filter
{
float value ;
ExtrusionRole role ;
GLVolume * volume ;
Filter ( float value , ExtrusionRole role )
: value ( value )
, role ( role )
, volume ( nullptr )
{
}
bool operator == ( const Filter & other ) const
{
if ( value != other . value )
return false ;
if ( role != other . role )
return false ;
return true ;
}
};
typedef std :: vector < Filter > FiltersList ;
2018-06-11 15:13:13 +02:00
size_t initial_volumes_count = m_volumes . volumes . size ();
2018-06-05 10:56:55 +02:00
// detects filters
FiltersList filters ;
for ( const GCodePreviewData :: Extrusion :: Layer & layer : preview_data . extrusion . layers )
{
for ( const ExtrusionPath & path : layer . paths )
{
ExtrusionRole role = path . role ();
float path_filter = Helper :: path_filter ( preview_data . extrusion . view_type , path );
if ( std :: find ( filters . begin (), filters . end (), Filter ( path_filter , role )) == filters . end ())
filters . emplace_back ( path_filter , role );
}
}
// nothing to render, return
if ( filters . empty ())
return ;
// creates a new volume for each filter
for ( Filter & filter : filters )
{
2018-06-11 15:13:13 +02:00
m_gcode_preview_volume_index . first_volumes . emplace_back ( GCodePreviewVolumeIndex :: Extrusion , ( unsigned int ) filter . role , ( unsigned int ) m_volumes . volumes . size ());
2018-06-05 10:56:55 +02:00
GLVolume * volume = new GLVolume ( Helper :: path_color ( preview_data , tool_colors , filter . value ). rgba );
if ( volume != nullptr )
{
filter . volume = volume ;
2018-07-27 08:49:58 +02:00
volume -> is_extrusion_path = true ;
2018-06-11 15:13:13 +02:00
m_volumes . volumes . emplace_back ( volume );
2018-06-05 10:56:55 +02:00
}
else
{
// an error occourred - restore to previous state and return
m_gcode_preview_volume_index . first_volumes . pop_back ();
2018-06-11 15:13:13 +02:00
if ( initial_volumes_count != m_volumes . volumes . size ())
2018-06-05 10:56:55 +02:00
{
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
GLVolumePtrs :: iterator begin = m_volumes . volumes . begin () + initial_volumes_count ;
GLVolumePtrs :: iterator end = m_volumes . volumes . end ();
for ( GLVolumePtrs :: iterator it = begin ; it < end ; ++ it )
#else
2018-06-11 15:13:13 +02:00
std :: vector < GLVolume *>:: iterator begin = m_volumes . volumes . begin () + initial_volumes_count ;
std :: vector < GLVolume *>:: iterator end = m_volumes . volumes . end ();
2018-06-05 10:56:55 +02:00
for ( std :: vector < GLVolume *>:: iterator it = begin ; it < end ; ++ it )
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-05 10:56:55 +02:00
{
GLVolume * volume = * it ;
delete volume ;
}
2018-06-11 15:13:13 +02:00
m_volumes . volumes . erase ( begin , end );
2018-06-05 10:56:55 +02:00
return ;
}
}
}
// populates volumes
for ( const GCodePreviewData :: Extrusion :: Layer & layer : preview_data . extrusion . layers )
{
for ( const ExtrusionPath & path : layer . paths )
{
float path_filter = Helper :: path_filter ( preview_data . extrusion . view_type , path );
FiltersList :: iterator filter = std :: find ( filters . begin (), filters . end (), Filter ( path_filter , path . role ()));
if ( filter != filters . end ())
{
filter -> volume -> print_zs . push_back ( layer . z );
filter -> volume -> offsets . push_back ( filter -> volume -> indexed_vertex_array . quad_indices . size ());
filter -> volume -> offsets . push_back ( filter -> volume -> indexed_vertex_array . triangle_indices . size ());
_3DScene :: extrusionentity_to_verts ( path , layer . z , * filter -> volume );
}
}
}
// finalize volumes and sends geometry to gpu
2018-06-11 15:13:13 +02:00
if ( m_volumes . volumes . size () > initial_volumes_count )
2018-06-05 10:56:55 +02:00
{
2018-06-11 15:13:13 +02:00
for ( size_t i = initial_volumes_count ; i < m_volumes . volumes . size (); ++ i )
2018-06-05 10:56:55 +02:00
{
2018-06-11 15:13:13 +02:00
GLVolume * volume = m_volumes . volumes [ i ];
2018-06-05 10:56:55 +02:00
volume -> bounding_box = volume -> indexed_vertex_array . bounding_box ();
2018-06-05 12:24:26 +02:00
volume -> indexed_vertex_array . finalize_geometry ( m_use_VBOs && m_initialized );
2018-06-05 10:56:55 +02:00
}
}
}
void GLCanvas3D :: _load_gcode_travel_paths ( const GCodePreviewData & preview_data , const std :: vector < float >& tool_colors )
{
2018-06-11 15:13:13 +02:00
size_t initial_volumes_count = m_volumes . volumes . size ();
2018-06-05 10:56:55 +02:00
m_gcode_preview_volume_index . first_volumes . emplace_back ( GCodePreviewVolumeIndex :: Travel , 0 , ( unsigned int ) initial_volumes_count );
bool res = true ;
switch ( preview_data . extrusion . view_type )
{
case GCodePreviewData :: Extrusion :: Feedrate :
{
res = _travel_paths_by_feedrate ( preview_data );
break ;
}
case GCodePreviewData :: Extrusion :: Tool :
{
res = _travel_paths_by_tool ( preview_data , tool_colors );
break ;
}
default :
{
res = _travel_paths_by_type ( preview_data );
break ;
}
}
if ( ! res )
{
// an error occourred - restore to previous state and return
2018-06-11 15:13:13 +02:00
if ( initial_volumes_count != m_volumes . volumes . size ())
2018-06-05 10:56:55 +02:00
{
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
GLVolumePtrs :: iterator begin = m_volumes . volumes . begin () + initial_volumes_count ;
GLVolumePtrs :: iterator end = m_volumes . volumes . end ();
for ( GLVolumePtrs :: iterator it = begin ; it < end ; ++ it )
#else
2018-06-11 15:13:13 +02:00
std :: vector < GLVolume *>:: iterator begin = m_volumes . volumes . begin () + initial_volumes_count ;
std :: vector < GLVolume *>:: iterator end = m_volumes . volumes . end ();
2018-06-05 10:56:55 +02:00
for ( std :: vector < GLVolume *>:: iterator it = begin ; it < end ; ++ it )
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-05 10:56:55 +02:00
{
GLVolume * volume = * it ;
delete volume ;
}
2018-06-11 15:13:13 +02:00
m_volumes . volumes . erase ( begin , end );
2018-06-05 10:56:55 +02:00
}
return ;
}
// finalize volumes and sends geometry to gpu
2018-06-11 15:13:13 +02:00
if ( m_volumes . volumes . size () > initial_volumes_count )
2018-06-05 10:56:55 +02:00
{
2018-06-11 15:13:13 +02:00
for ( size_t i = initial_volumes_count ; i < m_volumes . volumes . size (); ++ i )
2018-06-05 10:56:55 +02:00
{
2018-06-11 15:13:13 +02:00
GLVolume * volume = m_volumes . volumes [ i ];
2018-06-05 10:56:55 +02:00
volume -> bounding_box = volume -> indexed_vertex_array . bounding_box ();
2018-06-05 12:24:26 +02:00
volume -> indexed_vertex_array . finalize_geometry ( m_use_VBOs && m_initialized );
2018-06-05 10:56:55 +02:00
}
}
}
bool GLCanvas3D :: _travel_paths_by_type ( const GCodePreviewData & preview_data )
{
// Helper structure for types
struct Type
{
GCodePreviewData :: Travel :: EType value ;
GLVolume * volume ;
explicit Type ( GCodePreviewData :: Travel :: EType value )
: value ( value )
, volume ( nullptr )
{
}
bool operator == ( const Type & other ) const
{
return value == other . value ;
}
};
typedef std :: vector < Type > TypesList ;
// colors travels by travel type
// detects types
TypesList types ;
for ( const GCodePreviewData :: Travel :: Polyline & polyline : preview_data . travel . polylines )
{
if ( std :: find ( types . begin (), types . end (), Type ( polyline . type )) == types . end ())
types . emplace_back ( polyline . type );
}
// nothing to render, return
if ( types . empty ())
return true ;
// creates a new volume for each type
for ( Type & type : types )
{
GLVolume * volume = new GLVolume ( preview_data . travel . type_colors [ type . value ]. rgba );
if ( volume == nullptr )
return false ;
else
{
type . volume = volume ;
2018-06-11 15:13:13 +02:00
m_volumes . volumes . emplace_back ( volume );
2018-06-05 10:56:55 +02:00
}
}
// populates volumes
for ( const GCodePreviewData :: Travel :: Polyline & polyline : preview_data . travel . polylines )
{
TypesList :: iterator type = std :: find ( types . begin (), types . end (), Type ( polyline . type ));
if ( type != types . end ())
{
2018-08-21 17:43:05 +02:00
type -> volume -> print_zs . push_back ( unscale < double > ( polyline . polyline . bounding_box (). min ( 2 )));
2018-06-05 10:56:55 +02:00
type -> volume -> offsets . push_back ( type -> volume -> indexed_vertex_array . quad_indices . size ());
type -> volume -> offsets . push_back ( type -> volume -> indexed_vertex_array . triangle_indices . size ());
_3DScene :: polyline3_to_verts ( polyline . polyline , preview_data . travel . width , preview_data . travel . height , * type -> volume );
}
}
return true ;
}
bool GLCanvas3D :: _travel_paths_by_feedrate ( const GCodePreviewData & preview_data )
{
// Helper structure for feedrate
struct Feedrate
{
float value ;
GLVolume * volume ;
explicit Feedrate ( float value )
: value ( value )
, volume ( nullptr )
{
}
bool operator == ( const Feedrate & other ) const
{
return value == other . value ;
}
};
typedef std :: vector < Feedrate > FeedratesList ;
// colors travels by feedrate
// detects feedrates
FeedratesList feedrates ;
for ( const GCodePreviewData :: Travel :: Polyline & polyline : preview_data . travel . polylines )
{
if ( std :: find ( feedrates . begin (), feedrates . end (), Feedrate ( polyline . feedrate )) == feedrates . end ())
feedrates . emplace_back ( polyline . feedrate );
}
// nothing to render, return
if ( feedrates . empty ())
return true ;
// creates a new volume for each feedrate
for ( Feedrate & feedrate : feedrates )
{
GLVolume * volume = new GLVolume ( preview_data . get_feedrate_color ( feedrate . value ). rgba );
if ( volume == nullptr )
return false ;
else
{
feedrate . volume = volume ;
2018-06-11 15:13:13 +02:00
m_volumes . volumes . emplace_back ( volume );
2018-06-05 10:56:55 +02:00
}
}
// populates volumes
for ( const GCodePreviewData :: Travel :: Polyline & polyline : preview_data . travel . polylines )
{
FeedratesList :: iterator feedrate = std :: find ( feedrates . begin (), feedrates . end (), Feedrate ( polyline . feedrate ));
if ( feedrate != feedrates . end ())
{
2018-08-21 17:43:05 +02:00
feedrate -> volume -> print_zs . push_back ( unscale < double > ( polyline . polyline . bounding_box (). min ( 2 )));
2018-06-05 10:56:55 +02:00
feedrate -> volume -> offsets . push_back ( feedrate -> volume -> indexed_vertex_array . quad_indices . size ());
feedrate -> volume -> offsets . push_back ( feedrate -> volume -> indexed_vertex_array . triangle_indices . size ());
_3DScene :: polyline3_to_verts ( polyline . polyline , preview_data . travel . width , preview_data . travel . height , * feedrate -> volume );
}
}
return true ;
}
bool GLCanvas3D :: _travel_paths_by_tool ( const GCodePreviewData & preview_data , const std :: vector < float >& tool_colors )
{
// Helper structure for tool
struct Tool
{
unsigned int value ;
GLVolume * volume ;
explicit Tool ( unsigned int value )
: value ( value )
, volume ( nullptr )
{
}
bool operator == ( const Tool & other ) const
{
return value == other . value ;
}
};
typedef std :: vector < Tool > ToolsList ;
// colors travels by tool
// detects tools
ToolsList tools ;
for ( const GCodePreviewData :: Travel :: Polyline & polyline : preview_data . travel . polylines )
{
if ( std :: find ( tools . begin (), tools . end (), Tool ( polyline . extruder_id )) == tools . end ())
tools . emplace_back ( polyline . extruder_id );
}
// nothing to render, return
if ( tools . empty ())
return true ;
// creates a new volume for each tool
for ( Tool & tool : tools )
{
GLVolume * volume = new GLVolume ( tool_colors . data () + tool . value * 4 );
if ( volume == nullptr )
return false ;
else
{
tool . volume = volume ;
2018-06-11 15:13:13 +02:00
m_volumes . volumes . emplace_back ( volume );
2018-06-05 10:56:55 +02:00
}
}
// populates volumes
for ( const GCodePreviewData :: Travel :: Polyline & polyline : preview_data . travel . polylines )
{
ToolsList :: iterator tool = std :: find ( tools . begin (), tools . end (), Tool ( polyline . extruder_id ));
if ( tool != tools . end ())
{
2018-08-21 17:43:05 +02:00
tool -> volume -> print_zs . push_back ( unscale < double > ( polyline . polyline . bounding_box (). min ( 2 )));
2018-06-05 10:56:55 +02:00
tool -> volume -> offsets . push_back ( tool -> volume -> indexed_vertex_array . quad_indices . size ());
tool -> volume -> offsets . push_back ( tool -> volume -> indexed_vertex_array . triangle_indices . size ());
_3DScene :: polyline3_to_verts ( polyline . polyline , preview_data . travel . width , preview_data . travel . height , * tool -> volume );
}
}
return true ;
}
void GLCanvas3D :: _load_gcode_retractions ( const GCodePreviewData & preview_data )
{
2018-06-11 15:13:13 +02:00
m_gcode_preview_volume_index . first_volumes . emplace_back ( GCodePreviewVolumeIndex :: Retraction , 0 , ( unsigned int ) m_volumes . volumes . size ());
2018-06-05 10:56:55 +02:00
// nothing to render, return
if ( preview_data . retraction . positions . empty ())
return ;
GLVolume * volume = new GLVolume ( preview_data . retraction . color . rgba );
if ( volume != nullptr )
{
2018-06-11 15:13:13 +02:00
m_volumes . volumes . emplace_back ( volume );
2018-06-05 10:56:55 +02:00
GCodePreviewData :: Retraction :: PositionsList copy ( preview_data . retraction . positions );
2018-08-17 15:53:43 +02:00
std :: sort ( copy . begin (), copy . end (), []( const GCodePreviewData :: Retraction :: Position & p1 , const GCodePreviewData :: Retraction :: Position & p2 ){ return p1 . position ( 2 ) < p2 . position ( 2 ); });
2018-06-05 10:56:55 +02:00
for ( const GCodePreviewData :: Retraction :: Position & position : copy )
{
2018-08-21 17:43:05 +02:00
volume -> print_zs . push_back ( unscale < double > ( position . position ( 2 )));
2018-06-05 10:56:55 +02:00
volume -> offsets . push_back ( volume -> indexed_vertex_array . quad_indices . size ());
volume -> offsets . push_back ( volume -> indexed_vertex_array . triangle_indices . size ());
_3DScene :: point3_to_verts ( position . position , position . width , position . height , * volume );
}
// finalize volumes and sends geometry to gpu
volume -> bounding_box = volume -> indexed_vertex_array . bounding_box ();
2018-06-05 12:24:26 +02:00
volume -> indexed_vertex_array . finalize_geometry ( m_use_VBOs && m_initialized );
2018-06-05 10:56:55 +02:00
}
}
void GLCanvas3D :: _load_gcode_unretractions ( const GCodePreviewData & preview_data )
{
2018-06-11 15:13:13 +02:00
m_gcode_preview_volume_index . first_volumes . emplace_back ( GCodePreviewVolumeIndex :: Unretraction , 0 , ( unsigned int ) m_volumes . volumes . size ());
2018-06-05 10:56:55 +02:00
// nothing to render, return
if ( preview_data . unretraction . positions . empty ())
return ;
GLVolume * volume = new GLVolume ( preview_data . unretraction . color . rgba );
if ( volume != nullptr )
{
2018-06-11 15:13:13 +02:00
m_volumes . volumes . emplace_back ( volume );
2018-06-05 10:56:55 +02:00
GCodePreviewData :: Retraction :: PositionsList copy ( preview_data . unretraction . positions );
2018-08-17 15:53:43 +02:00
std :: sort ( copy . begin (), copy . end (), []( const GCodePreviewData :: Retraction :: Position & p1 , const GCodePreviewData :: Retraction :: Position & p2 ){ return p1 . position ( 2 ) < p2 . position ( 2 ); });
2018-06-05 10:56:55 +02:00
for ( const GCodePreviewData :: Retraction :: Position & position : copy )
{
2018-08-21 17:43:05 +02:00
volume -> print_zs . push_back ( unscale < double > ( position . position ( 2 )));
2018-06-05 10:56:55 +02:00
volume -> offsets . push_back ( volume -> indexed_vertex_array . quad_indices . size ());
volume -> offsets . push_back ( volume -> indexed_vertex_array . triangle_indices . size ());
_3DScene :: point3_to_verts ( position . position , position . width , position . height , * volume );
}
// finalize volumes and sends geometry to gpu
volume -> bounding_box = volume -> indexed_vertex_array . bounding_box ();
2018-06-05 12:24:26 +02:00
volume -> indexed_vertex_array . finalize_geometry ( m_use_VBOs && m_initialized );
2018-06-05 10:56:55 +02:00
}
}
void GLCanvas3D :: _load_shells ()
{
2018-06-11 15:13:13 +02:00
size_t initial_volumes_count = m_volumes . volumes . size ();
2018-06-05 10:56:55 +02:00
m_gcode_preview_volume_index . first_volumes . emplace_back ( GCodePreviewVolumeIndex :: Shell , 0 , ( unsigned int ) initial_volumes_count );
2018-09-12 11:59:02 +02:00
if ( m_print -> objects (). empty ())
2018-06-05 10:56:55 +02:00
// nothing to render, return
return ;
// adds objects' volumes
unsigned int object_id = 0 ;
2018-09-14 09:28:00 +02:00
for ( const PrintObject * obj : m_print -> objects ())
2018-06-05 10:56:55 +02:00
{
2018-09-14 09:28:00 +02:00
const ModelObject * model_obj = obj -> model_object ();
2018-06-05 10:56:55 +02:00
std :: vector < int > instance_ids ( model_obj -> instances . size ());
2018-06-13 09:26:58 +02:00
for ( int i = 0 ; i < ( int ) model_obj -> instances . size (); ++ i )
2018-06-05 10:56:55 +02:00
{
instance_ids [ i ] = i ;
}
2018-10-08 16:05:55 +02:00
#if ENABLE_EXTENDED_SELECTION
m_volumes . load_object ( model_obj , object_id , instance_ids , "object" , m_use_VBOs && m_initialized );
#else
2018-06-13 09:26:58 +02:00
m_volumes . load_object ( model_obj , object_id , instance_ids , "object" , "object" , "object" , m_use_VBOs && m_initialized );
2018-10-08 16:05:55 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-05 10:56:55 +02:00
++ object_id ;
}
// adds wipe tower's volume
2018-09-12 11:59:02 +02:00
double max_z = m_print -> objects ()[ 0 ] -> model_object () -> get_model () -> bounding_box (). max ( 2 );
const PrintConfig & config = m_print -> config ();
2018-06-05 10:56:55 +02:00
unsigned int extruders_count = config . nozzle_diameter . size ();
if (( extruders_count > 1 ) && config . single_extruder_multi_material && config . wipe_tower && ! config . complete_objects ) {
2018-07-27 15:56:27 +02:00
float depth = m_print -> get_wipe_tower_depth ();
2018-09-12 11:59:02 +02:00
if ( ! m_print -> is_step_done ( psWipeTower ))
2018-07-27 15:56:27 +02:00
depth = ( 900.f / config . wipe_tower_width ) * ( float )( extruders_count - 1 ) ;
m_volumes . load_wipe_tower_preview ( 1000 , config . wipe_tower_x , config . wipe_tower_y , config . wipe_tower_width , depth , max_z , config . wipe_tower_rotation_angle ,
2018-09-12 11:59:02 +02:00
m_use_VBOs && m_initialized , ! m_print -> is_step_done ( psWipeTower ), m_print -> config (). nozzle_diameter . values [ 0 ] * 1.25f * 4.5f );
2018-06-05 10:56:55 +02:00
}
}
void GLCanvas3D :: _update_gcode_volumes_visibility ( const GCodePreviewData & preview_data )
{
unsigned int size = ( unsigned int ) m_gcode_preview_volume_index . first_volumes . size ();
for ( unsigned int i = 0 ; i < size ; ++ i )
{
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
GLVolumePtrs :: iterator begin = m_volumes . volumes . begin () + m_gcode_preview_volume_index . first_volumes [ i ]. id ;
GLVolumePtrs :: iterator end = ( i + 1 < size ) ? m_volumes . volumes . begin () + m_gcode_preview_volume_index . first_volumes [ i + 1 ]. id : m_volumes . volumes . end ();
for ( GLVolumePtrs :: iterator it = begin ; it != end ; ++ it )
#else
2018-06-11 15:13:13 +02:00
std :: vector < GLVolume *>:: iterator begin = m_volumes . volumes . begin () + m_gcode_preview_volume_index . first_volumes [ i ]. id ;
std :: vector < GLVolume *>:: iterator end = ( i + 1 < size ) ? m_volumes . volumes . begin () + m_gcode_preview_volume_index . first_volumes [ i + 1 ]. id : m_volumes . volumes . end ();
2018-06-05 10:56:55 +02:00
for ( std :: vector < GLVolume *>:: iterator it = begin ; it != end ; ++ it )
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-05 10:56:55 +02:00
{
GLVolume * volume = * it ;
switch ( m_gcode_preview_volume_index . first_volumes [ i ]. type )
{
case GCodePreviewVolumeIndex :: Extrusion :
{
if (( ExtrusionRole ) m_gcode_preview_volume_index . first_volumes [ i ]. flag == erCustom )
volume -> zoom_to_volumes = false ;
volume -> is_active = preview_data . extrusion . is_role_flag_set (( ExtrusionRole ) m_gcode_preview_volume_index . first_volumes [ i ]. flag );
break ;
}
case GCodePreviewVolumeIndex :: Travel :
{
volume -> is_active = preview_data . travel . is_visible ;
volume -> zoom_to_volumes = false ;
break ;
}
case GCodePreviewVolumeIndex :: Retraction :
{
volume -> is_active = preview_data . retraction . is_visible ;
volume -> zoom_to_volumes = false ;
break ;
}
case GCodePreviewVolumeIndex :: Unretraction :
{
volume -> is_active = preview_data . unretraction . is_visible ;
volume -> zoom_to_volumes = false ;
break ;
}
case GCodePreviewVolumeIndex :: Shell :
{
volume -> is_active = preview_data . shell . is_visible ;
volume -> color [ 3 ] = 0.25f ;
volume -> zoom_to_volumes = false ;
break ;
}
default :
{
volume -> is_active = false ;
volume -> zoom_to_volumes = false ;
break ;
}
}
}
}
}
2018-07-24 13:39:17 +02:00
void GLCanvas3D :: _update_toolpath_volumes_outside_state ()
{
// tolerance to avoid false detection at bed edges
2018-08-24 10:20:00 +02:00
static const double tolerance_x = 0.05 ;
static const double tolerance_y = 0.05 ;
2018-07-24 13:39:17 +02:00
BoundingBoxf3 print_volume ;
if ( m_config != nullptr )
{
const ConfigOptionPoints * opt = dynamic_cast < const ConfigOptionPoints *> ( m_config -> option ( "bed_shape" ));
if ( opt != nullptr )
{
BoundingBox bed_box_2D = get_extents ( Polygon :: new_scale ( opt -> values ));
2018-08-21 17:43:05 +02:00
print_volume = BoundingBoxf3 ( Vec3d ( unscale < double > ( bed_box_2D . min ( 0 )) - tolerance_x , unscale < double > ( bed_box_2D . min ( 1 )) - tolerance_y , 0.0 ), Vec3d ( unscale < double > ( bed_box_2D . max ( 0 )) + tolerance_x , unscale < double > ( bed_box_2D . max ( 1 )) + tolerance_y , m_config -> opt_float ( "max_print_height" )));
2018-07-24 13:39:17 +02:00
// Allow the objects to protrude below the print bed
2018-08-17 18:07:45 +02:00
print_volume . min ( 2 ) = - 1e10 ;
2018-07-24 13:39:17 +02:00
}
}
for ( GLVolume * volume : m_volumes . volumes )
{
2018-07-27 08:49:58 +02:00
volume -> is_outside = (( print_volume . radius () > 0.0 ) && volume -> is_extrusion_path ) ? ! print_volume . contains ( volume -> bounding_box ) : false ;
2018-07-24 13:39:17 +02:00
}
}
void GLCanvas3D :: _show_warning_texture_if_needed ()
{
2018-07-27 09:38:39 +02:00
if ( _is_any_volume_outside ())
2018-07-24 13:39:17 +02:00
{
enable_warning_texture ( true );
_generate_warning_texture ( L ( "Detected toolpath outside print volume" ));
}
else
{
enable_warning_texture ( false );
_reset_warning_texture ();
}
}
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
void GLCanvas3D :: _on_move ()
{
if ( m_model == nullptr )
return ;
2018-10-10 11:04:04 +02:00
std :: set < std :: pair < int , int >> done ; // prevent moving instances twice
2018-10-08 14:02:12 +02:00
bool object_moved = false ;
Vec3d wipe_tower_origin = Vec3d :: Zero ();
2018-10-16 13:49:40 +02:00
for ( const GLVolume * v : m_volumes . volumes )
2018-10-08 14:02:12 +02:00
{
int object_idx = v -> object_idx ();
int instance_idx = v -> instance_idx ();
// prevent moving instances twice
2018-10-10 11:04:04 +02:00
std :: pair < int , int > done_id ( object_idx , instance_idx );
2018-10-08 14:02:12 +02:00
if ( done . find ( done_id ) != done . end ())
continue ;
done . insert ( done_id );
if ( object_idx < 1000 )
{
2018-10-09 15:56:34 +02:00
// Move instances.
2018-10-08 14:02:12 +02:00
ModelObject * model_object = m_model -> objects [ object_idx ];
if ( model_object != nullptr )
{
model_object -> instances [ instance_idx ] -> set_offset ( v -> get_offset ());
model_object -> invalidate_bounding_box ();
object_moved = true ;
}
}
else if ( object_idx == 1000 )
// Move a wipe tower proxy.
wipe_tower_origin = v -> get_offset ();
}
if ( object_moved )
2018-10-09 17:14:59 +02:00
post_event ( SimpleEvent ( EVT_GLCANVAS_INSTANCE_MOVED ));
2018-10-08 14:02:12 +02:00
if ( wipe_tower_origin != Vec3d :: Zero ())
post_event ( Vec3dEvent ( EVT_GLCANVAS_WIPETOWER_MOVED , std :: move ( wipe_tower_origin )));
}
2018-10-09 15:56:34 +02:00
void GLCanvas3D :: _on_rotate ()
{
if ( m_model == nullptr )
return ;
2018-10-10 11:04:04 +02:00
std :: set < std :: pair < int , int >> done ; // prevent rotating instances twice
2018-10-09 15:56:34 +02:00
2018-10-16 13:49:40 +02:00
for ( const GLVolume * v : m_volumes . volumes )
2018-10-09 15:56:34 +02:00
{
int object_idx = v -> object_idx ();
2018-10-10 11:04:04 +02:00
if ( object_idx >= 1000 )
continue ;
2018-10-09 15:56:34 +02:00
int instance_idx = v -> instance_idx ();
// prevent rotating instances twice
2018-10-10 11:04:04 +02:00
std :: pair < int , int > done_id ( object_idx , instance_idx );
2018-10-09 15:56:34 +02:00
if ( done . find ( done_id ) != done . end ())
continue ;
done . insert ( done_id );
2018-10-10 11:04:04 +02:00
// Rotate instances.
ModelObject * model_object = m_model -> objects [ object_idx ];
if ( model_object != nullptr )
2018-10-09 15:56:34 +02:00
{
2018-10-10 11:04:04 +02:00
model_object -> instances [ instance_idx ] -> set_rotation ( v -> get_rotation ());
model_object -> invalidate_bounding_box ();
2018-10-09 15:56:34 +02:00
}
}
2018-10-10 11:04:04 +02:00
// schedule_background_process
2018-10-09 15:56:34 +02:00
}
2018-10-12 15:43:29 +02:00
void GLCanvas3D :: _on_scale ()
{
if ( m_model == nullptr )
return ;
std :: set < std :: pair < int , int >> done ; // prevent scaling instances twice
2018-10-16 13:49:40 +02:00
for ( const GLVolume * v : m_volumes . volumes )
2018-10-12 15:43:29 +02:00
{
int object_idx = v -> object_idx ();
if ( object_idx >= 1000 )
continue ;
int instance_idx = v -> instance_idx ();
2018-10-16 13:49:40 +02:00
// prevent scaling instances twice
2018-10-12 15:43:29 +02:00
std :: pair < int , int > done_id ( object_idx , instance_idx );
if ( done . find ( done_id ) != done . end ())
continue ;
done . insert ( done_id );
// Rotate instances.
ModelObject * model_object = m_model -> objects [ object_idx ];
if ( model_object != nullptr )
{
model_object -> instances [ instance_idx ] -> set_scaling_factor ( v -> get_scaling_factor ());
model_object -> invalidate_bounding_box ();
}
}
// schedule_background_process
}
2018-10-16 13:49:40 +02:00
void GLCanvas3D :: _on_flatten ()
{
_on_rotate ();
}
2018-10-08 14:02:12 +02:00
#else
2018-06-07 11:18:28 +02:00
void GLCanvas3D :: _on_move ( const std :: vector < int >& volume_idxs )
{
2018-06-11 15:13:13 +02:00
if ( m_model == nullptr )
2018-06-07 11:18:28 +02:00
return ;
2018-08-06 16:37:41 +02:00
std :: set < std :: string > done ; // prevent moving instances twice
2018-06-07 11:18:28 +02:00
bool object_moved = false ;
2018-09-05 09:11:58 +02:00
Vec3d wipe_tower_origin = Vec3d :: Zero ();
2018-06-07 11:18:28 +02:00
for ( int volume_idx : volume_idxs )
{
2018-06-11 15:13:13 +02:00
GLVolume * volume = m_volumes . volumes [ volume_idx ];
2018-06-07 11:18:28 +02:00
int obj_idx = volume -> object_idx ();
int instance_idx = volume -> instance_idx ();
2018-08-06 16:37:41 +02:00
// prevent moving instances twice
2018-06-07 11:18:28 +02:00
char done_id [ 64 ];
:: sprintf ( done_id , "%d_%d" , obj_idx , instance_idx );
if ( done . find ( done_id ) != done . end ())
continue ;
done . insert ( done_id );
if ( obj_idx < 1000 )
{
// Move a regular object.
ModelObject * model_object = m_model -> objects [ obj_idx ];
2018-09-06 09:16:32 +02:00
if ( model_object != nullptr )
{
2018-09-17 15:12:13 +02:00
model_object -> instances [ instance_idx ] -> set_offset ( volume -> get_offset ());
2018-09-06 09:16:32 +02:00
model_object -> invalidate_bounding_box ();
2018-10-04 16:43:10 +02:00
wxGetApp (). obj_manipul () -> update_position_values ();
2018-09-06 09:16:32 +02:00
object_moved = true ;
}
2018-06-07 11:18:28 +02:00
}
else if ( obj_idx == 1000 )
// Move a wipe tower proxy.
2018-09-05 09:11:58 +02:00
wipe_tower_origin = volume -> get_offset ();
2018-06-07 11:18:28 +02:00
}
if ( object_moved )
2018-10-09 17:14:59 +02:00
post_event ( SimpleEvent ( EVT_GLCANVAS_INSTANCE_MOVED ));
2018-06-07 11:18:28 +02:00
2018-09-05 09:11:58 +02:00
if ( wipe_tower_origin != Vec3d :: Zero ())
2018-10-03 11:34:39 +02:00
post_event ( Vec3dEvent ( EVT_GLCANVAS_WIPETOWER_MOVED , std :: move ( wipe_tower_origin )));
2018-06-07 11:18:28 +02:00
}
2018-10-08 14:02:12 +02:00
#endif // ENABLE_EXTENDED_SELECTION
2018-06-07 11:18:28 +02:00
2018-09-06 16:10:31 +02:00
void GLCanvas3D :: _on_select ( int volume_idx , int object_idx )
2018-06-08 11:37:07 +02:00
{
2018-10-08 14:02:12 +02:00
#if ENABLE_EXTENDED_SELECTION
post_event ( ObjectSelectEvent ( object_idx , - 1 ));
#else
2018-09-06 16:10:31 +02:00
int vol_id = - 1 ;
int obj_id = - 1 ;
2018-06-13 15:44:04 +02:00
if (( volume_idx != - 1 ) && ( volume_idx < ( int ) m_volumes . volumes . size ()))
2018-06-11 11:40:11 +02:00
{
2018-06-13 15:44:04 +02:00
if ( m_select_by == "volume" )
2018-09-06 16:10:31 +02:00
{
if ( m_volumes . volumes [ volume_idx ] -> object_idx () != object_idx )
{
set_select_by ( "object" );
obj_id = m_volumes . volumes [ volume_idx ] -> object_idx ();
vol_id = - 1 ;
}
else
{
obj_id = object_idx ;
vol_id = m_volumes . volumes [ volume_idx ] -> volume_idx ();
}
}
2018-06-13 15:44:04 +02:00
else if ( m_select_by == "object" )
2018-09-06 16:10:31 +02:00
{
obj_id = m_volumes . volumes [ volume_idx ] -> object_idx ();
vol_id = - 1 ;
}
2018-06-11 11:40:11 +02:00
}
2018-09-06 16:10:31 +02:00
2018-10-03 11:34:39 +02:00
post_event ( ObjectSelectEvent ( obj_id , vol_id ));
2018-10-05 23:29:15 +02:00
wxGetApp (). obj_list () -> select_current_volume ( obj_id , vol_id );
2018-10-08 14:02:12 +02:00
#endif // !ENABLE_EXTENDED_SELECTION
2018-06-08 11:37:07 +02:00
}
2018-06-05 14:09:36 +02:00
std :: vector < float > GLCanvas3D :: _parse_colors ( const std :: vector < std :: string >& colors )
{
2018-06-08 11:37:07 +02:00
static const float INV_255 = 1.0f / 255.0f ;
2018-06-05 14:09:36 +02:00
std :: vector < float > output ( colors . size () * 4 , 1.0f );
2018-06-08 11:37:07 +02:00
for ( size_t i = 0 ; i < colors . size (); ++ i )
{
2018-06-05 14:09:36 +02:00
const std :: string & color = colors [ i ];
const char * c = color . data () + 1 ;
if (( color . size () == 7 ) && ( color . front () == '#' ))
{
for ( size_t j = 0 ; j < 3 ; ++ j )
{
int digit1 = hex_digit_to_int ( * c ++ );
int digit2 = hex_digit_to_int ( * c ++ );
if (( digit1 == - 1 ) || ( digit2 == - 1 ))
break ;
2018-06-08 11:37:07 +02:00
output [ i * 4 + j ] = float ( digit1 * 16 + digit2 ) * INV_255 ;
2018-06-05 14:09:36 +02:00
}
}
}
return output ;
}
2018-07-19 13:18:19 +02:00
void GLCanvas3D :: _generate_legend_texture ( const GCodePreviewData & preview_data , const std :: vector < float >& tool_colors )
{
2018-10-04 10:41:11 +02:00
#if !ENABLE_USE_UNIQUE_GLCONTEXT
2018-07-19 13:18:19 +02:00
if ( ! set_current ())
return ;
2018-10-04 10:41:11 +02:00
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
2018-07-19 13:18:19 +02:00
m_legend_texture . generate ( preview_data , tool_colors );
}
void GLCanvas3D :: _generate_warning_texture ( const std :: string & msg )
{
2018-10-04 10:41:11 +02:00
#if !ENABLE_USE_UNIQUE_GLCONTEXT
2018-07-19 13:18:19 +02:00
if ( ! set_current ())
return ;
2018-10-04 10:41:11 +02:00
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
2018-07-19 13:18:19 +02:00
m_warning_texture . generate ( msg );
}
void GLCanvas3D :: _reset_warning_texture ()
{
2018-10-04 10:41:11 +02:00
#if !ENABLE_USE_UNIQUE_GLCONTEXT
2018-07-19 13:18:19 +02:00
if ( ! set_current ())
return ;
2018-10-04 10:41:11 +02:00
#endif // !ENABLE_USE_UNIQUE_GLCONTEXT
2018-07-19 13:18:19 +02:00
m_warning_texture . reset ();
}
2018-07-27 09:38:39 +02:00
bool GLCanvas3D :: _is_any_volume_outside () const
{
for ( const GLVolume * volume : m_volumes . volumes )
{
if (( volume != nullptr ) && volume -> is_outside )
return true ;
}
return false ;
}
2018-07-31 12:25:00 +02:00
void GLCanvas3D :: _resize_toolbar () const
{
Size cnv_size = get_canvas_size ();
float zoom = get_camera_zoom ();
float inv_zoom = ( zoom != 0.0f ) ? 1.0f / zoom : 0.0f ;
switch ( m_toolbar . get_layout_type ())
{
default :
case GLToolbar :: Layout :: Horizontal :
{
// centers the toolbar on the top edge of the 3d scene
unsigned int toolbar_width = m_toolbar . get_width ();
float top = ( 0.5f * ( float ) cnv_size . get_height () - 2.0f ) * inv_zoom ;
float left = - 0.5f * ( float ) toolbar_width * inv_zoom ;
m_toolbar . set_position ( top , left );
break ;
}
case GLToolbar :: Layout :: Vertical :
{
// centers the toolbar on the right edge of the 3d scene
unsigned int toolbar_width = m_toolbar . get_width ();
unsigned int toolbar_height = m_toolbar . get_height ();
float top = 0.5f * ( float ) toolbar_height * inv_zoom ;
float left = ( 0.5f * ( float ) cnv_size . get_width () - toolbar_width - 2.0f ) * inv_zoom ;
m_toolbar . set_position ( top , left );
break ;
}
}
}
2018-05-09 10:47:04 +02:00
} // namespace GUI
} // namespace Slic3r