2014-01-29 19:09:30 -08:00
/ *
2018-04-03 08:36:21 -07:00
Copyright ( c ) 2018 , Lars Brubaker , John Lewin
2014-01-29 19:09:30 -08:00
All rights reserved .
Redistribution and use in source and binary forms , with or without
2015-04-08 15:20:10 -07:00
modification , are permitted provided that the following conditions are met :
2014-01-29 19:09:30 -08:00
1. Redistributions of source code must retain the above copyright notice , this
2015-04-08 15:20:10 -07:00
list of conditions and the following disclaimer .
2014-01-29 19:09:30 -08:00
2. Redistributions in binary form must reproduce the above copyright notice ,
this list of conditions and the following disclaimer in the documentation
2015-04-08 15:20:10 -07:00
and / or other materials provided with the distribution .
2014-01-29 19:09:30 -08:00
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES
( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ;
LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
The views and conclusions contained in the software and documentation are those
2015-04-08 15:20:10 -07:00
of the authors and should not be interpreted as representing official policies ,
2014-01-29 19:09:30 -08:00
either expressed or implied , of the FreeBSD Project .
* /
2022-03-02 00:52:04 +00:00
#define ENABLE_PERSPECTIVE_PROJECTION_DYNAMIC_NEAR_FAR
2021-12-17 10:15:55 -08:00
2019-01-08 17:55:59 -08:00
using AngleSharp.Dom ;
2020-12-07 07:58:13 -08:00
using AngleSharp.Html.Parser ;
2014-01-29 19:09:30 -08:00
using MatterHackers.Agg ;
2018-09-10 12:02:20 -07:00
using MatterHackers.Agg.Platform ;
2014-01-29 19:09:30 -08:00
using MatterHackers.Agg.UI ;
2021-05-15 22:33:27 -07:00
using MatterHackers.Agg.VertexSource ;
2017-03-15 16:17:06 -07:00
using MatterHackers.DataConverters3D ;
2021-05-21 14:24:45 -07:00
using MatterHackers.ImageProcessing ;
2014-10-15 14:11:14 -07:00
using MatterHackers.Localizations ;
2014-10-23 14:40:12 -07:00
using MatterHackers.MatterControl.CustomWidgets ;
2022-02-22 16:59:40 -08:00
using MatterHackers.MatterControl.DataStorage ;
2018-08-28 18:45:58 -07:00
using MatterHackers.MatterControl.DesignTools ;
Move to new library model and view
- Add new listview control for library content
- Migrate library providers to containers
- Cloud, Sqlite, Directories, Queue, History
- Migrate SideBar components to containers
- Primatives, Text, Braille, ImageConverter
- Create new library container types
- Zip files, Calibration parts, Printer SDCards
- Reduce leftnav to Library, Settings, Controls, Options
- Add DragDrop support for image content
2017-05-19 22:33:55 -07:00
using MatterHackers.MatterControl.Library ;
2021-06-24 10:36:32 -07:00
using MatterHackers.MatterControl.PrinterCommunication ;
2018-10-23 19:54:09 -07:00
using MatterHackers.MatterControl.PrinterControls.PrinterConnections ;
using MatterHackers.MatterControl.SlicerConfiguration ;
2022-03-02 00:52:04 +00:00
using MatterHackers.PolygonMesh ;
2021-11-20 20:38:26 -08:00
using MatterHackers.PolygonMesh.Processors ;
2014-01-29 19:09:30 -08:00
using MatterHackers.RayTracer ;
2014-04-15 18:13:27 -07:00
using MatterHackers.RenderOpenGl ;
2018-05-09 16:41:24 -07:00
using MatterHackers.RenderOpenGl.OpenGl ;
2014-04-15 18:13:27 -07:00
using MatterHackers.VectorMath ;
2018-04-03 08:36:21 -07:00
using MatterHackers.VectorMath.TrackBall ;
2021-12-17 10:15:55 -08:00
using System ;
using System.Collections.Generic ;
using System.Collections.ObjectModel ;
using System.ComponentModel ;
using System.IO ;
using System.Linq ;
using System.Text.RegularExpressions ;
using System.Threading ;
using System.Threading.Tasks ;
2014-01-29 19:09:30 -08:00
namespace MatterHackers.MatterControl.PartPreviewWindow
{
2021-12-17 10:15:55 -08:00
public class View3DWidget : GuiWidget , IDrawable
2015-04-08 15:20:10 -07:00
{
2022-03-02 00:52:04 +00:00
// Padded by this amount on each side, in case of unaccounted for scene geometry.
2022-03-04 20:10:03 +00:00
// For orthographic, this is an offset on either side scaled by far - near.
2022-03-02 00:52:04 +00:00
// For perspective, this + 1 is used to scale the near and far planes.
private const double DynamicNearFarBoundsPaddingFactor = 0.1 ;
2017-04-05 19:03:04 -07:00
private bool deferEditorTillMouseUp = false ;
2022-02-24 19:11:29 -07:00
private bool expandSelection ;
2017-04-05 19:03:04 -07:00
2020-09-12 08:53:40 -07:00
public int EditButtonHeight { get ; set ; } = 44 ;
2017-03-15 16:17:06 -07:00
2018-04-23 14:33:27 -07:00
public Matrix4X4 TransformOnMouseDown { get ; private set ; } = Matrix4X4 . Identity ;
2015-04-08 15:20:10 -07:00
2020-12-26 15:15:49 -08:00
private readonly TreeView treeView ;
2018-05-22 12:51:04 -07:00
2020-12-26 15:15:49 -08:00
private readonly PrinterConfig printer ;
2018-09-10 16:40:50 -07:00
2020-12-26 15:15:49 -08:00
private readonly ThemeConfig theme ;
2016-10-06 10:59:51 -07:00
2017-09-05 18:02:19 -07:00
public Vector3 BedCenter
{
get
{
2017-09-15 16:49:21 -07:00
return new Vector3 ( sceneContext . BedCenter ) ;
2017-09-05 18:02:19 -07:00
}
}
2021-04-15 00:35:27 +02:00
public TrackballTumbleWidgetExtended TrackballTumbleWidget { get ; private set ; }
2017-07-10 14:00:27 -07:00
2020-09-12 13:09:17 -07:00
public Object3DControlsLayer Object3DControlLayer { get ; }
2017-07-11 08:10:57 -07:00
2019-02-01 16:12:12 -08:00
public ISceneContext sceneContext ;
2017-09-15 16:49:21 -07:00
2018-04-23 18:48:17 -07:00
public PrinterConfig Printer { get ; private set ; }
2017-10-16 17:09:00 -07:00
2020-12-26 15:15:49 -08:00
private readonly PrinterTabPage printerTabPage ;
2022-07-16 07:46:44 -07:00
private ThemedRadioIconButton translateButton ;
private ThemedRadioIconButton rotateButton ;
private ThemedRadioIconButton zoomButton ;
private ThemedRadioIconButton partSelectButton ;
2017-10-16 17:09:00 -07:00
2022-02-02 17:31:44 -08:00
public View3DWidget ( PrinterConfig printer , ISceneContext sceneContext , ViewToolBarControls viewControls3D , ThemeConfig theme , DesignTabPage printerTabBase , Object3DControlsLayer . EditorType editorType = Object3DControlsLayer . EditorType . Part )
2015-05-30 12:48:16 -07:00
{
2017-09-15 16:49:21 -07:00
this . sceneContext = sceneContext ;
2017-10-30 08:53:53 -07:00
this . printerTabPage = printerTabBase as PrinterTabPage ;
2018-04-23 18:48:17 -07:00
this . Printer = printer ;
2017-07-10 14:00:27 -07:00
2020-09-12 13:09:17 -07:00
this . Object3DControlLayer = new Object3DControlsLayer ( sceneContext , theme , editorType )
2017-07-11 08:10:57 -07:00
{
2020-09-11 19:59:14 -07:00
Name = "Object3DControlLayer" ,
2017-07-11 08:10:57 -07:00
} ;
2020-09-11 19:59:14 -07:00
this . Object3DControlLayer . AnchorAll ( ) ;
2017-07-11 08:10:57 -07:00
2019-02-01 08:58:44 -08:00
// Register ourself as an IDrawable
2020-09-11 19:59:14 -07:00
this . Object3DControlLayer . RegisterDrawable ( this ) ;
2019-02-01 08:58:44 -08:00
2017-07-05 14:34:38 -07:00
this . viewControls3D = viewControls3D ;
2018-09-10 16:40:50 -07:00
this . printer = printer ;
2017-06-19 09:17:57 -07:00
this . theme = theme ;
2016-03-01 11:25:15 -08:00
this . Name = "View3DWidget" ;
2018-10-17 14:41:32 -07:00
this . BackgroundColor = theme . BedBackgroundColor ;
2019-05-02 18:06:28 -07:00
this . HAnchor = HAnchor . Stretch ; // HAnchor.MaxFitOrStretch,
2018-02-17 09:06:49 -08:00
this . VAnchor = VAnchor . Stretch ; // VAnchor.MaxFitOrStretch
2017-05-24 14:19:02 -07:00
2017-05-26 13:46:12 -07:00
viewControls3D . TransformStateChanged + = ViewControls3D_TransformStateChanged ;
2017-08-18 08:34:05 -07:00
// MeshViewer
2021-04-30 15:06:14 -07:00
TrackballTumbleWidget = new TrackballTumbleWidgetExtended ( sceneContext . World , this , Object3DControlLayer , theme )
2018-02-20 15:23:55 -08:00
{
2018-04-03 08:36:21 -07:00
TransformState = TrackBallTransformType . Rotation
2018-02-20 15:23:55 -08:00
} ;
2019-02-07 14:28:22 -08:00
TrackballTumbleWidget . GetNearFar = GetNearFar ;
2018-04-23 14:33:27 -07:00
TrackballTumbleWidget . AnchorAll ( ) ;
2018-02-20 15:23:55 -08:00
2018-04-18 09:36:53 -07:00
this . BoundsChanged + = UpdateRenderView ;
2017-08-18 08:34:05 -07:00
// TumbleWidget
2020-09-11 19:59:14 -07:00
this . Object3DControlLayer . AddChild ( TrackballTumbleWidget ) ;
2017-08-18 12:40:49 -07:00
2020-09-11 19:59:14 -07:00
this . Object3DControlLayer . SetRenderTarget ( this ) ;
2017-07-10 14:00:27 -07:00
2018-02-17 10:42:58 -08:00
// Add splitter support with the InteractionLayer on the left and resize containers on the right
var splitContainer = new FlowLayoutWidget ( )
{
Name = "SplitContainer" ,
HAnchor = HAnchor . Stretch ,
VAnchor = VAnchor . Stretch ,
} ;
2020-09-11 19:59:14 -07:00
splitContainer . AddChild ( this . Object3DControlLayer ) ;
2018-02-17 10:42:58 -08:00
this . AddChild ( splitContainer ) ;
2015-04-08 15:20:10 -07:00
2018-04-23 14:33:27 -07:00
Scene . SelectionChanged + = Scene_SelectionChanged ;
2015-04-08 15:20:10 -07:00
2018-10-26 09:04:31 -07:00
this . Scene . Invalidated + = Scene_Invalidated ;
2015-04-08 15:20:10 -07:00
this . AnchorAll ( ) ;
2014-01-29 19:09:30 -08:00
2018-04-23 14:33:27 -07:00
TrackballTumbleWidget . TransformState = TrackBallTransformType . Rotation ;
2014-03-01 23:27:34 -08:00
2018-05-31 20:07:49 -07:00
selectedObjectPanel = new SelectedObjectPanel ( this , sceneContext , theme )
2017-03-15 16:17:06 -07:00
{
2018-01-05 11:45:11 -08:00
VAnchor = VAnchor . Stretch ,
2017-03-15 16:17:06 -07:00
} ;
2017-10-19 15:29:01 -07:00
2018-10-14 17:50:54 -07:00
modelViewSidePanel = new VerticalResizeContainer ( theme , GrabBarSide . Left )
2017-10-19 15:29:01 -07:00
{
2020-06-07 23:09:01 -07:00
Width = UserSettings . Instance . SelectedObjectPanelWidth ,
2018-01-05 11:45:11 -08:00
VAnchor = VAnchor . Stretch ,
2018-02-17 10:42:58 -08:00
HAnchor = HAnchor . Absolute ,
2020-09-14 07:36:32 -07:00
BackgroundColor = theme . InteractionLayerOverlayColor ,
2018-10-25 14:43:21 -07:00
SplitterBarColor = theme . SplitterBackground ,
2017-11-27 16:41:52 -08:00
SplitterWidth = theme . SplitterWidth ,
2018-04-18 09:36:53 -07:00
MinimumSize = new Vector2 ( theme . SplitterWidth , 0 )
2017-10-19 15:29:01 -07:00
} ;
2018-04-18 09:36:53 -07:00
modelViewSidePanel . BoundsChanged + = UpdateRenderView ;
2018-04-05 15:40:38 -07:00
2018-05-30 16:42:17 -07:00
modelViewSidePanel . Resized + = ModelViewSidePanel_Resized ;
2018-05-22 12:51:04 -07:00
// add the tree view
2018-06-01 08:49:02 -07:00
treeView = new TreeView ( theme )
{
2022-02-24 19:11:29 -07:00
Name = "DesignTree" ,
2018-10-26 16:56:23 -07:00
Margin = new BorderDouble ( left : theme . DefaultContainerPadding + 12 ) ,
2018-06-01 08:49:02 -07:00
} ;
2018-08-07 16:42:59 -07:00
treeView . NodeMouseClick + = ( s , e ) = >
{
2018-08-07 17:41:41 -07:00
if ( e is MouseEventArgs sourceEvent
2018-10-31 22:13:59 -07:00
& & s is GuiWidget clickedWidget )
2018-08-07 16:42:59 -07:00
{
2022-02-24 19:11:29 -07:00
if ( ! AggregateSelection ( sourceEvent , clickedWidget ) )
2018-11-01 16:13:29 -07:00
{
2022-02-24 19:11:29 -07:00
var object3D = ( IObject3D ) treeView . SelectedNode . Tag ;
if ( object3D ! = Scene . SelectedItem )
{
Scene . SelectedItem = object3D ;
}
2018-11-01 16:13:29 -07:00
}
2018-10-31 22:13:59 -07:00
if ( sourceEvent . Button = = MouseButtons . Right )
2018-08-07 16:42:59 -07:00
{
2020-09-24 22:35:03 -07:00
var popupMenu = ApplicationController . Instance . GetActionMenuForSceneItem ( true , this ) ;
2020-07-09 07:12:00 -07:00
popupMenu . ShowMenu ( clickedWidget , sourceEvent ) ;
2018-10-31 22:13:59 -07:00
}
2018-08-07 16:42:59 -07:00
}
} ;
2018-10-26 16:56:23 -07:00
treeView . ScrollArea . HAnchor = HAnchor . Stretch ;
treeNodeContainer = new FlowLayoutWidget ( FlowDirection . TopToBottom )
{
HAnchor = HAnchor . Stretch ,
VAnchor = VAnchor . Fit ,
Margin = new BorderDouble ( 12 , 3 )
} ;
treeView . AddChild ( treeNodeContainer ) ;
2018-10-26 09:04:31 -07:00
var historyAndProperties = new Splitter ( )
2018-06-22 18:39:03 -07:00
{
2018-09-10 16:42:46 -07:00
Orientation = Orientation . Horizontal ,
2018-09-10 18:24:09 -07:00
Panel1Ratio = sceneContext . ViewState . SceneTreeRatio ,
2018-09-10 16:42:46 -07:00
SplitterSize = theme . SplitterWidth ,
SplitterBackground = theme . SplitterBackground
2018-06-22 18:39:03 -07:00
} ;
2019-01-24 17:49:44 -08:00
historyAndProperties . Panel1 . MinimumSize = new Vector2 ( 0 , 60 ) ;
historyAndProperties . Panel2 . MinimumSize = new Vector2 ( 0 , 60 ) ;
2018-05-22 12:51:04 -07:00
2018-09-10 16:42:46 -07:00
modelViewSidePanel . AddChild ( historyAndProperties ) ;
2018-10-26 16:56:23 -07:00
var titleAndTreeView = new FlowLayoutWidget ( FlowDirection . TopToBottom )
{
HAnchor = HAnchor . Stretch ,
VAnchor = VAnchor . Stretch
} ;
2018-11-01 14:31:47 -07:00
titleAndTreeView . AddChild ( workspaceName = new InlineStringEdit ( sceneContext . Scene . Name ? ? "" , theme , "WorkspaceName" , editable : false )
2018-10-26 16:56:23 -07:00
{
Border = new BorderDouble ( top : 1 ) ,
BorderColor = theme . SplitterBackground
} ) ;
titleAndTreeView . AddChild ( treeView ) ;
workspaceName . ActionArea . AddChild (
2022-07-16 07:46:44 -07:00
new ThemedIconButton ( StaticData . Instance . LoadIcon ( "fa-angle-right_12.png" , 12 , 12 ) . SetToColor ( theme . TextColor ) , theme )
2018-10-26 16:56:23 -07:00
{
Enabled = false
} ,
indexInChildrenList : 0 ) ;
// Remove left margin
workspaceName . ActionArea . Children < TextWidget > ( ) . First ( ) . Margin = 0 ;
// Resize buttons
2022-07-16 07:46:44 -07:00
foreach ( var iconButton in workspaceName . Descendants < ThemedIconButton > ( ) )
2018-10-26 16:56:23 -07:00
{
iconButton . Height = 26 ;
iconButton . Width = 26 ;
}
workspaceName . Margin = workspaceName . Margin . Clone ( bottom : 0 ) ;
historyAndProperties . Panel1 . AddChild ( titleAndTreeView ) ;
2018-09-10 16:42:46 -07:00
historyAndProperties . DistanceChanged + = ( s , e ) = >
{
2018-09-10 18:24:09 -07:00
sceneContext . ViewState . SceneTreeRatio = historyAndProperties . Panel1Ratio ;
2018-09-10 16:42:46 -07:00
} ;
historyAndProperties . Panel2 . AddChild ( selectedObjectPanel ) ;
2018-04-05 15:59:16 -07:00
splitContainer . AddChild ( modelViewSidePanel ) ;
2017-03-15 16:17:06 -07:00
2021-05-15 22:33:27 -07:00
CreateTumbleCubeAndControls ( theme ) ;
this . Object3DControlLayer . AfterDraw + = AfterDraw3DContent ;
Scene . SelectFirstChild ( ) ;
viewControls3D . ActiveButton = ViewControls3DButtons . PartSelect ;
2021-05-17 07:25:37 -07:00
UpdateControlButtons ( viewControls3D . ActiveButton ) ;
2021-05-15 22:33:27 -07:00
sceneContext . SceneLoaded + = SceneContext_SceneLoaded ;
if ( ! AppContext . IsLoading )
{
this . RebuildTree ( ) ;
}
if ( sceneContext ? . Scene ? . SelectedItem ! = null )
{
UiThread . RunOnIdle ( ( ) = >
{
// make sure the selected item is still selected after reload
2022-02-26 22:49:12 -08:00
using ( new SelectionMaintainer ( Scene ) )
{
}
2021-05-15 22:33:27 -07:00
} ) ;
}
}
private void CreateTumbleCubeAndControls ( ThemeConfig theme )
{
var controlLayer = this . Object3DControlLayer ;
var scale = GuiWidget . DeviceScale ;
var tumbleCubeControl = new TumbleCubeControl ( controlLayer , theme , TrackballTumbleWidget )
2017-11-29 16:40:46 -08:00
{
2021-05-15 22:33:27 -07:00
Margin = new BorderDouble ( 0 , 0 , 40 , 45 ) ,
2017-11-29 16:40:46 -08:00
VAnchor = VAnchor . Top ,
2018-05-24 12:05:25 -07:00
HAnchor = HAnchor . Right ,
2018-11-29 09:53:48 -08:00
Name = "Tumble Cube Control"
2018-09-10 12:02:20 -07:00
} ;
2021-05-15 22:33:27 -07:00
var cubeCenterFromRightTop = new Vector2 ( tumbleCubeControl . Margin . Right * scale + tumbleCubeControl . Width / 2 ,
tumbleCubeControl . Margin . Top * scale + tumbleCubeControl . Height / 2 ) ;
controlLayer . AddChild ( tumbleCubeControl ) ;
2021-05-22 08:36:02 -07:00
var hudBackground = controlLayer . AddChild ( new GuiWidget ( )
{
VAnchor = VAnchor . Stretch ,
HAnchor = HAnchor . Stretch ,
Selectable = false ,
2021-12-17 10:15:55 -08:00
// DoubleBuffer = true
2021-05-22 08:36:02 -07:00
} ) ;
2021-12-17 10:15:55 -08:00
// hudBackground.BackBuffer.SetRecieveBlender(new BlenderBGRA());
2021-12-15 18:09:21 -08:00
2021-05-16 09:44:14 -07:00
GuiWidget AddRoundButton ( GuiWidget widget , Vector2 offset , bool center = false )
2021-05-15 22:33:27 -07:00
{
2021-05-16 09:44:14 -07:00
widget . BackgroundRadius = new RadiusCorners ( Math . Min ( widget . Width / 2 , widget . Height / 2 ) ) ;
2021-05-15 22:33:27 -07:00
widget . BackgroundOutlineWidth = 1 ;
widget . VAnchor = VAnchor . Top ;
widget . HAnchor = HAnchor . Right ;
2021-05-16 09:44:14 -07:00
if ( center )
{
offset . X - = ( widget . Width / 2 ) / scale ;
}
2021-05-15 22:33:27 -07:00
widget . Margin = new BorderDouble ( 0 , 0 , offset . X , offset . Y ) ;
return controlLayer . AddChild ( widget ) ;
}
Vector2 RotatedMargin ( GuiWidget widget , double angle )
{
2021-05-16 09:44:14 -07:00
var radius = 70 * scale ;
2021-05-15 22:33:27 -07:00
var widgetCenter = new Vector2 ( widget . Width / 2 , widget . Height / 2 ) ;
// divide by scale to convert from pixels to margin units
return ( cubeCenterFromRightTop - widgetCenter - new Vector2 ( 0 , radius ) . GetRotated ( angle ) ) / scale ;
}
// add the view controls
var buttonGroupA = new ObservableCollection < GuiWidget > ( ) ;
2022-07-16 07:46:44 -07:00
partSelectButton = new ThemedRadioIconButton ( StaticData . Instance . LoadIcon ( Path . Combine ( "ViewTransformControls" , "partSelect.png" ) , 16 , 16 ) . SetToColor ( theme . TextColor ) , theme )
2021-05-15 22:33:27 -07:00
{
SiblingRadioButtonList = buttonGroupA ,
2021-05-17 07:25:37 -07:00
ToolTipText = "Select Parts" . Localize ( ) ,
2021-12-17 16:35:10 -08:00
Margin = theme . ButtonSpacing ,
2021-05-15 22:33:27 -07:00
} ;
2022-04-28 16:39:03 -07:00
partSelectButton . MouseEnterBounds + = ( s , e ) = > ApplicationController . Instance . UiHint = "Ctrl + A = Select Alll, 'Space' = Clear Selection, 'ESC' = Cancel Drag" . Localize ( ) ;
partSelectButton . MouseLeaveBounds + = ( s , e ) = > ApplicationController . Instance . UiHint = "" ;
2021-05-15 22:33:27 -07:00
AddRoundButton ( partSelectButton , RotatedMargin ( partSelectButton , MathHelper . Tau * . 15 ) ) ;
partSelectButton . Click + = ( s , e ) = > viewControls3D . ActiveButton = ViewControls3DButtons . PartSelect ;
buttonGroupA . Add ( partSelectButton ) ;
2022-07-16 07:46:44 -07:00
rotateButton = new ThemedRadioIconButton ( StaticData . Instance . LoadIcon ( Path . Combine ( "ViewTransformControls" , "rotate.png" ) , 16 , 16 ) . SetToColor ( theme . TextColor ) , theme )
2021-05-15 22:33:27 -07:00
{
SiblingRadioButtonList = buttonGroupA ,
2021-05-17 07:25:37 -07:00
ToolTipText = "Rotate View" . Localize ( ) ,
2021-05-15 22:33:27 -07:00
Margin = theme . ButtonSpacing
} ;
2022-04-28 16:39:03 -07:00
rotateButton . MouseEnterBounds + = ( s , e ) = > ApplicationController . Instance . UiHint = "Rotate: Right Mouse Button, Ctrl + Left Mouse Button, Arrow Keys" . Localize ( ) ;
2021-06-04 18:09:30 -07:00
rotateButton . MouseLeaveBounds + = ( s , e ) = > ApplicationController . Instance . UiHint = "" ;
2021-05-15 22:33:27 -07:00
AddRoundButton ( rotateButton , RotatedMargin ( rotateButton , MathHelper . Tau * . 05 ) ) ;
rotateButton . Click + = ( s , e ) = > viewControls3D . ActiveButton = ViewControls3DButtons . Rotate ;
buttonGroupA . Add ( rotateButton ) ;
2018-09-10 12:02:20 -07:00
2022-07-16 07:46:44 -07:00
translateButton = new ThemedRadioIconButton ( StaticData . Instance . LoadIcon ( Path . Combine ( "ViewTransformControls" , "translate.png" ) , 16 , 16 ) . SetToColor ( theme . TextColor ) , theme )
2018-09-10 12:02:20 -07:00
{
2021-05-15 22:33:27 -07:00
SiblingRadioButtonList = buttonGroupA ,
2021-05-17 07:25:37 -07:00
ToolTipText = "Move View" . Localize ( ) ,
2021-05-15 22:33:27 -07:00
Margin = theme . ButtonSpacing
2018-09-10 12:02:20 -07:00
} ;
2022-04-28 16:39:03 -07:00
translateButton . MouseEnterBounds + = ( s , e ) = > ApplicationController . Instance . UiHint = "Move: Middle Mouse Button, Ctrl + Shift + Left Mouse Button, Shift Arrow Keys" . Localize ( ) ;
2021-06-04 18:09:30 -07:00
translateButton . MouseLeaveBounds + = ( s , e ) = > ApplicationController . Instance . UiHint = "" ;
2021-05-15 22:33:27 -07:00
AddRoundButton ( translateButton , RotatedMargin ( translateButton , - MathHelper . Tau * . 05 ) ) ;
translateButton . Click + = ( s , e ) = > viewControls3D . ActiveButton = ViewControls3DButtons . Translate ;
buttonGroupA . Add ( translateButton ) ;
2018-09-10 12:02:20 -07:00
2022-07-16 07:46:44 -07:00
zoomButton = new ThemedRadioIconButton ( StaticData . Instance . LoadIcon ( Path . Combine ( "ViewTransformControls" , "scale.png" ) , 16 , 16 ) . SetToColor ( theme . TextColor ) , theme )
2018-09-10 12:02:20 -07:00
{
2021-05-15 22:33:27 -07:00
SiblingRadioButtonList = buttonGroupA ,
2021-05-17 07:25:37 -07:00
ToolTipText = "Zoom View" . Localize ( ) ,
2018-09-10 12:02:20 -07:00
Margin = theme . ButtonSpacing
} ;
2022-04-28 16:39:03 -07:00
zoomButton . MouseEnterBounds + = ( s , e ) = > ApplicationController . Instance . UiHint = "Zoom: Mouse Wheel, Ctrl + Alt + Left Mouse Button, Ctrl + '+' & Ctrl + '-'" . Localize ( ) ;
2021-06-04 18:09:30 -07:00
zoomButton . MouseLeaveBounds + = ( s , e ) = > ApplicationController . Instance . UiHint = "" ;
AddRoundButton ( zoomButton , RotatedMargin ( zoomButton , - MathHelper . Tau * . 15 ) ) ;
zoomButton . Click + = ( s , e ) = > viewControls3D . ActiveButton = ViewControls3DButtons . Scale ;
buttonGroupA . Add ( zoomButton ) ;
2018-09-10 12:02:20 -07:00
2021-05-21 18:04:57 -07:00
var bottomButtonOffset = 0 ;
2021-05-22 08:36:02 -07:00
var hudBackgroundColor = theme . BedBackgroundColor . WithAlpha ( 120 ) ;
var hudStrokeColor = theme . TextColor . WithAlpha ( 120 ) ;
2021-05-21 18:04:57 -07:00
2021-05-15 22:33:27 -07:00
// add the background render for the view controls
2021-05-22 08:36:02 -07:00
// controlLayer.BeforeDraw += (s, e) => // enable to debug any rendering errors that might be due to double buffered hudBackground
hudBackground . BeforeDraw + = ( s , e ) = >
2019-02-04 20:05:32 -08:00
{
2021-05-15 22:33:27 -07:00
var tumbleCubeRadius = tumbleCubeControl . Width / 2 ;
var tumbleCubeCenter = new Vector2 ( controlLayer . Width - tumbleCubeControl . Margin . Right * scale - tumbleCubeRadius ,
controlLayer . Height - tumbleCubeControl . Margin . Top * scale - tumbleCubeRadius ) ;
2021-05-21 18:04:57 -07:00
void renderPath ( IVertexSource vertexSource , double width )
{
var background = new Stroke ( vertexSource , width * 2 ) ;
background . LineCap = LineCap . Round ;
2021-05-22 08:36:02 -07:00
e . Graphics2D . Render ( background , hudBackgroundColor ) ;
e . Graphics2D . Render ( new Stroke ( background , scale ) , hudStrokeColor ) ;
2021-05-21 18:04:57 -07:00
}
2021-05-15 22:33:27 -07:00
void renderRoundedGroup ( double spanRatio , double startRatio )
2019-02-04 20:05:32 -08:00
{
2021-05-15 22:33:27 -07:00
var angle = MathHelper . Tau * spanRatio ;
2021-05-16 09:44:14 -07:00
var width = 17 * scale ;
2021-05-15 22:33:27 -07:00
var start = MathHelper . Tau * startRatio - angle / 2 ;
var end = MathHelper . Tau * startRatio + angle / 2 ;
2021-05-16 09:44:14 -07:00
var arc = new Arc ( tumbleCubeCenter , tumbleCubeRadius + 12 * scale + width / 2 , start , end ) ;
2021-05-21 18:04:57 -07:00
renderPath ( arc , width ) ;
2019-02-04 20:05:32 -08:00
}
2021-05-15 22:33:27 -07:00
2021-05-17 07:25:37 -07:00
renderRoundedGroup ( . 3 , . 25 ) ;
renderRoundedGroup ( . 1 , . 5 + . 1 ) ;
2021-06-02 14:30:29 -07:00
2021-12-17 10:15:55 -08:00
// render the perspective and turntable group background
renderRoundedGroup ( . 1 , 1 - . 1 ) ; // when we have both ortho and turntable
2021-05-15 22:33:27 -07:00
2021-05-21 18:04:57 -07:00
void renderRoundedLine ( double lineWidth , double heightBelowCenter )
{
lineWidth * = scale ;
var width = 17 * scale ;
var height = tumbleCubeCenter . Y - heightBelowCenter * scale ;
var start = tumbleCubeCenter . X - lineWidth ;
var end = tumbleCubeCenter . X + lineWidth ;
var line = new VertexStorage ( ) ;
line . MoveTo ( start , height ) ;
line . LineTo ( end , height ) ;
renderPath ( line , width ) ;
}
tumbleCubeCenter . X + = bottomButtonOffset ;
2021-05-22 08:36:02 -07:00
renderRoundedLine ( 18 , 101 ) ;
2021-12-17 16:35:10 -08:00
2021-05-15 22:33:27 -07:00
// e.Graphics2D.Circle(controlLayer.Width - cubeCenterFromRightTop.X, controlLayer.Height - cubeCenterFromRightTop.Y, 150, Color.Cyan);
2021-12-17 16:35:10 -08:00
// ImageIO.SaveImageData("C:\\temp\\test.png", hudBackground.BackBuffer);
2019-02-04 20:05:32 -08:00
} ;
2021-05-15 22:33:27 -07:00
// add the home button
2022-07-16 07:46:44 -07:00
var homeButton = new ThemedIconButton ( StaticData . Instance . LoadIcon ( "fa-home_16.png" , 16 , 16 ) . SetToColor ( theme . TextColor ) , theme )
2021-05-15 22:33:27 -07:00
{
ToolTipText = "Reset View" . Localize ( ) ,
Margin = theme . ButtonSpacing
} ;
2022-04-28 16:39:03 -07:00
homeButton . MouseEnterBounds + = ( s1 , e1 ) = > ApplicationController . Instance . UiHint = "W Key" ;
homeButton . MouseLeaveBounds + = ( s1 , e1 ) = > ApplicationController . Instance . UiHint = "" ;
2021-05-15 22:33:27 -07:00
AddRoundButton ( homeButton , RotatedMargin ( homeButton , MathHelper . Tau * . 3 ) ) . Click + = ( s , e ) = > viewControls3D . NotifyResetView ( ) ;
2022-07-16 07:46:44 -07:00
var zoomToSelectionButton = new ThemedIconButton ( StaticData . Instance . LoadIcon ( "select.png" , 16 , 16 ) . SetToColor ( theme . TextColor ) , theme )
2021-05-15 22:33:27 -07:00
{
2022-03-02 00:52:04 +00:00
Name = "Zoom to selection button" ,
2021-05-15 22:33:27 -07:00
ToolTipText = "Zoom to Selection" . Localize ( ) ,
Margin = theme . ButtonSpacing
} ;
2022-04-28 16:39:03 -07:00
zoomToSelectionButton . MouseEnterBounds + = ( s1 , e1 ) = > ApplicationController . Instance . UiHint = "Z Key" ;
zoomToSelectionButton . MouseLeaveBounds + = ( s1 , e1 ) = > ApplicationController . Instance . UiHint = "" ;
2021-05-21 14:24:45 -07:00
void SetZoomEnabled ( object s , EventArgs e )
{
2021-05-22 22:24:12 -07:00
zoomToSelectionButton . Enabled = this . Scene . SelectedItem ! = null
& & ( printer = = null | | printer . ViewState . ViewMode = = PartViewMode . Model ) ;
2021-05-21 14:24:45 -07:00
}
this . Scene . SelectionChanged + = SetZoomEnabled ;
this . Closed + = ( s , e ) = > this . Scene . SelectionChanged - = SetZoomEnabled ;
2021-05-25 17:25:44 -07:00
AddRoundButton ( zoomToSelectionButton , RotatedMargin ( zoomToSelectionButton , MathHelper . Tau * . 4 ) ) . Click + = ( s , e ) = > ZoomToSelection ( ) ;
2021-05-15 22:33:27 -07:00
2021-05-26 07:44:33 -07:00
var turntableEnabled = UserSettings . Instance . get ( UserSettingsKey . TurntableMode ) ! = "False" ;
TrackballTumbleWidget . TurntableEnabled = turntableEnabled ;
2022-07-16 07:46:44 -07:00
var turnTableButton = new ThemedRadioIconButton ( StaticData . Instance . LoadIcon ( "spin.png" , 16 , 16 ) . SetToColor ( theme . TextColor ) , theme )
2021-05-15 22:33:27 -07:00
{
2021-05-17 07:25:37 -07:00
ToolTipText = "Turntable Mode" . Localize ( ) ,
Margin = theme . ButtonSpacing ,
2021-06-02 14:30:29 -07:00
Padding = 2 ,
2021-05-17 07:25:37 -07:00
ToggleButton = true ,
SiblingRadioButtonList = new List < GuiWidget > ( ) ,
2021-05-26 07:44:33 -07:00
Checked = turntableEnabled ,
2021-12-17 16:35:10 -08:00
//DoubleBuffer = true,
2021-05-17 07:25:37 -07:00
} ;
2021-06-02 14:30:29 -07:00
2021-12-17 10:15:55 -08:00
AddRoundButton ( turnTableButton , RotatedMargin ( turnTableButton , - MathHelper . Tau * . 4 ) ) ; // 2 button position
2021-05-26 07:44:33 -07:00
turnTableButton . CheckedStateChanged + = ( s , e ) = >
2021-05-17 07:25:37 -07:00
{
2021-05-26 07:44:33 -07:00
UserSettings . Instance . set ( UserSettingsKey . TurntableMode , turnTableButton . Checked . ToString ( ) ) ;
2021-05-26 11:38:44 -07:00
TrackballTumbleWidget . TurntableEnabled = turnTableButton . Checked ;
if ( turnTableButton . Checked )
{
// Make sure the view has up going the right direction
// WIP, this should fix the current rotation rather than reset the view
viewControls3D . NotifyResetView ( ) ;
}
2021-05-15 22:33:27 -07:00
} ;
2022-03-02 00:52:04 +00:00
var perspectiveEnabled = UserSettings . Instance . get ( UserSettingsKey . PerspectiveMode ) ! = false . ToString ( ) ;
TrackballTumbleWidget . ChangeProjectionMode ( perspectiveEnabled , false ) ;
2022-07-16 07:46:44 -07:00
var projectionButton = new ThemedRadioIconButton ( StaticData . Instance . LoadIcon ( "perspective.png" , 16 , 16 ) . SetToColor ( theme . TextColor ) , theme )
2021-05-17 07:25:37 -07:00
{
2022-03-02 00:52:04 +00:00
Name = "Projection mode button" ,
2021-12-17 10:15:55 -08:00
ToolTipText = "Perspective Mode" . Localize ( ) ,
Margin = theme . ButtonSpacing ,
ToggleButton = true ,
SiblingRadioButtonList = new List < GuiWidget > ( ) ,
2022-03-02 00:52:04 +00:00
Checked = TrackballTumbleWidget . PerspectiveMode ,
2021-12-17 10:15:55 -08:00
} ;
AddRoundButton ( projectionButton , RotatedMargin ( projectionButton , - MathHelper . Tau * . 3 ) ) ;
projectionButton . CheckedStateChanged + = ( s , e ) = >
{
UserSettings . Instance . set ( UserSettingsKey . PerspectiveMode , projectionButton . Checked . ToString ( ) ) ;
2022-03-02 00:52:04 +00:00
TrackballTumbleWidget . ChangeProjectionMode ( projectionButton . Checked , true ) ;
2021-12-17 10:15:55 -08:00
if ( true )
2021-06-01 07:30:39 -07:00
{
2021-12-17 10:15:55 -08:00
// Make sure the view has up going the right direction
// WIP, this should fix the current rotation rather than reset the view
2022-03-02 00:52:04 +00:00
//ResetView();
2021-12-17 10:15:55 -08:00
}
2022-03-02 00:52:04 +00:00
2021-12-17 10:15:55 -08:00
Invalidate ( ) ;
} ;
2021-05-28 07:55:02 -07:00
2021-05-16 09:44:14 -07:00
var startHeight = 180 ;
var ySpacing = 40 ;
2021-05-21 18:04:57 -07:00
cubeCenterFromRightTop . X - = bottomButtonOffset ;
2021-05-17 07:25:37 -07:00
2021-05-15 22:33:27 -07:00
// put in the bed and build volume buttons
2022-07-16 07:46:44 -07:00
var bedButton = new ThemedRadioIconButton ( StaticData . Instance . LoadIcon ( "bed.png" , 16 , 16 ) . SetToColor ( theme . TextColor ) , theme )
2021-05-17 07:25:37 -07:00
{
Name = "Bed Button" ,
ToolTipText = "Show Print Bed" . Localize ( ) ,
Checked = sceneContext . RendererOptions . RenderBed ,
ToggleButton = true ,
SiblingRadioButtonList = new List < GuiWidget > ( ) ,
} ;
AddRoundButton ( bedButton , new Vector2 ( ( cubeCenterFromRightTop . X + 18 * scale - bedButton . Width / 2 ) / scale , startHeight ) ) ;
2022-07-16 07:46:44 -07:00
var printAreaButton = new ThemedRadioIconButton ( StaticData . Instance . LoadIcon ( "print_area.png" , 16 , 16 ) . SetToColor ( theme . TextColor ) , theme )
2021-05-17 07:25:37 -07:00
{
Name = "Bed Button" ,
ToolTipText = BuildHeightValid ( ) ? "Show Print Area" . Localize ( ) : "Define printer build height to enable" ,
Checked = sceneContext . RendererOptions . RenderBuildVolume ,
ToggleButton = true ,
2021-05-22 08:36:02 -07:00
Enabled = BuildHeightValid ( ) & & printer ? . ViewState . ViewMode ! = PartViewMode . Layers2D & & bedButton . Checked ,
2021-05-17 07:25:37 -07:00
SiblingRadioButtonList = new List < GuiWidget > ( ) ,
} ;
2021-05-22 08:36:02 -07:00
bedButton . CheckedStateChanged + = ( s , e ) = >
{
sceneContext . RendererOptions . RenderBed = bedButton . Checked ;
printAreaButton . Enabled = BuildHeightValid ( ) & & printer ? . ViewState . ViewMode ! = PartViewMode . Layers2D & & bedButton . Checked ;
} ;
bool BuildHeightValid ( ) = > sceneContext . BuildHeight > 0 ;
2021-05-17 07:25:37 -07:00
AddRoundButton ( printAreaButton , new Vector2 ( ( cubeCenterFromRightTop . X - 18 * scale - bedButton . Width / 2 ) / scale , startHeight ) ) ;
printAreaButton . CheckedStateChanged + = ( s , e ) = >
{
sceneContext . RendererOptions . RenderBuildVolume = printAreaButton . Checked ;
} ;
this . BindBedOptions ( controlLayer , bedButton , printAreaButton , sceneContext . RendererOptions ) ;
if ( printer ! = null )
{
// Disable print area button in GCode2D view
void ViewModeChanged ( object s , ViewModeChangedEventArgs e )
{
// Button is conditionally created based on BuildHeight, only set enabled if created
printAreaButton . Enabled = BuildHeightValid ( ) & & printer . ViewState . ViewMode ! = PartViewMode . Layers2D ;
}
printer . ViewState . ViewModeChanged + = ViewModeChanged ;
controlLayer . Closed + = ( s , e ) = >
{
printer . ViewState . ViewModeChanged - = ViewModeChanged ;
} ;
}
2021-05-15 22:33:27 -07:00
2021-05-22 08:36:02 -07:00
// declare the grid snap button
GridOptionsPanel gridSnapButton = null ;
2021-05-16 09:44:14 -07:00
// put in the view list buttons
2021-05-22 08:36:02 -07:00
var modelViewStyleButton = new ViewStyleButton ( sceneContext , theme )
2018-09-10 16:15:45 -07:00
{
PopupMate = new MatePoint ( )
{
2021-06-04 07:45:19 -07:00
Mate = new MateOptions ( MateEdge . Right , MateEdge . Top )
2018-09-10 16:15:45 -07:00
}
} ;
2018-09-10 16:40:50 -07:00
modelViewStyleButton . AnchorMate . Mate . VerticalEdge = MateEdge . Bottom ;
2021-06-04 07:45:19 -07:00
modelViewStyleButton . AnchorMate . Mate . HorizontalEdge = MateEdge . Right ;
2021-05-16 09:44:14 -07:00
var marginCenter = cubeCenterFromRightTop . X / scale ;
AddRoundButton ( modelViewStyleButton , new Vector2 ( marginCenter , startHeight + 1 * ySpacing ) , true ) ;
2021-05-22 08:36:02 -07:00
modelViewStyleButton . BackgroundColor = hudBackgroundColor ;
modelViewStyleButton . BorderColor = hudStrokeColor ;
void ViewState_ViewModeChanged ( object sender , ViewModeChangedEventArgs e )
{
modelViewStyleButton . Visible = e . ViewMode = = PartViewMode . Model ;
gridSnapButton . Visible = modelViewStyleButton . Visible ;
}
2018-09-10 16:40:50 -07:00
2018-09-10 18:24:09 -07:00
if ( printer ? . ViewState ! = null )
{
2021-05-22 08:36:02 -07:00
printer . ViewState . ViewModeChanged + = ViewState_ViewModeChanged ;
2018-09-10 18:24:09 -07:00
}
2018-09-10 12:02:20 -07:00
2021-05-22 08:36:02 -07:00
this . Closed + = ( s , e ) = >
{
if ( printer ? . ViewState ! = null )
{
printer . ViewState . ViewModeChanged - = ViewState_ViewModeChanged ;
}
} ;
2021-05-16 09:44:14 -07:00
// Add the grid snap button
2021-05-22 08:36:02 -07:00
gridSnapButton = new GridOptionsPanel ( Object3DControlLayer , theme )
2018-09-17 17:07:42 -07:00
{
PopupMate = new MatePoint ( )
{
Mate = new MateOptions ( MateEdge . Right , MateEdge . Top )
}
} ;
gridSnapButton . AnchorMate . Mate . VerticalEdge = MateEdge . Bottom ;
gridSnapButton . AnchorMate . Mate . HorizontalEdge = MateEdge . Right ;
2021-05-16 09:44:14 -07:00
AddRoundButton ( gridSnapButton , new Vector2 ( marginCenter , startHeight + 2 * ySpacing ) , true ) ;
2021-05-22 08:36:02 -07:00
gridSnapButton . BackgroundColor = hudBackgroundColor ;
gridSnapButton . BorderColor = hudStrokeColor ;
2018-09-17 07:41:31 -07:00
2021-05-15 22:33:27 -07:00
#if DEBUG
var renderOptionsButton = new RenderOptionsButton ( theme , this . Object3DControlLayer )
{
ToolTipText = "Debug Render Options" . Localize ( ) ,
PopupMate = new MatePoint ( )
{
Mate = new MateOptions ( MateEdge . Left , MateEdge . Top )
} ,
AnchorMate = new MatePoint ( )
{
Mate = new MateOptions ( MateEdge . Left , MateEdge . Bottom )
}
} ;
2021-05-16 09:44:14 -07:00
AddRoundButton ( renderOptionsButton , new Vector2 ( marginCenter , startHeight + 3 * ySpacing ) , true ) ;
2021-05-15 22:33:27 -07:00
#endif
}
2017-05-29 16:56:56 -07:00
2021-05-25 17:25:44 -07:00
public void ZoomToSelection ( )
{
var selectedItem = this . Scene . SelectedItem ;
if ( selectedItem ! = null )
{
2022-03-02 00:52:04 +00:00
TrackballTumbleWidget . ZoomToAABB ( selectedItem . GetAxisAlignedBoundingBox ( ) ) ;
2021-05-25 17:25:44 -07:00
}
}
2021-05-17 07:25:37 -07:00
public void UpdateControlButtons ( ViewControls3DButtons activeTransformState )
2021-05-15 22:33:27 -07:00
{
2021-05-17 07:25:37 -07:00
switch ( activeTransformState )
2021-05-15 22:33:27 -07:00
{
2021-05-17 07:25:37 -07:00
case ViewControls3DButtons . Rotate :
if ( rotateButton ! = null )
{
rotateButton . Checked = true ;
}
break ;
2021-05-15 22:33:27 -07:00
2021-05-17 07:25:37 -07:00
case ViewControls3DButtons . Translate :
if ( translateButton ! = null )
{
translateButton . Checked = true ;
}
break ;
2021-05-15 22:33:27 -07:00
2021-05-17 07:25:37 -07:00
case ViewControls3DButtons . Scale :
2021-06-04 18:09:30 -07:00
if ( zoomButton ! = null )
2021-05-17 07:25:37 -07:00
{
2021-06-04 18:09:30 -07:00
zoomButton . Checked = true ;
2021-05-17 07:25:37 -07:00
}
break ;
2021-05-15 22:33:27 -07:00
2021-05-17 07:25:37 -07:00
case ViewControls3DButtons . PartSelect :
if ( partSelectButton ! = null )
{
partSelectButton . Checked = true ;
}
break ;
2018-10-26 09:42:04 -07:00
}
2021-05-15 22:33:27 -07:00
}
2020-12-27 07:30:06 -08:00
2021-05-15 22:33:27 -07:00
public void BindBedOptions ( GuiWidget container , ICheckbox bedButton , ICheckbox printAreaButton , View3DConfig renderOptions )
{
void SyncProperties ( object s , PropertyChangedEventArgs e )
2020-12-27 07:30:06 -08:00
{
2021-05-15 22:33:27 -07:00
switch ( e . PropertyName )
2020-12-27 07:30:06 -08:00
{
2021-05-15 22:33:27 -07:00
case nameof ( renderOptions . RenderBed ) :
bedButton . Checked = renderOptions . RenderBed ;
break ;
case nameof ( renderOptions . RenderBuildVolume ) when printAreaButton ! = null :
printAreaButton . Checked = renderOptions . RenderBuildVolume ;
break ;
}
2020-12-27 07:30:06 -08:00
}
2021-05-15 22:33:27 -07:00
renderOptions . PropertyChanged + = SyncProperties ;
container . Closed + = ( s , e ) = >
{
renderOptions . PropertyChanged - = SyncProperties ;
} ;
2018-10-23 09:38:03 -07:00
}
2022-03-02 00:52:04 +00:00
private void GetNearFar ( WorldView world , out double zNear , out double zFar )
2019-02-07 14:28:22 -08:00
{
2022-03-02 00:52:04 +00:00
// All but the near and far planes.
Plane [ ] worldspacePlanes = Frustum . FrustumFromProjectionMatrix ( world . ProjectionMatrix ) . Planes . Take ( 4 )
. Select ( p = > p . Transform ( world . InverseModelviewMatrix ) ) . ToArray ( ) ;
2019-02-07 14:28:22 -08:00
2022-03-02 00:52:04 +00:00
// TODO: (Possibly) Compute a dynamic near plane based on visible triangles rather than clipped AABBs.
// Currently, the near and far planes are fit to clipped AABBs in the scene.
// A rudimentary implementation to start off with. The significant limitation is that zNear is ~zero when inside an AABB.
// The resulting frustum can be visualized using the debug menu.
// The far plane is less important.
// Other ideas are an infinite far plane, depth clamping, multi-pass rendering, AABB feedback from the previous frame, dedicated scene manager for all 3D rendering.
Tuple < double , double > nearFar = null ;
foreach ( var aabb in Object3DControlLayer . MakeListOfObjectControlBoundingBoxes ( ) )
{
2022-04-10 16:35:13 -07:00
if ( ! double . IsNaN ( aabb . MinXYZ . X ) & & ! double . IsInfinity ( aabb . MinXYZ . X ) )
{
nearFar = ExpandNearAndFarToClippedBounds ( nearFar , world . IsOrthographic , worldspacePlanes , world . ModelviewMatrix , aabb ) ;
}
2022-03-02 00:52:04 +00:00
}
nearFar = ExpandNearAndFarToClippedBounds ( nearFar , world . IsOrthographic , worldspacePlanes , world . ModelviewMatrix , Object3DControlLayer . GetPrinterNozzleAABB ( ) ) ;
nearFar = ExpandNearAndFarToClippedBounds ( nearFar , world . IsOrthographic , worldspacePlanes , world . ModelviewMatrix , Scene . GetAxisAlignedBoundingBox ( ) ) ;
2019-02-07 14:28:22 -08:00
2022-03-02 00:52:04 +00:00
zNear = nearFar ! = null ? nearFar . Item1 : WorldView . DefaultNearZ ;
zFar = nearFar ! = null ? nearFar . Item2 : WorldView . DefaultFarZ ;
2019-02-07 14:28:22 -08:00
2022-03-02 00:52:04 +00:00
if ( world . IsOrthographic )
2019-02-07 14:28:22 -08:00
{
2022-03-02 00:52:04 +00:00
WorldView . SanitiseOrthographicNearFar ( ref zNear , ref zFar ) ;
2019-02-07 14:28:22 -08:00
2022-03-02 00:52:04 +00:00
// Add some padding in case of unaccounted geometry.
double padding = ( zFar - zNear ) * DynamicNearFarBoundsPaddingFactor ;
zNear - = padding ;
zFar + = padding ;
2019-02-07 14:28:22 -08:00
2022-03-02 00:52:04 +00:00
WorldView . SanitiseOrthographicNearFar ( ref zNear , ref zFar ) ;
2019-02-07 14:28:22 -08:00
}
2022-03-02 00:52:04 +00:00
else
{
#if ENABLE_PERSPECTIVE_PROJECTION_DYNAMIC_NEAR_FAR
WorldView . SanitisePerspectiveNearFar ( ref zNear , ref zFar ) ;
zNear / = 1 + DynamicNearFarBoundsPaddingFactor ;
zFar * = 1 + DynamicNearFarBoundsPaddingFactor ;
#else
zNear = WorldView . DefaultNearZ ;
zFar = WorldView . DefaultFarZ ;
#endif
WorldView . SanitisePerspectiveNearFar ( ref zNear , ref zFar ) ;
}
System . Diagnostics . Debug . Assert ( zNear < zFar & & zFar < double . PositiveInfinity ) ;
2019-02-07 14:28:22 -08:00
}
2022-03-02 00:52:04 +00:00
private static Tuple < double , double > ExpandNearAndFarToClippedBounds ( Tuple < double , double > nearFar , bool isOrthographic , Plane [ ] worldspacePlanes , Matrix4X4 worldToViewspace , AxisAlignedBoundingBox bounds )
2019-02-07 14:28:22 -08:00
{
2022-03-02 00:52:04 +00:00
Tuple < double , double > thisNearFar = CameraFittingUtil . ComputeNearFarOfClippedWorldspaceAABB ( isOrthographic , worldspacePlanes , worldToViewspace , bounds ) ;
if ( nearFar = = null )
2019-02-07 14:28:22 -08:00
{
2022-03-02 00:52:04 +00:00
return thisNearFar ;
}
else if ( thisNearFar = = null )
{
return nearFar ;
}
else
{
return Tuple . Create ( Math . Min ( nearFar . Item1 , thisNearFar . Item1 ) , Math . Max ( nearFar . Item2 , thisNearFar . Item2 ) ) ;
2019-02-07 14:28:22 -08:00
}
}
2020-12-26 15:15:49 -08:00
private readonly Dictionary < IObject3D , TreeNode > treeNodesByObject = new Dictionary < IObject3D , TreeNode > ( ) ;
2018-10-26 09:05:04 -07:00
2020-09-12 08:53:40 -07:00
private bool watingToScroll = false ;
2018-10-26 09:04:31 -07:00
private void RebuildTree ( )
{
2019-01-25 17:00:33 -08:00
rebuildTreePending = false ;
2018-10-26 16:56:23 -07:00
workspaceName . Text = sceneContext . Scene . Name ? ? "" ;
2020-09-12 08:53:40 -07:00
if ( ! watingToScroll )
{
// This is a mess. The goal is that the tree view will not scroll as we add and remove items.
// This is better than always reseting to the top, but only a little.
watingToScroll = true ;
beforeReubildScrollPosition = treeView . TopLeftOffset ;
UiThread . RunOnIdle ( ( ) = >
{
treeView . TopLeftOffset = beforeReubildScrollPosition ;
watingToScroll = false ;
} , . 5 ) ;
}
2018-10-26 09:04:31 -07:00
// Top level selection only - rebuild tree
2021-01-29 16:44:47 -08:00
treeNodeContainer . CloseChildren ( ) ;
2018-10-26 09:04:31 -07:00
2019-03-07 18:06:01 -08:00
treeNodesByObject . Clear ( ) ;
2018-10-26 09:04:31 -07:00
2022-02-26 19:40:30 -08:00
IEnumerable < IObject3D > children = sceneContext . Scene . Children ;
if ( children . Any ( c = > c is SelectionGroupObject3D ) )
{
var selectionChildern = children . Where ( c = > c is SelectionGroupObject3D ) . First ( ) . Children ;
var notSelectionChildern = children . Where ( c = > ! ( c is SelectionGroupObject3D ) ) ;
children = selectionChildern . Union ( notSelectionChildern ) ;
}
foreach ( var child in children . OrderBy ( i = > i . Name ) )
2018-10-26 09:04:31 -07:00
{
2019-05-04 19:39:11 -07:00
if ( child . GetType ( ) . GetCustomAttributes ( typeof ( HideFromTreeViewAttribute ) , true ) . Any ( ) )
2019-01-07 10:11:12 -08:00
{
continue ;
}
2019-03-07 18:06:01 -08:00
var rootNode = Object3DTreeBuilder . BuildTree ( child , treeNodesByObject , theme ) ;
2018-10-26 16:56:23 -07:00
treeNodeContainer . AddChild ( rootNode ) ;
rootNode . TreeView = treeView ;
2018-10-31 22:13:59 -07:00
}
2018-10-26 16:56:23 -07:00
2022-02-26 19:40:30 -08:00
void HighLightSelection ( )
{
var scene = sceneContext . Scene ;
var nodes = treeNodeContainer . DescendantsAndSelf ( ) . Where ( t = > t is TreeNode ) ;
foreach ( var uncastNode in nodes )
{
var node = ( TreeNode ) uncastNode ;
if ( node . Tag is IObject3D item )
{
if ( scene . SelectedItem = = item
| | ( scene . SelectedItem is SelectionGroupObject3D selectionGroup
& & selectionGroup . Children . Contains ( item ) ) )
{
node . HighlightRegion . BackgroundColor = theme . AccentMimimalOverlay ;
}
else
{
node . HighlightRegion . BackgroundColor = Color . Transparent ;
}
}
}
}
2018-10-31 22:13:59 -07:00
// Ensure selectedItem is selected
var selectedItem = sceneContext . Scene . SelectedItem ;
if ( selectedItem ! = null
2019-03-07 18:06:01 -08:00
& & treeNodesByObject . TryGetValue ( selectedItem , out TreeNode treeNode ) )
2018-10-31 22:13:59 -07:00
{
treeView . SelectedNode = treeNode ;
2022-02-24 19:11:29 -07:00
if ( expandSelection )
{
expandSelection = false ;
treeNode . Expanded = true ;
}
2018-10-26 09:04:31 -07:00
}
2018-11-01 16:49:43 -07:00
2020-09-12 08:53:40 -07:00
treeView . TopLeftOffset = beforeReubildScrollPosition ;
2022-02-26 19:40:30 -08:00
HighLightSelection ( ) ;
2018-11-01 16:49:43 -07:00
Invalidate ( ) ;
2018-10-26 09:04:31 -07:00
}
2018-05-30 16:42:17 -07:00
private void ModelViewSidePanel_Resized ( object sender , EventArgs e )
{
2020-06-07 23:09:01 -07:00
UserSettings . Instance . SelectedObjectPanelWidth = selectedObjectPanel . Width ;
2018-05-30 16:42:17 -07:00
}
2018-04-18 09:36:53 -07:00
private void UpdateRenderView ( object sender , EventArgs e )
{
2022-03-21 13:55:53 -07:00
UiThread . RunOnUiThread ( ( ) = > TrackballTumbleWidget . CenterOffsetX = - modelViewSidePanel . Width ) ;
2018-04-18 09:36:53 -07:00
}
2017-11-29 07:35:37 -08:00
private void SceneContext_SceneLoaded ( object sender , EventArgs e )
{
2018-10-30 08:50:48 -07:00
if ( AppContext . IsLoading )
{
return ;
}
2019-05-12 21:00:37 -07:00
if ( printerTabPage ? . PrinterActionsBar ? . sliceButton is GuiWidget sliceButton )
2017-11-29 13:50:25 -08:00
{
sliceButton . Enabled = sceneContext . EditableScene ;
}
2019-05-12 21:00:37 -07:00
if ( printerTabPage ? . PrinterActionsBar ? . modelViewButton is GuiWidget button )
2018-02-07 18:43:09 -08:00
{
button . Enabled = sceneContext . EditableScene ;
2018-05-07 17:15:08 -07:00
if ( sceneContext . ContentType = = "gcode"
2019-05-12 21:00:37 -07:00
& & printerTabPage ? . PrinterActionsBar ? . layers3DButton is GuiWidget gcodeButton )
2018-05-07 17:15:08 -07:00
{
gcodeButton . InvokeClick ( ) ;
}
2018-02-07 18:43:09 -08:00
}
2018-10-26 09:04:31 -07:00
this . RebuildTree ( ) ;
2017-11-29 07:35:37 -08:00
this . Invalidate ( ) ;
2017-03-15 16:17:06 -07:00
}
2018-10-23 19:54:09 -07:00
public void PushToPrinterAndPrint ( )
{
2018-10-25 11:36:31 -07:00
// If invoked from a printer tab, simply start the print
2018-10-25 08:42:36 -07:00
if ( this . Printer ! = null )
{
// Save any pending changes before starting print operation
2018-12-05 13:48:25 -08:00
ApplicationController . Instance . Tasks . Execute ( "Saving Changes" . Localize ( ) , printer , printer . Bed . SaveChanges ) . ContinueWith ( task = >
2018-10-25 08:42:36 -07:00
{
ApplicationController . Instance . PrintPart (
printer . Bed . EditContext ,
printer ,
null ,
2021-01-14 15:49:34 -08:00
CancellationToken . None ,
2021-06-24 10:36:32 -07:00
PrinterConnection . PrintingModes . Normal ) . ConfigureAwait ( false ) ;
2018-10-25 08:42:36 -07:00
} ) ;
}
2018-10-25 11:36:31 -07:00
else if ( ProfileManager . Instance . ActiveProfiles . Count ( ) < = 0 )
2018-10-23 19:54:09 -07:00
{
2018-10-25 11:36:31 -07:00
// If no printer profiles exist, show the printer setup wizard
2022-01-06 16:55:55 -08:00
var window = DialogWindow . Show ( new SetupStepMakeModelName ( false ) ) ;
2019-06-29 08:33:24 -07:00
window . Closed + = ( s2 , e2 ) = >
2018-10-23 19:54:09 -07:00
{
2019-06-29 08:33:24 -07:00
if ( ApplicationController . Instance . ActivePrinters . FirstOrDefault ( ) is PrinterConfig printer
& & printer . Settings . PrinterSelected )
2018-10-23 19:54:09 -07:00
{
2019-06-29 08:33:24 -07:00
CopyPlateToPrinter ( sceneContext , printer ) ;
}
} ;
2018-10-23 19:54:09 -07:00
}
2018-11-11 21:25:50 -08:00
else if ( ApplicationController . Instance . ActivePrinters . Count ( ) is int printerCount & & printerCount > 0 )
2018-10-23 19:54:09 -07:00
{
2018-11-11 21:25:50 -08:00
if ( printerCount = = 1
& & ApplicationController . Instance . ActivePrinters . FirstOrDefault ( ) is PrinterConfig firstPrinter )
{
// If one printer exists, stash plate with undo operation, then load this scene onto the printer bed
CopyPlateToPrinter ( sceneContext , firstPrinter ) ;
}
else
{
// If multiple active printers exist, show select printer dialog
2018-11-03 00:20:03 -07:00
DialogWindow . Show (
2018-11-12 08:30:03 -08:00
new OpenPrinterPage (
2018-11-03 00:20:03 -07:00
"Next" . Localize ( ) ,
2019-06-29 08:33:24 -07:00
( selectedPrinter ) = >
2018-11-03 00:20:03 -07:00
{
2019-06-29 08:33:24 -07:00
if ( selectedPrinter ? . Settings . PrinterSelected = = true )
2018-11-03 00:20:03 -07:00
{
2019-06-29 08:33:24 -07:00
CopyPlateToPrinter ( sceneContext , selectedPrinter ) ;
2018-11-03 00:20:03 -07:00
}
} ) ) ;
2019-06-29 08:33:24 -07:00
}
}
else if ( ProfileManager . Instance . ActiveProfiles . Any ( ) )
{
// If no active printer but profiles exist, show select printer
DialogWindow . Show (
new OpenPrinterPage (
"Next" . Localize ( ) ,
( loadedPrinter ) = >
{
if ( loadedPrinter is PrinterConfig activePrinter
& & activePrinter . Settings . PrinterSelected )
{
CopyPlateToPrinter ( sceneContext , activePrinter ) ;
}
} ) ) ;
2018-10-25 11:36:31 -07:00
}
2018-10-23 19:54:09 -07:00
}
2019-02-01 16:12:12 -08:00
private static void CopyPlateToPrinter ( ISceneContext sceneContext , PrinterConfig printer )
2018-10-23 19:54:09 -07:00
{
Task . Run ( async ( ) = >
{
2022-02-22 14:28:27 -08:00
var scene = sceneContext . Scene ;
2018-10-23 19:54:09 -07:00
// Clear bed to get new MCX on disk for this item
printer . Bed . ClearPlate ( ) ;
2022-02-22 14:28:27 -08:00
if ( sceneContext . EditContext . ContentStore = = null )
{
// The scene is on a bed plate that has never been saved.
// Create a temp version of the scene so that we can plate it
2022-02-22 16:59:40 -08:00
var filename = ApplicationController . Instance . SanitizeFileName ( $"{DateTime.Now.ToString(" yyyy - MM - dd HH_mm_ss ")}.mcx" ) ;
var mcxPath = Path . Combine ( ApplicationDataStorage . Instance . PrintHistoryPath , filename ) ;
2022-02-22 16:01:06 -08:00
File . WriteAllText ( mcxPath , await new Object3D ( ) . ToJson ( ) ) ;
2022-02-22 14:28:27 -08:00
var historyContainer = ApplicationController . Instance . Library . PlatingHistory ;
sceneContext = new BedConfig ( historyContainer ) ;
sceneContext . EditContext = new EditContext ( )
{
ContentStore = historyContainer ,
SourceItem = new FileSystemFileItem ( mcxPath )
} ;
printer . Bed . Scene . Load ( scene . Clone ( ) ) ;
}
else
{
await ApplicationController . Instance . Tasks . Execute ( "Saving" . Localize ( ) , printer , sceneContext . SaveChanges ) ;
// Load current scene into new printer scene
await printer . Bed . LoadIntoCurrent ( sceneContext . EditContext , null ) ;
}
2018-10-23 19:54:09 -07:00
bool allInBounds = true ;
foreach ( var item in printer . Bed . Scene . VisibleMeshes ( ) )
{
allInBounds & = printer . InsideBuildVolume ( item ) ;
}
if ( ! allInBounds )
{
var bounds = printer . Bed . Scene . GetAxisAlignedBoundingBox ( ) ;
var boundsCenter = bounds . Center ;
// don't move the z of our stuff
boundsCenter . Z = 0 ;
if ( bounds . XSize < = printer . Bed . ViewerVolume . X
& & bounds . YSize < = printer . Bed . ViewerVolume . Y )
{
// center the collection of stuff
var bedCenter = new Vector3 ( printer . Bed . BedCenter ) ;
foreach ( var item in printer . Bed . Scene . Children )
{
item . Matrix * = Matrix4X4 . CreateTranslation ( - boundsCenter + bedCenter ) ;
}
}
else
{
// arrange the stuff the best we can
await printer . Bed . Scene . AutoArrangeChildren ( new Vector3 ( printer . Bed . BedCenter ) ) ;
}
}
// Switch to printer
2022-01-27 16:49:46 -08:00
ApplicationController . Instance . MainView . TabControl . SelectedTabKey = printer . PrinterName ;
2018-10-23 19:54:09 -07:00
// Save any pending changes before starting print operation
2018-12-05 13:48:25 -08:00
await ApplicationController . Instance . Tasks . Execute ( "Saving Changes" . Localize ( ) , printer , printer . Bed . SaveChanges ) ;
2018-10-23 19:54:09 -07:00
2018-10-25 11:36:31 -07:00
// Slice and print
2018-10-23 19:54:09 -07:00
await ApplicationController . Instance . PrintPart (
printer . Bed . EditContext ,
printer ,
null ,
2021-01-14 15:49:34 -08:00
CancellationToken . None ,
2021-06-24 10:36:32 -07:00
PrinterConnection . PrintingModes . Normal ) ;
2018-10-23 19:54:09 -07:00
} ) ;
}
2017-05-26 13:46:12 -07:00
private void ViewControls3D_TransformStateChanged ( object sender , TransformStateChangedEventArgs e )
{
switch ( e . TransformMode )
{
case ViewControls3DButtons . Rotate :
2018-04-23 14:33:27 -07:00
TrackballTumbleWidget . TransformState = TrackBallTransformType . Rotation ;
2017-05-26 13:46:12 -07:00
break ;
case ViewControls3DButtons . Translate :
2018-04-23 14:33:27 -07:00
TrackballTumbleWidget . TransformState = TrackBallTransformType . Translation ;
2017-05-26 13:46:12 -07:00
break ;
case ViewControls3DButtons . Scale :
2018-04-23 14:33:27 -07:00
TrackballTumbleWidget . TransformState = TrackBallTransformType . Scale ;
2017-05-26 13:46:12 -07:00
break ;
case ViewControls3DButtons . PartSelect :
2018-04-23 14:33:27 -07:00
TrackballTumbleWidget . TransformState = TrackBallTransformType . None ;
2017-05-26 13:46:12 -07:00
break ;
}
}
2017-03-15 16:17:06 -07:00
public void SelectAll ( )
{
2018-04-23 14:33:27 -07:00
Scene . ClearSelection ( ) ;
2018-06-01 16:16:17 -07:00
// Select All - set selection to all scene children
Scene . SetSelection ( Scene . Children . ToList ( ) ) ;
2017-03-15 16:17:06 -07:00
}
2017-07-01 08:21:29 -07:00
2017-03-15 16:17:06 -07:00
public void AddUndoOperation ( IUndoRedoCommand operation )
2016-02-14 17:53:44 -08:00
{
2018-04-23 14:33:27 -07:00
Scene . UndoBuffer . Add ( operation ) ;
2016-02-14 17:53:44 -08:00
}
2015-05-30 12:48:16 -07:00
public bool DisplayAllValueData { get ; set ; }
2014-12-20 11:12:02 -08:00
2018-08-23 16:44:11 -07:00
public override void OnClosed ( EventArgs e )
2015-05-30 12:48:16 -07:00
{
2017-05-26 13:46:12 -07:00
viewControls3D . TransformStateChanged - = ViewControls3D_TransformStateChanged ;
2018-10-26 09:04:31 -07:00
// Release events
this . Scene . SelectionChanged - = Scene_SelectionChanged ;
this . Scene . Invalidated - = Scene_Invalidated ;
sceneContext . SceneLoaded - = SceneContext_SceneLoaded ;
modelViewSidePanel . Resized - = ModelViewSidePanel_Resized ;
2020-09-11 19:59:14 -07:00
if ( this . Object3DControlLayer ! = null )
2017-07-05 15:43:58 -07:00
{
2020-09-11 19:59:14 -07:00
this . Object3DControlLayer . AfterDraw - = AfterDraw3DContent ;
2017-07-05 15:43:58 -07:00
}
2017-03-15 16:17:06 -07:00
base . OnClosed ( e ) ;
2015-05-30 12:48:16 -07:00
}
2017-03-15 16:17:06 -07:00
private GuiWidget topMostParent ;
2020-12-26 15:15:49 -08:00
private readonly PlaneShape bedPlane = new PlaneShape ( Vector3 . UnitZ , 0 , null ) ;
2017-03-15 16:17:06 -07:00
2017-09-19 19:59:55 -07:00
public bool DragOperationActive { get ; private set ; }
2018-06-21 21:02:37 -07:00
public InsertionGroupObject3D DragDropObject { get ; private set ; }
2018-05-10 17:21:59 -07:00
2018-02-12 13:55:31 -08:00
public ILibraryAssetStream SceneReplacement { get ; private set ; }
2017-09-19 19:59:55 -07:00
2017-03-15 16:17:06 -07:00
/// <summary>
/// Provides a View3DWidget specific drag implementation
/// </summary>
/// <param name="screenSpaceMousePosition">The screen space mouse position.</param>
2018-10-07 15:43:31 -07:00
public void ExternalDragOver ( Vector2 screenSpaceMousePosition , GuiWidget sourceWidget )
2015-04-08 15:20:10 -07:00
{
2017-09-19 19:59:55 -07:00
if ( this . HasBeenClosed )
2017-03-15 16:17:06 -07:00
{
2017-09-19 19:59:55 -07:00
return ;
2017-03-15 16:17:06 -07:00
}
2017-09-19 19:59:55 -07:00
// If the mouse is within the MeshViewer process the Drag move
2020-09-11 19:59:14 -07:00
var meshViewerScreenBounds = this . Object3DControlLayer . TransformToScreenSpace ( this . Object3DControlLayer . LocalBounds ) ;
2018-10-14 17:50:54 -07:00
if ( meshViewerScreenBounds . Contains ( screenSpaceMousePosition ) )
2015-05-30 12:48:16 -07:00
{
2017-09-19 19:59:55 -07:00
// If already started, process drag move
if ( this . DragOperationActive )
2015-05-30 12:48:16 -07:00
{
2017-09-19 19:59:55 -07:00
this . DragOver ( screenSpaceMousePosition ) ;
2017-03-15 16:17:06 -07:00
}
2017-09-19 19:59:55 -07:00
else
2017-03-15 16:17:06 -07:00
{
2018-05-10 17:21:59 -07:00
if ( this . Printer ! = null
& & this . Printer . ViewState . ViewMode ! = PartViewMode . Model )
{
this . Printer . ViewState . ViewMode = PartViewMode . Model ;
}
2017-09-19 19:59:55 -07:00
2018-10-07 15:43:31 -07:00
IEnumerable < ILibraryItem > selectedItems ;
2018-10-24 21:20:56 -07:00
if ( sourceWidget is LibraryListView listView )
2018-10-07 15:43:31 -07:00
{
2017-09-19 19:59:55 -07:00
// Project from ListViewItem to ILibraryItem
2018-10-07 15:43:31 -07:00
selectedItems = listView . SelectedItems . Select ( l = > l . Model ) ;
}
2020-08-19 22:51:03 -07:00
else // Project from ListViewItem to ILibraryItem
2018-10-07 15:43:31 -07:00
{
selectedItems = Enumerable . Empty < ILibraryItem > ( ) ;
}
// Otherwise begin an externally started DragDropOperation hard-coded to use LibraryView->SelectedItems
this . StartDragDrop ( selectedItems , screenSpaceMousePosition ) ;
2015-05-30 12:48:16 -07:00
}
}
2017-09-19 19:59:55 -07:00
}
2017-03-15 16:17:06 -07:00
2017-09-19 19:59:55 -07:00
private void DragOver ( Vector2 screenSpaceMousePosition )
{
2018-05-18 16:58:18 -07:00
IObject3D selectedItem = Scene . SelectedItem ;
2017-09-19 19:59:55 -07:00
// Move the object being dragged
if ( this . DragOperationActive
2018-09-25 18:00:19 -07:00
& & this . DragDropObject ! = null
& & selectedItem ! = null )
2017-09-19 19:59:55 -07:00
{
// Move the DropDropObject the target item
2020-09-11 19:59:14 -07:00
DragSelectedObject ( selectedItem , localMousePosition : this . Object3DControlLayer . TransformFromScreenSpace ( screenSpaceMousePosition ) ) ;
2017-09-19 19:59:55 -07:00
}
2015-04-08 15:20:10 -07:00
}
2017-12-19 14:49:48 -08:00
private void StartDragDrop ( IEnumerable < ILibraryItem > items , Vector2 screenSpaceMousePosition , bool trackSourceFiles = false )
Move to new library model and view
- Add new listview control for library content
- Migrate library providers to containers
- Cloud, Sqlite, Directories, Queue, History
- Migrate SideBar components to containers
- Primatives, Text, Braille, ImageConverter
- Create new library container types
- Zip files, Calibration parts, Printer SDCards
- Reduce leftnav to Library, Settings, Controls, Options
- Add DragDrop support for image content
2017-05-19 22:33:55 -07:00
{
2017-09-19 19:59:55 -07:00
this . DragOperationActive = true ;
Move to new library model and view
- Add new listview control for library content
- Migrate library providers to containers
- Cloud, Sqlite, Directories, Queue, History
- Migrate SideBar components to containers
- Primatives, Text, Braille, ImageConverter
- Create new library container types
- Zip files, Calibration parts, Printer SDCards
- Reduce leftnav to Library, Settings, Controls, Options
- Add DragDrop support for image content
2017-05-19 22:33:55 -07:00
2018-10-04 22:06:55 -07:00
// ContentStore is null for plated gcode, call ClearPlate to exit mode and return to bed mcx
2022-02-18 17:56:01 -08:00
// Unsaved New Design also have a null ContentStore but they don't have gcode, so test both.
if ( sceneContext . Printer ? . Bed ? . LoadedGCode ! = null
& & sceneContext . EditContext . ContentStore = = null )
2018-10-04 22:06:55 -07:00
{
2022-02-18 17:56:01 -08:00
this . ClearPlate ( ) ;
2018-10-04 22:06:55 -07:00
}
2017-11-29 14:16:21 -08:00
var firstItem = items . FirstOrDefault ( ) ;
2018-02-12 13:55:31 -08:00
if ( ( firstItem is ILibraryAssetStream contentStream
2017-11-29 13:50:25 -08:00
& & contentStream . ContentType = = "gcode" )
2017-11-29 14:16:21 -08:00
| | firstItem is SceneReplacementFileItem )
2017-11-29 13:50:25 -08:00
{
DragDropObject = null ;
2018-02-12 13:55:31 -08:00
this . SceneReplacement = firstItem as ILibraryAssetStream ;
2017-11-29 13:50:25 -08:00
// TODO: Figure out a mechanism to disable View3DWidget with dark overlay, displaying something like "Switch to xxx.gcode", make disappear on mouseLeaveBounds and dragfinish
2020-09-11 19:59:14 -07:00
this . Object3DControlLayer . BackgroundColor = new Color ( Color . Black , 200 ) ;
2017-11-29 13:50:25 -08:00
return ;
}
2017-09-19 19:59:55 -07:00
// Set the hitplane to the bed plane
CurrentSelectInfo . HitPlane = bedPlane ;
2017-06-05 09:27:30 -07:00
2019-01-10 15:21:33 -08:00
// Add item to scene
var insertionGroup = sceneContext . AddToPlate ( items , Vector2 . Zero , moveToOpenPosition : false ) ;
2017-07-11 17:31:50 -07:00
2017-09-19 19:59:55 -07:00
// Find intersection position of the mouse with the bed plane
var intersectInfo = GetIntersectPosition ( screenSpaceMousePosition ) ;
if ( intersectInfo ! = null )
{
2018-01-23 16:56:15 -08:00
CalculateDragStartPosition ( insertionGroup , intersectInfo ) ;
}
else
{
CurrentSelectInfo . LastMoveDelta = Vector3 . PositiveInfinity ;
2017-09-19 19:59:55 -07:00
}
2017-09-16 01:11:44 -07:00
2017-09-19 19:59:55 -07:00
this . deferEditorTillMouseUp = true ;
2015-04-08 15:20:10 -07:00
2018-04-23 14:33:27 -07:00
Scene . SelectedItem = insertionGroup ;
2017-03-15 16:17:06 -07:00
2017-11-29 07:05:48 -08:00
this . DragDropObject = insertionGroup ;
2017-09-19 19:59:55 -07:00
}
Move to new library model and view
- Add new listview control for library content
- Migrate library providers to containers
- Cloud, Sqlite, Directories, Queue, History
- Migrate SideBar components to containers
- Primatives, Text, Braille, ImageConverter
- Create new library container types
- Zip files, Calibration parts, Printer SDCards
- Reduce leftnav to Library, Settings, Controls, Options
- Add DragDrop support for image content
2017-05-19 22:33:55 -07:00
2018-01-23 16:56:15 -08:00
private void CalculateDragStartPosition ( IObject3D insertionGroup , IntersectInfo intersectInfo )
{
// Set the initial transform on the inject part to the current transform mouse position
2019-05-21 07:27:58 -07:00
var sourceItemBounds = insertionGroup . GetAxisAlignedBoundingBox ( ) ;
2018-01-23 16:56:15 -08:00
var center = sourceItemBounds . Center ;
2019-01-11 16:49:34 -08:00
insertionGroup . Matrix * = Matrix4X4 . CreateTranslation ( - center . X , - center . Y , - sourceItemBounds . MinXYZ . Z ) ;
2018-01-23 16:56:15 -08:00
insertionGroup . Matrix * = Matrix4X4 . CreateTranslation ( new Vector3 ( intersectInfo . HitPosition ) ) ;
CurrentSelectInfo . PlaneDownHitPos = intersectInfo . HitPosition ;
CurrentSelectInfo . LastMoveDelta = Vector3 . Zero ;
}
2017-09-19 19:59:55 -07:00
internal void FinishDrop ( bool mouseUpInBounds )
{
if ( this . DragOperationActive )
2017-03-15 16:17:06 -07:00
{
2020-09-11 19:59:14 -07:00
this . Object3DControlLayer . BackgroundColor = Color . Transparent ;
2017-09-19 19:59:55 -07:00
this . DragOperationActive = false ;
Move to new library model and view
- Add new listview control for library content
- Migrate library providers to containers
- Cloud, Sqlite, Directories, Queue, History
- Migrate SideBar components to containers
- Primatives, Text, Braille, ImageConverter
- Create new library container types
- Zip files, Calibration parts, Printer SDCards
- Reduce leftnav to Library, Settings, Controls, Options
- Add DragDrop support for image content
2017-05-19 22:33:55 -07:00
2017-09-19 19:59:55 -07:00
if ( mouseUpInBounds )
{
2017-11-29 13:50:25 -08:00
if ( this . DragDropObject = = null
& & this . SceneReplacement ! = null )
2017-10-23 13:36:08 -07:00
{
2017-11-29 13:50:25 -08:00
// Drop handler for special case of GCode or similar (change loaded scene to new context)
sceneContext . LoadContent (
new EditContext ( )
{
SourceItem = this . SceneReplacement ,
2018-10-04 19:42:58 -07:00
// No content store for GCode
ContentStore = null
2022-02-20 07:59:28 -08:00
} , null ) . ConfigureAwait ( false ) ;
2017-11-29 13:50:25 -08:00
this . SceneReplacement = null ;
}
2017-04-05 19:03:04 -07:00
}
2017-09-19 19:59:55 -07:00
else
2017-04-05 19:03:04 -07:00
{
2018-04-23 14:33:27 -07:00
Scene . Children . Modify ( list = > list . Remove ( this . DragDropObject ) ) ;
Scene . ClearSelection ( ) ;
2017-09-19 19:59:55 -07:00
}
Move to new library model and view
- Add new listview control for library content
- Migrate library providers to containers
- Cloud, Sqlite, Directories, Queue, History
- Migrate SideBar components to containers
- Primatives, Text, Braille, ImageConverter
- Create new library container types
- Zip files, Calibration parts, Printer SDCards
- Reduce leftnav to Library, Settings, Controls, Options
- Add DragDrop support for image content
2017-05-19 22:33:55 -07:00
2017-09-19 19:59:55 -07:00
this . DragDropObject = null ;
Move to new library model and view
- Add new listview control for library content
- Migrate library providers to containers
- Cloud, Sqlite, Directories, Queue, History
- Migrate SideBar components to containers
- Primatives, Text, Braille, ImageConverter
- Create new library container types
- Zip files, Calibration parts, Printer SDCards
- Reduce leftnav to Library, Settings, Controls, Options
- Add DragDrop support for image content
2017-05-19 22:33:55 -07:00
2017-09-19 19:59:55 -07:00
this . deferEditorTillMouseUp = false ;
Scene_SelectionChanged ( null , null ) ;
Move to new library model and view
- Add new listview control for library content
- Migrate library providers to containers
- Cloud, Sqlite, Directories, Queue, History
- Migrate SideBar components to containers
- Primatives, Text, Braille, ImageConverter
- Create new library container types
- Zip files, Calibration parts, Printer SDCards
- Reduce leftnav to Library, Settings, Controls, Options
- Add DragDrop support for image content
2017-05-19 22:33:55 -07:00
2019-01-28 14:19:40 -08:00
Scene . Invalidate ( new InvalidateArgs ( null , InvalidateType . Children ) ) ;
Move to new library model and view
- Add new listview control for library content
- Migrate library providers to containers
- Cloud, Sqlite, Directories, Queue, History
- Migrate SideBar components to containers
- Primatives, Text, Braille, ImageConverter
- Create new library container types
- Zip files, Calibration parts, Printer SDCards
- Reduce leftnav to Library, Settings, Controls, Options
- Add DragDrop support for image content
2017-05-19 22:33:55 -07:00
2017-09-19 19:59:55 -07:00
// Set focus to View3DWidget after drag-drop
2019-06-29 08:33:24 -07:00
this . Focus ( ) ;
2017-09-19 19:59:55 -07:00
}
}
Move to new library model and view
- Add new listview control for library content
- Migrate library providers to containers
- Cloud, Sqlite, Directories, Queue, History
- Migrate SideBar components to containers
- Primatives, Text, Braille, ImageConverter
- Create new library container types
- Zip files, Calibration parts, Printer SDCards
- Reduce leftnav to Library, Settings, Controls, Options
- Add DragDrop support for image content
2017-05-19 22:33:55 -07:00
2017-09-19 19:59:55 -07:00
public override void OnLoad ( EventArgs args )
{
topMostParent = this . TopmostParent ( ) ;
Move to new library model and view
- Add new listview control for library content
- Migrate library providers to containers
- Cloud, Sqlite, Directories, Queue, History
- Migrate SideBar components to containers
- Primatives, Text, Braille, ImageConverter
- Create new library container types
- Zip files, Calibration parts, Printer SDCards
- Reduce leftnav to Library, Settings, Controls, Options
- Add DragDrop support for image content
2017-05-19 22:33:55 -07:00
2017-09-19 19:59:55 -07:00
// Set reference on show
var dragDropData = ApplicationController . Instance . DragDropData ;
dragDropData . View3DWidget = this ;
2017-10-05 22:32:23 -07:00
dragDropData . SceneContext = sceneContext ;
2017-06-05 09:27:30 -07:00
2017-09-19 19:59:55 -07:00
base . OnLoad ( args ) ;
2017-03-15 16:17:06 -07:00
}
2022-08-30 17:24:56 -07:00
public override void OnDraw ( Graphics2D graphics2D )
2016-07-24 17:26:24 -07:00
{
2022-08-30 17:24:56 -07:00
using ( new QuickTimerReport ( "View3DWidget.OnDraw" ) )
2015-05-30 12:48:16 -07:00
{
2022-08-30 17:24:56 -07:00
var selectedItem = Scene . SelectedItem ;
if ( selectedItem ! = null )
2016-02-16 08:30:13 -08:00
{
2022-08-30 17:24:56 -07:00
foreach ( var volume in this . Object3DControlLayer . Object3DControls )
{
volume . SetPosition ( selectedItem , CurrentSelectInfo ) ;
}
2016-02-16 08:30:13 -08:00
}
2014-03-20 18:20:52 -07:00
2022-08-30 17:24:56 -07:00
TrackballTumbleWidget . RecalculateProjection ( ) ;
2022-03-02 00:52:04 +00:00
2022-08-30 17:24:56 -07:00
base . OnDraw ( graphics2D ) ;
}
2018-05-18 16:58:18 -07:00
}
2017-05-31 17:47:12 -07:00
private void AfterDraw3DContent ( object sender , DrawEventArgs e )
2017-05-29 16:56:56 -07:00
{
2017-06-01 09:42:40 -07:00
if ( DragSelectionInProgress )
{
2017-06-05 15:44:52 -07:00
var selectionRectangle = new RectangleDouble ( DragSelectionStartPosition , DragSelectionEndPosition ) ;
2018-04-11 10:22:06 -07:00
e . Graphics2D . Rectangle ( selectionRectangle , Color . Red ) ;
2017-06-05 15:44:52 -07:00
}
}
2020-08-19 22:51:03 -07:00
private bool foundTriangleInSelectionBounds ;
2017-06-06 10:57:13 -07:00
private void DoRectangleSelection ( DrawEventArgs e )
2017-06-05 15:44:52 -07:00
{
2017-06-05 17:23:55 -07:00
var allResults = new List < BvhIterator > ( ) ;
2018-04-23 14:33:27 -07:00
var matchingSceneChildren = Scene . Children . Where ( item = >
2017-06-05 15:44:52 -07:00
{
2017-06-06 10:06:38 -07:00
foundTriangleInSelectionBounds = false ;
2017-06-06 10:57:13 -07:00
// Filter the IPrimitive trace data finding matches as defined in InSelectionBounds
2020-04-01 20:48:59 -07:00
var filteredResults = item . GetBVHData ( ) . Filter ( InSelectionBounds ) ;
2017-06-05 17:23:55 -07:00
2017-06-06 10:57:13 -07:00
// Accumulate all matching BvhIterator results for debug rendering
2017-06-05 17:23:55 -07:00
allResults . AddRange ( filteredResults ) ;
2017-06-06 10:06:38 -07:00
return foundTriangleInSelectionBounds ;
2017-06-05 15:44:52 -07:00
} ) ;
2017-06-06 10:57:13 -07:00
// Apply selection
if ( matchingSceneChildren . Any ( ) )
2017-06-05 17:23:55 -07:00
{
2017-06-21 15:56:25 -07:00
// If we are actually doing the selection rather than debugging the data
if ( e = = null )
{
2018-04-23 14:33:27 -07:00
Scene . ClearSelection ( ) ;
2018-06-01 09:04:10 -07:00
Scene . SetSelection ( matchingSceneChildren . ToList ( ) ) ;
2022-02-24 19:11:29 -07:00
if ( Scene . SelectedItem is SelectionGroupObject3D selectionGroup )
{
selectionGroup . Expanded = true ;
}
2017-06-21 15:56:25 -07:00
}
else
2017-06-05 17:23:55 -07:00
{
2020-09-12 13:09:17 -07:00
Object3DControlsLayer . RenderBounds ( e , sceneContext . World , allResults ) ;
2017-06-05 17:23:55 -07:00
}
}
2017-06-06 10:06:38 -07:00
}
2017-06-05 15:44:52 -07:00
2021-12-03 07:44:38 -08:00
private bool InSelectionBounds ( BvhIterator iterator )
2017-06-06 10:06:38 -07:00
{
2021-12-03 07:44:38 -08:00
var world = sceneContext ? . World ;
2021-12-15 18:09:21 -08:00
if ( world = = null
| | iterator = = null
| | iterator . Bvh = = null )
2021-12-03 07:44:38 -08:00
{
return false ;
}
2017-06-06 10:06:38 -07:00
var selectionRectangle = new RectangleDouble ( DragSelectionStartPosition , DragSelectionEndPosition ) ;
2017-06-05 15:44:52 -07:00
2020-08-19 22:51:03 -07:00
var traceBottoms = new Vector2 [ 4 ] ;
var traceTops = new Vector2 [ 4 ] ;
2017-06-05 15:44:52 -07:00
2017-06-06 10:06:38 -07:00
if ( foundTriangleInSelectionBounds )
{
return false ;
}
2020-08-19 22:51:03 -07:00
2021-12-03 07:44:38 -08:00
if ( iterator . Bvh is ITriangle tri )
2017-06-06 10:06:38 -07:00
{
// check if any vertex in screen rect
// calculate all the top and bottom screen positions
for ( int i = 0 ; i < 3 ; i + + )
{
2021-12-03 07:44:38 -08:00
Vector3 bottomStartPosition = Vector3Ex . Transform ( tri . GetVertex ( i ) , iterator . TransformToWorld ) ;
traceBottoms [ i ] = world . GetScreenPosition ( bottomStartPosition ) ;
2017-06-05 15:44:52 -07:00
}
2017-06-06 10:06:38 -07:00
for ( int i = 0 ; i < 3 ; i + + )
2017-06-05 15:44:52 -07:00
{
2017-06-06 10:06:38 -07:00
if ( selectionRectangle . ClipLine ( traceBottoms [ i ] , traceBottoms [ ( i + 1 ) % 3 ] ) )
2017-06-05 15:44:52 -07:00
{
2017-06-06 10:06:38 -07:00
foundTriangleInSelectionBounds = true ;
return true ;
2017-06-05 15:44:52 -07:00
}
}
2017-06-06 10:06:38 -07:00
}
else
{
// calculate all the top and bottom screen positions
for ( int i = 0 ; i < 4 ; i + + )
2017-06-05 15:44:52 -07:00
{
2021-12-03 07:44:38 -08:00
Vector3 bottomStartPosition = Vector3Ex . Transform ( iterator . Bvh . GetAxisAlignedBoundingBox ( ) . GetBottomCorner ( i ) , iterator . TransformToWorld ) ;
traceBottoms [ i ] = world . GetScreenPosition ( bottomStartPosition ) ;
2017-06-06 10:06:38 -07:00
2021-12-03 07:44:38 -08:00
Vector3 topStartPosition = Vector3Ex . Transform ( iterator . Bvh . GetAxisAlignedBoundingBox ( ) . GetTopCorner ( i ) , iterator . TransformToWorld ) ;
traceTops [ i ] = world . GetScreenPosition ( topStartPosition ) ;
2017-06-05 15:44:52 -07:00
}
2017-06-01 09:42:40 -07:00
2017-06-06 10:06:38 -07:00
RectangleDouble . OutCode allPoints = RectangleDouble . OutCode . Inside ;
// check if we are inside all the points
for ( int i = 0 ; i < 4 ; i + + )
{
allPoints | = selectionRectangle . ComputeOutCode ( traceBottoms [ i ] ) ;
allPoints | = selectionRectangle . ComputeOutCode ( traceTops [ i ] ) ;
}
if ( allPoints = = RectangleDouble . OutCode . Surrounded )
2017-05-30 17:51:52 -07:00
{
return true ;
}
2017-06-06 10:06:38 -07:00
for ( int i = 0 ; i < 4 ; i + + )
{
if ( selectionRectangle . ClipLine ( traceBottoms [ i ] , traceBottoms [ ( i + 1 ) % 4 ] )
| | selectionRectangle . ClipLine ( traceTops [ i ] , traceTops [ ( i + 1 ) % 4 ] )
| | selectionRectangle . ClipLine ( traceTops [ i ] , traceBottoms [ i ] ) )
{
return true ;
}
}
}
2017-05-31 11:56:25 -07:00
2017-06-06 10:06:38 -07:00
return false ;
}
2017-05-31 11:56:25 -07:00
2015-12-22 15:26:51 -08:00
private ViewControls3DButtons ? activeButtonBeforeMouseOverride = null ;
2015-05-29 15:13:56 -07:00
2020-08-19 22:51:03 -07:00
private Vector2 lastMouseMove ;
private Vector2 mouseDownPositon = Vector2 . Zero ;
private Matrix4X4 worldMatrixOnMouseDown ;
2015-05-30 12:48:16 -07:00
public override void OnMouseDown ( MouseEventArgs mouseEvent )
2015-04-08 15:20:10 -07:00
{
2022-07-05 15:37:30 -07:00
using ( new QuickTimer ( "View3DWidget_OnMouseDown" , . 3 ) )
{
var selectedItem = Scene . SelectedItem ;
mouseDownPositon = mouseEvent . Position ;
worldMatrixOnMouseDown = sceneContext . World . GetTransform4X4 ( ) ;
// Show transform override
if ( activeButtonBeforeMouseOverride = = null
& & ( mouseEvent . Button = = MouseButtons . Right | | Keyboard . IsKeyDown ( Keys . Control ) ) )
2018-01-18 09:38:57 -08:00
{
2022-07-05 15:37:30 -07:00
if ( Keyboard . IsKeyDown ( Keys . Shift ) )
{
activeButtonBeforeMouseOverride = viewControls3D . ActiveButton ;
viewControls3D . ActiveButton = ViewControls3DButtons . Translate ;
}
else if ( Keyboard . IsKeyDown ( Keys . Alt ) )
{
activeButtonBeforeMouseOverride = viewControls3D . ActiveButton ;
viewControls3D . ActiveButton = ViewControls3DButtons . Scale ;
}
else
{
activeButtonBeforeMouseOverride = viewControls3D . ActiveButton ;
viewControls3D . ActiveButton = ViewControls3DButtons . Rotate ;
}
2018-01-18 09:38:57 -08:00
}
2022-07-05 15:37:30 -07:00
else if ( activeButtonBeforeMouseOverride = = null & & mouseEvent . Button = = MouseButtons . Middle )
2018-01-20 22:17:39 -08:00
{
activeButtonBeforeMouseOverride = viewControls3D . ActiveButton ;
2022-07-05 15:37:30 -07:00
viewControls3D . ActiveButton = ViewControls3DButtons . Translate ;
2018-01-20 22:17:39 -08:00
}
2022-07-05 15:37:30 -07:00
if ( mouseEvent . Button = = MouseButtons . Right | |
mouseEvent . Button = = MouseButtons . Middle )
2018-01-18 09:38:57 -08:00
{
2022-07-05 15:37:30 -07:00
this . Object3DControlLayer . SuppressObject3DControls = true ;
2018-01-18 09:38:57 -08:00
}
2017-03-15 16:17:06 -07:00
2022-07-05 15:37:30 -07:00
base . OnMouseDown ( mouseEvent ) ;
2017-03-15 16:17:06 -07:00
2022-07-05 15:37:30 -07:00
if ( TrackballTumbleWidget . UnderMouseState = = UnderMouseState . FirstUnderMouse
& & sceneContext . ViewState . ModelView )
2015-04-08 15:20:10 -07:00
{
2022-07-05 15:37:30 -07:00
if ( ( mouseEvent . Button = = MouseButtons . Left
& & viewControls3D . ActiveButton = = ViewControls3DButtons . PartSelect
& & ModifierKeys = = Keys . Shift )
| | ( TrackballTumbleWidget . TransformState = = TrackBallTransformType . None
& & ModifierKeys ! = Keys . Control
& & ModifierKeys ! = Keys . Alt ) )
2015-05-30 12:48:16 -07:00
{
2022-07-05 15:37:30 -07:00
if ( ! this . Object3DControlLayer . MouseDownOnObject3DControlVolume )
2017-05-31 17:47:12 -07:00
{
2022-07-05 15:37:30 -07:00
this . Object3DControlLayer . SuppressObject3DControls = true ;
IObject3D hitObject = null ;
IntersectInfo info = null ;
using ( new QuickTimer ( "View3DWidget_OnMouseDown_FindHitObject3D" , . 3 ) )
2017-05-31 17:47:12 -07:00
{
2022-07-05 15:37:30 -07:00
hitObject = FindHitObject3D ( mouseEvent . Position , out info ) ;
2017-05-31 17:47:12 -07:00
}
2022-07-05 15:37:30 -07:00
if ( hitObject = = null )
2017-03-15 16:17:06 -07:00
{
2022-07-05 15:37:30 -07:00
if ( selectedItem ! = null )
2017-03-15 16:17:06 -07:00
{
2022-07-05 15:37:30 -07:00
Scene . ClearSelection ( ) ;
2017-03-15 16:17:06 -07:00
}
2022-07-05 15:37:30 -07:00
// start a selection rect
DragSelectionStartPosition = mouseEvent . Position - OffsetToMeshViewerWidget ( ) ;
DragSelectionEndPosition = DragSelectionStartPosition ;
DragSelectionInProgress = true ;
}
else
{
CurrentSelectInfo . HitPlane = new PlaneShape ( Vector3 . UnitZ , CurrentSelectInfo . PlaneDownHitPos . Z , null ) ;
if ( hitObject ! = selectedItem )
2017-03-15 16:17:06 -07:00
{
2022-07-05 15:37:30 -07:00
if ( selectedItem = = null )
{
// No selection exists
Scene . SelectedItem = hitObject ;
}
else if ( ( ModifierKeys = = Keys . Shift | | ModifierKeys = = Keys . Control )
& & ! selectedItem . Children . Contains ( hitObject ) )
{
expandSelection = ! ( selectedItem is SelectionGroupObject3D ) ;
Scene . AddToSelection ( hitObject ) ;
}
else if ( selectedItem = = hitObject | | selectedItem . Children . Contains ( hitObject ) )
{
// Selection should not be cleared and drag should occur
}
else if ( ModifierKeys ! = Keys . Shift )
{
Scene . SelectedItem = hitObject ;
}
// Selection may have changed, update local reference to current value
selectedItem = Scene . SelectedItem ;
Invalidate ( ) ;
2017-03-15 16:17:06 -07:00
}
2022-07-05 15:37:30 -07:00
TransformOnMouseDown = selectedItem . Matrix ;
2018-08-11 09:54:16 -07:00
2017-10-25 17:04:55 -07:00
Invalidate ( ) ;
2022-07-05 15:37:30 -07:00
CurrentSelectInfo . DownOnPart = true ;
2016-02-19 08:29:49 -08:00
2022-07-05 15:37:30 -07:00
AxisAlignedBoundingBox selectedBounds = selectedItem . GetAxisAlignedBoundingBox ( ) ;
2016-02-19 08:29:49 -08:00
2022-07-05 15:37:30 -07:00
if ( info . HitPosition . X < selectedBounds . Center . X )
2016-02-19 08:29:49 -08:00
{
2022-07-05 15:37:30 -07:00
if ( info . HitPosition . Y < selectedBounds . Center . Y )
{
CurrentSelectInfo . HitQuadrant = HitQuadrant . LB ;
}
else
{
CurrentSelectInfo . HitQuadrant = HitQuadrant . LT ;
}
2016-02-19 08:29:49 -08:00
}
else
{
2022-07-05 15:37:30 -07:00
if ( info . HitPosition . Y < selectedBounds . Center . Y )
{
CurrentSelectInfo . HitQuadrant = HitQuadrant . RB ;
}
else
{
CurrentSelectInfo . HitQuadrant = HitQuadrant . RT ;
}
2016-02-19 08:29:49 -08:00
}
}
2015-05-30 12:48:16 -07:00
}
}
2015-04-08 15:20:10 -07:00
}
2015-05-30 12:48:16 -07:00
}
}
2015-02-27 11:09:37 -08:00
2018-01-19 13:43:39 -08:00
public override void OnMouseMove ( MouseEventArgs mouseEvent )
{
2018-05-18 16:58:18 -07:00
IObject3D selectedItem = Scene . SelectedItem ;
lastMouseMove = mouseEvent . Position ;
2019-05-02 18:06:28 -07:00
if ( lastMouseMove ! = mouseDownPositon )
2018-08-07 16:22:37 -07:00
{
mouseDownPositon = Vector2 . Zero ;
}
2018-01-19 13:43:39 -08:00
// File system Drop validation
mouseEvent . AcceptDrop = this . AllowDragDrop ( )
& & mouseEvent . DragFiles ? . Count > 0
2018-05-09 07:35:42 -07:00
& & mouseEvent . DragFiles . TrueForAll ( filePath = >
{
2019-01-08 17:55:59 -08:00
return filePath . StartsWith ( "html:" , StringComparison . OrdinalIgnoreCase )
2019-01-03 22:17:28 -08:00
| | filePath . StartsWith ( "data:" , StringComparison . OrdinalIgnoreCase )
| | filePath . StartsWith ( "text:" , StringComparison . OrdinalIgnoreCase )
| | ApplicationController . Instance . IsLoadableFile ( filePath )
// Disallow GCode drop in part view
& & ( this . Printer ! = null | | ! string . Equals ( System . IO . Path . GetExtension ( filePath ) , ".gcode" , StringComparison . OrdinalIgnoreCase ) ) ;
2018-05-09 07:35:42 -07:00
} ) ;
2018-01-19 13:43:39 -08:00
// View3DWidgets Filesystem DropDrop handler
if ( mouseEvent . AcceptDrop
& & this . PositionWithinLocalBounds ( mouseEvent . X , mouseEvent . Y ) )
{
if ( this . DragOperationActive )
{
DragOver ( screenSpaceMousePosition : this . TransformToScreenSpace ( mouseEvent . Position ) ) ;
}
else
{
// Project DragFiles to IEnumerable<FileSystemFileItem>
this . StartDragDrop (
2019-01-03 21:43:35 -08:00
mouseEvent . DragFiles . Select < string , ILibraryItem > ( path = >
{
2019-01-08 17:55:59 -08:00
if ( path . StartsWith ( "html:" ) )
{
var html = path ;
int startTagPosition = html . IndexOf ( "<html" ) ;
html = html . Substring ( startTagPosition ) ;
// Parse HTML into something usable for the scene
var parser = new HtmlParser ( ) ;
2020-12-07 07:58:13 -08:00
var document = parser . ParseDocument ( html ) ;
2019-01-08 17:55:59 -08:00
// TODO: This needs to become much smarter. Ideally it would inject a yet to be built Object3D for HTML
// snippets which could initially infer the content to use but would allow for interactive selection.
// There's already a model for this in the experimental SVG tool. For now, find any embedded svg
if ( document . QuerySelector ( "img" ) is IElement img )
{
path = img . Attributes [ "src" ] . Value ;
}
else
{
2019-01-09 16:55:20 -08:00
// If no image was found, extract the text content
2019-01-08 17:55:59 -08:00
path = "text:" + document . DocumentElement . TextContent ;
}
}
2019-01-03 21:56:14 -08:00
if ( path . StartsWith ( "data:" ) )
2019-01-03 21:43:35 -08:00
{
2019-01-03 21:56:14 -08:00
// Basic support for images encoded as Base64 data urls
var match = Regex . Match ( path , @"data:(?<type>.+?);base64,(?<data>.+)" ) ;
var base64Data = match . Groups [ "data" ] . Value ;
var contentType = match . Groups [ "type" ] . Value ;
var binData = Convert . FromBase64String ( base64Data ) ;
return new BufferLibraryItem ( binData , contentType , "unknown" ) ;
}
else if ( path . StartsWith ( "http" , StringComparison . OrdinalIgnoreCase ) )
{
// Basic support for images via remote urls
2019-01-03 21:43:35 -08:00
return new RemoteLibraryItem ( path , null ) ;
}
2019-01-03 22:17:28 -08:00
else if ( path . StartsWith ( "text:" ) )
{
2019-01-08 17:55:59 -08:00
return new OnDemandLibraryItem ( "xxx" )
2019-01-03 22:17:28 -08:00
{
2019-01-08 17:55:59 -08:00
Object3DProvider = async ( ) = >
{
var text = new TextObject3D ( )
{
NameToWrite = path . Substring ( 5 )
} ;
2019-01-03 21:43:35 -08:00
2019-01-08 17:55:59 -08:00
await text . Rebuild ( ) ;
2019-01-03 22:17:28 -08:00
2019-01-08 17:55:59 -08:00
return text ;
}
} ;
2019-01-03 22:17:28 -08:00
}
else
{
return new FileSystemFileItem ( path ) ;
}
2019-01-03 21:43:35 -08:00
} ) ,
2018-01-19 13:43:39 -08:00
screenSpaceMousePosition : this . TransformToScreenSpace ( mouseEvent . Position ) ,
trackSourceFiles : true ) ;
}
}
2018-05-22 12:51:04 -07:00
if ( CurrentSelectInfo . DownOnPart
2018-05-18 16:58:18 -07:00
& & TrackballTumbleWidget . TransformState = = TrackBallTransformType . None
& & selectedItem ! = null )
2018-01-19 13:43:39 -08:00
{
2018-05-18 16:58:18 -07:00
DragSelectedObject ( selectedItem , new Vector2 ( mouseEvent . X , mouseEvent . Y ) ) ;
2018-01-19 13:43:39 -08:00
}
if ( DragSelectionInProgress )
{
DragSelectionEndPosition = mouseEvent . Position - OffsetToMeshViewerWidget ( ) ;
DragSelectionEndPosition = new Vector2 (
2020-09-11 19:59:14 -07:00
Math . Max ( Math . Min ( ( double ) DragSelectionEndPosition . X , this . Object3DControlLayer . LocalBounds . Right ) , this . Object3DControlLayer . LocalBounds . Left ) ,
Math . Max ( Math . Min ( ( double ) DragSelectionEndPosition . Y , this . Object3DControlLayer . LocalBounds . Top ) , this . Object3DControlLayer . LocalBounds . Bottom ) ) ;
2018-01-19 13:43:39 -08:00
Invalidate ( ) ;
}
base . OnMouseMove ( mouseEvent ) ;
}
2017-03-15 16:17:06 -07:00
public IntersectInfo GetIntersectPosition ( Vector2 screenSpacePosition )
{
// Translate to local
2020-09-11 19:59:14 -07:00
Vector2 localPosition = this . Object3DControlLayer . TransformFromScreenSpace ( screenSpacePosition ) ;
2017-03-15 16:17:06 -07:00
2019-02-01 15:20:16 -08:00
Ray ray = sceneContext . World . GetRayForLocalBounds ( localPosition ) ;
2017-03-15 16:17:06 -07:00
2022-03-02 00:52:04 +00:00
return CurrentSelectInfo . HitPlane . GetClosestIntersectionWithinRayDistanceRange ( ray ) ;
2017-03-15 16:17:06 -07:00
}
2018-05-18 16:58:18 -07:00
public void DragSelectedObject ( IObject3D selectedItem , Vector2 localMousePosition )
2015-05-30 12:48:16 -07:00
{
2018-07-16 17:00:00 -07:00
if ( ! PositionWithinLocalBounds ( localMousePosition . X , localMousePosition . Y ) )
{
2020-12-26 15:15:49 -08:00
var totalTransform = Matrix4X4 . CreateTranslation ( new Vector3 ( - CurrentSelectInfo . LastMoveDelta ) ) ;
2018-07-16 17:00:00 -07:00
selectedItem . Matrix * = totalTransform ;
CurrentSelectInfo . LastMoveDelta = Vector3 . Zero ;
Invalidate ( ) ;
return ;
}
2020-09-11 19:59:14 -07:00
Vector2 meshViewerWidgetScreenPosition = this . Object3DControlLayer . TransformFromParentSpace ( this , localMousePosition ) ;
2019-05-18 13:18:28 -07:00
Ray ray = sceneContext . World . GetRayForLocalBounds ( meshViewerWidgetScreenPosition ) ;
2022-03-02 00:52:04 +00:00
IntersectInfo info = CurrentSelectInfo . HitPlane . GetClosestIntersectionWithinRayDistanceRange ( ray ) ;
2017-03-15 16:17:06 -07:00
if ( info ! = null )
2015-05-30 12:48:16 -07:00
{
2018-01-23 16:56:15 -08:00
if ( CurrentSelectInfo . LastMoveDelta = = Vector3 . PositiveInfinity )
{
2018-05-18 16:58:18 -07:00
CalculateDragStartPosition ( selectedItem , info ) ;
2018-01-23 16:56:15 -08:00
}
2017-03-15 16:17:06 -07:00
// move the mesh back to the start position
{
2020-12-26 15:15:49 -08:00
var totalTransform = Matrix4X4 . CreateTranslation ( new Vector3 ( - CurrentSelectInfo . LastMoveDelta ) ) ;
2018-05-18 16:58:18 -07:00
selectedItem . Matrix * = totalTransform ;
2017-03-15 16:17:06 -07:00
}
2017-06-16 11:12:24 -07:00
Vector3 delta = info . HitPosition - CurrentSelectInfo . PlaneDownHitPos ;
2017-03-15 16:17:06 -07:00
2020-09-11 19:59:14 -07:00
double snapGridDistance = this . Object3DControlLayer . SnapGridDistance ;
2017-03-15 16:17:06 -07:00
if ( snapGridDistance > 0 )
2015-02-27 11:09:37 -08:00
{
2017-03-15 16:17:06 -07:00
// snap this position to the grid
2019-05-21 07:27:58 -07:00
AxisAlignedBoundingBox selectedBounds = selectedItem . GetAxisAlignedBoundingBox ( ) ;
2017-03-15 16:17:06 -07:00
2019-01-11 16:49:34 -08:00
double xSnapOffset = selectedBounds . MinXYZ . X ;
2017-03-15 16:17:06 -07:00
// snap the x position
if ( CurrentSelectInfo . HitQuadrant = = HitQuadrant . RB
| | CurrentSelectInfo . HitQuadrant = = HitQuadrant . RT )
2016-02-18 09:36:01 -08:00
{
2017-03-15 16:17:06 -07:00
// switch to the other side
2019-01-11 16:49:34 -08:00
xSnapOffset = selectedBounds . MaxXYZ . X ;
2016-02-18 09:36:01 -08:00
}
2017-10-31 12:51:16 -07:00
double xToSnap = xSnapOffset + delta . X ;
2016-02-18 09:36:01 -08:00
2022-04-08 19:01:39 +01:00
double snappedX = Math . Round ( xToSnap / snapGridDistance ) * snapGridDistance ;
2017-10-31 12:51:16 -07:00
delta . X = snappedX - xSnapOffset ;
2015-02-27 11:09:37 -08:00
2019-01-11 16:49:34 -08:00
double ySnapOffset = selectedBounds . MinXYZ . Y ;
2017-03-15 16:17:06 -07:00
// snap the y position
if ( CurrentSelectInfo . HitQuadrant = = HitQuadrant . LT
| | CurrentSelectInfo . HitQuadrant = = HitQuadrant . RT )
2016-02-17 08:23:09 -08:00
{
2017-03-15 16:17:06 -07:00
// switch to the other side
2019-01-11 16:49:34 -08:00
ySnapOffset = selectedBounds . MaxXYZ . Y ;
2017-03-15 16:17:06 -07:00
}
2017-10-31 12:51:16 -07:00
double yToSnap = ySnapOffset + delta . Y ;
2016-02-17 08:23:09 -08:00
2022-04-08 19:01:39 +01:00
double snappedY = Math . Round ( yToSnap / snapGridDistance ) * snapGridDistance ;
2017-10-31 12:51:16 -07:00
delta . Y = snappedY - ySnapOffset ;
2017-03-15 16:17:06 -07:00
}
2016-02-17 08:23:09 -08:00
2017-08-15 11:05:32 -07:00
// if the shift key is down only move on the major axis of x or y
2018-02-07 18:01:49 -08:00
if ( Keyboard . IsKeyDown ( Keys . ShiftKey ) )
2017-08-15 11:05:32 -07:00
{
2018-02-07 18:01:49 -08:00
if ( Math . Abs ( delta . X ) < Math . Abs ( delta . Y ) )
2017-08-15 11:05:32 -07:00
{
2017-10-31 12:51:16 -07:00
delta . X = 0 ;
2017-08-15 11:05:32 -07:00
}
else
{
2017-10-31 12:51:16 -07:00
delta . Y = 0 ;
2017-08-15 11:05:32 -07:00
}
}
2017-03-15 16:17:06 -07:00
// move the mesh back to the new position
{
2020-12-26 15:15:49 -08:00
var totalTransform = Matrix4X4 . CreateTranslation ( new Vector3 ( delta ) ) ;
2015-02-27 11:09:37 -08:00
2018-05-18 16:58:18 -07:00
selectedItem . Matrix * = totalTransform ;
2016-02-18 09:36:01 -08:00
2017-03-15 16:17:06 -07:00
CurrentSelectInfo . LastMoveDelta = delta ;
}
2016-02-18 09:36:01 -08:00
2017-03-15 16:17:06 -07:00
Invalidate ( ) ;
}
}
2015-02-27 11:09:37 -08:00
2017-05-31 17:47:12 -07:00
Vector2 OffsetToMeshViewerWidget ( )
{
2020-12-26 15:15:49 -08:00
var parents = new List < GuiWidget > ( ) ;
2020-09-11 19:59:14 -07:00
GuiWidget parent = this . Object3DControlLayer . Parent ;
2017-05-31 17:47:12 -07:00
while ( parent ! = this )
{
parents . Add ( parent ) ;
parent = parent . Parent ;
}
2020-12-26 15:15:49 -08:00
var offset = default ( Vector2 ) ;
2018-02-07 18:01:49 -08:00
for ( int i = parents . Count - 1 ; i > = 0 ; i - - )
2017-05-31 17:47:12 -07:00
{
offset + = parents [ i ] . OriginRelativeParent ;
}
return offset ;
}
2021-05-24 07:36:04 -07:00
Vector3 homeEyePosition = default ( Vector3 ) ;
Matrix4X4 homeRotation = default ( Matrix4X4 ) ;
2017-07-10 14:00:27 -07:00
public void ResetView ( )
{
2021-05-24 07:36:04 -07:00
var world = sceneContext . World ;
2021-05-22 14:20:28 -07:00
TrackballTumbleWidget . SetRotationCenter ( new Vector3 ( sceneContext . BedCenter ) ) ;
2017-07-10 14:00:27 -07:00
2021-05-24 07:36:04 -07:00
void ResetHome ( )
{
world . Reset ( ) ;
world . Scale = . 03 ;
world . Translate ( - new Vector3 ( sceneContext . BedCenter ) ) ;
world . Rotate ( Quaternion . FromEulerAngles ( new Vector3 ( 0 , 0 , - MathHelper . Tau / 16 ) ) ) ;
world . Rotate ( Quaternion . FromEulerAngles ( new Vector3 ( MathHelper . Tau * . 19 , 0 , 0 ) ) ) ;
2017-07-10 14:00:27 -07:00
2021-05-24 07:36:04 -07:00
homeEyePosition = world . EyePosition ;
homeRotation = world . RotationMatrix ;
Invalidate ( ) ;
}
2018-04-23 14:33:27 -07:00
2021-05-24 07:36:04 -07:00
void Rotate ( )
{
TrackballTumbleWidget . AnimateRotation ( homeRotation ) ; //, ResetHome);
}
void Translate ( )
{
TrackballTumbleWidget . AnimateTranslation ( world . EyePosition , homeEyePosition ) ;
}
if ( homeEyePosition . Z ! = 0 )
{
// pan to the center
//var screenCenter = new Vector2(world.Width / 2 - selectedObjectPanel.Width / 2, world.Height / 2);
//var centerRay = world.GetRayForLocalBounds(screenCenter);
//TrackballTumbleWidget.AnimateTranslation(new Vector3(sceneContext.BedCenter, 0), centerRay.origin + centerRay.directionNormal * 80);
ResetHome ( ) ;
}
else
{
ResetHome ( ) ;
}
2017-07-10 14:00:27 -07:00
}
2015-05-30 12:48:16 -07:00
public override void OnMouseUp ( MouseEventArgs mouseEvent )
2015-04-08 15:20:10 -07:00
{
2018-08-11 08:50:40 -07:00
var selectedItem = Scene . SelectedItem ;
2017-09-19 19:59:55 -07:00
if ( this . DragOperationActive )
2017-04-05 16:25:43 -07:00
{
2017-09-19 19:59:55 -07:00
this . FinishDrop ( mouseUpInBounds : true ) ;
2017-04-05 16:25:43 -07:00
}
2018-04-23 14:33:27 -07:00
if ( TrackballTumbleWidget . TransformState = = TrackBallTransformType . None )
2015-04-08 15:20:10 -07:00
{
2018-08-11 08:50:40 -07:00
if ( selectedItem ! = null
2017-07-11 12:50:21 -07:00
& & CurrentSelectInfo . DownOnPart
& & CurrentSelectInfo . LastMoveDelta ! = Vector3 . Zero )
2016-02-27 13:56:57 -08:00
{
2019-02-01 15:09:30 -08:00
this . Scene . AddTransformSnapshot ( TransformOnMouseDown ) ;
2017-05-31 17:47:12 -07:00
}
else if ( DragSelectionInProgress )
{
2017-06-06 10:57:13 -07:00
DoRectangleSelection ( null ) ;
2017-05-31 17:47:12 -07:00
DragSelectionInProgress = false ;
2016-02-27 13:56:57 -08:00
}
2015-05-30 12:48:16 -07:00
}
2015-04-08 15:20:10 -07:00
2020-09-12 09:51:43 -07:00
this . Object3DControlLayer . SuppressObject3DControls = false ;
2017-03-15 16:17:06 -07:00
2016-02-19 08:29:49 -08:00
CurrentSelectInfo . DownOnPart = false ;
2015-04-08 15:20:10 -07:00
2015-05-29 15:13:56 -07:00
if ( activeButtonBeforeMouseOverride ! = null )
{
viewControls3D . ActiveButton = ( ViewControls3DButtons ) activeButtonBeforeMouseOverride ;
activeButtonBeforeMouseOverride = null ;
}
2018-05-09 16:41:24 -07:00
// if we had a down and an up that did not move the view
2019-02-01 15:20:16 -08:00
if ( worldMatrixOnMouseDown = = sceneContext . World . GetTransform4X4 ( ) )
2018-05-09 16:41:24 -07:00
{
// and we are the first under mouse
if ( TrackballTumbleWidget . UnderMouseState = = UnderMouseState . FirstUnderMouse )
{
// and the control key is pressed
if ( ModifierKeys = = Keys . Control )
{
// find the think we clicked on
2020-12-26 15:15:49 -08:00
var hitObject = FindHitObject3D ( mouseEvent . Position , out IntersectInfo info ) ;
2018-05-09 16:41:24 -07:00
if ( hitObject ! = null )
{
2018-08-11 08:50:40 -07:00
if ( selectedItem = = hitObject
& & ! ( selectedItem is SelectionGroupObject3D ) )
2018-05-09 16:41:24 -07:00
{
Scene . SelectedItem = null ;
}
else
{
IObject3D selectedHitItem = null ;
2018-08-11 08:50:40 -07:00
if ( selectedItem ! = null )
2018-05-09 16:41:24 -07:00
{
2018-08-11 08:50:40 -07:00
foreach ( Object3D object3D in selectedItem . Children )
2018-05-09 16:41:24 -07:00
{
2020-04-01 20:48:59 -07:00
if ( object3D . GetBVHData ( ) . Contains ( info . HitPosition ) )
2018-05-09 16:41:24 -07:00
{
CurrentSelectInfo . PlaneDownHitPos = info . HitPosition ;
2019-05-02 18:06:28 -07:00
CurrentSelectInfo . LastMoveDelta = default ( Vector3 ) ;
2018-05-09 16:41:24 -07:00
selectedHitItem = object3D ;
break ;
}
}
}
if ( selectedHitItem ! = null )
{
2018-08-11 08:50:40 -07:00
selectedItem . Children . Remove ( selectedHitItem ) ;
2019-05-02 18:06:28 -07:00
if ( selectedItem . Children . Count = = 0 )
2018-05-09 16:41:24 -07:00
{
Scene . SelectedItem = null ;
}
2020-08-19 22:51:03 -07:00
2018-05-09 16:41:24 -07:00
Scene . Children . Add ( selectedHitItem ) ;
}
else
{
Scene . AddToSelection ( hitObject ) ;
}
2018-08-11 09:54:16 -07:00
// Selection may have changed, update local reference to current value
selectedItem = Scene . SelectedItem ;
2018-05-09 16:41:24 -07:00
}
}
}
}
}
2018-08-07 16:22:37 -07:00
if ( mouseEvent . Button = = MouseButtons . Right
& & mouseDownPositon = = mouseEvent . Position
& & this . TrackballTumbleWidget . FirstWidgetUnderMouse )
{
2020-12-26 15:15:49 -08:00
if ( FindHitObject3D ( mouseEvent . Position , out _ ) is IObject3D hitObject
2018-08-29 08:31:16 -07:00
& & ( this . Printer = = null // Allow Model -> Right Click in Part view
| | this . Printer ? . ViewState . ViewMode = = PartViewMode . Model ) ) // Disallow Model -> Right Click in GCode views
2018-08-07 16:22:37 -07:00
{
2018-08-08 07:28:02 -07:00
// Object3D/hit item context menu
2018-08-11 08:50:40 -07:00
if ( hitObject ! = selectedItem )
2018-08-07 16:22:37 -07:00
{
Scene . SelectedItem = null ;
Scene . SelectedItem = hitObject ;
2018-08-11 08:50:40 -07:00
selectedItem = hitObject ;
2018-08-07 16:22:37 -07:00
}
2018-10-23 09:43:01 -07:00
this . ShowPartContextMenu ( mouseEvent , selectedItem ) ;
}
else // Allow right click on bed in all modes
{
this . ShowBedContextMenu ( mouseEvent . Position ) ;
}
}
2018-08-07 16:22:37 -07:00
2018-10-23 09:43:01 -07:00
base . OnMouseUp ( mouseEvent ) ;
2018-08-08 07:28:02 -07:00
2018-10-23 09:43:01 -07:00
if ( deferEditorTillMouseUp )
{
this . deferEditorTillMouseUp = false ;
Scene_SelectionChanged ( null , null ) ;
}
}
private void ShowPartContextMenu ( MouseEventArgs mouseEvent , IObject3D selectedItem )
{
2020-09-24 22:35:03 -07:00
var popupMenu = ApplicationController . Instance . GetActionMenuForSceneItem ( true , this ) ;
2020-07-09 07:12:00 -07:00
popupMenu . ShowMenu ( this , mouseEvent ) ;
2018-10-18 17:25:56 -07:00
}
2018-10-23 09:43:01 -07:00
public void ShowBedContextMenu ( Vector2 position )
2018-10-18 17:25:56 -07:00
{
// Workspace/plate context menu
2019-06-29 08:33:24 -07:00
var popupMenu = new PopupMenu ( ApplicationController . Instance . MenuTheme ) ;
2018-10-18 17:25:56 -07:00
2019-06-29 08:33:24 -07:00
var workspaceActions = ApplicationController . Instance . GetWorkspaceActions ( this ) ;
2019-06-12 11:48:45 -07:00
2019-06-29 08:33:24 -07:00
var actions = new [ ]
{
2018-10-23 09:43:01 -07:00
new ActionSeparator ( ) ,
new NamedAction ( )
{
Title = "Paste" . Localize ( ) ,
Action = ( ) = >
{
2018-11-11 08:51:16 -08:00
sceneContext . Paste ( ) ;
2018-10-23 09:43:01 -07:00
} ,
IsEnabled = ( ) = > Clipboard . Instance . ContainsImage | | Clipboard . Instance . GetText ( ) = = "!--IObjectSelection--!"
} ,
2019-06-12 11:50:55 -07:00
workspaceActions [ "Save" ] ,
workspaceActions [ "SaveAs" ] ,
workspaceActions [ "Export" ] ,
2018-10-23 09:43:01 -07:00
new ActionSeparator ( ) ,
2019-06-12 11:50:55 -07:00
workspaceActions [ "Print" ] ,
2018-10-23 19:54:09 -07:00
new ActionSeparator ( ) ,
2019-06-12 11:50:55 -07:00
workspaceActions [ "ArrangeAll" ] ,
workspaceActions [ "ClearBed" ] ,
2018-10-23 09:43:01 -07:00
} ;
2018-08-07 16:22:37 -07:00
2019-06-29 08:33:24 -07:00
theme . CreateMenuItems ( popupMenu , actions ) ;
2018-08-07 16:22:37 -07:00
2019-06-29 08:33:24 -07:00
var popupBounds = new RectangleDouble ( position . X + 1 , position . Y + 1 , position . X + 1 , position . Y + 1 ) ;
2018-08-07 16:22:37 -07:00
2019-06-29 08:33:24 -07:00
var systemWindow = this . Parents < SystemWindow > ( ) . FirstOrDefault ( ) ;
systemWindow . ShowPopup (
2022-07-15 19:13:44 -07:00
theme ,
2019-06-29 08:33:24 -07:00
new MatePoint ( this )
{
Mate = new MateOptions ( MateEdge . Left , MateEdge . Bottom ) ,
2020-05-29 19:08:41 -07:00
AltMate = new MateOptions ( MateEdge . Right , MateEdge . Top )
2019-06-29 08:33:24 -07:00
} ,
new MatePoint ( popupMenu )
{
2020-05-29 19:08:41 -07:00
Mate = new MateOptions ( MateEdge . Left , MateEdge . Bottom ) ,
AltMate = new MateOptions ( MateEdge . Right , MateEdge . Top )
2019-06-29 08:33:24 -07:00
} ,
altBounds : popupBounds ) ;
2015-05-30 12:48:16 -07:00
}
2015-04-08 15:20:10 -07:00
2017-09-16 01:36:19 -07:00
// TODO: Consider if we should always allow DragDrop or if we should prevent during printer or other scenarios
2017-09-15 22:34:46 -07:00
private bool AllowDragDrop ( ) = > true ;
2015-05-30 12:48:16 -07:00
2019-01-25 17:00:33 -08:00
private bool rebuildTreePending = false ;
2018-10-26 09:04:31 -07:00
private void Scene_Invalidated ( object sender , InvalidateArgs e )
{
2020-09-12 08:53:40 -07:00
if ( Scene . Descendants ( ) . Count ( ) ! = lastSceneDescendantsCount
& & ! rebuildTreePending )
2019-11-10 21:16:08 -08:00
{
rebuildTreePending = true ;
UiThread . RunOnIdle ( this . RebuildTree ) ;
}
2019-01-28 14:19:40 -08:00
if ( e . InvalidateType . HasFlag ( InvalidateType . Children )
2019-01-25 17:00:33 -08:00
& & ! rebuildTreePending )
2018-10-26 09:04:31 -07:00
{
2019-01-25 17:00:33 -08:00
rebuildTreePending = true ;
2019-06-30 08:24:30 -07:00
UiThread . RunOnIdle ( this . RebuildTree ) ;
2018-10-26 09:04:31 -07:00
}
2018-11-14 14:48:09 -08:00
2019-01-28 14:19:40 -08:00
if ( e . InvalidateType . HasFlag ( InvalidateType . Name ) )
2018-12-11 16:42:35 -08:00
{
// clear and restore the selection so we have the name change
2022-02-26 22:49:12 -08:00
using ( new SelectionMaintainer ( Scene ) )
2019-01-25 17:00:33 -08:00
{
2022-02-26 22:21:29 -08:00
if ( ! rebuildTreePending )
{
rebuildTreePending = true ;
UiThread . RunOnIdle ( this . RebuildTree ) ;
}
2022-02-06 08:22:31 -08:00
}
2018-12-11 16:42:35 -08:00
}
2019-11-10 21:16:08 -08:00
lastSceneDescendantsCount = Scene . Descendants ( ) . Count ( ) ;
2018-11-14 14:48:09 -08:00
// Invalidate widget on scene invalidate
this . Invalidate ( ) ;
2018-10-26 09:04:31 -07:00
}
2017-03-15 16:17:06 -07:00
private void Scene_SelectionChanged ( object sender , EventArgs e )
{
2018-11-01 07:13:05 -07:00
if ( deferEditorTillMouseUp )
2018-11-01 06:46:34 -07:00
{
2018-11-01 07:13:05 -07:00
selectedObjectPanel . SetActiveItem ( null ) ;
2018-11-01 06:46:34 -07:00
}
2018-11-01 07:13:05 -07:00
else
2018-11-01 06:46:34 -07:00
{
2018-11-01 07:13:05 -07:00
var selectedItem = Scene . SelectedItem ;
2018-10-31 22:13:59 -07:00
2018-11-01 07:13:05 -07:00
// Change tree selection to current node
if ( selectedItem ! = null
2019-03-07 18:06:01 -08:00
& & treeNodesByObject . TryGetValue ( selectedItem , out TreeNode treeNode ) )
2018-11-01 07:13:05 -07:00
{
treeView . SelectedNode = treeNode ;
}
else
{
// Clear the TreeView and release node references when no item is selected
treeView . SelectedNode = null ;
}
2017-04-05 19:03:04 -07:00
2020-09-24 22:35:03 -07:00
selectedObjectPanel . SetActiveItem ( this . sceneContext ) ;
2018-05-22 12:51:04 -07:00
}
2017-03-15 16:17:06 -07:00
}
2015-05-30 12:48:16 -07:00
2018-06-22 00:04:03 -07:00
public void ClearPlate ( )
{
2022-02-10 12:20:32 -08:00
if ( Printer ! = null )
{
selectedObjectPanel . SetActiveItem ( null ) ;
sceneContext . ClearPlate ( ) ;
sceneContext . Scene . UndoBuffer . ClearHistory ( ) ;
}
else
2022-09-01 13:24:08 -07:00
{
2022-02-10 12:20:32 -08:00
this . SelectAll ( ) ;
SceneActions . DeleteSelection ( Scene ) ;
}
2018-06-22 00:04:03 -07:00
2018-10-04 22:04:53 -07:00
this . Invalidate ( ) ;
2018-06-22 00:04:03 -07:00
}
2016-01-10 10:18:53 -08:00
public static Regex fileNameNumberMatch = new Regex ( "\\(\\d+\\)" , RegexOptions . Compiled ) ;
2020-12-26 15:15:49 -08:00
private readonly SelectedObjectPanel selectedObjectPanel ;
2017-10-20 07:26:14 -07:00
2018-10-14 17:50:54 -07:00
internal VerticalResizeContainer modelViewSidePanel ;
2017-10-19 09:04:36 -07:00
2017-05-31 17:47:12 -07:00
public Vector2 DragSelectionStartPosition { get ; private set ; }
2020-08-19 22:51:03 -07:00
2017-05-31 17:47:12 -07:00
public bool DragSelectionInProgress { get ; private set ; }
2020-08-19 22:51:03 -07:00
2017-05-31 17:47:12 -07:00
public Vector2 DragSelectionEndPosition { get ; private set ; }
2018-01-21 21:07:14 -08:00
internal GuiWidget ShowOverflowMenu ( PopupMenu popupMenu )
2017-05-25 17:58:20 -07:00
{
2018-03-16 17:42:34 -07:00
this . ShowBedViewOptions ( popupMenu ) ;
2017-11-09 15:48:05 -08:00
2017-11-09 18:26:32 -08:00
return popupMenu ;
2017-11-09 15:48:05 -08:00
}
2017-05-25 17:58:20 -07:00
2018-03-16 17:42:34 -07:00
internal void ShowBedViewOptions ( PopupMenu popupMenu )
{
2018-04-02 16:19:45 -07:00
// TODO: Extend popup menu if applicable
// popupMenu.CreateHorizontalLine();
2018-03-16 17:42:34 -07:00
}
2020-12-26 15:15:49 -08:00
private readonly bool assigningTreeNode ;
private readonly FlowLayoutWidget treeNodeContainer ;
2019-06-30 08:26:18 -07:00
2020-12-26 15:15:49 -08:00
private readonly InlineStringEdit workspaceName ;
2019-06-30 08:26:18 -07:00
private int lastSceneDescendantsCount ;
2020-09-12 08:53:40 -07:00
private Vector2 beforeReubildScrollPosition ;
2017-07-05 14:34:38 -07:00
2018-04-23 14:33:27 -07:00
public InteractiveScene Scene = > sceneContext . Scene ;
2017-07-05 14:34:38 -07:00
2021-09-13 21:47:46 -07:00
protected ViewToolBarControls viewControls3D { get ; }
2017-07-05 14:34:38 -07:00
2017-07-12 21:57:30 -07:00
public MeshSelectInfo CurrentSelectInfo { get ; } = new MeshSelectInfo ( ) ;
2017-07-05 14:34:38 -07:00
2019-02-01 11:32:10 -08:00
2020-09-13 19:09:09 -07:00
private IObject3D FindHitObject3D ( Vector2 screenPosition , out IntersectInfo intersectionInfo )
2017-07-05 14:34:38 -07:00
{
2020-09-11 19:59:14 -07:00
Vector2 meshViewerWidgetScreenPosition = this . Object3DControlLayer . TransformFromParentSpace ( this , screenPosition ) ;
2019-02-01 15:20:16 -08:00
Ray ray = sceneContext . World . GetRayForLocalBounds ( meshViewerWidgetScreenPosition ) ;
2017-07-05 14:34:38 -07:00
2020-04-01 20:48:59 -07:00
intersectionInfo = Scene . GetBVHData ( ) . GetClosestIntersection ( ray ) ;
2017-07-05 14:34:38 -07:00
if ( intersectionInfo ! = null )
{
2020-09-13 19:09:09 -07:00
foreach ( var object3D in Scene . Children )
2017-07-05 14:34:38 -07:00
{
2020-04-01 20:48:59 -07:00
if ( object3D . GetBVHData ( ) . Contains ( intersectionInfo . HitPosition ) )
2017-07-05 14:34:38 -07:00
{
CurrentSelectInfo . PlaneDownHitPos = intersectionInfo . HitPosition ;
2019-05-02 18:06:28 -07:00
CurrentSelectInfo . LastMoveDelta = default ( Vector3 ) ;
2017-07-05 14:34:38 -07:00
return object3D ;
}
}
}
return null ;
}
2018-02-07 18:01:49 -08:00
2018-04-23 14:33:27 -07:00
public void Save ( )
{
2018-12-05 13:48:25 -08:00
ApplicationController . Instance . Tasks . Execute ( "Saving" . Localize ( ) , printer , sceneContext . SaveChanges ) ;
2018-04-23 14:33:27 -07:00
}
2019-02-01 08:58:44 -08:00
void IDrawable . Draw ( GuiWidget sender , DrawEventArgs e , Matrix4X4 itemMaxtrix , WorldView world )
{
if ( CurrentSelectInfo . DownOnPart
& & TrackballTumbleWidget . TransformState = = TrackBallTransformType . None
& & Keyboard . IsKeyDown ( Keys . ShiftKey ) )
{
// draw marks on the bed to show that the part is constrained to x and y
2019-05-21 07:27:58 -07:00
AxisAlignedBoundingBox selectedBounds = Scene . SelectedItem . GetAxisAlignedBoundingBox ( ) ;
2019-02-01 08:58:44 -08:00
var drawCenter = CurrentSelectInfo . PlaneDownHitPos ;
2020-10-08 22:17:52 -07:00
var drawColor = Color . Red . WithAlpha ( 20 ) ;
2019-02-01 08:58:44 -08:00
bool zBuffer = false ;
for ( int i = 0 ; i < 2 ; i + + )
{
2019-02-01 15:20:16 -08:00
sceneContext . World . Render3DLine (
2019-02-01 08:58:44 -08:00
drawCenter - new Vector3 ( - 50 , 0 , 0 ) ,
2019-05-02 18:06:28 -07:00
drawCenter - new Vector3 ( 50 , 0 , 0 ) ,
drawColor ,
zBuffer ,
2 ) ;
2019-02-01 08:58:44 -08:00
2019-02-01 15:20:16 -08:00
sceneContext . World . Render3DLine (
2019-02-01 08:58:44 -08:00
drawCenter - new Vector3 ( 0 , - 50 , 0 ) ,
2019-05-02 18:06:28 -07:00
drawCenter - new Vector3 ( 0 , 50 , 0 ) ,
drawColor ,
zBuffer ,
2 ) ;
2019-02-01 08:58:44 -08:00
drawColor = Color . Black ;
drawCenter . Z = 0 ;
zBuffer = true ;
}
GL . Enable ( EnableCap . Lighting ) ;
}
2021-04-30 17:36:50 -07:00
TrackballTumbleWidget . OnDraw3D ( ) ;
2019-02-01 08:58:44 -08:00
// Render 3D GCode if applicable
if ( sceneContext . LoadedGCode ! = null
& & sceneContext . GCodeRenderer ! = null
2020-12-30 10:35:47 -08:00
& & printerTabPage ? . Printer . ViewState . ViewMode = = PartViewMode . Layers3D )
2019-02-01 08:58:44 -08:00
{
2020-12-30 10:35:47 -08:00
printerTabPage . Printer . Bed . RenderGCode3D ( e ) ;
2019-02-01 08:58:44 -08:00
}
}
2019-02-01 11:32:10 -08:00
2022-03-02 00:52:04 +00:00
AxisAlignedBoundingBox IDrawable . GetWorldspaceAABB ( )
{
AxisAlignedBoundingBox box = AxisAlignedBoundingBox . Empty ( ) ;
if ( CurrentSelectInfo . DownOnPart
& & TrackballTumbleWidget . TransformState = = TrackBallTransformType . None
& & Keyboard . IsKeyDown ( Keys . ShiftKey ) )
{
var drawCenter = CurrentSelectInfo . PlaneDownHitPos ;
for ( int i = 0 ; i < 2 ; i + + )
{
box . ExpandToInclude ( drawCenter - new Vector3 ( - 50 , 0 , 0 ) ) ;
box . ExpandToInclude ( drawCenter - new Vector3 ( 50 , 0 , 0 ) ) ;
box . ExpandToInclude ( drawCenter - new Vector3 ( 0 , - 50 , 0 ) ) ;
box . ExpandToInclude ( drawCenter - new Vector3 ( 0 , 50 , 0 ) ) ;
drawCenter . Z = 0 ;
}
}
// Render 3D GCode if applicable
if ( sceneContext . LoadedGCode ! = null
& & sceneContext . GCodeRenderer ! = null
& & printerTabPage ? . Printer . ViewState . ViewMode = = PartViewMode . Layers3D )
{
box = AxisAlignedBoundingBox . Union ( box , printerTabPage . Printer . Bed . GetAabbOfRenderGCode3D ( ) ) ;
}
return box ;
}
2019-02-01 11:32:10 -08:00
string IDrawable . Title { get ; } = "View3DWidget Extensions" ;
string IDrawable . Description { get ; } = "Render axis indicators for shift drag and 3D GCode view" ;
DrawStage IDrawable . DrawStage { get ; } = DrawStage . OpaqueContent ;
2022-02-24 19:11:29 -07:00
private bool AggregateSelection ( MouseEventArgs sourceEvent , GuiWidget clickedWidget )
{
if ( ! Keyboard . IsKeyDown ( Keys . Control ) )
{
return false ;
}
if ( sourceEvent . Button ! = MouseButtons . Left )
{
return false ;
}
if ( Scene . SelectedItem = = null )
{
return false ;
}
var node = ( TreeNode ) clickedWidget . Parent ;
var object3D = ( IObject3D ) node . Tag ;
if ( object3D = = Scene . SelectedItem )
{
// Control-click on the existing selection doesn't change anything.
return true ;
}
if ( object3D . Parent = = Scene . SelectedItem & & Scene . SelectedItem is SelectionGroupObject3D )
{
// Part is in a selection group so remove it.
var remainingParts = Scene . SelectedItem . Children . Except ( new [ ] { object3D } ) . ToList ( ) ;
Scene . ClearSelection ( ) ;
Scene . SetSelection ( remainingParts ) ;
expandSelection = true ;
return true ;
}
if ( treeNodesByObject . TryGetValue ( Scene . SelectedItem , out var priorSelectedNode ) )
{
if ( priorSelectedNode . NodeParent ! = null )
{
// The existing selection isn't a top-level node so don't create a
// new selection group. Restore visuals to prior selected node.
treeView . SelectedNode = priorSelectedNode ;
return true ;
}
}
if ( node . NodeParent ! = null )
{
// Only top-level nodes can be aggregated into a selection.
treeView . SelectedNode = priorSelectedNode ;
return true ;
}
Scene . AddToSelection ( object3D ) ;
if ( ! treeNodesByObject . TryGetValue ( Scene . SelectedItem , out _ ) )
{
// A new selection group has been created and should be expanded by default.
// However, the expansion has to be deferred until after tree gets rebuilt
// so the new selection group object is included in the tree.
expandSelection = true ;
}
return true ;
}
2016-02-19 08:29:49 -08:00
}
2015-05-30 12:48:16 -07:00
2020-09-12 19:44:18 -07:00
public enum HitQuadrant
{
LB ,
LT ,
RB ,
RT
}
2018-03-01 09:56:50 -08:00
2016-02-19 08:29:49 -08:00
public class MeshSelectInfo
{
2020-09-12 19:44:18 -07:00
public HitQuadrant HitQuadrant { get ; set ; }
public bool DownOnPart { get ; set ; }
public PlaneShape HitPlane { get ; set ; }
public Vector3 LastMoveDelta { get ; set ; }
public Vector3 PlaneDownHitPos { get ; set ; }
2015-04-08 15:20:10 -07:00
}
2017-08-20 02:34:39 -07:00
}