Editor/Tool/ArchToolView.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Editor;
using Sandbox;
namespace Sunless.Architecture;
public enum ArchViewAxis {
Free,
Top,
Front,
Side
}
public abstract partial class ArchTool {
public float? SectionDepth { get; set; }
// Read off the viewport, never stored - CTRL+SPACE and the gizmo change the view without passing through here.
public ArchViewAxis Axis => AxisOf( FocusedViewport() );
public bool InPlanMode => Axis != ArchViewAxis.Free;
public bool InElevation => Axis is ArchViewAxis.Front or ArchViewAxis.Side;
public static SceneViewportWidget FocusedViewport() => SceneViewWidget.Current?.LastSelectedViewportWidget;
// Ordered by pane id - a dictionary hands them back in whatever order it likes.
public static List<SceneViewportWidget> Panes() {
if ( SceneViewWidget.Current is not { } view ) {
return new List<SceneViewportWidget>();
}
return view._viewports
.OrderBy( entry => entry.Key )
.Select( entry => entry.Value )
.Where( pane => pane.IsValid() )
.ToList();
}
public static ArchViewAxis AxisOf( SceneViewportWidget viewport ) {
return viewport?.State?.View switch {
SceneViewportWidget.ViewMode.Top2d => ArchViewAxis.Top,
SceneViewportWidget.ViewMode.Front2d => ArchViewAxis.Front,
SceneViewportWidget.ViewMode.Side2d => ArchViewAxis.Side,
_ => ArchViewAxis.Free
};
}
static SceneViewportWidget.ViewMode ModeOf( ArchViewAxis axis ) {
return axis switch {
ArchViewAxis.Top => SceneViewportWidget.ViewMode.Top2d,
ArchViewAxis.Front => SceneViewportWidget.ViewMode.Front2d,
ArchViewAxis.Side => SceneViewportWidget.ViewMode.Side2d,
_ => SceneViewportWidget.ViewMode.Perspective
};
}
// The viewport still owns which view it is in, so the engine's own controls stay first-class.
public void Show( SceneViewportWidget viewport, ArchViewAxis axis ) {
if ( viewport is null ) {
return;
}
// Setting the mode picks the rotation, so framing follows.
viewport.State.View = ModeOf( axis );
if ( axis != ArchViewAxis.Free ) {
FrameOn( viewport, EditedExtent() );
}
viewport.SaveState();
}
// SetLayout destroys every viewport widget, so the split has to come before the assignments.
public void Arrange( SceneViewWidget.ViewportLayoutMode layout, params ArchViewAxis[] axes ) {
if ( SceneViewWidget.Current is not { } view ) {
return;
}
view.SetLayout( layout );
var panes = Panes();
for ( var index = 0; index < panes.Count && index < axes.Length; index++ ) {
Show( panes[index], axes[index] );
}
view.SaveState();
}
public void Frame() => Frame( EditedExtent() );
public void Frame( BBox bounds ) {
foreach ( var pane in Panes() ) {
if ( AxisOf( pane ) != ArchViewAxis.Free ) {
FrameOn( pane, bounds );
}
}
}
void FrameOn( SceneViewportWidget viewport, BBox bounds ) {
var size = viewport.Renderer?.Size ?? new Vector2( 1280f, 800f );
var aspect = size.y > 1f ? size.x / size.y : 1.6f;
viewport.State.CameraPosition = bounds.Center;
viewport.State.CameraOrthoHeight = ArchShot.Framing( bounds, viewport.State.CameraRotation, aspect, 0.12f );
}
BBox EditedExtent() => Extent( EditedBuilding() );
public float Depth( ArchViewAxis axis ) {
if ( SectionDepth is { } chosen ) {
return Grid.Height( chosen );
}
var centre = EditedExtent().Center;
return Grid.Height( axis == ArchViewAxis.Front ? centre.x : centre.y );
}
}