+
+
+
+
+
+
+/*
+ =============
+ CSG_Tool
+ =============
+ */
+#include "mainframe.h"
+#include <gtk/gtk.h>
+#include "gtkutil/dialog.h"
+#include "gtkutil/button.h"
+#include "gtkutil/accelerator.h"
+#include "xywindow.h"
+#include "camwindow.h"
+
+struct CSGToolDialog
+{
+ GtkSpinButton* spin;
+ bool allocated{false};
+ ui::Window window{ui::null};
+ GtkToggleButton *radFaces, *radProj, *radCam, *caulk, *removeInner;
+};
+
+CSGToolDialog g_csgtool_dialog;
+
+DoubleVector3 getExclusion(){
+ if( gtk_toggle_button_get_active( g_csgtool_dialog.radProj ) ){
+ if( GlobalXYWnd_getCurrentViewType() == YZ ){
+ return DoubleVector3( 1, 0, 0 );
+ }
+ else if( GlobalXYWnd_getCurrentViewType() == XZ ){
+ return DoubleVector3( 0, 1, 0 );
+ }
+ else if( GlobalXYWnd_getCurrentViewType() == XY ){
+ return DoubleVector3( 0, 0, 1 );
+ }
+ }
+ if( gtk_toggle_button_get_active( g_csgtool_dialog.radCam ) ){
+ Vector3 angles( Camera_getAngles( *g_pParentWnd->GetCamWnd() ) );
+// globalOutputStream() << angles << " angles\n";
+ DoubleVector3 radangles( degrees_to_radians( angles[0] ), degrees_to_radians( angles[1] ), degrees_to_radians( angles[2] ) );
+// globalOutputStream() << radangles << " radangles\n";
+// x = cos(yaw)*cos(pitch)
+// y = sin(yaw)*cos(pitch)
+// z = sin(pitch)
+ DoubleVector3 viewvector;
+ viewvector[0] = cos( radangles[1] ) * cos( radangles[0] );
+ viewvector[1] = sin( radangles[1] ) * cos( radangles[0] );
+ viewvector[2] = sin( radangles[0] );
+// globalOutputStream() << viewvector << " viewvector\n";
+ return viewvector;
+ }
+ return DoubleVector3( 0, 0, 0 );
+}
+
+bool getCaulk(){
+ if( gtk_toggle_button_get_active( g_csgtool_dialog.caulk ) ){
+ return true;
+ }
+ return false;
+}
+
+bool getRemoveInner(){
+ if( gtk_toggle_button_get_active( g_csgtool_dialog.removeInner ) ){
+ return true;
+ }
+ return false;
+}
+
+class BrushFaceOffset
+{
+float offset;
+public:
+BrushFaceOffset( float offset )
+ : offset( offset ){
+}
+void operator()( BrushInstance& brush ) const {
+ double mindot = 0;
+ double maxdot = 0;
+ doublevector_vector_t exclude_vec;
+ Brush_forEachFace( brush, FaceExclude( getExclusion(), mindot, maxdot ) );
+ if( mindot == 0 && maxdot == 0 ){
+ Brush_ForEachFaceInstance( brush, FaceExcludeSelected( exclude_vec ) );
+ }
+ Brush_forEachFace( brush, FaceOffset( offset, getExclusion(), mindot, maxdot, exclude_vec ) );
+}
+};
+
+//=================DLG
+
+static gboolean CSGdlg_HollowDiag( GtkWidget *widget, CSGToolDialog* dialog ){
+ float offset = static_cast<float>( gtk_spin_button_get_value( dialog->spin ) );
+ UndoableCommand undo( "brushHollow::Diag" );
+ GlobalSceneGraph().traverse( BrushHollowSelectedWalker( offset, diag ) );
+ if( getRemoveInner() )
+ GlobalSceneGraph().traverse( BrushDeleteSelected() );
+ SceneChangeNotify();
+ return TRUE;
+}
+
+static gboolean CSGdlg_HollowWrap( GtkWidget *widget, CSGToolDialog* dialog ){
+ float offset = static_cast<float>( gtk_spin_button_get_value( dialog->spin ) );
+ UndoableCommand undo( "brushHollow::Wrap" );
+ GlobalSceneGraph().traverse( BrushHollowSelectedWalker( offset, wrap ) );
+ if( getRemoveInner() )
+ GlobalSceneGraph().traverse( BrushDeleteSelected() );
+ SceneChangeNotify();
+ return TRUE;
+}
+
+static gboolean CSGdlg_HollowExtrude( GtkWidget *widget, CSGToolDialog* dialog ){
+ float offset = static_cast<float>( gtk_spin_button_get_value( dialog->spin ) );
+ UndoableCommand undo( "brushHollow::Extrude" );
+ GlobalSceneGraph().traverse( BrushHollowSelectedWalker( offset, extrude ) );
+ if( getRemoveInner() )
+ GlobalSceneGraph().traverse( BrushDeleteSelected() );
+ SceneChangeNotify();
+ return TRUE;
+}
+
+static gboolean CSGdlg_HollowPull( GtkWidget *widget, CSGToolDialog* dialog ){
+ float offset = static_cast<float>( gtk_spin_button_get_value( dialog->spin ) );
+ UndoableCommand undo( "brushHollow::Pull" );
+ GlobalSceneGraph().traverse( BrushHollowSelectedWalker( offset, pull ) );
+ if( getRemoveInner() )
+ GlobalSceneGraph().traverse( BrushDeleteSelected() );
+ SceneChangeNotify();
+ return TRUE;
+}
+
+static gboolean CSGdlg_BrushShrink( GtkWidget *widget, CSGToolDialog* dialog ){
+ gtk_spin_button_update ( dialog->spin );
+ float offset = static_cast<float>( gtk_spin_button_get_value( dialog->spin ) );
+ offset *= -1;
+ UndoableCommand undo( "Shrink brush" );
+// GlobalSceneGraph().traverse( OffsetBrushFacesSelectedWalker( offset ) );
+ //Scene_ForEachSelectedBrush_ForEachFace( GlobalSceneGraph(), BrushFaceOffset( offset ) );
+ Scene_forEachSelectedBrush( BrushFaceOffset( offset ) );
+ SceneChangeNotify();
+ return TRUE;
+}
+
+static gboolean CSGdlg_BrushExpand( GtkWidget *widget, CSGToolDialog* dialog ){
+ gtk_spin_button_update ( dialog->spin );
+ float offset = static_cast<float>( gtk_spin_button_get_value( dialog->spin ) );
+ UndoableCommand undo( "Expand brush" );
+// GlobalSceneGraph().traverse( OffsetBrushFacesSelectedWalker( offset ) );
+ //Scene_ForEachSelectedBrush_ForEachFace( GlobalSceneGraph(), BrushFaceOffset( offset ) );
+ Scene_forEachSelectedBrush( BrushFaceOffset( offset ) );
+ SceneChangeNotify();
+ return TRUE;
+}
+
+static gboolean CSGdlg_grid2spin( GtkWidget *widget, CSGToolDialog* dialog ){
+ gtk_spin_button_set_value( dialog->spin, GetGridSize() );
+ return TRUE;
+}
+
+static gboolean CSGdlg_delete( GtkWidget *widget, GdkEventAny *event, CSGToolDialog* dialog ){
+ gtk_widget_hide( GTK_WIDGET( dialog->window ) );
+ return TRUE;
+}
+
+void CSG_Tool(){
+ // FIXME: there is probably improvements to do less raw GTK stuff, more GTK wrapper
+ if ( !g_csgtool_dialog.allocated ) {
+ g_csgtool_dialog.allocated = true;
+ g_csgtool_dialog.window = MainFrame_getWindow().create_dialog_window( "CSG Tool", G_CALLBACK( CSGdlg_delete ), &g_csgtool_dialog );
+ gtk_window_set_type_hint( g_csgtool_dialog.window, GDK_WINDOW_TYPE_HINT_UTILITY );
+
+ //GtkAccelGroup* accel = gtk_accel_group_new();
+ //gtk_window_add_accel_group( g_csgtool_dialog.window, accel );
+ global_accel_connect_window( g_csgtool_dialog.window );
+
+ {
+ auto hbox = create_dialog_hbox( 4, 4 );
+ gtk_container_add( GTK_CONTAINER( g_csgtool_dialog.window ), GTK_WIDGET( hbox ) );
+ {
+ auto table = create_dialog_table( 3, 8, 4, 4 );
+ gtk_box_pack_start( GTK_BOX( hbox ), GTK_WIDGET( table ), TRUE, TRUE, 0 );
+ {
+ //GtkWidget* label = gtk_label_new( "<->" );
+ //gtk_widget_show( label );
+ auto button = ui::Button( "Grid->" );
+ table.attach( button, {0, 1, 0, 1}, {0, 0} );
+ button.show();
+ g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( CSGdlg_grid2spin ), &g_csgtool_dialog );
+ }
+ {
+ GtkAdjustment* adj = GTK_ADJUSTMENT( gtk_adjustment_new( 16, 0, 9999, 1, 10, 0 ) );
+ GtkSpinButton* spin = GTK_SPIN_BUTTON( gtk_spin_button_new( adj, 1, 3 ) );
+ gtk_widget_show( GTK_WIDGET( spin ) );
+ gtk_widget_set_tooltip_text( GTK_WIDGET( spin ), "Thickness" );
+ gtk_table_attach( table, GTK_WIDGET( spin ), 1, 2, 0, 1,
+ (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
+ (GtkAttachOptions) ( 0 ), 0, 0 );
+ gtk_widget_set_size_request( GTK_WIDGET( spin ), 64, -1 );
+ gtk_spin_button_set_numeric( spin, TRUE );
+
+ g_csgtool_dialog.spin = spin;
+ }
+ {
+ //radio button group for choosing the exclude axis
+ GtkWidget* radFaces = gtk_radio_button_new_with_label( NULL, "-faces" );
+ gtk_widget_set_tooltip_text( radFaces, "Exclude selected faces" );
+ GtkWidget* radProj = gtk_radio_button_new_with_label_from_widget( GTK_RADIO_BUTTON(radFaces), "-proj" );
+ gtk_widget_set_tooltip_text( radProj, "Exclude faces, most orthogonal to active projection" );
+ GtkWidget* radCam = gtk_radio_button_new_with_label_from_widget( GTK_RADIO_BUTTON(radFaces), "-cam" );
+ gtk_widget_set_tooltip_text( radCam, "Exclude faces, most orthogonal to camera view" );
+
+ gtk_widget_show( radFaces );
+ gtk_widget_show( radProj );
+ gtk_widget_show( radCam );
+
+ gtk_table_attach( table, radFaces, 2, 3, 0, 1,
+ (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
+ (GtkAttachOptions) ( 0 ), 0, 0 );
+ gtk_table_attach( table, radProj, 3, 4, 0, 1,
+ (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
+ (GtkAttachOptions) ( 0 ), 0, 0 );
+ gtk_table_attach( table, radCam, 4, 5, 0, 1,
+ (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
+ (GtkAttachOptions) ( 0 ), 0, 0 );
+
+ g_csgtool_dialog.radFaces = GTK_TOGGLE_BUTTON( radFaces );
+ g_csgtool_dialog.radProj = GTK_TOGGLE_BUTTON( radProj );
+ g_csgtool_dialog.radCam = GTK_TOGGLE_BUTTON( radCam );
+ }
+ {
+ GtkWidget* button = gtk_toggle_button_new();
+ auto ubutton = ui::Button::from( button );
+ button_set_icon( ubutton, "f-caulk.png" );
+ gtk_button_set_relief( GTK_BUTTON( button ), GTK_RELIEF_NONE );
+ table.attach( ubutton, { 6, 7, 0, 1 }, { GTK_EXPAND, 0 } );
+ gtk_widget_set_tooltip_text( button, "Caulk some faces" );
+ gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( button ), TRUE );
+ ubutton.show();
+ g_csgtool_dialog.caulk = GTK_TOGGLE_BUTTON( button );
+ }
+ {
+ GtkWidget* button = gtk_toggle_button_new();
+ auto ubutton = ui::Button::from( button );
+ button_set_icon( ubutton, "csgtool_removeinner.png" );
+ gtk_button_set_relief( GTK_BUTTON( button ), GTK_RELIEF_NONE );
+ table.attach( ubutton, { 7, 8, 0, 1 }, { GTK_EXPAND, 0 } );
+ gtk_widget_set_tooltip_text( button, "Remove inner brush" );
+ gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( button ), TRUE );
+ ubutton.show();
+ g_csgtool_dialog.removeInner = GTK_TOGGLE_BUTTON( button );
+ }
+ {
+ GtkWidget* sep = gtk_hseparator_new();
+ gtk_widget_show( sep );
+ gtk_table_attach( table, sep, 0, 8, 1, 2,
+ (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
+ (GtkAttachOptions) ( 0 ), 0, 0 );
+ }
+ {
+ GtkWidget* button = gtk_button_new();
+ auto ubutton = ui::Button::from( button );
+ button_set_icon( ubutton, "csgtool_shrink.png" );
+ table.attach( ubutton, { 0, 1, 2, 3 }, { GTK_EXPAND, 0 } );
+ gtk_widget_set_tooltip_text( button, "Shrink brush" );
+ ubutton.show();
+ g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( CSGdlg_BrushShrink ), &g_csgtool_dialog );
+ }
+ {
+ GtkWidget* button = gtk_button_new();
+ auto ubutton = ui::Button::from( button );
+ button_set_icon( ubutton, "csgtool_expand.png" );
+ table.attach( ubutton, { 1, 2, 2, 3 }, { GTK_EXPAND, 0 } );
+ gtk_widget_set_tooltip_text( button, "Expand brush" );
+ ubutton.show();
+ g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( CSGdlg_BrushExpand ), &g_csgtool_dialog );
+ }
+ {
+ GtkWidget* button = gtk_button_new();
+ auto ubutton = ui::Button::from( button );
+ button_set_icon( ubutton, "csgtool_diagonal.png" );
+ table.attach( ubutton, { 3, 4, 2, 3 }, { GTK_EXPAND, 0 } );
+ gtk_widget_set_tooltip_text( button, "Hollow::diagonal joints" );
+ ubutton.show();
+ g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( CSGdlg_HollowDiag ), &g_csgtool_dialog );
+ }
+ {
+ GtkWidget* button = gtk_button_new();
+ auto ubutton = ui::Button::from( button );
+ button_set_icon( ubutton, "csgtool_wrap.png" );
+ table.attach( ubutton, { 4, 5, 2, 3 }, { GTK_EXPAND, 0 } );
+ gtk_widget_set_tooltip_text( button, "Hollow::warp" );
+ ubutton.show();
+ g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( CSGdlg_HollowWrap ), &g_csgtool_dialog );
+ }
+ {
+ GtkWidget* button = gtk_button_new();
+ auto ubutton = ui::Button::from( button );
+ button_set_icon( ubutton, "csgtool_extrude.png" );
+ table.attach( ubutton, { 5, 6, 2, 3 }, { GTK_EXPAND, 0 } );
+ gtk_widget_set_tooltip_text( button, "Hollow::extrude faces" );
+ ubutton.show();
+ g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( CSGdlg_HollowExtrude ), &g_csgtool_dialog );
+ }
+ {
+ GtkWidget* button = gtk_button_new();
+ auto ubutton = ui::Button::from( button );
+ button_set_icon( ubutton, "csgtool_pull.png" );
+ table.attach( ubutton, { 6, 7, 2, 3 }, { GTK_EXPAND, 0 } );
+ gtk_widget_set_tooltip_text( button, "Hollow::pull faces" );
+ ubutton.show();
+ g_signal_connect( G_OBJECT( button ), "clicked", G_CALLBACK( CSGdlg_HollowPull ), &g_csgtool_dialog );
+ }
+
+ }
+ }
+ }
+
+ gtk_widget_show( GTK_WIDGET( g_csgtool_dialog.window ) );
+ gtk_window_present( g_csgtool_dialog.window );
+}
+