2018-11-26 15:19:42 +01:00
// Include GLGizmo.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
#include "GLGizmo.hpp"
2018-06-13 15:44:04 +02:00
#include <GL/glew.h>
2018-11-26 14:41:58 +01:00
#include <Eigen/Dense>
2018-06-13 09:12:16 +02:00
2018-11-26 14:41:58 +01:00
#include "libslic3r/libslic3r.h"
#include "libslic3r/Geometry.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/SLA/SLASupportTree.hpp"
2018-12-21 12:35:20 +01:00
#include "libslic3r/SLAPrint.hpp"
2018-11-06 18:01:18 +01:00
2018-10-18 15:13:38 +02:00
#include <cstdio>
2018-08-20 13:02:54 +02:00
#include <numeric>
2018-10-18 15:13:38 +02:00
#include <algorithm>
#include <wx/sizer.h>
#include <wx/panel.h>
#include <wx/button.h>
#include <wx/checkbox.h>
#include <wx/stattext.h>
#include <wx/debug.h>
2018-12-07 14:10:16 +01:00
#include "Tab.hpp"
2018-10-18 15:13:38 +02:00
#include "GUI.hpp"
#include "GUI_Utils.hpp"
#include "GUI_App.hpp"
2018-11-26 14:41:58 +01:00
#include "I18N.hpp"
#include "PresetBundle.hpp"
2018-10-18 15:13:38 +02:00
2018-11-22 10:14:31 +01:00
#include <wx/defs.h>
2018-10-18 15:13:38 +02:00
// TODO: Display tooltips quicker on Linux
2018-06-13 09:12:16 +02:00
2018-08-20 10:23:17 +02:00
static const float DEFAULT_BASE_COLOR [ 3 ] = { 0.625f , 0.625f , 0.625f };
static const float DEFAULT_DRAG_COLOR [ 3 ] = { 1.0f , 1.0f , 1.0f };
static const float DEFAULT_HIGHLIGHT_COLOR [ 3 ] = { 1.0f , 0.38f , 0.0f };
2018-09-07 13:40:26 +02:00
static const float AXES_COLOR [ 3 ][ 3 ] = { { 1.0f , 0.0f , 0.0f }, { 0.0f , 1.0f , 0.0f }, { 0.0f , 0.0f , 1.0f } };
2018-08-21 08:50:35 +02:00
2018-06-13 09:12:16 +02:00
namespace Slic3r {
namespace GUI {
2018-09-11 14:48:17 +02:00
const float GLGizmoBase :: Grabber :: SizeFactor = 0.025f ;
const float GLGizmoBase :: Grabber :: MinHalfSize = 1.5f ;
2018-08-20 10:23:17 +02:00
const float GLGizmoBase :: Grabber :: DraggingScaleFactor = 1.25f ;
2018-06-15 14:10:28 +02:00
GLGizmoBase :: Grabber :: Grabber ()
2018-08-28 13:23:28 +02:00
: center ( Vec3d :: Zero ())
, angles ( Vec3d :: Zero ())
2018-08-20 10:23:17 +02:00
, dragging ( false )
2018-09-11 12:40:42 +02:00
, enabled ( true )
2018-06-15 14:10:28 +02:00
{
color [ 0 ] = 1.0f ;
color [ 1 ] = 1.0f ;
color [ 2 ] = 1.0f ;
}
2018-10-12 16:18:37 +02:00
void GLGizmoBase :: Grabber :: render ( bool hover , float size ) const
2018-06-15 14:10:28 +02:00
{
2018-08-20 10:23:17 +02:00
float render_color [ 3 ];
if ( hover )
{
render_color [ 0 ] = 1.0f - color [ 0 ];
render_color [ 1 ] = 1.0f - color [ 1 ];
render_color [ 2 ] = 1.0f - color [ 2 ];
}
else
:: memcpy (( void * ) render_color , ( const void * ) color , 3 * sizeof ( float ));
2018-06-15 14:10:28 +02:00
2018-10-12 16:18:37 +02:00
render ( size , render_color , true );
2018-08-20 10:23:17 +02:00
}
2018-11-15 10:16:51 +01:00
float GLGizmoBase :: Grabber :: get_half_size ( float size ) const
{
return std :: max ( size * SizeFactor , MinHalfSize );
}
float GLGizmoBase :: Grabber :: get_dragging_half_size ( float size ) const
{
return std :: max ( size * SizeFactor * DraggingScaleFactor , MinHalfSize );
}
2018-10-12 16:18:37 +02:00
void GLGizmoBase :: Grabber :: render ( float size , const float * render_color , bool use_lighting ) const
2018-08-20 10:23:17 +02:00
{
2018-11-15 10:16:51 +01:00
float half_size = dragging ? get_dragging_half_size ( size ) : get_half_size ( size );
2018-09-11 14:48:17 +02:00
2018-08-21 14:27:36 +02:00
if ( use_lighting )
:: glEnable ( GL_LIGHTING );
2018-08-20 10:23:17 +02:00
2018-10-11 10:52:50 +02:00
:: glColor3fv ( render_color );
2018-06-15 14:10:28 +02:00
2018-06-15 16:16:55 +02:00
:: glPushMatrix ();
2018-10-11 10:52:50 +02:00
:: glTranslated ( center ( 0 ), center ( 1 ), center ( 2 ));
2018-08-20 10:23:17 +02:00
2018-10-15 11:30:50 +02:00
:: glRotated ( Geometry :: rad2deg ( angles ( 2 )), 0.0 , 0.0 , 1.0 );
:: glRotated ( Geometry :: rad2deg ( angles ( 1 )), 0.0 , 1.0 , 0.0 );
:: glRotated ( Geometry :: rad2deg ( angles ( 0 )), 1.0 , 0.0 , 0.0 );
2018-06-15 16:16:55 +02:00
2018-08-21 14:27:36 +02:00
// face min x
:: glPushMatrix ();
:: glTranslatef ( - ( GLfloat ) half_size , 0.0f , 0.0f );
:: glRotatef ( - 90.0f , 0.0f , 1.0f , 0.0f );
render_face ( half_size );
:: glPopMatrix ();
// face max x
:: glPushMatrix ();
:: glTranslatef (( GLfloat ) half_size , 0.0f , 0.0f );
:: glRotatef ( 90.0f , 0.0f , 1.0f , 0.0f );
render_face ( half_size );
:: glPopMatrix ();
// face min y
:: glPushMatrix ();
:: glTranslatef ( 0.0f , - ( GLfloat ) half_size , 0.0f );
:: glRotatef ( 90.0f , 1.0f , 0.0f , 0.0f );
render_face ( half_size );
:: glPopMatrix ();
// face max y
:: glPushMatrix ();
:: glTranslatef ( 0.0f , ( GLfloat ) half_size , 0.0f );
:: glRotatef ( - 90.0f , 1.0f , 0.0f , 0.0f );
render_face ( half_size );
:: glPopMatrix ();
// face min z
:: glPushMatrix ();
:: glTranslatef ( 0.0f , 0.0f , - ( GLfloat ) half_size );
:: glRotatef ( 180.0f , 1.0f , 0.0f , 0.0f );
render_face ( half_size );
:: glPopMatrix ();
// face max z
:: glPushMatrix ();
:: glTranslatef ( 0.0f , 0.0f , ( GLfloat ) half_size );
render_face ( half_size );
:: glPopMatrix ();
2018-06-15 14:10:28 +02:00
2018-06-15 16:16:55 +02:00
:: glPopMatrix ();
2018-08-21 14:27:36 +02:00
if ( use_lighting )
:: glDisable ( GL_LIGHTING );
2018-06-15 14:10:28 +02:00
}
2018-06-14 10:00:59 +02:00
2018-08-21 14:27:36 +02:00
void GLGizmoBase :: Grabber :: render_face ( float half_size ) const
{
:: glBegin ( GL_TRIANGLES );
:: glNormal3f ( 0.0f , 0.0f , 1.0f );
:: glVertex3f ( - ( GLfloat ) half_size , - ( GLfloat ) half_size , 0.0f );
:: glVertex3f (( GLfloat ) half_size , - ( GLfloat ) half_size , 0.0f );
:: glVertex3f (( GLfloat ) half_size , ( GLfloat ) half_size , 0.0f );
:: glVertex3f (( GLfloat ) half_size , ( GLfloat ) half_size , 0.0f );
:: glVertex3f ( - ( GLfloat ) half_size , ( GLfloat ) half_size , 0.0f );
:: glVertex3f ( - ( GLfloat ) half_size , - ( GLfloat ) half_size , 0.0f );
:: glEnd ();
}
2018-08-24 14:11:41 +02:00
GLGizmoBase :: GLGizmoBase ( GLCanvas3D & parent )
: m_parent ( parent )
, m_group_id ( - 1 )
2018-08-20 10:23:17 +02:00
, m_state ( Off )
2018-11-22 10:14:31 +01:00
, m_shortcut_key ( 0 )
2018-06-14 15:32:26 +02:00
, m_hover_id ( - 1 )
2018-09-10 10:01:49 +02:00
, m_dragging ( false )
2018-11-27 12:00:44 +01:00
#if ENABLE_IMGUI
2018-11-26 10:56:07 +01:00
, m_imgui ( wxGetApp (). imgui ())
2018-11-27 12:00:44 +01:00
#endif // ENABLE_IMGUI
2018-06-13 09:12:16 +02:00
{
2018-08-20 10:23:17 +02:00
:: memcpy (( void * ) m_base_color , ( const void * ) DEFAULT_BASE_COLOR , 3 * sizeof ( float ));
:: memcpy (( void * ) m_drag_color , ( const void * ) DEFAULT_DRAG_COLOR , 3 * sizeof ( float ));
:: memcpy (( void * ) m_highlight_color , ( const void * ) DEFAULT_HIGHLIGHT_COLOR , 3 * sizeof ( float ));
2018-06-13 10:49:59 +02:00
}
2018-06-14 15:32:26 +02:00
void GLGizmoBase :: set_hover_id ( int id )
{
2018-08-27 14:00:53 +02:00
if ( m_grabbers . empty () || ( id < ( int ) m_grabbers . size ()))
2018-08-20 10:23:17 +02:00
{
2018-06-15 14:10:28 +02:00
m_hover_id = id ;
2018-08-20 10:23:17 +02:00
on_set_hover_id ();
}
}
void GLGizmoBase :: set_highlight_color ( const float * color )
{
if ( color != nullptr )
:: memcpy (( void * ) m_highlight_color , ( const void * ) color , 3 * sizeof ( float ));
2018-06-15 14:10:28 +02:00
}
2018-09-11 12:40:42 +02:00
void GLGizmoBase :: enable_grabber ( unsigned int id )
{
if (( 0 <= id ) && ( id < ( unsigned int ) m_grabbers . size ()))
m_grabbers [ id ]. enabled = true ;
on_enable_grabber ( id );
}
void GLGizmoBase :: disable_grabber ( unsigned int id )
{
if (( 0 <= id ) && ( id < ( unsigned int ) m_grabbers . size ()))
m_grabbers [ id ]. enabled = false ;
on_disable_grabber ( id );
}
2018-10-15 11:30:50 +02:00
void GLGizmoBase :: start_dragging ( const GLCanvas3D :: Selection & selection )
2018-06-15 14:10:28 +02:00
{
2018-09-10 10:01:49 +02:00
m_dragging = true ;
2018-08-20 10:23:17 +02:00
for ( int i = 0 ; i < ( int ) m_grabbers . size (); ++ i )
{
m_grabbers [ i ]. dragging = ( m_hover_id == i );
}
2018-10-15 11:30:50 +02:00
on_start_dragging ( selection );
2018-06-15 14:10:28 +02:00
}
2018-07-12 11:26:13 +02:00
void GLGizmoBase :: stop_dragging ()
{
2018-09-10 10:01:49 +02:00
m_dragging = false ;
2018-08-24 15:49:57 +02:00
2018-08-20 10:23:17 +02:00
for ( int i = 0 ; i < ( int ) m_grabbers . size (); ++ i )
{
m_grabbers [ i ]. dragging = false ;
}
2018-07-12 11:26:13 +02:00
on_stop_dragging ();
}
2019-01-10 13:13:11 +01:00
void GLGizmoBase :: update ( const UpdateData & data , const GLCanvas3D :: Selection & selection )
2018-06-15 14:10:28 +02:00
{
if ( m_hover_id != - 1 )
2019-01-10 13:13:11 +01:00
on_update ( data , selection );
2018-06-14 15:32:26 +02:00
}
2018-08-20 10:23:17 +02:00
float GLGizmoBase :: picking_color_component ( unsigned int id ) const
2018-07-12 11:26:13 +02:00
{
2018-08-20 10:23:17 +02:00
int color = 254 - ( int ) id ;
if ( m_group_id > - 1 )
color -= m_group_id ;
2018-07-12 11:26:13 +02:00
2018-08-20 10:23:17 +02:00
return ( float ) color / 255.0f ;
2018-06-18 15:07:17 +02:00
}
2018-09-11 14:48:17 +02:00
void GLGizmoBase :: render_grabbers ( const BoundingBoxf3 & box ) const
2018-06-14 10:00:59 +02:00
{
2018-10-12 16:18:37 +02:00
float size = ( float ) box . max_size ();
2018-06-19 09:46:26 +02:00
for ( int i = 0 ; i < ( int ) m_grabbers . size (); ++ i )
2018-06-14 15:32:26 +02:00
{
2018-09-11 12:40:42 +02:00
if ( m_grabbers [ i ]. enabled )
2018-10-12 16:18:37 +02:00
m_grabbers [ i ]. render (( m_hover_id == i ), size );
2018-06-14 15:32:26 +02:00
}
2018-06-14 10:00:59 +02:00
}
2018-11-28 16:09:04 +01:00
void GLGizmoBase :: render_grabbers ( float size ) const
{
for ( int i = 0 ; i < ( int ) m_grabbers . size (); ++ i )
{
if ( m_grabbers [ i ]. enabled )
m_grabbers [ i ]. render (( m_hover_id == i ), size );
}
}
2018-09-11 14:48:17 +02:00
void GLGizmoBase :: render_grabbers_for_picking ( const BoundingBoxf3 & box ) const
2018-08-20 10:23:17 +02:00
{
2018-10-12 16:18:37 +02:00
float size = ( float ) box . max_size ();
2018-09-07 12:20:56 +02:00
for ( unsigned int i = 0 ; i < ( unsigned int ) m_grabbers . size (); ++ i )
2018-08-20 10:23:17 +02:00
{
2018-09-11 12:40:42 +02:00
if ( m_grabbers [ i ]. enabled )
{
m_grabbers [ i ]. color [ 0 ] = 1.0f ;
m_grabbers [ i ]. color [ 1 ] = 1.0f ;
m_grabbers [ i ]. color [ 2 ] = picking_color_component ( i );
2018-10-12 16:18:37 +02:00
m_grabbers [ i ]. render_for_picking ( size );
2018-09-11 12:40:42 +02:00
}
2018-08-20 10:23:17 +02:00
}
}
2018-11-27 12:18:43 +01:00
#if !ENABLE_IMGUI
2018-10-18 15:13:38 +02:00
void GLGizmoBase :: create_external_gizmo_widgets ( wxWindow * parent ) {}
2018-11-26 10:56:07 +01:00
#endif // not ENABLE_IMGUI
2018-10-18 15:13:38 +02:00
2018-08-24 14:11:41 +02:00
void GLGizmoBase :: set_tooltip ( const std :: string & tooltip ) const
{
m_parent . set_tooltip ( tooltip );
}
std :: string GLGizmoBase :: format ( float value , unsigned int decimals ) const
{
2018-11-19 11:07:39 +01:00
return Slic3r :: string_printf ( "%.*f" , decimals , value );
2018-08-24 14:11:41 +02:00
}
2018-06-14 10:00:59 +02:00
const float GLGizmoRotate :: Offset = 5.0f ;
const unsigned int GLGizmoRotate :: CircleResolution = 64 ;
2018-06-15 16:16:55 +02:00
const unsigned int GLGizmoRotate :: AngleResolution = 64 ;
2018-08-20 10:23:17 +02:00
const unsigned int GLGizmoRotate :: ScaleStepsCount = 72 ;
2018-06-14 10:00:59 +02:00
const float GLGizmoRotate :: ScaleStepRad = 2.0f * ( float ) PI / GLGizmoRotate :: ScaleStepsCount ;
2018-08-20 10:23:17 +02:00
const unsigned int GLGizmoRotate :: ScaleLongEvery = 2 ;
2018-09-21 13:44:38 +02:00
const float GLGizmoRotate :: ScaleLongTooth = 0.1f ; // in percent of radius
2018-06-14 10:00:59 +02:00
const unsigned int GLGizmoRotate :: SnapRegionsCount = 8 ;
2018-09-21 13:44:38 +02:00
const float GLGizmoRotate :: GrabberOffset = 0.15f ; // in percent of radius
2018-06-14 10:00:59 +02:00
2018-08-24 14:11:41 +02:00
GLGizmoRotate :: GLGizmoRotate ( GLCanvas3D & parent , GLGizmoRotate :: Axis axis )
: GLGizmoBase ( parent )
2018-08-20 10:23:17 +02:00
, m_axis ( axis )
2018-08-28 13:23:28 +02:00
, m_angle ( 0.0 )
2018-12-03 09:37:46 +01:00
, m_quadric ( nullptr )
2018-08-23 15:37:38 +02:00
, m_center ( 0.0 , 0.0 , 0.0 )
2018-06-15 16:16:55 +02:00
, m_radius ( 0.0f )
2018-09-21 13:44:38 +02:00
, m_snap_coarse_in_radius ( 0.0f )
, m_snap_coarse_out_radius ( 0.0f )
, m_snap_fine_in_radius ( 0.0f )
, m_snap_fine_out_radius ( 0.0f )
2018-06-13 09:12:16 +02:00
{
2018-12-03 09:37:46 +01:00
m_quadric = :: gluNewQuadric ();
if ( m_quadric != nullptr )
:: gluQuadricDrawStyle ( m_quadric , GLU_FILL );
}
GLGizmoRotate :: GLGizmoRotate ( const GLGizmoRotate & other )
: GLGizmoBase ( other . m_parent )
, m_axis ( other . m_axis )
, m_angle ( other . m_angle )
, m_quadric ( nullptr )
, m_center ( other . m_center )
, m_radius ( other . m_radius )
, m_snap_coarse_in_radius ( other . m_snap_coarse_in_radius )
, m_snap_coarse_out_radius ( other . m_snap_coarse_out_radius )
, m_snap_fine_in_radius ( other . m_snap_fine_in_radius )
, m_snap_fine_out_radius ( other . m_snap_fine_out_radius )
{
m_quadric = :: gluNewQuadric ();
if ( m_quadric != nullptr )
:: gluQuadricDrawStyle ( m_quadric , GLU_FILL );
}
GLGizmoRotate ::~ GLGizmoRotate ()
{
if ( m_quadric != nullptr )
:: gluDeleteQuadric ( m_quadric );
2018-06-13 09:12:16 +02:00
}
2018-08-28 13:23:28 +02:00
void GLGizmoRotate :: set_angle ( double angle )
2018-06-19 09:46:26 +02:00
{
2018-08-28 13:23:28 +02:00
if ( std :: abs ( angle - 2.0 * ( double ) PI ) < EPSILON )
angle = 0.0 ;
2018-06-19 09:46:26 +02:00
2018-08-20 10:23:17 +02:00
m_angle = angle ;
2018-06-19 09:46:26 +02:00
}
2018-06-13 09:12:16 +02:00
bool GLGizmoRotate :: on_init ()
{
2018-06-15 14:10:28 +02:00
m_grabbers . push_back ( Grabber ());
2018-06-13 09:12:16 +02:00
return true ;
}
2018-10-15 11:30:50 +02:00
void GLGizmoRotate :: on_start_dragging ( const GLCanvas3D :: Selection & selection )
2018-09-10 10:01:49 +02:00
{
2018-10-15 11:30:50 +02:00
const BoundingBoxf3 & box = selection . get_bounding_box ();
2018-09-10 10:01:49 +02:00
m_center = box . center ();
m_radius = Offset + box . radius ();
2018-09-21 13:44:38 +02:00
m_snap_coarse_in_radius = m_radius / 3.0f ;
m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius ;
m_snap_fine_in_radius = m_radius ;
m_snap_fine_out_radius = m_snap_fine_in_radius + m_radius * ScaleLongTooth ;
2018-09-10 10:01:49 +02:00
}
2019-01-10 13:13:11 +01:00
void GLGizmoRotate :: on_update ( const UpdateData & data , const GLCanvas3D :: Selection & selection )
2018-11-14 12:57:12 +01:00
{
2019-01-10 13:13:11 +01:00
Vec2d mouse_pos = to_2d ( mouse_position_in_local_plane ( data . mouse_ray , selection ));
2018-08-20 10:23:17 +02:00
2018-08-23 15:37:38 +02:00
Vec2d orig_dir = Vec2d :: UnitX ();
Vec2d new_dir = mouse_pos . normalized ();
2018-08-20 10:23:17 +02:00
2018-08-23 15:37:38 +02:00
double theta = :: acos ( clamp ( - 1.0 , 1.0 , new_dir . dot ( orig_dir )));
if ( cross2 ( orig_dir , new_dir ) < 0.0 )
2018-08-24 10:20:00 +02:00
theta = 2.0 * ( double ) PI - theta ;
2018-06-15 16:16:55 +02:00
2018-08-23 15:37:38 +02:00
double len = mouse_pos . norm ();
2018-08-24 12:16:11 +02:00
2018-09-21 13:44:38 +02:00
// snap to coarse snap region
if (( m_snap_coarse_in_radius <= len ) && ( len <= m_snap_coarse_out_radius ))
2018-06-15 16:16:55 +02:00
{
2018-08-24 10:20:00 +02:00
double step = 2.0 * ( double ) PI / ( double ) SnapRegionsCount ;
theta = step * ( double ) std :: round ( theta / step );
2018-06-15 16:16:55 +02:00
}
2018-08-24 12:16:11 +02:00
else
{
2018-09-21 13:44:38 +02:00
// snap to fine snap region (scale)
if (( m_snap_fine_in_radius <= len ) && ( len <= m_snap_fine_out_radius ))
2018-08-24 12:16:11 +02:00
{
double step = 2.0 * ( double ) PI / ( double ) ScaleStepsCount ;
theta = step * ( double ) std :: round ( theta / step );
}
}
2018-06-15 16:16:55 +02:00
2018-08-24 10:20:00 +02:00
if ( theta == 2.0 * ( double ) PI )
2018-06-15 16:16:55 +02:00
theta = 0.0 ;
2018-06-19 09:46:26 +02:00
2018-08-28 13:23:28 +02:00
m_angle = theta ;
2018-06-15 14:10:28 +02:00
}
2018-10-15 11:30:50 +02:00
void GLGizmoRotate :: on_render ( const GLCanvas3D :: Selection & selection ) const
2018-06-13 15:44:04 +02:00
{
2018-09-11 12:40:42 +02:00
if ( ! m_grabbers [ 0 ]. enabled )
return ;
2018-10-15 11:30:50 +02:00
const BoundingBoxf3 & box = selection . get_bounding_box ();
2018-11-21 09:28:27 +01:00
#if !ENABLE_WORLD_ROTATIONS
2018-11-12 09:54:04 +01:00
bool single_selection = selection . is_single_full_instance () || selection . is_single_modifier () || selection . is_single_volume ();
2018-11-21 09:28:27 +01:00
#endif // !ENABLE_WORLD_ROTATIONS
2018-10-15 11:30:50 +02:00
2018-10-24 14:12:33 +02:00
std :: string axis ;
switch ( m_axis )
{
2018-12-03 10:17:42 +01:00
case X : { axis = "X" ; break ; }
case Y : { axis = "Y" ; break ; }
case Z : { axis = "Z" ; break ; }
2018-10-24 14:12:33 +02:00
}
2018-11-21 09:28:27 +01:00
#if ENABLE_WORLD_ROTATIONS
2018-12-03 10:17:42 +01:00
if ( ! m_dragging && ( m_hover_id == 0 ))
set_tooltip ( axis );
else if ( m_dragging )
2018-11-21 09:28:27 +01:00
#else
2018-11-12 09:54:04 +01:00
if (( single_selection && ( m_hover_id == 0 )) || m_dragging )
2018-11-21 09:28:27 +01:00
#endif // ENABLE_WORLD_ROTATIONS
2018-12-03 10:17:42 +01:00
set_tooltip ( axis + ": " + format (( float ) Geometry :: rad2deg ( m_angle ), 4 ) + " \u00B0 " );
2018-09-10 10:01:49 +02:00
else
2018-07-12 11:26:13 +02:00
{
2018-08-15 12:50:06 +02:00
m_center = box . center ();
2018-08-20 10:23:17 +02:00
m_radius = Offset + box . radius ();
2018-09-21 13:44:38 +02:00
m_snap_coarse_in_radius = m_radius / 3.0f ;
m_snap_coarse_out_radius = 2.0f * m_snap_coarse_in_radius ;
m_snap_fine_in_radius = m_radius ;
m_snap_fine_out_radius = m_radius * ( 1.0f + ScaleLongTooth );
2018-07-12 11:26:13 +02:00
}
2018-06-14 10:00:59 +02:00
2018-09-10 10:01:49 +02:00
:: glEnable ( GL_DEPTH_TEST );
2018-08-20 10:23:17 +02:00
:: glPushMatrix ();
2019-01-10 13:13:11 +01:00
transform_to_local ( selection );
2018-08-20 10:23:17 +02:00
2018-08-21 09:03:38 +02:00
:: glLineWidth (( m_hover_id != - 1 ) ? 2.0f : 1.5f );
:: glColor3fv (( m_hover_id != - 1 ) ? m_drag_color : m_highlight_color );
2018-06-15 16:16:55 +02:00
2018-08-20 10:23:17 +02:00
render_circle ();
2018-08-28 12:50:26 +02:00
2018-08-20 10:23:17 +02:00
if ( m_hover_id != - 1 )
{
render_scale ();
render_snap_radii ();
render_reference_radius ();
}
:: glColor3fv ( m_highlight_color );
2018-08-28 12:50:26 +02:00
2018-08-20 10:23:17 +02:00
if ( m_hover_id != - 1 )
render_angle ();
2018-09-11 14:48:17 +02:00
render_grabber ( box );
2018-11-29 15:10:11 +01:00
render_grabber_extension ( box , false );
2018-08-20 10:23:17 +02:00
:: glPopMatrix ();
2018-06-14 10:00:59 +02:00
}
2018-10-15 11:30:50 +02:00
void GLGizmoRotate :: on_render_for_picking ( const GLCanvas3D :: Selection & selection ) const
2018-06-14 15:32:26 +02:00
{
:: glDisable ( GL_DEPTH_TEST );
2018-08-20 10:23:17 +02:00
:: glPushMatrix ();
2018-09-07 12:20:56 +02:00
2019-01-10 13:13:11 +01:00
transform_to_local ( selection );
2018-11-29 15:10:11 +01:00
const BoundingBoxf3 & box = selection . get_bounding_box ();
render_grabbers_for_picking ( box );
render_grabber_extension ( box , true );
2018-08-20 10:23:17 +02:00
:: glPopMatrix ();
2018-06-14 15:32:26 +02:00
}
2018-08-20 10:23:17 +02:00
void GLGizmoRotate :: render_circle () const
2018-06-14 10:00:59 +02:00
{
:: glBegin ( GL_LINE_LOOP );
for ( unsigned int i = 0 ; i < ScaleStepsCount ; ++ i )
{
float angle = ( float ) i * ScaleStepRad ;
2018-08-20 10:23:17 +02:00
float x = :: cos ( angle ) * m_radius ;
float y = :: sin ( angle ) * m_radius ;
float z = 0.0f ;
:: glVertex3f (( GLfloat ) x , ( GLfloat ) y , ( GLfloat ) z );
2018-06-14 10:00:59 +02:00
}
:: glEnd ();
}
2018-08-20 10:23:17 +02:00
void GLGizmoRotate :: render_scale () const
2018-06-14 10:00:59 +02:00
{
2018-09-21 13:44:38 +02:00
float out_radius_long = m_snap_fine_out_radius ;
float out_radius_short = m_radius * ( 1.0f + 0.5f * ScaleLongTooth );
2018-06-14 10:00:59 +02:00
:: glBegin ( GL_LINES );
for ( unsigned int i = 0 ; i < ScaleStepsCount ; ++ i )
{
float angle = ( float ) i * ScaleStepRad ;
float cosa = :: cos ( angle );
float sina = :: sin ( angle );
2018-08-20 10:23:17 +02:00
float in_x = cosa * m_radius ;
float in_y = sina * m_radius ;
float in_z = 0.0f ;
float out_x = ( i % ScaleLongEvery == 0 ) ? cosa * out_radius_long : cosa * out_radius_short ;
float out_y = ( i % ScaleLongEvery == 0 ) ? sina * out_radius_long : sina * out_radius_short ;
float out_z = 0.0f ;
:: glVertex3f (( GLfloat ) in_x , ( GLfloat ) in_y , ( GLfloat ) in_z );
:: glVertex3f (( GLfloat ) out_x , ( GLfloat ) out_y , ( GLfloat ) out_z );
2018-06-14 10:00:59 +02:00
}
:: glEnd ();
}
2018-08-20 10:23:17 +02:00
void GLGizmoRotate :: render_snap_radii () const
2018-06-14 10:00:59 +02:00
{
2018-06-15 16:16:55 +02:00
float step = 2.0f * ( float ) PI / ( float ) SnapRegionsCount ;
2018-06-14 10:00:59 +02:00
2018-06-15 16:16:55 +02:00
float in_radius = m_radius / 3.0f ;
2018-06-14 10:00:59 +02:00
float out_radius = 2.0f * in_radius ;
:: glBegin ( GL_LINES );
for ( unsigned int i = 0 ; i < SnapRegionsCount ; ++ i )
{
2018-06-15 16:16:55 +02:00
float angle = ( float ) i * step ;
2018-06-14 10:00:59 +02:00
float cosa = :: cos ( angle );
float sina = :: sin ( angle );
2018-08-20 10:23:17 +02:00
float in_x = cosa * in_radius ;
float in_y = sina * in_radius ;
float in_z = 0.0f ;
float out_x = cosa * out_radius ;
float out_y = sina * out_radius ;
float out_z = 0.0f ;
:: glVertex3f (( GLfloat ) in_x , ( GLfloat ) in_y , ( GLfloat ) in_z );
:: glVertex3f (( GLfloat ) out_x , ( GLfloat ) out_y , ( GLfloat ) out_z );
2018-06-14 10:00:59 +02:00
}
:: glEnd ();
}
2018-08-20 10:23:17 +02:00
void GLGizmoRotate :: render_reference_radius () const
2018-06-14 10:00:59 +02:00
{
:: glBegin ( GL_LINES );
2018-08-20 10:23:17 +02:00
:: glVertex3f ( 0.0f , 0.0f , 0.0f );
2018-09-21 13:44:38 +02:00
:: glVertex3f (( GLfloat )( m_radius * ( 1.0f + GrabberOffset )), 0.0f , 0.0f );
2018-06-14 10:00:59 +02:00
:: glEnd ();
}
2018-08-20 10:23:17 +02:00
void GLGizmoRotate :: render_angle () const
2018-06-14 10:00:59 +02:00
{
2018-08-28 13:23:28 +02:00
float step_angle = ( float ) m_angle / AngleResolution ;
2018-09-21 13:44:38 +02:00
float ex_radius = m_radius * ( 1.0f + GrabberOffset );
2018-06-14 10:00:59 +02:00
2018-06-15 16:16:55 +02:00
:: glBegin ( GL_LINE_STRIP );
for ( unsigned int i = 0 ; i <= AngleResolution ; ++ i )
{
float angle = ( float ) i * step_angle ;
2018-08-20 10:23:17 +02:00
float x = :: cos ( angle ) * ex_radius ;
float y = :: sin ( angle ) * ex_radius ;
float z = 0.0f ;
:: glVertex3f (( GLfloat ) x , ( GLfloat ) y , ( GLfloat ) z );
2018-06-15 16:16:55 +02:00
}
:: glEnd ();
}
2018-09-11 14:48:17 +02:00
void GLGizmoRotate :: render_grabber ( const BoundingBoxf3 & box ) const
2018-06-15 16:16:55 +02:00
{
2018-11-21 09:28:27 +01:00
#if ENABLE_WORLD_ROTATIONS
double grabber_radius = ( double ) m_radius * ( 1.0 + ( double ) GrabberOffset );
#else
2018-11-15 10:16:51 +01:00
double grabber_radius = ( double ) m_radius * ( 1.0 + ( double ) GrabberOffset ) + 2.0 * ( double ) m_axis * ( double ) m_grabbers [ 0 ]. get_half_size (( float ) box . max_size ());
2018-11-21 09:28:27 +01:00
#endif // ENABLE_WORLD_ROTATIONS
2018-08-23 15:37:38 +02:00
m_grabbers [ 0 ]. center = Vec3d ( :: cos ( m_angle ) * grabber_radius , :: sin ( m_angle ) * grabber_radius , 0.0 );
2018-08-28 13:23:28 +02:00
m_grabbers [ 0 ]. angles ( 2 ) = m_angle ;
2018-08-20 10:23:17 +02:00
2018-08-21 09:03:38 +02:00
:: glColor3fv (( m_hover_id != - 1 ) ? m_drag_color : m_highlight_color );
2018-06-15 16:16:55 +02:00
2018-06-14 10:00:59 +02:00
:: glBegin ( GL_LINES );
2018-08-20 10:23:17 +02:00
:: glVertex3f ( 0.0f , 0.0f , 0.0f );
2018-10-11 10:52:50 +02:00
:: glVertex3dv ( m_grabbers [ 0 ]. center . data ());
2018-06-14 10:00:59 +02:00
:: glEnd ();
2018-08-20 10:23:17 +02:00
:: memcpy (( void * ) m_grabbers [ 0 ]. color , ( const void * ) m_highlight_color , 3 * sizeof ( float ));
2018-09-11 14:48:17 +02:00
render_grabbers ( box );
2018-06-13 15:44:04 +02:00
}
2018-11-29 15:10:11 +01:00
void GLGizmoRotate :: render_grabber_extension ( const BoundingBoxf3 & box , bool picking ) const
{
2018-12-03 09:37:46 +01:00
if ( m_quadric == nullptr )
return ;
2018-12-03 08:51:18 +01:00
double size = m_dragging ? ( double ) m_grabbers [ 0 ]. get_dragging_half_size (( float ) box . max_size ()) : ( double ) m_grabbers [ 0 ]. get_half_size (( float ) box . max_size ());
2018-11-29 15:10:11 +01:00
float color [ 3 ];
:: memcpy (( void * ) color , ( const void * ) m_grabbers [ 0 ]. color , 3 * sizeof ( float ));
if ( ! picking && ( m_hover_id != - 1 ))
{
color [ 0 ] = 1.0f - color [ 0 ];
color [ 1 ] = 1.0f - color [ 1 ];
color [ 2 ] = 1.0f - color [ 2 ];
}
if ( ! picking )
:: glEnable ( GL_LIGHTING );
:: glColor3fv ( color );
:: glPushMatrix ();
:: glTranslated ( m_grabbers [ 0 ]. center ( 0 ), m_grabbers [ 0 ]. center ( 1 ), m_grabbers [ 0 ]. center ( 2 ));
:: glRotated ( Geometry :: rad2deg ( m_angle ), 0.0 , 0.0 , 1.0 );
:: glRotated ( 90.0 , 1.0 , 0.0 , 0.0 );
:: glTranslated ( 0.0 , 0.0 , 2.0 * size );
2018-12-03 09:37:46 +01:00
:: gluQuadricOrientation ( m_quadric , GLU_OUTSIDE );
:: gluCylinder ( m_quadric , 0.75 * size , 0.0 , 3.0 * size , 36 , 1 );
:: gluQuadricOrientation ( m_quadric , GLU_INSIDE );
:: gluDisk ( m_quadric , 0.0 , 0.75 * size , 36 , 1 );
2018-11-29 15:10:11 +01:00
:: glPopMatrix ();
:: glPushMatrix ();
:: glTranslated ( m_grabbers [ 0 ]. center ( 0 ), m_grabbers [ 0 ]. center ( 1 ), m_grabbers [ 0 ]. center ( 2 ));
:: glRotated ( Geometry :: rad2deg ( m_angle ), 0.0 , 0.0 , 1.0 );
:: glRotated ( - 90.0 , 1.0 , 0.0 , 0.0 );
:: glTranslated ( 0.0 , 0.0 , 2.0 * size );
2018-12-03 09:37:46 +01:00
:: gluQuadricOrientation ( m_quadric , GLU_OUTSIDE );
:: gluCylinder ( m_quadric , 0.75 * size , 0.0 , 3.0 * size , 36 , 1 );
:: gluQuadricOrientation ( m_quadric , GLU_INSIDE );
:: gluDisk ( m_quadric , 0.0 , 0.75 * size , 36 , 1 );
2018-11-29 15:10:11 +01:00
:: glPopMatrix ();
if ( ! picking )
:: glDisable ( GL_LIGHTING );
}
2019-01-10 13:13:11 +01:00
void GLGizmoRotate :: transform_to_local ( const GLCanvas3D :: Selection & selection ) const
2018-08-20 10:23:17 +02:00
{
2018-10-11 10:52:50 +02:00
:: glTranslated ( m_center ( 0 ), m_center ( 1 ), m_center ( 2 ));
2018-08-20 10:23:17 +02:00
2019-01-10 13:13:11 +01:00
if ( selection . is_single_volume () || selection . is_single_modifier ())
{
Transform3d orient_matrix = selection . get_volume ( * selection . get_volume_idxs (). begin ()) -> get_instance_transformation (). get_matrix ( true , false , true , true );
:: glMultMatrixd ( orient_matrix . data ());
}
2018-08-20 10:23:17 +02:00
switch ( m_axis )
{
case X :
{
:: glRotatef ( 90.0f , 0.0f , 1.0f , 0.0f );
2018-12-07 10:01:30 +01:00
:: glRotatef ( - 90.0f , 0.0f , 0.0f , 1.0f );
2018-08-20 10:23:17 +02:00
break ;
}
case Y :
{
2018-12-07 10:01:30 +01:00
:: glRotatef ( - 90.0f , 0.0f , 0.0f , 1.0f );
:: glRotatef ( - 90.0f , 0.0f , 1.0f , 0.0f );
2018-08-20 10:23:17 +02:00
break ;
}
default :
case Z :
{
// no rotation
break ;
}
}
}
2019-01-10 13:13:11 +01:00
Vec3d GLGizmoRotate :: mouse_position_in_local_plane ( const Linef3 & mouse_ray , const GLCanvas3D :: Selection & selection ) const
2018-08-20 10:23:17 +02:00
{
2018-08-23 15:37:38 +02:00
double half_pi = 0.5 * ( double ) PI ;
2018-08-21 08:50:35 +02:00
2018-08-23 15:37:38 +02:00
Transform3d m = Transform3d :: Identity ();
2018-08-20 10:23:17 +02:00
switch ( m_axis )
{
case X :
{
2018-12-07 10:01:30 +01:00
m . rotate ( Eigen :: AngleAxisd ( half_pi , Vec3d :: UnitZ ()));
2018-08-23 15:37:38 +02:00
m . rotate ( Eigen :: AngleAxisd ( - half_pi , Vec3d :: UnitY ()));
2018-08-20 10:23:17 +02:00
break ;
}
case Y :
{
2018-12-07 10:01:30 +01:00
m . rotate ( Eigen :: AngleAxisd ( half_pi , Vec3d :: UnitY ()));
m . rotate ( Eigen :: AngleAxisd ( half_pi , Vec3d :: UnitZ ()));
2018-08-20 10:23:17 +02:00
break ;
}
default :
case Z :
{
// no rotation applied
break ;
}
}
2019-01-10 13:13:11 +01:00
if ( selection . is_single_volume () || selection . is_single_modifier ())
m = m * selection . get_volume ( * selection . get_volume_idxs (). begin ()) -> get_instance_transformation (). get_matrix ( true , false , true , true ). inverse ();
2018-08-23 15:37:38 +02:00
m . translate ( - m_center );
2018-08-20 10:23:17 +02:00
2018-08-28 13:23:28 +02:00
return transform ( mouse_ray , m ). intersect_plane ( 0.0 );
2018-08-20 10:23:17 +02:00
}
2018-08-24 14:11:41 +02:00
GLGizmoRotate3D :: GLGizmoRotate3D ( GLCanvas3D & parent )
: GLGizmoBase ( parent )
2018-08-20 10:23:17 +02:00
{
2018-09-10 10:01:49 +02:00
m_gizmos . emplace_back ( parent , GLGizmoRotate :: X );
m_gizmos . emplace_back ( parent , GLGizmoRotate :: Y );
m_gizmos . emplace_back ( parent , GLGizmoRotate :: Z );
for ( unsigned int i = 0 ; i < 3 ; ++ i )
{
m_gizmos [ i ]. set_group_id ( i );
}
2018-08-20 10:23:17 +02:00
}
bool GLGizmoRotate3D :: on_init ()
{
2018-09-10 10:01:49 +02:00
for ( GLGizmoRotate & g : m_gizmos )
{
if ( ! g . init ())
return false ;
}
2018-08-20 10:23:17 +02:00
2018-09-10 10:01:49 +02:00
for ( unsigned int i = 0 ; i < 3 ; ++ i )
{
m_gizmos [ i ]. set_highlight_color ( AXES_COLOR [ i ]);
}
2018-08-20 10:23:17 +02:00
std :: string path = resources_dir () + "/icons/overlay/" ;
2018-11-14 13:49:41 +01:00
if ( ! m_textures [ Off ]. load_from_file ( path + "rotate_off.png" , false ))
2018-08-20 10:23:17 +02:00
return false ;
2018-11-14 13:49:41 +01:00
if ( ! m_textures [ Hover ]. load_from_file ( path + "rotate_hover.png" , false ))
2018-08-20 10:23:17 +02:00
return false ;
2018-11-14 13:49:41 +01:00
if ( ! m_textures [ On ]. load_from_file ( path + "rotate_on.png" , false ))
2018-08-20 10:23:17 +02:00
return false ;
2018-11-22 10:14:31 +01:00
m_shortcut_key = WXK_CONTROL_R ;
2018-08-20 10:23:17 +02:00
return true ;
}
2018-10-25 09:35:08 +02:00
std :: string GLGizmoRotate3D :: on_get_name () const
{
2019-01-16 11:51:30 +01:00
return L ( "Rotate [R]" );
2018-10-25 09:35:08 +02:00
}
2018-10-15 11:30:50 +02:00
void GLGizmoRotate3D :: on_start_dragging ( const GLCanvas3D :: Selection & selection )
{
if (( 0 <= m_hover_id ) && ( m_hover_id < 3 ))
m_gizmos [ m_hover_id ]. start_dragging ( selection );
}
2018-08-20 10:23:17 +02:00
void GLGizmoRotate3D :: on_stop_dragging ()
{
2018-09-10 10:01:49 +02:00
if (( 0 <= m_hover_id ) && ( m_hover_id < 3 ))
m_gizmos [ m_hover_id ]. stop_dragging ();
2018-08-20 10:23:17 +02:00
}
2018-10-15 11:30:50 +02:00
void GLGizmoRotate3D :: on_render ( const GLCanvas3D :: Selection & selection ) const
{
2018-11-14 10:43:52 +01:00
:: glClear ( GL_DEPTH_BUFFER_BIT );
2018-10-15 11:30:50 +02:00
if (( m_hover_id == - 1 ) || ( m_hover_id == 0 ))
m_gizmos [ X ]. render ( selection );
if (( m_hover_id == - 1 ) || ( m_hover_id == 1 ))
m_gizmos [ Y ]. render ( selection );
if (( m_hover_id == - 1 ) || ( m_hover_id == 2 ))
m_gizmos [ Z ]. render ( selection );
}
2018-08-20 10:23:17 +02:00
2018-10-31 10:19:44 +01:00
#if ENABLE_IMGUI
2018-11-26 10:56:07 +01:00
void GLGizmoRotate3D :: on_render_input_window ( float x , float y , const GLCanvas3D :: Selection & selection )
2018-10-31 10:19:44 +01:00
{
2018-12-07 16:57:43 +01:00
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
2018-10-31 10:19:44 +01:00
Vec3d rotation ( Geometry :: rad2deg ( m_gizmos [ 0 ]. get_angle ()), Geometry :: rad2deg ( m_gizmos [ 1 ]. get_angle ()), Geometry :: rad2deg ( m_gizmos [ 2 ]. get_angle ()));
2018-11-26 10:56:07 +01:00
wxString label = _ ( L ( "Rotation (deg)" ));
2018-10-31 10:19:44 +01:00
2018-11-26 10:56:07 +01:00
m_imgui -> set_next_window_pos ( x , y , ImGuiCond_Always );
m_imgui -> set_next_window_bg_alpha ( 0.5f );
m_imgui -> begin ( label , ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse );
m_imgui -> input_vec3 ( "" , rotation , 100.0f , "%.2f" );
m_imgui -> end ();
2018-12-07 16:57:43 +01:00
#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
2018-10-31 10:19:44 +01:00
}
#endif // ENABLE_IMGUI
2018-08-21 08:50:35 +02:00
const float GLGizmoScale3D :: Offset = 5.0f ;
2018-08-24 14:11:41 +02:00
GLGizmoScale3D :: GLGizmoScale3D ( GLCanvas3D & parent )
: GLGizmoBase ( parent )
2018-08-28 13:23:28 +02:00
, m_scale ( Vec3d :: Ones ())
2018-11-14 12:57:12 +01:00
, m_snap_step ( 0.05 )
2018-08-28 13:23:28 +02:00
, m_starting_scale ( Vec3d :: Ones ())
2018-08-21 08:50:35 +02:00
{
}
bool GLGizmoScale3D :: on_init ()
{
std :: string path = resources_dir () + "/icons/overlay/" ;
2018-11-14 13:49:41 +01:00
if ( ! m_textures [ Off ]. load_from_file ( path + "scale_off.png" , false ))
2018-08-21 08:50:35 +02:00
return false ;
2018-11-14 13:49:41 +01:00
if ( ! m_textures [ Hover ]. load_from_file ( path + "scale_hover.png" , false ))
2018-08-21 08:50:35 +02:00
return false ;
2018-11-14 13:49:41 +01:00
if ( ! m_textures [ On ]. load_from_file ( path + "scale_on.png" , false ))
2018-08-21 08:50:35 +02:00
return false ;
2018-08-21 14:27:36 +02:00
for ( int i = 0 ; i < 10 ; ++ i )
2018-08-21 08:50:35 +02:00
{
m_grabbers . push_back ( Grabber ());
}
2018-08-28 13:23:28 +02:00
double half_pi = 0.5 * ( double ) PI ;
2018-08-21 08:50:35 +02:00
// x axis
2018-08-28 13:23:28 +02:00
m_grabbers [ 0 ]. angles ( 1 ) = half_pi ;
m_grabbers [ 1 ]. angles ( 1 ) = half_pi ;
2018-08-21 08:50:35 +02:00
// y axis
2018-08-28 13:23:28 +02:00
m_grabbers [ 2 ]. angles ( 0 ) = half_pi ;
m_grabbers [ 3 ]. angles ( 0 ) = half_pi ;
2018-08-21 08:50:35 +02:00
2018-11-22 10:14:31 +01:00
m_shortcut_key = WXK_CONTROL_S ;
2018-08-21 08:50:35 +02:00
return true ;
}
2018-10-25 09:35:08 +02:00
std :: string GLGizmoScale3D :: on_get_name () const
{
2019-01-16 11:51:30 +01:00
return L ( "Scale [S]" );
2018-10-25 09:35:08 +02:00
}
2018-10-15 11:30:50 +02:00
void GLGizmoScale3D :: on_start_dragging ( const GLCanvas3D :: Selection & selection )
2018-08-21 08:50:35 +02:00
{
if ( m_hover_id != - 1 )
2018-08-22 11:22:07 +02:00
{
2018-08-21 08:50:35 +02:00
m_starting_drag_position = m_grabbers [ m_hover_id ]. center ;
2018-10-15 13:22:36 +02:00
m_starting_box = selection . get_bounding_box ();
2018-08-22 11:22:07 +02:00
}
2018-08-21 08:50:35 +02:00
}
2019-01-10 13:13:11 +01:00
void GLGizmoScale3D :: on_update ( const UpdateData & data , const GLCanvas3D :: Selection & selection )
2018-08-21 08:50:35 +02:00
{
if (( m_hover_id == 0 ) || ( m_hover_id == 1 ))
2018-11-14 12:57:12 +01:00
do_scale_x ( data );
2018-08-21 08:50:35 +02:00
else if (( m_hover_id == 2 ) || ( m_hover_id == 3 ))
2018-11-14 12:57:12 +01:00
do_scale_y ( data );
2018-08-21 08:50:35 +02:00
else if (( m_hover_id == 4 ) || ( m_hover_id == 5 ))
2018-11-14 12:57:12 +01:00
do_scale_z ( data );
2018-08-21 14:27:36 +02:00
else if ( m_hover_id >= 6 )
2018-11-14 12:57:12 +01:00
do_scale_uniform ( data );
2018-08-21 08:50:35 +02:00
}
2018-10-15 11:30:50 +02:00
void GLGizmoScale3D :: on_render ( const GLCanvas3D :: Selection & selection ) const
2018-08-21 08:50:35 +02:00
{
2018-10-24 13:56:43 +02:00
bool single_instance = selection . is_single_full_instance ();
2018-11-12 09:54:04 +01:00
bool single_volume = selection . is_single_modifier () || selection . is_single_volume ();
bool single_selection = single_instance || single_volume ;
Vec3f scale = 100.0f * Vec3f :: Ones ();
if ( single_instance )
scale = 100.0f * selection . get_volume ( * selection . get_volume_idxs (). begin ()) -> get_instance_scaling_factor (). cast < float > ();
else if ( single_volume )
scale = 100.0f * selection . get_volume ( * selection . get_volume_idxs (). begin ()) -> get_volume_scaling_factor (). cast < float > ();
2018-10-24 13:56:43 +02:00
2018-11-12 09:54:04 +01:00
if (( single_selection && (( m_hover_id == 0 ) || ( m_hover_id == 1 ))) || m_grabbers [ 0 ]. dragging || m_grabbers [ 1 ]. dragging )
2018-10-24 13:56:43 +02:00
set_tooltip ( "X: " + format ( scale ( 0 ), 4 ) + "%" );
2018-12-03 10:17:42 +01:00
else if ( ! m_grabbers [ 0 ]. dragging && ! m_grabbers [ 1 ]. dragging && (( m_hover_id == 0 ) || ( m_hover_id == 1 )))
set_tooltip ( "X" );
2018-11-12 09:54:04 +01:00
else if (( single_selection && (( m_hover_id == 2 ) || ( m_hover_id == 3 ))) || m_grabbers [ 2 ]. dragging || m_grabbers [ 3 ]. dragging )
2018-10-24 13:56:43 +02:00
set_tooltip ( "Y: " + format ( scale ( 1 ), 4 ) + "%" );
2018-12-03 10:17:42 +01:00
else if ( ! m_grabbers [ 2 ]. dragging && ! m_grabbers [ 3 ]. dragging && (( m_hover_id == 2 ) || ( m_hover_id == 3 )))
set_tooltip ( "Y" );
2018-11-12 09:54:04 +01:00
else if (( single_selection && (( m_hover_id == 4 ) || ( m_hover_id == 5 ))) || m_grabbers [ 4 ]. dragging || m_grabbers [ 5 ]. dragging )
2018-10-24 13:56:43 +02:00
set_tooltip ( "Z: " + format ( scale ( 2 ), 4 ) + "%" );
2018-12-03 10:17:42 +01:00
else if ( ! m_grabbers [ 4 ]. dragging && ! m_grabbers [ 5 ]. dragging && (( m_hover_id == 4 ) || ( m_hover_id == 5 )))
set_tooltip ( "Z" );
2018-11-12 09:54:04 +01:00
else if (( single_selection && (( m_hover_id == 6 ) || ( m_hover_id == 7 ) || ( m_hover_id == 8 ) || ( m_hover_id == 9 )))
2018-10-24 13:56:43 +02:00
|| m_grabbers [ 6 ]. dragging || m_grabbers [ 7 ]. dragging || m_grabbers [ 8 ]. dragging || m_grabbers [ 9 ]. dragging )
{
std :: string tooltip = "X: " + format ( scale ( 0 ), 4 ) + "% \n " ;
tooltip += "Y: " + format ( scale ( 1 ), 4 ) + "% \n " ;
tooltip += "Z: " + format ( scale ( 2 ), 4 ) + "%" ;
set_tooltip ( tooltip );
}
2018-12-03 10:17:42 +01:00
else if ( ! m_grabbers [ 6 ]. dragging && ! m_grabbers [ 7 ]. dragging && ! m_grabbers [ 8 ]. dragging && ! m_grabbers [ 9 ]. dragging &&
(( m_hover_id == 6 ) || ( m_hover_id == 7 ) || ( m_hover_id == 8 ) || ( m_hover_id == 9 )))
set_tooltip ( "X/Y/Z" );
2018-08-24 14:11:41 +02:00
2018-11-14 10:43:52 +01:00
:: glClear ( GL_DEPTH_BUFFER_BIT );
2018-08-21 14:27:36 +02:00
:: glEnable ( GL_DEPTH_TEST );
2018-08-21 08:50:35 +02:00
2018-10-15 11:30:50 +02:00
BoundingBoxf3 box ;
Transform3d transform = Transform3d :: Identity ();
Vec3d angles = Vec3d :: Zero ();
2018-10-19 11:24:27 +02:00
Transform3d offsets_transform = Transform3d :: Identity ();
2018-10-15 11:30:50 +02:00
2018-11-28 16:09:04 +01:00
Vec3d grabber_size = Vec3d :: Zero ();
2018-10-24 13:56:43 +02:00
if ( single_instance )
2018-10-15 11:30:50 +02:00
{
// calculate bounding box in instance local reference system
const GLCanvas3D :: Selection :: IndicesList & idxs = selection . get_volume_idxs ();
for ( unsigned int idx : idxs )
{
2018-11-20 15:39:36 +01:00
const GLVolume * vol = selection . get_volume ( idx );
box . merge ( vol -> bounding_box . transformed ( vol -> get_volume_transformation (). get_matrix ()));
2018-10-15 11:30:50 +02:00
}
// gets transform from first selected volume
2018-10-19 11:24:27 +02:00
const GLVolume * v = selection . get_volume ( * idxs . begin ());
2018-11-20 15:39:36 +01:00
transform = v -> get_instance_transformation (). get_matrix ();
2018-10-19 11:24:27 +02:00
// gets angles from first selected volume
2018-11-02 12:11:28 +01:00
angles = v -> get_instance_rotation ();
2018-10-19 11:24:27 +02:00
// consider rotation+mirror only components of the transform for offsets
2018-11-02 12:11:28 +01:00
offsets_transform = Geometry :: assemble_transform ( Vec3d :: Zero (), angles , Vec3d :: Ones (), v -> get_instance_mirror ());
2018-11-28 16:09:04 +01:00
grabber_size = v -> get_instance_transformation (). get_matrix ( true , true , false , true ) * box . size ();
2018-11-12 09:54:04 +01:00
}
else if ( single_volume )
{
const GLVolume * v = selection . get_volume ( * selection . get_volume_idxs (). begin ());
box = v -> bounding_box ;
transform = v -> world_matrix ();
angles = Geometry :: extract_euler_angles ( transform );
// consider rotation+mirror only components of the transform for offsets
offsets_transform = Geometry :: assemble_transform ( Vec3d :: Zero (), angles , Vec3d :: Ones (), v -> get_instance_mirror ());
2018-11-28 16:09:04 +01:00
grabber_size = v -> get_volume_transformation (). get_matrix ( true , true , false , true ) * box . size ();
2018-10-15 11:30:50 +02:00
}
else
2018-11-28 16:09:04 +01:00
{
2018-10-15 11:30:50 +02:00
box = selection . get_bounding_box ();
2018-11-28 16:09:04 +01:00
grabber_size = box . size ();
}
2018-10-15 13:22:36 +02:00
m_box = box ;
2018-10-15 11:30:50 +02:00
2018-08-23 15:37:38 +02:00
const Vec3d & center = m_box . center ();
2018-10-19 11:24:27 +02:00
Vec3d offset_x = offsets_transform * Vec3d (( double ) Offset , 0.0 , 0.0 );
Vec3d offset_y = offsets_transform * Vec3d ( 0.0 , ( double ) Offset , 0.0 );
Vec3d offset_z = offsets_transform * Vec3d ( 0.0 , 0.0 , ( double ) Offset );
2018-08-21 08:50:35 +02:00
// x axis
2018-10-15 13:22:36 +02:00
m_grabbers [ 0 ]. center = transform * Vec3d ( m_box . min ( 0 ), center ( 1 ), center ( 2 )) - offset_x ;
m_grabbers [ 1 ]. center = transform * Vec3d ( m_box . max ( 0 ), center ( 1 ), center ( 2 )) + offset_x ;
2018-09-07 13:40:26 +02:00
:: memcpy (( void * ) m_grabbers [ 0 ]. color , ( const void * ) & AXES_COLOR [ 0 ], 3 * sizeof ( float ));
:: memcpy (( void * ) m_grabbers [ 1 ]. color , ( const void * ) & AXES_COLOR [ 0 ], 3 * sizeof ( float ));
2018-08-21 14:27:36 +02:00
// y axis
2018-10-15 13:22:36 +02:00
m_grabbers [ 2 ]. center = transform * Vec3d ( center ( 0 ), m_box . min ( 1 ), center ( 2 )) - offset_y ;
m_grabbers [ 3 ]. center = transform * Vec3d ( center ( 0 ), m_box . max ( 1 ), center ( 2 )) + offset_y ;
2018-09-07 13:40:26 +02:00
:: memcpy (( void * ) m_grabbers [ 2 ]. color , ( const void * ) & AXES_COLOR [ 1 ], 3 * sizeof ( float ));
:: memcpy (( void * ) m_grabbers [ 3 ]. color , ( const void * ) & AXES_COLOR [ 1 ], 3 * sizeof ( float ));
2018-08-21 14:27:36 +02:00
// z axis
2018-10-15 13:22:36 +02:00
m_grabbers [ 4 ]. center = transform * Vec3d ( center ( 0 ), center ( 1 ), m_box . min ( 2 )) - offset_z ;
m_grabbers [ 5 ]. center = transform * Vec3d ( center ( 0 ), center ( 1 ), m_box . max ( 2 )) + offset_z ;
2018-09-07 13:40:26 +02:00
:: memcpy (( void * ) m_grabbers [ 4 ]. color , ( const void * ) & AXES_COLOR [ 2 ], 3 * sizeof ( float ));
:: memcpy (( void * ) m_grabbers [ 5 ]. color , ( const void * ) & AXES_COLOR [ 2 ], 3 * sizeof ( float ));
2018-08-21 08:50:35 +02:00
2018-08-21 14:27:36 +02:00
// uniform
2018-10-15 13:22:36 +02:00
m_grabbers [ 6 ]. center = transform * Vec3d ( m_box . min ( 0 ), m_box . min ( 1 ), center ( 2 )) - offset_x - offset_y ;
m_grabbers [ 7 ]. center = transform * Vec3d ( m_box . max ( 0 ), m_box . min ( 1 ), center ( 2 )) + offset_x - offset_y ;
m_grabbers [ 8 ]. center = transform * Vec3d ( m_box . max ( 0 ), m_box . max ( 1 ), center ( 2 )) + offset_x + offset_y ;
m_grabbers [ 9 ]. center = transform * Vec3d ( m_box . min ( 0 ), m_box . max ( 1 ), center ( 2 )) - offset_x + offset_y ;
2018-08-21 14:27:36 +02:00
for ( int i = 6 ; i < 10 ; ++ i )
{
:: memcpy (( void * ) m_grabbers [ i ]. color , ( const void * ) m_highlight_color , 3 * sizeof ( float ));
}
2018-10-15 11:30:50 +02:00
// sets grabbers orientation
for ( int i = 0 ; i < 10 ; ++ i )
{
m_grabbers [ i ]. angles = angles ;
}
2018-08-21 14:27:36 +02:00
:: glLineWidth (( m_hover_id != - 1 ) ? 2.0f : 1.5f );
2018-08-21 08:50:35 +02:00
2018-11-28 16:09:04 +01:00
float grabber_max_size = ( float ) std :: max ( grabber_size ( 0 ), std :: max ( grabber_size ( 1 ), grabber_size ( 2 )));
2018-10-12 16:18:37 +02:00
2018-08-21 08:50:35 +02:00
if ( m_hover_id == - 1 )
{
2018-09-07 13:40:26 +02:00
// draw connections
2018-09-11 12:40:42 +02:00
if ( m_grabbers [ 0 ]. enabled && m_grabbers [ 1 ]. enabled )
{
:: glColor3fv ( m_grabbers [ 0 ]. color );
render_grabbers_connection ( 0 , 1 );
}
if ( m_grabbers [ 2 ]. enabled && m_grabbers [ 3 ]. enabled )
{
:: glColor3fv ( m_grabbers [ 2 ]. color );
render_grabbers_connection ( 2 , 3 );
}
if ( m_grabbers [ 4 ]. enabled && m_grabbers [ 5 ]. enabled )
{
:: glColor3fv ( m_grabbers [ 4 ]. color );
render_grabbers_connection ( 4 , 5 );
}
2018-10-12 14:23:34 +02:00
:: glColor3fv ( m_base_color );
render_grabbers_connection ( 6 , 7 );
render_grabbers_connection ( 7 , 8 );
render_grabbers_connection ( 8 , 9 );
render_grabbers_connection ( 9 , 6 );
2018-08-21 08:50:35 +02:00
// draw grabbers
2018-11-28 16:09:04 +01:00
render_grabbers ( grabber_max_size );
2018-08-21 08:50:35 +02:00
}
else if (( m_hover_id == 0 ) || ( m_hover_id == 1 ))
{
2018-08-21 14:27:36 +02:00
// draw connection
2018-08-29 13:36:03 +02:00
:: glColor3fv ( m_grabbers [ 0 ]. color );
2018-08-21 14:27:36 +02:00
render_grabbers_connection ( 0 , 1 );
// draw grabbers
2018-11-28 16:09:04 +01:00
m_grabbers [ 0 ]. render ( true , grabber_max_size );
m_grabbers [ 1 ]. render ( true , grabber_max_size );
2018-08-21 08:50:35 +02:00
}
else if (( m_hover_id == 2 ) || ( m_hover_id == 3 ))
{
2018-08-21 14:27:36 +02:00
// draw connection
2018-08-29 13:36:03 +02:00
:: glColor3fv ( m_grabbers [ 2 ]. color );
2018-08-21 14:27:36 +02:00
render_grabbers_connection ( 2 , 3 );
// draw grabbers
2018-11-28 16:09:04 +01:00
m_grabbers [ 2 ]. render ( true , grabber_max_size );
m_grabbers [ 3 ]. render ( true , grabber_max_size );
2018-08-21 08:50:35 +02:00
}
else if (( m_hover_id == 4 ) || ( m_hover_id == 5 ))
{
2018-08-21 14:27:36 +02:00
// draw connection
2018-08-29 13:36:03 +02:00
:: glColor3fv ( m_grabbers [ 4 ]. color );
2018-08-21 14:27:36 +02:00
render_grabbers_connection ( 4 , 5 );
// draw grabbers
2018-11-28 16:09:04 +01:00
m_grabbers [ 4 ]. render ( true , grabber_max_size );
m_grabbers [ 5 ]. render ( true , grabber_max_size );
2018-08-21 08:50:35 +02:00
}
2018-08-21 14:27:36 +02:00
else if ( m_hover_id >= 6 )
2018-08-21 08:50:35 +02:00
{
2018-10-12 14:23:34 +02:00
// draw connection
2018-08-21 08:50:35 +02:00
:: glColor3fv ( m_drag_color );
2018-10-12 14:23:34 +02:00
render_grabbers_connection ( 6 , 7 );
render_grabbers_connection ( 7 , 8 );
render_grabbers_connection ( 8 , 9 );
render_grabbers_connection ( 9 , 6 );
2018-08-21 14:27:36 +02:00
// draw grabbers
for ( int i = 6 ; i < 10 ; ++ i )
{
2018-11-28 16:09:04 +01:00
m_grabbers [ i ]. render ( true , grabber_max_size );
2018-08-21 14:27:36 +02:00
}
2018-08-21 08:50:35 +02:00
}
}
2018-10-15 11:30:50 +02:00
void GLGizmoScale3D :: on_render_for_picking ( const GLCanvas3D :: Selection & selection ) const
{
:: glDisable ( GL_DEPTH_TEST );
render_grabbers_for_picking ( selection . get_bounding_box ());
}
2018-08-21 08:50:35 +02:00
2018-10-31 10:19:44 +01:00
#if ENABLE_IMGUI
2018-11-26 10:56:07 +01:00
void GLGizmoScale3D :: on_render_input_window ( float x , float y , const GLCanvas3D :: Selection & selection )
2018-10-31 10:19:44 +01:00
{
2018-12-07 16:57:43 +01:00
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
2018-10-31 10:19:44 +01:00
bool single_instance = selection . is_single_full_instance ();
2018-11-26 10:56:07 +01:00
wxString label = _ ( L ( "Scale (%)" ));
2018-10-31 10:19:44 +01:00
2018-11-26 10:56:07 +01:00
m_imgui -> set_next_window_pos ( x , y , ImGuiCond_Always );
m_imgui -> set_next_window_bg_alpha ( 0.5f );
m_imgui -> begin ( label , ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse );
2018-12-07 14:10:16 +01:00
m_imgui -> input_vec3 ( "" , m_scale * 100.f , 100.0f , "%.2f" );
2018-11-26 10:56:07 +01:00
m_imgui -> end ();
2018-12-07 16:57:43 +01:00
#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
2018-10-31 10:19:44 +01:00
}
#endif // ENABLE_IMGUI
2018-08-21 14:27:36 +02:00
void GLGizmoScale3D :: render_grabbers_connection ( unsigned int id_1 , unsigned int id_2 ) const
2018-08-21 08:50:35 +02:00
{
2018-08-21 14:27:36 +02:00
unsigned int grabbers_count = ( unsigned int ) m_grabbers . size ();
if (( id_1 < grabbers_count ) && ( id_2 < grabbers_count ))
{
:: glBegin ( GL_LINES );
2018-10-11 10:52:50 +02:00
:: glVertex3dv ( m_grabbers [ id_1 ]. center . data ());
:: glVertex3dv ( m_grabbers [ id_2 ]. center . data ());
2018-08-21 14:27:36 +02:00
:: glEnd ();
}
2018-08-21 08:50:35 +02:00
}
2018-11-14 12:57:12 +01:00
void GLGizmoScale3D :: do_scale_x ( const UpdateData & data )
2018-08-21 08:50:35 +02:00
{
2018-11-14 12:57:12 +01:00
double ratio = calc_ratio ( data );
2018-08-22 11:22:07 +02:00
if ( ratio > 0.0 )
2018-08-28 13:23:28 +02:00
m_scale ( 0 ) = m_starting_scale ( 0 ) * ratio ;
2018-08-21 08:50:35 +02:00
}
2018-11-14 12:57:12 +01:00
void GLGizmoScale3D :: do_scale_y ( const UpdateData & data )
2018-08-21 08:50:35 +02:00
{
2018-11-14 12:57:12 +01:00
double ratio = calc_ratio ( data );
2018-08-22 11:22:07 +02:00
if ( ratio > 0.0 )
2018-09-24 15:54:09 +02:00
m_scale ( 1 ) = m_starting_scale ( 1 ) * ratio ;
2018-08-21 08:50:35 +02:00
}
2018-11-14 12:57:12 +01:00
void GLGizmoScale3D :: do_scale_z ( const UpdateData & data )
2018-08-21 08:50:35 +02:00
{
2018-11-14 12:57:12 +01:00
double ratio = calc_ratio ( data );
2018-08-22 11:22:07 +02:00
if ( ratio > 0.0 )
2018-09-24 15:54:09 +02:00
m_scale ( 2 ) = m_starting_scale ( 2 ) * ratio ;
2018-08-21 08:50:35 +02:00
}
2018-11-14 12:57:12 +01:00
void GLGizmoScale3D :: do_scale_uniform ( const UpdateData & data )
2018-08-21 08:50:35 +02:00
{
2018-11-14 12:57:12 +01:00
double ratio = calc_ratio ( data );
2018-08-22 11:22:07 +02:00
if ( ratio > 0.0 )
2018-08-28 13:23:28 +02:00
m_scale = m_starting_scale * ratio ;
2018-08-22 11:22:07 +02:00
}
2018-08-21 08:50:35 +02:00
2018-11-14 12:57:12 +01:00
double GLGizmoScale3D :: calc_ratio ( const UpdateData & data ) const
2018-10-15 15:21:37 +02:00
{
double ratio = 0.0 ;
// vector from the center to the starting position
Vec3d starting_vec = m_starting_drag_position - m_starting_box . center ();
double len_starting_vec = starting_vec . norm ();
if ( len_starting_vec != 0.0 )
{
2018-11-14 12:57:12 +01:00
Vec3d mouse_dir = data . mouse_ray . unit_vector ();
2018-10-15 15:21:37 +02:00
// finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position
// use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
// in our case plane normal and ray direction are the same (orthogonal view)
// when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
2018-11-14 12:57:12 +01:00
Vec3d inters = data . mouse_ray . a + ( m_starting_drag_position - data . mouse_ray . a ). dot ( mouse_dir ) / mouse_dir . squaredNorm () * mouse_dir ;
2018-10-15 15:21:37 +02:00
// vector from the starting position to the found intersection
Vec3d inters_vec = inters - m_starting_drag_position ;
// finds projection of the vector along the staring direction
double proj = inters_vec . dot ( starting_vec . normalized ());
2018-11-14 12:57:12 +01:00
ratio = ( len_starting_vec + proj ) / len_starting_vec ;
2018-10-15 15:21:37 +02:00
}
2018-11-14 12:57:12 +01:00
if ( data . shift_down )
ratio = m_snap_step * ( double ) std :: round ( ratio / m_snap_step );
2018-10-15 15:21:37 +02:00
return ratio ;
}
2018-08-21 08:50:35 +02:00
2018-09-11 09:00:28 +02:00
const double GLGizmoMove3D :: Offset = 10.0 ;
GLGizmoMove3D :: GLGizmoMove3D ( GLCanvas3D & parent )
: GLGizmoBase ( parent )
2018-10-08 14:02:12 +02:00
, m_displacement ( Vec3d :: Zero ())
2018-11-14 13:29:57 +01:00
, m_snap_step ( 1.0 )
2018-09-11 09:00:28 +02:00
, m_starting_drag_position ( Vec3d :: Zero ())
, m_starting_box_center ( Vec3d :: Zero ())
2018-09-14 14:37:13 +02:00
, m_starting_box_bottom_center ( Vec3d :: Zero ())
2018-12-03 09:37:46 +01:00
, m_quadric ( nullptr )
2018-09-11 09:00:28 +02:00
{
2018-12-03 09:37:46 +01:00
m_quadric = :: gluNewQuadric ();
if ( m_quadric != nullptr )
:: gluQuadricDrawStyle ( m_quadric , GLU_FILL );
}
GLGizmoMove3D ::~ GLGizmoMove3D ()
{
if ( m_quadric != nullptr )
:: gluDeleteQuadric ( m_quadric );
2018-09-11 09:00:28 +02:00
}
bool GLGizmoMove3D :: on_init ()
{
std :: string path = resources_dir () + "/icons/overlay/" ;
2018-11-14 13:49:41 +01:00
if ( ! m_textures [ Off ]. load_from_file ( path + "move_off.png" , false ))
2018-09-11 09:00:28 +02:00
return false ;
2018-11-14 13:49:41 +01:00
if ( ! m_textures [ Hover ]. load_from_file ( path + "move_hover.png" , false ))
2018-09-11 09:00:28 +02:00
return false ;
2018-11-14 13:49:41 +01:00
if ( ! m_textures [ On ]. load_from_file ( path + "move_on.png" , false ))
2018-09-11 09:00:28 +02:00
return false ;
for ( int i = 0 ; i < 3 ; ++ i )
{
m_grabbers . push_back ( Grabber ());
}
2018-11-22 10:14:31 +01:00
m_shortcut_key = WXK_CONTROL_M ;
2018-09-11 09:00:28 +02:00
return true ;
}
2018-10-25 09:35:08 +02:00
std :: string GLGizmoMove3D :: on_get_name () const
{
2019-01-16 11:51:30 +01:00
return L ( "Move [M]" );
2018-10-25 09:35:08 +02:00
}
2018-10-15 11:30:50 +02:00
void GLGizmoMove3D :: on_start_dragging ( const GLCanvas3D :: Selection & selection )
2018-09-11 09:00:28 +02:00
{
if ( m_hover_id != - 1 )
{
2018-10-08 14:02:12 +02:00
m_displacement = Vec3d :: Zero ();
2018-10-15 11:30:50 +02:00
const BoundingBoxf3 & box = selection . get_bounding_box ();
2018-09-11 09:00:28 +02:00
m_starting_drag_position = m_grabbers [ m_hover_id ]. center ;
m_starting_box_center = box . center ();
2018-09-14 14:37:13 +02:00
m_starting_box_bottom_center = box . center ();
m_starting_box_bottom_center ( 2 ) = box . min ( 2 );
2018-09-11 09:00:28 +02:00
}
}
2018-10-24 13:28:35 +02:00
void GLGizmoMove3D :: on_stop_dragging ()
{
m_displacement = Vec3d :: Zero ();
}
2019-01-10 13:13:11 +01:00
void GLGizmoMove3D :: on_update ( const UpdateData & data , const GLCanvas3D :: Selection & selection )
2018-09-11 09:00:28 +02:00
{
2018-10-08 14:02:12 +02:00
if ( m_hover_id == 0 )
2018-11-14 12:57:12 +01:00
m_displacement ( 0 ) = calc_projection ( data );
2018-10-08 14:02:12 +02:00
else if ( m_hover_id == 1 )
2018-11-14 12:57:12 +01:00
m_displacement ( 1 ) = calc_projection ( data );
2018-10-08 14:02:12 +02:00
else if ( m_hover_id == 2 )
2018-11-14 12:57:12 +01:00
m_displacement ( 2 ) = calc_projection ( data );
2018-09-11 09:00:28 +02:00
}
2018-10-15 11:30:50 +02:00
void GLGizmoMove3D :: on_render ( const GLCanvas3D :: Selection & selection ) const
2018-09-11 09:00:28 +02:00
{
2018-10-24 13:28:35 +02:00
bool show_position = selection . is_single_full_instance ();
const Vec3d & position = selection . get_bounding_box (). center ();
if (( show_position && ( m_hover_id == 0 )) || m_grabbers [ 0 ]. dragging )
set_tooltip ( "X: " + format ( show_position ? position ( 0 ) : m_displacement ( 0 ), 2 ));
2018-12-03 10:17:42 +01:00
else if ( ! m_grabbers [ 0 ]. dragging && ( m_hover_id == 0 ))
set_tooltip ( "X" );
2018-10-24 13:28:35 +02:00
else if (( show_position && ( m_hover_id == 1 )) || m_grabbers [ 1 ]. dragging )
set_tooltip ( "Y: " + format ( show_position ? position ( 1 ) : m_displacement ( 1 ), 2 ));
2018-12-03 10:17:42 +01:00
else if ( ! m_grabbers [ 1 ]. dragging && ( m_hover_id == 1 ))
set_tooltip ( "Y" );
2018-10-24 13:28:35 +02:00
else if (( show_position && ( m_hover_id == 2 )) || m_grabbers [ 2 ]. dragging )
set_tooltip ( "Z: " + format ( show_position ? position ( 2 ) : m_displacement ( 2 ), 2 ));
2018-12-03 10:17:42 +01:00
else if ( ! m_grabbers [ 2 ]. dragging && ( m_hover_id == 2 ))
set_tooltip ( "Z" );
2018-09-11 09:00:28 +02:00
2018-11-14 10:43:52 +01:00
:: glClear ( GL_DEPTH_BUFFER_BIT );
2018-09-11 09:00:28 +02:00
:: glEnable ( GL_DEPTH_TEST );
2018-10-15 11:30:50 +02:00
const BoundingBoxf3 & box = selection . get_bounding_box ();
2018-09-11 09:00:28 +02:00
const Vec3d & center = box . center ();
// x axis
m_grabbers [ 0 ]. center = Vec3d ( box . max ( 0 ) + Offset , center ( 1 ), center ( 2 ));
:: memcpy (( void * ) m_grabbers [ 0 ]. color , ( const void * ) & AXES_COLOR [ 0 ], 3 * sizeof ( float ));
// y axis
m_grabbers [ 1 ]. center = Vec3d ( center ( 0 ), box . max ( 1 ) + Offset , center ( 2 ));
:: memcpy (( void * ) m_grabbers [ 1 ]. color , ( const void * ) & AXES_COLOR [ 1 ], 3 * sizeof ( float ));
// z axis
m_grabbers [ 2 ]. center = Vec3d ( center ( 0 ), center ( 1 ), box . max ( 2 ) + Offset );
:: memcpy (( void * ) m_grabbers [ 2 ]. color , ( const void * ) & AXES_COLOR [ 2 ], 3 * sizeof ( float ));
:: glLineWidth (( m_hover_id != - 1 ) ? 2.0f : 1.5f );
if ( m_hover_id == - 1 )
{
// draw axes
for ( unsigned int i = 0 ; i < 3 ; ++ i )
{
2018-09-11 12:40:42 +02:00
if ( m_grabbers [ i ]. enabled )
{
:: glColor3fv ( AXES_COLOR [ i ]);
:: glBegin ( GL_LINES );
2018-10-11 10:52:50 +02:00
:: glVertex3dv ( center . data ());
:: glVertex3dv ( m_grabbers [ i ]. center . data ());
2018-09-11 12:40:42 +02:00
:: glEnd ();
}
2018-09-11 09:00:28 +02:00
}
// draw grabbers
2018-09-11 14:48:17 +02:00
render_grabbers ( box );
2018-12-12 13:04:06 +01:00
for ( unsigned int i = 0 ; i < 3 ; ++ i )
{
if ( m_grabbers [ i ]. enabled )
render_grabber_extension (( Axis ) i , box , false );
}
2018-09-11 09:00:28 +02:00
}
else
{
// draw axis
:: glColor3fv ( AXES_COLOR [ m_hover_id ]);
:: glBegin ( GL_LINES );
2018-10-11 10:52:50 +02:00
:: glVertex3dv ( center . data ());
:: glVertex3dv ( m_grabbers [ m_hover_id ]. center . data ());
2018-09-11 09:00:28 +02:00
:: glEnd ();
// draw grabber
2018-10-12 16:18:37 +02:00
m_grabbers [ m_hover_id ]. render ( true , box . max_size ());
2018-12-03 08:51:18 +01:00
render_grabber_extension (( Axis ) m_hover_id , box , false );
2018-09-11 09:00:28 +02:00
}
}
2018-10-15 11:30:50 +02:00
void GLGizmoMove3D :: on_render_for_picking ( const GLCanvas3D :: Selection & selection ) const
{
:: glDisable ( GL_DEPTH_TEST );
2018-12-03 08:51:18 +01:00
const BoundingBoxf3 & box = selection . get_bounding_box ();
render_grabbers_for_picking ( box );
render_grabber_extension ( X , box , true );
render_grabber_extension ( Y , box , true );
render_grabber_extension ( Z , box , true );
2018-10-15 11:30:50 +02:00
}
2018-09-11 09:00:28 +02:00
2018-10-31 10:19:44 +01:00
#if ENABLE_IMGUI
2018-11-26 10:56:07 +01:00
void GLGizmoMove3D :: on_render_input_window ( float x , float y , const GLCanvas3D :: Selection & selection )
2018-10-31 10:19:44 +01:00
{
2018-12-07 16:57:43 +01:00
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
2018-10-31 10:19:44 +01:00
bool show_position = selection . is_single_full_instance ();
const Vec3d & position = selection . get_bounding_box (). center ();
Vec3d displacement = show_position ? position : m_displacement ;
2018-11-26 10:56:07 +01:00
wxString label = show_position ? _ ( L ( "Position (mm)" )) : _ ( L ( "Displacement (mm)" ));
2018-10-31 10:19:44 +01:00
2018-11-26 10:56:07 +01:00
m_imgui -> set_next_window_pos ( x , y , ImGuiCond_Always );
m_imgui -> set_next_window_bg_alpha ( 0.5f );
m_imgui -> begin ( label , ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse );
m_imgui -> input_vec3 ( "" , displacement , 100.0f , "%.2f" );
2018-10-31 10:19:44 +01:00
2018-11-26 10:56:07 +01:00
m_imgui -> end ();
2018-12-07 16:57:43 +01:00
#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
2018-10-31 10:19:44 +01:00
}
#endif // ENABLE_IMGUI
2018-11-14 12:57:12 +01:00
double GLGizmoMove3D :: calc_projection ( const UpdateData & data ) const
2018-10-15 15:21:37 +02:00
{
double projection = 0.0 ;
Vec3d starting_vec = m_starting_drag_position - m_starting_box_center ;
double len_starting_vec = starting_vec . norm ();
if ( len_starting_vec != 0.0 )
{
2018-11-14 12:57:12 +01:00
Vec3d mouse_dir = data . mouse_ray . unit_vector ();
2018-10-15 15:21:37 +02:00
// finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position
// use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
// in our case plane normal and ray direction are the same (orthogonal view)
// when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
2018-11-14 12:57:12 +01:00
Vec3d inters = data . mouse_ray . a + ( m_starting_drag_position - data . mouse_ray . a ). dot ( mouse_dir ) / mouse_dir . squaredNorm () * mouse_dir ;
2018-10-15 15:21:37 +02:00
// vector from the starting position to the found intersection
Vec3d inters_vec = inters - m_starting_drag_position ;
// finds projection of the vector along the staring direction
projection = inters_vec . dot ( starting_vec . normalized ());
}
2018-11-14 13:29:57 +01:00
if ( data . shift_down )
projection = m_snap_step * ( double ) std :: round ( projection / m_snap_step );
2018-10-15 15:21:37 +02:00
return projection ;
}
2018-08-09 16:55:43 +02:00
2018-12-03 08:51:18 +01:00
void GLGizmoMove3D :: render_grabber_extension ( Axis axis , const BoundingBoxf3 & box , bool picking ) const
{
2018-12-03 09:37:46 +01:00
if ( m_quadric == nullptr )
return ;
2018-12-03 08:51:18 +01:00
double size = m_dragging ? ( double ) m_grabbers [ axis ]. get_dragging_half_size (( float ) box . max_size ()) : ( double ) m_grabbers [ axis ]. get_half_size (( float ) box . max_size ());
float color [ 3 ];
:: memcpy (( void * ) color , ( const void * ) m_grabbers [ axis ]. color , 3 * sizeof ( float ));
if ( ! picking && ( m_hover_id != - 1 ))
{
color [ 0 ] = 1.0f - color [ 0 ];
color [ 1 ] = 1.0f - color [ 1 ];
color [ 2 ] = 1.0f - color [ 2 ];
}
if ( ! picking )
:: glEnable ( GL_LIGHTING );
:: glColor3fv ( color );
:: glPushMatrix ();
:: glTranslated ( m_grabbers [ axis ]. center ( 0 ), m_grabbers [ axis ]. center ( 1 ), m_grabbers [ axis ]. center ( 2 ));
if ( axis == X )
:: glRotated ( 90.0 , 0.0 , 1.0 , 0.0 );
else if ( axis == Y )
:: glRotated ( - 90.0 , 1.0 , 0.0 , 0.0 );
:: glTranslated ( 0.0 , 0.0 , 2.0 * size );
2018-12-03 09:37:46 +01:00
:: gluQuadricOrientation ( m_quadric , GLU_OUTSIDE );
:: gluCylinder ( m_quadric , 0.75 * size , 0.0 , 3.0 * size , 36 , 1 );
:: gluQuadricOrientation ( m_quadric , GLU_INSIDE );
:: gluDisk ( m_quadric , 0.0 , 0.75 * size , 36 , 1 );
2018-12-03 08:51:18 +01:00
:: glPopMatrix ();
if ( ! picking )
:: glDisable ( GL_LIGHTING );
}
2018-08-27 14:00:53 +02:00
GLGizmoFlatten :: GLGizmoFlatten ( GLCanvas3D & parent )
: GLGizmoBase ( parent )
2018-09-10 10:01:49 +02:00
, m_normal ( Vec3d :: Zero ())
2018-09-10 11:58:24 +02:00
, m_starting_center ( Vec3d :: Zero ())
2018-08-27 14:00:53 +02:00
{
}
2018-08-09 16:55:43 +02:00
bool GLGizmoFlatten :: on_init ()
{
std :: string path = resources_dir () + "/icons/overlay/" ;
2018-11-14 13:49:41 +01:00
if ( ! m_textures [ Off ]. load_from_file ( path + "layflat_off.png" , false ))
2018-08-09 16:55:43 +02:00
return false ;
2018-11-14 13:49:41 +01:00
if ( ! m_textures [ Hover ]. load_from_file ( path + "layflat_hover.png" , false ))
2018-08-09 16:55:43 +02:00
return false ;
2018-11-14 13:49:41 +01:00
if ( ! m_textures [ On ]. load_from_file ( path + "layflat_on.png" , false ))
2018-08-09 16:55:43 +02:00
return false ;
2018-11-22 10:14:31 +01:00
m_shortcut_key = WXK_CONTROL_F ;
2018-08-09 16:55:43 +02:00
return true ;
}
2018-10-25 09:35:08 +02:00
std :: string GLGizmoFlatten :: on_get_name () const
{
2019-01-16 11:51:30 +01:00
return L ( "Place on face [F]" );
2018-10-25 09:35:08 +02:00
}
2018-11-28 12:06:17 +01:00
bool GLGizmoFlatten :: on_is_activable ( const GLCanvas3D :: Selection & selection ) const
{
2018-12-03 11:14:17 +01:00
return selection . is_single_full_instance ();
2018-11-28 12:06:17 +01:00
}
2018-10-15 11:30:50 +02:00
void GLGizmoFlatten :: on_start_dragging ( const GLCanvas3D :: Selection & selection )
2018-08-09 16:55:43 +02:00
{
if ( m_hover_id != - 1 )
2018-09-10 10:01:49 +02:00
{
2018-08-09 16:55:43 +02:00
m_normal = m_planes [ m_hover_id ]. normal ;
2018-10-15 11:30:50 +02:00
m_starting_center = selection . get_bounding_box (). center ();
2018-09-10 10:01:49 +02:00
}
2018-08-09 16:55:43 +02:00
}
2018-10-15 11:30:50 +02:00
void GLGizmoFlatten :: on_render ( const GLCanvas3D :: Selection & selection ) const
2018-08-09 16:55:43 +02:00
{
2018-11-26 13:02:40 +01:00
:: glClear ( GL_DEPTH_BUFFER_BIT );
2018-12-03 11:14:17 +01:00
2018-08-17 15:40:47 +02:00
:: glEnable ( GL_DEPTH_TEST );
2018-11-26 13:02:40 +01:00
:: glEnable ( GL_BLEND );
2018-08-09 16:55:43 +02:00
2018-12-03 11:14:17 +01:00
if ( selection . is_single_full_instance ())
{
const Transform3d & m = selection . get_volume ( * selection . get_volume_idxs (). begin ()) -> get_instance_transformation (). get_matrix ();
:: glPushMatrix ();
:: glMultMatrixd ( m . data ());
2018-12-21 13:19:10 +01:00
:: glTranslatef ( 0.f , 0.f , selection . get_volume ( * selection . get_volume_idxs (). begin ()) -> get_sla_shift_z ());
2018-12-03 11:14:17 +01:00
for ( int i = 0 ; i < ( int ) m_planes . size (); ++ i )
{
if ( i == m_hover_id )
:: glColor4f ( 0.9f , 0.9f , 0.9f , 0.75f );
else
:: glColor4f ( 0.9f , 0.9f , 0.9f , 0.5f );
2018-11-02 15:20:26 +01:00
2018-12-03 11:14:17 +01:00
:: glBegin ( GL_POLYGON );
for ( const Vec3d & vertex : m_planes [ i ]. vertices )
{
:: glVertex3dv ( vertex . data ());
2018-10-16 13:49:40 +02:00
}
2018-12-03 11:14:17 +01:00
:: glEnd ();
2018-10-16 13:49:40 +02:00
}
2018-12-03 11:14:17 +01:00
:: glPopMatrix ();
2018-08-09 16:55:43 +02:00
}
2018-09-04 14:42:14 +02:00
2018-10-19 13:26:42 +02:00
:: glEnable ( GL_CULL_FACE );
2018-09-04 14:42:14 +02:00
:: glDisable ( GL_BLEND );
2018-08-09 16:55:43 +02:00
}
2018-10-15 11:30:50 +02:00
void GLGizmoFlatten :: on_render_for_picking ( const GLCanvas3D :: Selection & selection ) const
2018-08-09 16:55:43 +02:00
{
2018-11-26 13:02:40 +01:00
:: glDisable ( GL_DEPTH_TEST );
:: glDisable ( GL_BLEND );
2018-12-03 11:14:17 +01:00
if ( selection . is_single_full_instance ())
{
const Transform3d & m = selection . get_volume ( * selection . get_volume_idxs (). begin ()) -> get_instance_transformation (). get_matrix ();
:: glPushMatrix ();
:: glMultMatrixd ( m . data ());
2018-12-21 13:19:10 +01:00
:: glTranslatef ( 0.f , 0.f , selection . get_volume ( * selection . get_volume_idxs (). begin ()) -> get_sla_shift_z ());
2018-12-03 11:14:17 +01:00
for ( int i = 0 ; i < ( int ) m_planes . size (); ++ i )
{
:: glColor3f ( 1.0f , 1.0f , picking_color_component ( i ));
:: glBegin ( GL_POLYGON );
for ( const Vec3d & vertex : m_planes [ i ]. vertices )
{
:: glVertex3dv ( vertex . data ());
2018-10-16 13:49:40 +02:00
}
2018-12-03 11:14:17 +01:00
:: glEnd ();
}
:: glPopMatrix ();
2018-08-09 16:55:43 +02:00
}
2018-10-19 13:26:42 +02:00
:: glEnable ( GL_CULL_FACE );
2018-08-09 16:55:43 +02:00
}
2018-08-17 15:40:47 +02:00
void GLGizmoFlatten :: set_flattening_data ( const ModelObject * model_object )
{
2018-09-26 13:54:09 +02:00
m_starting_center = Vec3d :: Zero ();
2018-11-02 15:20:26 +01:00
bool object_changed = m_model_object != model_object ;
2018-08-17 15:40:47 +02:00
m_model_object = model_object ;
2018-11-02 15:20:26 +01:00
if ( object_changed && is_plane_update_necessary ())
2018-08-17 15:40:47 +02:00
update_planes ();
}
void GLGizmoFlatten :: update_planes ()
{
TriangleMesh ch ;
for ( const ModelVolume * vol : m_model_object -> volumes )
2018-11-02 12:11:28 +01:00
{
2018-11-26 13:02:40 +01:00
if ( vol -> type () != ModelVolume :: Type :: MODEL_PART )
continue ;
2018-11-02 12:11:28 +01:00
TriangleMesh vol_ch = vol -> get_convex_hull ();
2018-11-02 13:47:47 +01:00
vol_ch . transform ( vol -> get_matrix ());
2018-11-02 12:11:28 +01:00
ch . merge ( vol_ch );
}
2018-08-17 15:40:47 +02:00
ch = ch . convex_hull_3d ();
2018-08-09 16:55:43 +02:00
m_planes . clear ();
2019-01-21 12:53:30 +01:00
const Transform3d & inst_matrix = m_model_object -> instances . front () -> get_matrix ( true );
2018-08-09 16:55:43 +02:00
2019-01-04 17:47:08 +01:00
// Following constants are used for discarding too small polygons.
2019-01-07 12:05:16 +01:00
const float minimal_area = 5.f ; // in square mm (world coordinates)
2019-01-04 17:47:08 +01:00
const float minimal_side = 1.f ; // mm
// Now we'll go through all the facets and append Points of facets sharing the same normal.
// This part is still performed in mesh coordinate system.
2018-08-17 15:40:47 +02:00
const int num_of_facets = ch . stl . stats . number_of_facets ;
std :: vector < int > facet_queue ( num_of_facets , 0 );
std :: vector < bool > facet_visited ( num_of_facets , false );
int facet_queue_cnt = 0 ;
const stl_normal * normal_ptr = nullptr ;
while ( 1 ) {
// Find next unvisited triangle:
int facet_idx = 0 ;
for (; facet_idx < num_of_facets ; ++ facet_idx )
if ( ! facet_visited [ facet_idx ]) {
facet_queue [ facet_queue_cnt ++ ] = facet_idx ;
facet_visited [ facet_idx ] = true ;
normal_ptr = & ch . stl . facet_start [ facet_idx ]. normal ;
m_planes . emplace_back ();
break ;
}
if ( facet_idx == num_of_facets )
break ; // Everything was visited already
while ( facet_queue_cnt > 0 ) {
int facet_idx = facet_queue [ -- facet_queue_cnt ];
2018-08-27 14:00:53 +02:00
const stl_normal & this_normal = ch . stl . facet_start [ facet_idx ]. normal ;
2019-01-16 14:39:25 +01:00
if ( std :: abs ( this_normal ( 0 ) - ( * normal_ptr )( 0 )) < 0.001 && std :: abs ( this_normal ( 1 ) - ( * normal_ptr )( 1 )) < 0.001 && std :: abs ( this_normal ( 2 ) - ( * normal_ptr )( 2 )) < 0.001 ) {
2018-08-17 15:40:47 +02:00
stl_vertex * first_vertex = ch . stl . facet_start [ facet_idx ]. vertex ;
for ( int j = 0 ; j < 3 ; ++ j )
2018-09-24 10:19:40 +02:00
m_planes . back (). vertices . emplace_back (( double ) first_vertex [ j ]( 0 ), ( double ) first_vertex [ j ]( 1 ), ( double ) first_vertex [ j ]( 2 ));
2018-08-17 15:40:47 +02:00
facet_visited [ facet_idx ] = true ;
for ( int j = 0 ; j < 3 ; ++ j ) {
int neighbor_idx = ch . stl . neighbors_start [ facet_idx ]. neighbor [ j ];
if ( ! facet_visited [ neighbor_idx ])
facet_queue [ facet_queue_cnt ++ ] = neighbor_idx ;
}
}
}
2019-01-04 17:47:08 +01:00
m_planes . back (). normal = normal_ptr -> cast < double > ();
2018-08-24 11:46:54 +02:00
2019-01-04 17:47:08 +01:00
// Now we'll transform all the points into world coordinates, so that the areas, angles and distances
// make real sense.
m_planes . back (). vertices = transform ( m_planes . back (). vertices , inst_matrix );
// if this is a just a very small triangle, remove it to speed up further calculations (it would be rejected later anyway):
2018-08-24 11:46:54 +02:00
if ( m_planes . back (). vertices . size () == 3 &&
2019-01-04 17:47:08 +01:00
(( m_planes . back (). vertices [ 0 ] - m_planes . back (). vertices [ 1 ]). norm () < minimal_side
|| ( m_planes . back (). vertices [ 0 ] - m_planes . back (). vertices [ 2 ]). norm () < minimal_side
|| ( m_planes . back (). vertices [ 1 ] - m_planes . back (). vertices [ 2 ]). norm () < minimal_side ))
2018-09-24 10:19:40 +02:00
m_planes . pop_back ();
2018-08-09 16:55:43 +02:00
}
2018-08-17 15:40:47 +02:00
// Now we'll go through all the polygons, transform the points into xy plane to process them:
for ( unsigned int polygon_id = 0 ; polygon_id < m_planes . size (); ++ polygon_id ) {
Pointf3s & polygon = m_planes [ polygon_id ]. vertices ;
2018-08-27 14:00:53 +02:00
const Vec3d & normal = m_planes [ polygon_id ]. normal ;
2018-08-17 15:40:47 +02:00
2019-01-04 17:47:08 +01:00
// let's transform the normal accodring to the instance matrix:
Geometry :: Transformation t ( inst_matrix );
Vec3d scaling = t . get_scaling_factor ();
t . set_scaling_factor ( Vec3d ( 1. / ( scaling ( 0 ) * scaling ( 0 )), 1. / ( scaling ( 0 ) * scaling ( 0 )), 1. / ( scaling ( 0 ) * scaling ( 0 ))));
Vec3d normal_transformed = t . get_matrix () * normal ;
2018-08-17 15:40:47 +02:00
// We are going to rotate about z and y to flatten the plane
2018-09-24 10:19:40 +02:00
Eigen :: Quaterniond q ;
2018-08-28 09:03:03 +02:00
Transform3d m = Transform3d :: Identity ();
2019-01-04 17:47:08 +01:00
m . matrix (). block ( 0 , 0 , 3 , 3 ) = q . setFromTwoVectors ( normal_transformed , Vec3d :: UnitZ ()). toRotationMatrix ();
2018-08-28 09:03:03 +02:00
polygon = transform ( polygon , m );
2019-01-04 17:47:08 +01:00
// Now to remove the inner points. We'll misuse Geometry::convex_hull for that, but since
// it works in fixed point representation, we will rescale the polygon to avoid overflows.
// And yes, it is a nasty thing to do. Whoever has time is free to refactor.
Vec3d bb_size = BoundingBoxf3 ( polygon ). size ();
float sf = std :: min ( 1. / bb_size ( 0 ), 1. / bb_size ( 1 ));
Transform3d tr = Geometry :: assemble_transform ( Vec3d :: Zero (), Vec3d :: Zero (), Vec3d ( sf , sf , 1.f ));
polygon = transform ( polygon , tr );
polygon = Slic3r :: Geometry :: convex_hull ( polygon );
polygon = transform ( polygon , tr . inverse ());
2018-08-17 15:40:47 +02:00
2019-01-04 17:47:08 +01:00
// Calculate area of the polygons and discard ones that are too small
2018-08-21 15:56:40 +02:00
float & area = m_planes [ polygon_id ]. area ;
area = 0.f ;
2018-08-17 15:40:47 +02:00
for ( unsigned int i = 0 ; i < polygon . size (); i ++ ) // Shoelace formula
2018-08-27 14:00:53 +02:00
area += polygon [ i ]( 0 ) * polygon [ i + 1 < polygon . size () ? i + 1 : 0 ]( 1 ) - polygon [ i + 1 < polygon . size () ? i + 1 : 0 ]( 0 ) * polygon [ i ]( 1 );
2018-09-24 10:19:40 +02:00
area = 0.5f * std :: abs ( area );
2018-08-17 15:40:47 +02:00
2018-09-24 10:19:40 +02:00
bool discard = false ;
2019-01-04 17:47:08 +01:00
if ( area < minimal_area )
discard = true ;
else {
// We also check the inner angles and discard polygons with angles smaller than the following threshold
const double angle_threshold = :: cos ( 10.0 * ( double ) PI / 180.0 );
2018-09-24 10:19:40 +02:00
2019-01-04 17:47:08 +01:00
for ( unsigned int i = 0 ; i < polygon . size (); ++ i ) {
const Vec3d & prec = polygon [( i == 0 ) ? polygon . size () - 1 : i - 1 ];
const Vec3d & curr = polygon [ i ];
const Vec3d & next = polygon [( i == polygon . size () - 1 ) ? 0 : i + 1 ];
2018-09-24 10:19:40 +02:00
2019-01-04 17:47:08 +01:00
if (( prec - curr ). normalized (). dot (( next - curr ). normalized ()) > angle_threshold ) {
discard = true ;
break ;
}
2018-09-24 10:19:40 +02:00
}
}
2019-01-04 17:47:08 +01:00
if ( discard ) {
2018-09-24 10:19:40 +02:00
m_planes . erase ( m_planes . begin () + ( polygon_id -- ));
continue ;
}
2018-08-17 15:40:47 +02:00
// We will shrink the polygon a little bit so it does not touch the object edges:
2018-08-27 14:00:53 +02:00
Vec3d centroid = std :: accumulate ( polygon . begin (), polygon . end (), Vec3d ( 0.0 , 0.0 , 0.0 ));
centroid /= ( double ) polygon . size ();
2018-08-17 15:40:47 +02:00
for ( auto & vertex : polygon )
vertex = 0.9f * vertex + 0.1f * centroid ;
// Polygon is now simple and convex, we'll round the corners to make them look nicer.
// The algorithm takes a vertex, calculates middles of respective sides and moves the vertex
// towards their average (controlled by 'aggressivity'). This is repeated k times.
// In next iterations, the neighbours are not always taken at the middle (to increase the
// rounding effect at the corners, where we need it most).
const unsigned int k = 10 ; // number of iterations
const float aggressivity = 0.2f ; // agressivity
const unsigned int N = polygon . size ();
std :: vector < std :: pair < unsigned int , unsigned int >> neighbours ;
if ( k != 0 ) {
Pointf3s points_out ( 2 * k * N ); // vector long enough to store the future vertices
for ( unsigned int j = 0 ; j < N ; ++ j ) {
points_out [ j * 2 * k ] = polygon [ j ];
neighbours . push_back ( std :: make_pair (( int )( j * 2 * k - k ) < 0 ? ( N - 1 ) * 2 * k + k : j * 2 * k - k , j * 2 * k + k ));
}
for ( unsigned int i = 0 ; i < k ; ++ i ) {
// Calculate middle of each edge so that neighbours points to something useful:
for ( unsigned int j = 0 ; j < N ; ++ j )
if ( i == 0 )
points_out [ j * 2 * k + k ] = 0.5f * ( points_out [ j * 2 * k ] + points_out [ j == N - 1 ? 0 : ( j + 1 ) * 2 * k ]);
else {
float r = 0.2 + 0.3 / ( k - 1 ) * i ; // the neighbours are not always taken in the middle
points_out [ neighbours [ j ]. first ] = r * points_out [ j * 2 * k ] + ( 1 - r ) * points_out [ neighbours [ j ]. first - 1 ];
points_out [ neighbours [ j ]. second ] = r * points_out [ j * 2 * k ] + ( 1 - r ) * points_out [ neighbours [ j ]. second + 1 ];
}
// Now we have a triangle and valid neighbours, we can do an iteration:
for ( unsigned int j = 0 ; j < N ; ++ j )
points_out [ 2 * k * j ] = ( 1 - aggressivity ) * points_out [ 2 * k * j ] +
aggressivity * 0.5f * ( points_out [ neighbours [ j ]. first ] + points_out [ neighbours [ j ]. second ]);
for ( auto & n : neighbours ) {
++ n . first ;
-- n . second ;
}
}
polygon = points_out ; // replace the coarse polygon with the smooth one that we just created
}
2018-08-28 09:03:03 +02:00
2019-01-04 17:47:08 +01:00
// Raise a bit above the object surface to avoid flickering:
for ( auto & b : polygon )
b ( 2 ) += 0.1f ;
// Transform back to 3D (and also back to mesh coordinates)
polygon = transform ( polygon , inst_matrix . inverse () * m . inverse ());
2018-08-17 15:40:47 +02:00
}
2018-09-06 09:16:32 +02:00
// We'll sort the planes by area and only keep the 254 largest ones (because of the picking pass limitations):
2018-08-21 15:56:40 +02:00
std :: sort ( m_planes . rbegin (), m_planes . rend (), []( const PlaneData & a , const PlaneData & b ) { return a . area < b . area ; });
2018-09-06 09:16:32 +02:00
m_planes . resize ( std :: min (( int ) m_planes . size (), 254 ));
2018-08-21 15:56:40 +02:00
2018-08-17 15:40:47 +02:00
// Planes are finished - let's save what we calculated it from:
2018-11-28 11:37:17 +01:00
m_volumes_matrices . clear ();
2019-01-04 17:47:08 +01:00
m_volumes_types . clear ();
for ( const ModelVolume * vol : m_model_object -> volumes ) {
2018-11-28 11:37:17 +01:00
m_volumes_matrices . push_back ( vol -> get_matrix ());
2019-01-04 17:47:08 +01:00
m_volumes_types . push_back ( vol -> type ());
}
m_first_instance_scale = m_model_object -> instances . front () -> get_scaling_factor ();
2019-01-21 12:53:30 +01:00
m_first_instance_mirror = m_model_object -> instances . front () -> get_mirror ();
2018-08-17 15:40:47 +02:00
}
2019-01-04 17:47:08 +01:00
2018-08-17 15:40:47 +02:00
bool GLGizmoFlatten :: is_plane_update_necessary () const
{
if ( m_state != On || ! m_model_object || m_model_object -> instances . empty ())
return false ;
2018-11-28 11:37:17 +01:00
if ( m_model_object -> volumes . size () != m_volumes_matrices . size ())
2018-09-20 15:00:40 +02:00
return true ;
2018-08-17 15:40:47 +02:00
2019-01-04 17:47:08 +01:00
// We want to recalculate when the scale changes - some planes could (dis)appear.
2019-01-21 12:53:30 +01:00
if ( ! m_model_object -> instances . front () -> get_scaling_factor (). isApprox ( m_first_instance_scale )
|| ! m_model_object -> instances . front () -> get_mirror (). isApprox ( m_first_instance_mirror ))
2019-01-04 17:47:08 +01:00
return true ;
2018-11-28 11:37:17 +01:00
for ( unsigned int i = 0 ; i < m_model_object -> volumes . size (); ++ i )
2019-01-04 17:47:08 +01:00
if ( ! m_model_object -> volumes [ i ] -> get_matrix (). isApprox ( m_volumes_matrices [ i ])
|| m_model_object -> volumes [ i ] -> type () != m_volumes_types [ i ])
2018-08-17 15:40:47 +02:00
return true ;
return false ;
2018-08-09 16:55:43 +02:00
}
2018-11-02 15:20:26 +01:00
Vec3d GLGizmoFlatten :: get_flattening_normal () const
2018-09-20 15:00:40 +02:00
{
2018-11-02 15:20:26 +01:00
Vec3d out = m_normal ;
2018-09-20 15:00:40 +02:00
m_normal = Vec3d :: Zero ();
2018-10-26 15:45:52 +02:00
m_starting_center = Vec3d :: Zero ();
2018-11-02 15:20:26 +01:00
return out ;
2018-09-20 15:00:40 +02:00
}
2018-08-09 16:55:43 +02:00
2018-09-12 12:14:20 +02:00
GLGizmoSlaSupports :: GLGizmoSlaSupports ( GLCanvas3D & parent )
2018-12-03 14:02:58 +01:00
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
: GLGizmoBase ( parent ), m_starting_center ( Vec3d :: Zero ()), m_quadric ( nullptr )
#else
2018-09-26 13:54:09 +02:00
: GLGizmoBase ( parent ), m_starting_center ( Vec3d :: Zero ())
2018-12-03 14:02:58 +01:00
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
2018-09-12 12:14:20 +02:00
{
2018-12-03 14:02:58 +01:00
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
m_quadric = :: gluNewQuadric ();
if ( m_quadric != nullptr )
2018-12-07 10:36:43 +01:00
// using GLU_FILL does not work when the instance's transformation
// contains mirroring (normals are reverted)
2019-01-04 17:47:08 +01:00
:: gluQuadricDrawStyle ( m_quadric , GLU_FILL );
2018-12-03 14:02:58 +01:00
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
2018-09-12 12:14:20 +02:00
}
2018-12-03 14:02:58 +01:00
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
GLGizmoSlaSupports ::~ GLGizmoSlaSupports ()
{
if ( m_quadric != nullptr )
:: gluDeleteQuadric ( m_quadric );
}
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
2018-09-12 12:14:20 +02:00
bool GLGizmoSlaSupports :: on_init ()
{
std :: string path = resources_dir () + "/icons/overlay/" ;
2018-11-14 13:49:41 +01:00
if ( ! m_textures [ Off ]. load_from_file ( path + "sla_support_points_off.png" , false ))
2018-09-12 12:14:20 +02:00
return false ;
2018-11-14 13:49:41 +01:00
if ( ! m_textures [ Hover ]. load_from_file ( path + "sla_support_points_hover.png" , false ))
2018-09-12 12:14:20 +02:00
return false ;
2018-11-14 13:49:41 +01:00
if ( ! m_textures [ On ]. load_from_file ( path + "sla_support_points_on.png" , false ))
2018-09-12 12:14:20 +02:00
return false ;
2018-11-22 10:14:31 +01:00
m_shortcut_key = WXK_CONTROL_L ;
2018-09-12 12:14:20 +02:00
return true ;
}
2018-12-03 14:02:58 +01:00
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
void GLGizmoSlaSupports :: set_sla_support_data ( ModelObject * model_object , const GLCanvas3D :: Selection & selection )
{
m_starting_center = Vec3d :: Zero ();
m_old_model_object = m_model_object ;
m_model_object = model_object ;
2018-12-05 10:26:08 +01:00
if ( selection . is_empty ())
m_old_instance_id = - 1 ;
2018-12-03 14:02:58 +01:00
if (( model_object != nullptr ) && selection . is_from_single_instance ())
{
if ( is_mesh_update_necessary ())
update_mesh ();
2018-12-21 12:35:20 +01:00
// If there are no points, let's ask the backend if it calculated some.
if ( model_object -> sla_support_points . empty () && m_parent . sla_print () -> is_step_done ( slaposSupportPoints )) {
for ( const SLAPrintObject * po : m_parent . sla_print () -> objects ()) {
if ( po -> model_object () -> id () == model_object -> id ()) {
const Eigen :: MatrixXd & points = po -> get_support_points ();
for ( unsigned int i = 0 ; i < points . rows (); ++ i )
model_object -> sla_support_points . push_back ( Vec3f ( po -> trafo (). inverse (). cast < float > () * Vec3f ( points ( i , 0 ), points ( i , 1 ), points ( i , 2 ))));
break ;
}
}
}
2018-12-03 14:02:58 +01:00
}
}
#else
2018-09-26 13:54:09 +02:00
void GLGizmoSlaSupports :: set_model_object_ptr ( ModelObject * model_object )
{
2018-12-07 14:10:16 +01:00
if ( model_object != nullptr ) {
2018-11-02 12:11:28 +01:00
m_starting_center = Vec3d :: Zero ();
m_model_object = model_object ;
2018-11-26 10:49:25 +01:00
int selected_instance = m_parent . get_selection (). get_instance_idx ();
assert ( selected_instance < ( int ) model_object -> instances . size ());
m_instance_matrix = model_object -> instances [ selected_instance ] -> get_matrix ();
2018-11-02 12:11:28 +01:00
if ( is_mesh_update_necessary ())
update_mesh ();
}
2018-09-26 13:54:09 +02:00
}
2018-12-03 14:02:58 +01:00
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
2018-09-26 13:54:09 +02:00
2018-10-26 15:45:52 +02:00
void GLGizmoSlaSupports :: on_render ( const GLCanvas3D :: Selection & selection ) const
2018-09-12 12:14:20 +02:00
{
:: glEnable ( GL_BLEND );
:: glEnable ( GL_DEPTH_TEST );
2018-12-03 14:02:58 +01:00
#if !ENABLE_SLA_SUPPORT_GIZMO_MOD
2018-09-26 13:54:09 +02:00
// the dragged_offset is a vector measuring where was the object moved
2018-11-26 10:49:25 +01:00
// with the gizmo being on. This is reset in set_model_object_ptr and
2018-09-26 13:54:09 +02:00
// does not work correctly when there are multiple copies.
2018-10-26 15:45:52 +02:00
if ( m_starting_center == Vec3d :: Zero ())
m_starting_center = selection . get_bounding_box (). center ();
Vec3d dragged_offset = selection . get_bounding_box (). center () - m_starting_center ;
2018-12-03 14:02:58 +01:00
#endif // !ENABLE_SLA_SUPPORT_GIZMO_MOD
2018-09-26 13:54:09 +02:00
2018-12-07 14:10:16 +01:00
2018-09-19 14:59:57 +02:00
for ( auto & g : m_grabbers ) {
g . color [ 0 ] = 1.f ;
g . color [ 1 ] = 0.f ;
g . color [ 2 ] = 0.f ;
2018-09-12 12:14:20 +02:00
}
2018-12-03 14:02:58 +01:00
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
render_grabbers ( selection , false );
#else
2018-11-26 10:49:25 +01:00
//::glTranslatef((GLfloat)dragged_offset(0), (GLfloat)dragged_offset(1), (GLfloat)dragged_offset(2));
2018-11-26 20:39:18 +01:00
render_grabbers ( false );
2018-12-03 14:02:58 +01:00
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
2018-09-19 14:59:57 +02:00
2018-11-27 12:18:43 +01:00
#if !ENABLE_IMGUI
2018-09-26 14:37:25 +02:00
render_tooltip_texture ();
2018-11-26 15:54:12 +01:00
#endif // not ENABLE_IMGUI
2018-09-12 12:14:20 +02:00
:: glDisable ( GL_BLEND );
}
2018-10-26 15:45:52 +02:00
void GLGizmoSlaSupports :: on_render_for_picking ( const GLCanvas3D :: Selection & selection ) const
2018-09-12 12:14:20 +02:00
{
:: glEnable ( GL_DEPTH_TEST );
2018-09-19 14:59:57 +02:00
for ( unsigned int i = 0 ; i < m_grabbers . size (); ++ i ) {
m_grabbers [ i ]. color [ 0 ] = 1.0f ;
m_grabbers [ i ]. color [ 1 ] = 1.0f ;
m_grabbers [ i ]. color [ 2 ] = picking_color_component ( i );
}
2018-12-03 14:02:58 +01:00
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
render_grabbers ( selection , true );
#else
2018-09-26 14:37:25 +02:00
render_grabbers ( true );
2018-12-03 14:02:58 +01:00
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
2018-09-19 14:59:57 +02:00
}
2018-09-12 12:14:20 +02:00
2018-12-03 14:02:58 +01:00
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
void GLGizmoSlaSupports :: render_grabbers ( const GLCanvas3D :: Selection & selection , bool picking ) const
{
if ( m_quadric == nullptr )
return ;
if ( ! selection . is_from_single_instance ())
return ;
const GLVolume * v = selection . get_volume ( * selection . get_volume_idxs (). begin ());
double z_shift = v -> get_sla_shift_z ();
:: glPushMatrix ();
:: glTranslated ( 0.0 , 0.0 , z_shift );
const Transform3d & m = selection . get_volume ( * selection . get_volume_idxs (). begin ()) -> get_instance_transformation (). get_matrix ();
:: glMultMatrixd ( m . data ());
if ( ! picking )
:: glEnable ( GL_LIGHTING );
float render_color [ 3 ];
for ( int i = 0 ; i < ( int ) m_grabbers . size (); ++ i )
{
2018-12-07 14:10:16 +01:00
// first precalculate the grabber position in world coordinates, so that the grabber
// is not scaled with the object (as it would be if rendered with current gl matrix).
Eigen :: Matrix < GLfloat , 4 , 4 > glmatrix ;
glGetFloatv ( GL_MODELVIEW_MATRIX , glmatrix . data ());
Eigen :: Matrix < float , 4 , 1 > grabber_pos ;
for ( int j = 0 ; j < 3 ; ++ j )
grabber_pos ( j ) = m_grabbers [ i ]. center ( j );
grabber_pos [ 3 ] = 1.f ;
Eigen :: Matrix < float , 4 , 1 > grabber_world_position = glmatrix * grabber_pos ;
2018-12-03 14:02:58 +01:00
if ( ! picking && ( m_hover_id == i ))
{
render_color [ 0 ] = 1.0f - m_grabbers [ i ]. color [ 0 ];
render_color [ 1 ] = 1.0f - m_grabbers [ i ]. color [ 1 ];
render_color [ 2 ] = 1.0f - m_grabbers [ i ]. color [ 2 ];
}
else
:: memcpy (( void * ) render_color , ( const void * ) m_grabbers [ i ]. color , 3 * sizeof ( float ));
:: glColor3fv ( render_color );
:: glPushMatrix ();
2018-12-07 14:10:16 +01:00
:: glLoadIdentity ();
:: glTranslated ( grabber_world_position ( 0 ), grabber_world_position ( 1 ), grabber_world_position ( 2 ) + z_shift );
2019-01-04 17:47:08 +01:00
const float diameter = 0.8f ;
:: gluSphere ( m_quadric , diameter / 2.f , 64 , 36 );
2018-12-03 14:02:58 +01:00
:: glPopMatrix ();
}
if ( ! picking )
:: glDisable ( GL_LIGHTING );
:: glPopMatrix ();
}
#else
2018-09-26 14:37:25 +02:00
void GLGizmoSlaSupports :: render_grabbers ( bool picking ) const
2018-09-19 14:59:57 +02:00
{
2018-11-26 19:22:16 +01:00
if ( m_parent . get_selection (). is_empty ())
return ;
float z_shift = m_parent . get_selection (). get_volume ( 0 ) -> get_sla_shift_z ();
:: glTranslatef (( GLfloat ) 0 , ( GLfloat ) 0 , ( GLfloat ) z_shift );
2018-11-26 20:39:18 +01:00
int selected_instance = m_parent . get_selection (). get_instance_idx ();
assert ( selected_instance < ( int ) m_model_object -> instances . size ());
float render_color_inactive [ 3 ] = { 0.5f , 0.5f , 0.5f };
2018-11-26 10:49:25 +01:00
for ( const ModelInstance * inst : m_model_object -> instances ) {
2018-11-26 20:39:18 +01:00
bool active = inst == m_model_object -> instances [ selected_instance ];
if ( picking && ! active )
continue ;
2018-11-26 10:49:25 +01:00
for ( int i = 0 ; i < ( int ) m_grabbers . size (); ++ i )
{
if ( ! m_grabbers [ i ]. enabled )
continue ;
2018-09-12 12:14:20 +02:00
2018-11-26 10:49:25 +01:00
float render_color [ 3 ];
2018-11-26 20:39:18 +01:00
if ( ! picking && active && m_hover_id == i ) {
2018-11-26 10:49:25 +01:00
render_color [ 0 ] = 1.0f - m_grabbers [ i ]. color [ 0 ];
render_color [ 1 ] = 1.0f - m_grabbers [ i ]. color [ 1 ];
render_color [ 2 ] = 1.0f - m_grabbers [ i ]. color [ 2 ];
}
else
2018-11-26 20:39:18 +01:00
:: memcpy (( void * ) render_color , active ? ( const void * ) m_grabbers [ i ]. color : ( const void * ) render_color_inactive , 3 * sizeof ( float ));
2018-11-26 10:49:25 +01:00
if ( ! picking )
:: glEnable ( GL_LIGHTING );
:: glColor3f (( GLfloat ) render_color [ 0 ], ( GLfloat ) render_color [ 1 ], ( GLfloat ) render_color [ 2 ]);
:: glPushMatrix ();
Vec3d center = inst -> get_matrix () * m_grabbers [ i ]. center ;
:: glTranslatef (( GLfloat ) center ( 0 ), ( GLfloat ) center ( 1 ), ( GLfloat ) center ( 2 ));
GLUquadricObj * quadric ;
quadric = :: gluNewQuadric ();
:: gluQuadricDrawStyle ( quadric , GLU_FILL );
2019-01-04 17:47:08 +01:00
:: gluSphere ( quadric , 0.4 , 64 , 32 );
2018-11-26 10:49:25 +01:00
:: gluDeleteQuadric ( quadric );
:: glPopMatrix ();
if ( ! picking )
:: glDisable ( GL_LIGHTING );
2018-09-12 12:14:20 +02:00
}
}
2018-11-26 19:22:16 +01:00
:: glTranslatef (( GLfloat ) 0 , ( GLfloat ) 0 , ( GLfloat ) - z_shift );
2018-09-12 12:14:20 +02:00
}
2018-12-03 14:02:58 +01:00
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
2018-09-12 12:14:20 +02:00
2018-09-24 13:39:44 +02:00
bool GLGizmoSlaSupports :: is_mesh_update_necessary () const
{
2018-12-03 14:02:58 +01:00
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
return ( m_state == On ) && ( m_model_object != nullptr ) && ( m_model_object != m_old_model_object ) && ! m_model_object -> instances . empty ();
#else
return m_state == On && m_model_object && ! m_model_object -> instances . empty () && ! m_instance_matrix . isApprox ( m_source_data . matrix );
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
2018-09-24 13:39:44 +02:00
2018-11-26 19:22:16 +01:00
//if (m_state != On || !m_model_object || m_model_object->instances.empty() || ! m_instance_matrix.isApprox(m_source_data.matrix))
// return false;
2018-09-24 13:39:44 +02:00
// following should detect direct mesh changes (can be removed after the mesh is made completely immutable):
2018-10-26 15:45:52 +02:00
/*const float* first_vertex = m_model_object->volumes.front()->get_convex_hull().first_vertex();
2018-09-24 13:39:44 +02:00
Vec3d first_point((double)first_vertex[0], (double)first_vertex[1], (double)first_vertex[2]);
if (first_point != m_source_data.mesh_first_point)
2018-10-26 15:45:52 +02:00
return true;*/
2018-09-24 13:39:44 +02:00
}
void GLGizmoSlaSupports :: update_mesh ()
{
Eigen :: MatrixXf & V = m_V ;
Eigen :: MatrixXi & F = m_F ;
2018-11-26 19:22:16 +01:00
// Composite mesh of all instances in the world coordinate system.
// This mesh does not account for the possible Z up SLA offset.
2018-11-26 20:39:18 +01:00
TriangleMesh mesh = m_model_object -> raw_mesh ();
2018-11-08 14:55:25 +01:00
const stl_file & stl = mesh . stl ;
V . resize ( 3 * stl . stats . number_of_facets , 3 );
2018-09-24 13:39:44 +02:00
F . resize ( stl . stats . number_of_facets , 3 );
for ( unsigned int i = 0 ; i < stl . stats . number_of_facets ; ++ i ) {
const stl_facet * facet = stl . facet_start + i ;
V ( 3 * i + 0 , 0 ) = facet -> vertex [ 0 ]( 0 ); V ( 3 * i + 0 , 1 ) = facet -> vertex [ 0 ]( 1 ); V ( 3 * i + 0 , 2 ) = facet -> vertex [ 0 ]( 2 );
V ( 3 * i + 1 , 0 ) = facet -> vertex [ 1 ]( 0 ); V ( 3 * i + 1 , 1 ) = facet -> vertex [ 1 ]( 1 ); V ( 3 * i + 1 , 2 ) = facet -> vertex [ 1 ]( 2 );
V ( 3 * i + 2 , 0 ) = facet -> vertex [ 2 ]( 0 ); V ( 3 * i + 2 , 1 ) = facet -> vertex [ 2 ]( 1 ); V ( 3 * i + 2 , 2 ) = facet -> vertex [ 2 ]( 2 );
F ( i , 0 ) = 3 * i + 0 ;
F ( i , 1 ) = 3 * i + 1 ;
F ( i , 2 ) = 3 * i + 2 ;
}
2018-11-26 10:49:25 +01:00
m_AABB = igl :: AABB < Eigen :: MatrixXf , 3 > ();
m_AABB . init ( m_V , m_F );
2018-12-03 14:02:58 +01:00
#if !ENABLE_SLA_SUPPORT_GIZMO_MOD
2018-11-26 10:49:25 +01:00
m_source_data . matrix = m_instance_matrix ;
2018-12-03 14:02:58 +01:00
#endif // !ENABLE_SLA_SUPPORT_GIZMO_MOD
2018-11-26 10:49:25 +01:00
2018-09-24 13:39:44 +02:00
// we'll now reload Grabbers (selection might have changed):
m_grabbers . clear ();
2018-12-07 14:10:16 +01:00
2018-09-24 13:39:44 +02:00
for ( const Vec3f & point : m_model_object -> sla_support_points ) {
m_grabbers . push_back ( Grabber ());
m_grabbers . back (). center = point . cast < double > ();
}
}
2018-09-19 14:59:57 +02:00
Vec3f GLGizmoSlaSupports :: unproject_on_mesh ( const Vec2d & mouse_pos )
2018-09-12 12:14:20 +02:00
{
2018-09-19 14:59:57 +02:00
// if the gizmo doesn't have the V, F structures for igl, calculate them first:
2018-12-03 14:02:58 +01:00
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
if ( m_V . size () == 0 )
#else
2018-09-24 13:39:44 +02:00
if ( m_V . size () == 0 || is_mesh_update_necessary ())
2018-12-03 14:02:58 +01:00
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
2018-09-24 13:39:44 +02:00
update_mesh ();
2018-09-12 12:14:20 +02:00
Eigen :: Matrix < GLint , 4 , 1 , Eigen :: DontAlign > viewport ;
:: glGetIntegerv ( GL_VIEWPORT , viewport . data ());
Eigen :: Matrix < GLdouble , 4 , 4 , Eigen :: DontAlign > modelview_matrix ;
:: glGetDoublev ( GL_MODELVIEW_MATRIX , modelview_matrix . data ());
Eigen :: Matrix < GLdouble , 4 , 4 , Eigen :: DontAlign > projection_matrix ;
:: glGetDoublev ( GL_PROJECTION_MATRIX , projection_matrix . data ());
2018-11-26 10:49:25 +01:00
Vec3d point1 ;
Vec3d point2 ;
:: gluUnProject ( mouse_pos ( 0 ), viewport ( 3 ) - mouse_pos ( 1 ), 0.f , modelview_matrix . data (), projection_matrix . data (), viewport . data (), & point1 ( 0 ), & point1 ( 1 ), & point1 ( 2 ));
:: gluUnProject ( mouse_pos ( 0 ), viewport ( 3 ) - mouse_pos ( 1 ), 1.f , modelview_matrix . data (), projection_matrix . data (), viewport . data (), & point2 ( 0 ), & point2 ( 1 ), & point2 ( 2 ));
2018-09-19 14:59:57 +02:00
2018-11-26 10:49:25 +01:00
igl :: Hit hit ;
2018-12-03 14:02:58 +01:00
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
const GLCanvas3D :: Selection & selection = m_parent . get_selection ();
const GLVolume * volume = selection . get_volume ( * selection . get_volume_idxs (). begin ());
double z_offset = volume -> get_sla_shift_z ();
#else
double z_offset = m_parent . get_selection (). get_volume ( 0 ) -> get_sla_shift_z ();
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
point1 ( 2 ) -= z_offset ;
2018-11-26 19:22:16 +01:00
point2 ( 2 ) -= z_offset ;
2018-12-03 14:02:58 +01:00
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
Transform3d inv = volume -> get_instance_transformation (). get_matrix (). inverse ();
#else
2018-11-26 20:39:18 +01:00
Transform3d inv = m_instance_matrix . inverse ();
2018-12-03 14:02:58 +01:00
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
2018-11-26 20:39:18 +01:00
point1 = inv * point1 ;
point2 = inv * point2 ;
2018-11-26 19:22:16 +01:00
2018-11-26 10:49:25 +01:00
if ( ! m_AABB . intersect_ray ( m_V , m_F , point1 . cast < float > (), ( point2 - point1 ). cast < float > (), hit ))
throw std :: invalid_argument ( "unproject_on_mesh(): No intersection found." );
int fid = hit . id ;
Vec3f bc ( 1 - hit . u - hit . v , hit . u , hit . v );
2018-11-26 20:39:18 +01:00
return bc ( 0 ) * m_V . row ( m_F ( fid , 0 )) + bc ( 1 ) * m_V . row ( m_F ( fid , 1 )) + bc ( 2 ) * m_V . row ( m_F ( fid , 2 ));
2018-09-19 14:59:57 +02:00
}
void GLGizmoSlaSupports :: clicked_on_object ( const Vec2d & mouse_position )
{
2018-12-03 14:02:58 +01:00
#if ENABLE_SLA_SUPPORT_GIZMO_MOD
int instance_id = m_parent . get_selection (). get_instance_idx ();
if ( m_old_instance_id != instance_id )
{
2018-12-05 10:26:08 +01:00
bool something_selected = ( m_old_instance_id != - 1 );
2018-12-03 14:02:58 +01:00
m_old_instance_id = instance_id ;
2018-12-05 10:26:08 +01:00
if ( something_selected )
return ;
2018-12-03 14:02:58 +01:00
}
if ( instance_id == - 1 )
return ;
#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD
2018-09-19 14:59:57 +02:00
Vec3f new_pos ;
try {
new_pos = unproject_on_mesh ( mouse_position ); // this can throw - we don't want to create a new grabber in that case
}
2018-11-26 10:49:25 +01:00
catch (...) { return ; }
m_grabbers . push_back ( Grabber ());
m_grabbers . back (). center = new_pos . cast < double > ();
m_model_object -> sla_support_points . push_back ( new_pos );
// This should trigger the support generation
// wxGetApp().plater()->reslice();
m_parent . post_event ( SimpleEvent ( EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS ));
2018-09-19 14:59:57 +02:00
}
void GLGizmoSlaSupports :: delete_current_grabber ( bool delete_all )
{
if ( delete_all ) {
m_grabbers . clear ();
2018-09-24 13:39:44 +02:00
m_model_object -> sla_support_points . clear ();
2018-11-12 17:35:57 +01:00
// This should trigger the support generation
2018-11-13 11:53:54 +01:00
// wxGetApp().plater()->reslice();
2018-09-19 14:59:57 +02:00
}
else
if ( m_hover_id != - 1 ) {
m_grabbers . erase ( m_grabbers . begin () + m_hover_id );
2018-09-24 13:39:44 +02:00
m_model_object -> sla_support_points . erase ( m_model_object -> sla_support_points . begin () + m_hover_id );
2018-09-19 14:59:57 +02:00
m_hover_id = - 1 ;
2018-11-12 17:35:57 +01:00
// This should trigger the support generation
2018-11-13 11:53:54 +01:00
// wxGetApp().plater()->reslice();
2018-09-19 14:59:57 +02:00
}
2018-11-26 10:49:25 +01:00
m_parent . post_event ( SimpleEvent ( EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS ));
2018-09-19 14:59:57 +02:00
}
2019-01-10 13:13:11 +01:00
void GLGizmoSlaSupports :: on_update ( const UpdateData & data , const GLCanvas3D :: Selection & selection )
2018-09-19 14:59:57 +02:00
{
2018-11-14 12:57:12 +01:00
if ( m_hover_id != - 1 && data . mouse_pos ) {
2018-09-19 14:59:57 +02:00
Vec3f new_pos ;
try {
2018-11-14 12:57:12 +01:00
new_pos = unproject_on_mesh ( Vec2d (( * data . mouse_pos )( 0 ), ( * data . mouse_pos )( 1 )));
2018-09-19 14:59:57 +02:00
}
2018-11-26 10:49:25 +01:00
catch (...) { return ; }
m_grabbers [ m_hover_id ]. center = new_pos . cast < double > ();
m_model_object -> sla_support_points [ m_hover_id ] = new_pos ;
2018-11-26 19:22:16 +01:00
// Do not update immediately, wait until the mouse is released.
// m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
2018-09-12 12:14:20 +02:00
}
}
2018-11-27 12:18:43 +01:00
#if !ENABLE_IMGUI
2018-09-19 14:59:57 +02:00
void GLGizmoSlaSupports :: render_tooltip_texture () const {
if ( m_tooltip_texture . get_id () == 0 )
2018-11-26 10:49:25 +01:00
if ( ! m_tooltip_texture . load_from_file ( resources_dir () + "/icons/sla_support_points_tooltip.png" , false ))
2018-09-19 14:59:57 +02:00
return ;
if ( m_reset_texture . get_id () == 0 )
2018-11-26 10:49:25 +01:00
if ( ! m_reset_texture . load_from_file ( resources_dir () + "/icons/sla_support_points_reset.png" , false ))
2018-09-19 14:59:57 +02:00
return ;
float zoom = m_parent . get_camera_zoom ();
float inv_zoom = ( zoom != 0.0f ) ? 1.0f / zoom : 0.0f ;
float gap = 30.0f * inv_zoom ;
const Size & cnv_size = m_parent . get_canvas_size ();
float l = gap - cnv_size . get_width () / 2.f * inv_zoom ;
float r = l + ( float ) m_tooltip_texture . get_width () * inv_zoom ;
float b = gap - cnv_size . get_height () / 2.f * inv_zoom ;
float t = b + ( float ) m_tooltip_texture . get_height () * inv_zoom ;
Rect reset_rect = m_parent . get_gizmo_reset_rect ( m_parent , true );
:: glDisable ( GL_DEPTH_TEST );
:: glPushMatrix ();
:: glLoadIdentity ();
GLTexture :: render_texture ( m_tooltip_texture . get_id (), l , r , b , t );
GLTexture :: render_texture ( m_reset_texture . get_id (), reset_rect . get_left (), reset_rect . get_right (), reset_rect . get_bottom (), reset_rect . get_top ());
:: glPopMatrix ();
:: glEnable ( GL_DEPTH_TEST );
}
2018-11-26 15:54:12 +01:00
#endif // not ENABLE_IMGUI
#if ENABLE_IMGUI
void GLGizmoSlaSupports :: on_render_input_window ( float x , float y , const GLCanvas3D :: Selection & selection )
{
2018-12-21 12:35:20 +01:00
bool first_run = true ; // This is a hack to redraw the button when all points are removed,
// so it is not delayed until the background process finishes.
RENDER_AGAIN :
2018-11-26 15:54:12 +01:00
m_imgui -> set_next_window_pos ( x , y , ImGuiCond_Always );
m_imgui -> set_next_window_bg_alpha ( 0.5f );
m_imgui -> begin ( on_get_name (), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse );
ImGui :: PushItemWidth ( 100.0f );
m_imgui -> text ( _ ( L ( "Left mouse click - add point" )));
m_imgui -> text ( _ ( L ( "Right mouse click - remove point" )));
m_imgui -> text ( " " );
2018-12-07 14:10:16 +01:00
bool generate = m_imgui -> button ( _ ( L ( "Generate points automatically" )));
bool remove_all_clicked = m_imgui -> button ( _ ( L ( "Remove all points" )) + ( m_model_object == nullptr ? "" : " (" + std :: to_string ( m_model_object -> sla_support_points . size ()) + ")" ));
2018-11-26 15:54:12 +01:00
m_imgui -> end ();
2018-12-21 12:35:20 +01:00
if ( remove_all_clicked ) {
2018-11-26 15:54:12 +01:00
delete_current_grabber ( true );
2018-12-21 12:35:20 +01:00
if ( first_run ) {
first_run = false ;
goto RENDER_AGAIN ;
2018-12-07 14:10:16 +01:00
}
}
if ( remove_all_clicked || generate ) {
m_parent . reload_scene ( true );
m_parent . post_event ( SimpleEvent ( EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS ));
}
2018-11-26 15:54:12 +01:00
}
#endif // ENABLE_IMGUI
2018-09-19 14:59:57 +02:00
2018-11-15 18:15:27 +01:00
bool GLGizmoSlaSupports :: on_is_activable ( const GLCanvas3D :: Selection & selection ) const
{
2018-11-26 10:49:25 +01:00
return ( wxGetApp (). preset_bundle -> printers . get_edited_preset (). printer_technology () == ptSLA )
&& selection . is_from_single_instance ();
2018-11-15 18:15:27 +01:00
}
2018-09-12 12:14:20 +02:00
2018-11-19 14:46:37 +01:00
bool GLGizmoSlaSupports :: on_is_selectable () const
{
return ( wxGetApp (). preset_bundle -> printers . get_edited_preset (). printer_technology () == ptSLA );
}
2018-10-26 15:45:52 +02:00
std :: string GLGizmoSlaSupports :: on_get_name () const
{
2019-01-16 11:51:30 +01:00
return L ( "SLA Support Points [L]" );
2018-10-26 15:45:52 +02:00
}
2018-10-18 15:13:38 +02:00
// GLGizmoCut
class GLGizmoCutPanel : public wxPanel
{
public :
GLGizmoCutPanel ( wxWindow * parent );
void display ( bool display );
private :
bool m_active ;
wxCheckBox * m_cb_rotate ;
wxButton * m_btn_cut ;
wxButton * m_btn_cancel ;
};
GLGizmoCutPanel :: GLGizmoCutPanel ( wxWindow * parent )
: wxPanel ( parent )
, m_active ( false )
, m_cb_rotate ( new wxCheckBox ( this , wxID_ANY , _ ( L ( "Rotate lower part upwards" ))))
, m_btn_cut ( new wxButton ( this , wxID_OK , _ ( L ( "Perform cut" ))))
, m_btn_cancel ( new wxButton ( this , wxID_CANCEL , _ ( L ( "Cancel" ))))
{
enum { MARGIN = 5 };
2018-12-04 15:17:24 +01:00
auto * sizer = new wxBoxSizer ( wxHORIZONTAL );
2018-10-18 15:13:38 +02:00
auto * label = new wxStaticText ( this , wxID_ANY , _ ( L ( "Cut object:" )));
sizer -> Add ( label , 0 , wxALL | wxALIGN_CENTER , MARGIN );
sizer -> Add ( m_cb_rotate , 0 , wxALL | wxALIGN_CENTER , MARGIN );
sizer -> AddStretchSpacer ();
sizer -> Add ( m_btn_cut , 0 , wxALL | wxALIGN_CENTER , MARGIN );
sizer -> Add ( m_btn_cancel , 0 , wxALL | wxALIGN_CENTER , MARGIN );
SetSizer ( sizer );
}
void GLGizmoCutPanel :: display ( bool display )
{
Show ( display );
GetParent () -> Layout ();
}
const double GLGizmoCut :: Offset = 10.0 ;
const double GLGizmoCut :: Margin = 20.0 ;
const std :: array < float , 3 > GLGizmoCut :: GrabberColor = { 1.0 , 0.5 , 0.0 };
GLGizmoCut :: GLGizmoCut ( GLCanvas3D & parent )
: GLGizmoBase ( parent )
, m_cut_z ( 0.0 )
2018-11-29 12:21:42 +01:00
, m_max_z ( 0.0 )
2018-11-27 12:18:43 +01:00
#if !ENABLE_IMGUI
2018-10-18 15:13:38 +02:00
, m_panel ( nullptr )
2018-11-26 10:56:07 +01:00
#endif // not ENABLE_IMGUI
, m_keep_upper ( true )
, m_keep_lower ( true )
, m_rotate_lower ( false )
2018-10-18 15:13:38 +02:00
{}
2018-11-27 12:18:43 +01:00
#if !ENABLE_IMGUI
2018-10-18 15:13:38 +02:00
void GLGizmoCut :: create_external_gizmo_widgets ( wxWindow * parent )
{
wxASSERT ( m_panel == nullptr );
m_panel = new GLGizmoCutPanel ( parent );
parent -> GetSizer () -> Add ( m_panel , 0 , wxEXPAND );
parent -> Layout ();
parent -> Fit ();
auto prev_heigh = parent -> GetMinSize (). GetHeight ();
parent -> SetMinSize ( wxSize ( - 1 , std :: max ( prev_heigh , m_panel -> GetSize (). GetHeight ())));
m_panel -> Hide ();
m_panel -> Bind ( wxEVT_BUTTON , [ this ]( wxCommandEvent & ) {
2018-11-26 10:56:07 +01:00
perform_cut ( m_parent . get_selection ());
2018-10-18 15:13:38 +02:00
}, wxID_OK );
}
2018-11-26 10:56:07 +01:00
#endif // not ENABLE_IMGUI
2018-10-18 15:13:38 +02:00
bool GLGizmoCut :: on_init ()
{
// TODO: icon
std :: string path = resources_dir () + "/icons/overlay/" ;
if ( ! m_textures [ Off ]. load_from_file ( path + "cut_off.png" , false )) {
return false ;
}
if ( ! m_textures [ Hover ]. load_from_file ( path + "cut_hover.png" , false )) {
return false ;
}
if ( ! m_textures [ On ]. load_from_file ( path + "cut_on.png" , false )) {
return false ;
}
m_grabbers . emplace_back ();
2018-11-22 10:14:31 +01:00
m_shortcut_key = WXK_CONTROL_C ;
2018-10-18 15:13:38 +02:00
return true ;
}
std :: string GLGizmoCut :: on_get_name () const
{
2019-01-16 11:51:30 +01:00
return L ( "Cut [C]" );
2018-10-18 15:13:38 +02:00
}
void GLGizmoCut :: on_set_state ()
{
// Reset m_cut_z on gizmo activation
if ( get_state () == On ) {
2018-11-26 12:08:10 +01:00
m_cut_z = m_parent . get_selection (). get_bounding_box (). size ()( 2 ) / 2.0 ;
2018-10-18 15:13:38 +02:00
}
2018-11-27 12:18:43 +01:00
#if !ENABLE_IMGUI
2018-10-18 15:13:38 +02:00
// Display or hide the extra panel
if ( m_panel != nullptr ) {
m_panel -> display ( get_state () == On );
}
2018-11-26 10:56:07 +01:00
#endif // not ENABLE_IMGUI
2018-10-18 15:13:38 +02:00
}
bool GLGizmoCut :: on_is_activable ( const GLCanvas3D :: Selection & selection ) const
{
return selection . is_single_full_instance () && ! selection . is_wipe_tower ();
}
void GLGizmoCut :: on_start_dragging ( const GLCanvas3D :: Selection & selection )
{
if ( m_hover_id == - 1 ) { return ; }
const BoundingBoxf3 & box = selection . get_bounding_box ();
m_start_z = m_cut_z ;
2018-11-29 12:21:42 +01:00
update_max_z ( selection );
2018-10-18 15:13:38 +02:00
m_drag_pos = m_grabbers [ m_hover_id ]. center ;
m_drag_center = box . center ();
2018-11-26 12:08:10 +01:00
m_drag_center ( 2 ) = m_cut_z ;
2018-10-18 15:13:38 +02:00
}
2019-01-10 13:13:11 +01:00
void GLGizmoCut :: on_update ( const UpdateData & data , const GLCanvas3D :: Selection & selection )
2018-10-18 15:13:38 +02:00
{
if ( m_hover_id != - 1 ) {
2018-11-29 12:21:42 +01:00
set_cut_z ( m_start_z + calc_projection ( data . mouse_ray ));
2018-10-18 15:13:38 +02:00
}
}
void GLGizmoCut :: on_render ( const GLCanvas3D :: Selection & selection ) const
{
if ( m_grabbers [ 0 ]. dragging ) {
set_tooltip ( "Z: " + format ( m_cut_z , 2 ));
}
2018-11-29 12:21:42 +01:00
update_max_z ( selection );
2018-10-18 15:13:38 +02:00
const BoundingBoxf3 & box = selection . get_bounding_box ();
Vec3d plane_center = box . center ();
2018-11-26 12:08:10 +01:00
plane_center ( 2 ) = m_cut_z ;
2018-10-18 15:13:38 +02:00
const float min_x = box . min ( 0 ) - Margin ;
const float max_x = box . max ( 0 ) + Margin ;
const float min_y = box . min ( 1 ) - Margin ;
const float max_y = box . max ( 1 ) + Margin ;
:: glEnable ( GL_DEPTH_TEST );
:: glDisable ( GL_CULL_FACE );
:: glEnable ( GL_BLEND );
:: glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA );
// Draw the cutting plane
:: glBegin ( GL_QUADS );
:: glColor4f ( 0.8f , 0.8f , 0.8f , 0.5f );
:: glVertex3f ( min_x , min_y , plane_center ( 2 ));
:: glVertex3f ( max_x , min_y , plane_center ( 2 ));
:: glVertex3f ( max_x , max_y , plane_center ( 2 ));
:: glVertex3f ( min_x , max_y , plane_center ( 2 ));
:: glEnd ();
:: glEnable ( GL_CULL_FACE );
:: glDisable ( GL_BLEND );
// TODO: draw cut part contour?
// Draw the grabber and the connecting line
m_grabbers [ 0 ]. center = plane_center ;
m_grabbers [ 0 ]. center ( 2 ) = plane_center ( 2 ) + Offset ;
:: glDisable ( GL_DEPTH_TEST );
:: glLineWidth ( m_hover_id != - 1 ? 2.0f : 1.5f );
:: glColor3f ( 1.0 , 1.0 , 0.0 );
:: glBegin ( GL_LINES );
:: glVertex3dv ( plane_center . data ());
:: glVertex3dv ( m_grabbers [ 0 ]. center . data ());
:: glEnd ();
std :: copy ( std :: begin ( GrabberColor ), std :: end ( GrabberColor ), m_grabbers [ 0 ]. color );
m_grabbers [ 0 ]. render ( m_hover_id == 0 , box . max_size ());
}
void GLGizmoCut :: on_render_for_picking ( const GLCanvas3D :: Selection & selection ) const
{
:: glDisable ( GL_DEPTH_TEST );
render_grabbers_for_picking ( selection . get_bounding_box ());
}
2018-11-26 10:56:07 +01:00
#if ENABLE_IMGUI
void GLGizmoCut :: on_render_input_window ( float x , float y , const GLCanvas3D :: Selection & selection )
2018-10-18 15:13:38 +02:00
{
2018-11-26 10:56:07 +01:00
m_imgui -> set_next_window_pos ( x , y , ImGuiCond_Always );
m_imgui -> set_next_window_bg_alpha ( 0.5f );
m_imgui -> begin ( _ ( L ( "Cut" )), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse );
2018-10-18 15:13:38 +02:00
2018-11-26 10:56:07 +01:00
ImGui :: PushItemWidth ( 100.0f );
bool _value_changed = ImGui :: InputDouble ( "Z" , & m_cut_z , 0.0f , 0.0f , "%.2f" );
m_imgui -> checkbox ( _ ( L ( "Keep upper part" )), m_keep_upper );
m_imgui -> checkbox ( _ ( L ( "Keep lower part" )), m_keep_lower );
m_imgui -> checkbox ( _ ( L ( "Rotate lower part upwards" )), m_rotate_lower );
2019-01-18 15:02:07 +01:00
m_imgui -> disabled_begin ( ! m_keep_upper && ! m_keep_lower );
2018-11-26 10:56:07 +01:00
const bool cut_clicked = m_imgui -> button ( _ ( L ( "Perform cut" )));
2019-01-18 15:02:07 +01:00
m_imgui -> disabled_end ();
2018-11-26 10:56:07 +01:00
m_imgui -> end ();
2019-01-18 15:02:07 +01:00
if ( cut_clicked && ( m_keep_upper || m_keep_lower )) {
2018-11-26 10:56:07 +01:00
perform_cut ( selection );
}
}
#endif // ENABLE_IMGUI
2018-11-29 12:21:42 +01:00
void GLGizmoCut :: update_max_z ( const GLCanvas3D :: Selection & selection ) const
{
m_max_z = selection . get_bounding_box (). size ()( 2 );
set_cut_z ( m_cut_z );
}
void GLGizmoCut :: set_cut_z ( double cut_z ) const
{
// Clamp the plane to the object's bounding box
m_cut_z = std :: max ( 0.0 , std :: min ( m_max_z , cut_z ));
}
2018-11-26 10:56:07 +01:00
void GLGizmoCut :: perform_cut ( const GLCanvas3D :: Selection & selection )
{
2018-10-18 15:13:38 +02:00
const auto instance_idx = selection . get_instance_idx ();
const auto object_idx = selection . get_object_idx ();
wxCHECK_RET ( instance_idx >= 0 && object_idx >= 0 , "GLGizmoCut: Invalid object selection" );
2018-11-26 10:56:07 +01:00
wxGetApp (). plater () -> cut ( object_idx , instance_idx , m_cut_z , m_keep_upper , m_keep_lower , m_rotate_lower );
2018-10-18 15:13:38 +02:00
}
double GLGizmoCut :: calc_projection ( const Linef3 & mouse_ray ) const
{
double projection = 0.0 ;
const Vec3d starting_vec = m_drag_pos - m_drag_center ;
const double len_starting_vec = starting_vec . norm ();
if ( len_starting_vec != 0.0 )
{
Vec3d mouse_dir = mouse_ray . unit_vector ();
// finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position
// use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
// in our case plane normal and ray direction are the same (orthogonal view)
// when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
Vec3d inters = mouse_ray . a + ( m_drag_pos - mouse_ray . a ). dot ( mouse_dir ) / mouse_dir . squaredNorm () * mouse_dir ;
// vector from the starting position to the found intersection
Vec3d inters_vec = inters - m_drag_pos ;
// finds projection of the vector along the staring direction
projection = inters_vec . dot ( starting_vec . normalized ());
}
return projection ;
}
2018-06-13 09:12:16 +02:00
} // namespace GUI
} // namespace Slic3r