{"TotalCount":10,"Files":[{"Ident":"ali3nsystems.lib-metrify","Path":"MetersAttribute.cs","FileName":"MetersAttribute.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338943,"Code":"using System;\n\nnamespace Metrify;\n\n[AttributeUsage( AttributeTargets.Property | AttributeTargets.Field )]\npublic class MetersAttribute : Attribute\n{\n}\n"},{"Ident":"ali3nsystems.lib-metrify","Path":"Editor/MetricRowInjector.cs","FileName":"MetricRowInjector.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":338943,"Code":"using System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.CompilerServices;\nusing Editor;\nusing Editor.Inspectors;\nusing Sandbox;\n\nnamespace Metrify;\n\npublic static class MetricRowInjector\n{\n\tprivate static readonly ConditionalWeakTable\u003CWidget, object\u003E _injected = new();\n\tprivate static RealTimeSince _sinceLastScan;\n\n\t[EditorEvent.Frame]\n\tpublic static void OnFrame()\n\t{\n\t\t// The UI tree only changes on selection/edit; a 10Hz scan is plenty and keeps the\n\t\t// recursive traversal off the per-frame cost.\n\t\tif ( _sinceLastScan \u003C 0.1f ) return;\n\t\t_sinceLastScan = 0;\n\n\t\tvar root = EditorWindow;\n\t\tif ( !root.IsValid() ) return;\n\n\t\tforeach ( var inspector in FindDescendants\u003CGameObjectInspector\u003E( root ) )\n\t\t{\n\t\t\tvar transformSheet = FindDescendants\u003CComponentSheet\u003E( inspector )\n\t\t\t\t.FirstOrDefault( s =\u003E s.Header?.Title == \u0022Transform\u0022 );\n\t\t\tif ( !transformSheet.IsValid() ) continue;\n\n\t\t\tvar editorWidget = FindDescendants\u003CComponentEditorWidget\u003E( transformSheet ).FirstOrDefault();\n\t\t\tif ( !editorWidget.IsValid() || editorWidget.Layout is null ) continue;\n\n\t\t\tif ( _injected.TryGetValue( editorWidget, out _ ) ) continue;\n\n\t\t\tvar metric = BuildMetricProperty( inspector.SerializedObject );\n\t\t\tif ( metric is null ) continue;\n\n\t\t\tvar sheet = new ControlSheet();\n\t\t\tsheet.IncludePropertyNames = true;\n\t\t\tsheet.AddRow( metric );\n\n\t\t\teditorWidget.Layout.Add( sheet );\n\t\t\t_injected.Add( editorWidget, null );\n\t\t}\n\t}\n\n\tprivate static SerializedProperty BuildMetricProperty( SerializedObject inspectorObject )\n\t{\n\t\tvar transformProp = inspectorObject?.GetProperty( nameof( GameObject.Transform ) );\n\t\tif ( transformProp is null ) return null;\n\t\tif ( !transformProp.TryGetAsObject( out var transform ) ) return null;\n\n\t\tvar localPosition = transform.GetProperty( \u0022LocalPosition\u0022 );\n\t\tif ( localPosition is null ) return null;\n\n\t\tvar customizable = localPosition.GetCustomizable();\n\t\tcustomizable.SetDisplayName( \u0022Local Position (meters)\u0022 );\n\t\tcustomizable.AddAttribute( new MetersAttribute() );\n\n\t\treturn customizable;\n\t}\n\n\tprivate static IEnumerable\u003CT\u003E FindDescendants\u003CT\u003E( Widget root ) where T : Widget\n\t{\n\t\tforeach ( var child in root.Children )\n\t\t{\n\t\t\tif ( !child.IsValid() ) continue;\n\n\t\t\tif ( child is T match )\n\t\t\t\tyield return match;\n\n\t\t\tforeach ( var descendant in FindDescendants\u003CT\u003E( child ) )\n\t\t\t\tyield return descendant;\n\t\t}\n\t}\n}\n"},{"Ident":"ali3nsystems.lib-metrify","Path":"Code/MetersAttribute.cs","FileName":"MetersAttribute.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338943,"Code":"using System;\n\nnamespace Metrify;\n\n[AttributeUsage( AttributeTargets.Property | AttributeTargets.Field )]\npublic class MetersAttribute : Attribute\n{\n}\n"},{"Ident":"ali3nsystems.lib-metrify","Path":".obj/__compiler_extra.cs","FileName":"__compiler_extra.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338943,"Code":"global using static Sandbox.Internal.GlobalGameNamespace;\r\nglobal using Microsoft.AspNetCore.Components;\r\nglobal using Microsoft.AspNetCore.Components.Rendering;\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022AddonTitle\u0022, \u0022Metrify\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022AddonIdent\u0022, \u0022lib-metrify\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022OrgIdent\u0022, \u0022ali3nsystems\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022Ident\u0022, \u0022ali3nsystems.lib-metrify\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022EngineVersion\u0022, \u002228\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022EngineMinorVersion\u0022, \u00221\u0022 )]\r\n\r\n[assembly: System.Runtime.Versioning.TargetFramework( \u0022.NETCoreApp,Version=v9.0\u0022, FrameworkDisplayName = \u0022.NET 9.0\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \u0022CompileTime\u0022, \u00222026-08-05T13:23:28.7534796Z\u0022 )]\r\n[assembly: global::System.Reflection.AssemblyVersion(\u00220.0.117.0\u0022)]\r\n[assembly: global::System.Reflection.AssemblyFileVersion(\u00220.0.117.0\u0022)]"},{"Ident":"ali3nsystems.lib-metrify","Path":"Code/UnitExtensions.cs","FileName":"UnitExtensions.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338943,"Code":"using Sandbox;\n\nnamespace Metrify;\npublic static class UnitExtensions\n{\n\t/// \u003Csummary\u003EInches (world units) to metres.\u003C/summary\u003E\n\tpublic static float ToMeters( this float inches ) =\u003E Units.ToMeters( inches );\n\n\t/// \u003Csummary\u003EMetres to inches (world units).\u003C/summary\u003E\n\tpublic static float ToWorldUnits( this float meters ) =\u003E Units.ToInches( meters );\n\n\t/// \u003Csummary\u003EInches (world units) to metres.\u003C/summary\u003E\n\tpublic static Vector3 ToMeters( this Vector3 inches ) =\u003E Units.ToMeters( inches );\n\n\t/// \u003Csummary\u003EMetres to inches (world units).\u003C/summary\u003E\n\tpublic static Vector3 ToWorldUnits( this Vector3 meters ) =\u003E Units.ToInches( meters );\n\n\t/// \u003Csummary\u003EThis object\u0027s world position in metres.\u003C/summary\u003E\n\tpublic static Vector3 WorldPositionMeters( this GameObject go ) =\u003E Units.ToMeters( go.WorldPosition );\n\n\t/// \u003Csummary\u003EThis object\u0027s local position in metres.\u003C/summary\u003E\n\tpublic static Vector3 LocalPositionMeters( this GameObject go ) =\u003E Units.ToMeters( go.LocalPosition );\n\n\t/// \u003Csummary\u003ESet this object\u0027s world position from metres.\u003C/summary\u003E\n\tpublic static void SetWorldPositionMeters( this GameObject go, Vector3 meters )\n\t\t=\u003E go.WorldPosition = Units.ToInches( meters );\n\n\t/// \u003Csummary\u003ESet this object\u0027s local position from metres.\u003C/summary\u003E\n\tpublic static void SetLocalPositionMeters( this GameObject go, Vector3 meters )\n\t\t=\u003E go.LocalPosition = Units.ToInches( meters );\n}\n"},{"Ident":"ali3nsystems.lib-metrify","Path":"Editor/MeterProperty.cs","FileName":"MeterProperty.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":338943,"Code":"using Sandbox;\n\nnamespace Metrify;\n\ninternal sealed class MeterFloatProperty : SerializedProperty.Proxy\n{\n\tprivate readonly SerializedProperty _target;\n\tprotected override SerializedProperty ProxyTarget =\u003E _target;\n\n\tpublic MeterFloatProperty( SerializedProperty target )\n\t{\n\t\t_target = target;\n\t}\n\n\tpublic override T GetValue\u003CT\u003E( T defaultValue = default )\n\t{\n\t\tif ( typeof( T ) == typeof( float ) || typeof( T ) == typeof( double ) ||\n\t\t     typeof( T ) == typeof( string ) || typeof( T ) == typeof( object ) )\n\t\t{\n\t\t\tvar meters = Units.ToMeters( _target.GetValue\u003Cfloat\u003E() );\n\n\t\t\tif ( typeof( T ) == typeof( float ) ) return (T)(object)meters;\n\t\t\tif ( typeof( T ) == typeof( double ) ) return (T)(object)(double)meters;\n\t\t\tif ( typeof( T ) == typeof( string ) ) return (T)(object)meters.ToString( \u00220.###\u0022 );\n\t\t\treturn (T)(object)meters;\n\t\t}\n\n\t\treturn _target.GetValue( defaultValue );\n\t}\n\n\tpublic override void SetValue\u003CT\u003E( T value )\n\t{\n\t\tswitch ( value )\n\t\t{\n\t\t\tcase float meters:\n\t\t\t\t_target.SetValue( Units.ToInches( meters ) );\n\t\t\t\treturn;\n\n\t\t\tcase double meters:\n\t\t\t\t_target.SetValue( Units.ToInches( (float)meters ) );\n\t\t\t\treturn;\n\n\t\t\tcase string text when float.TryParse( text, out var meters ):\n\t\t\t\t_target.SetValue( Units.ToInches( meters ) );\n\t\t\t\treturn;\n\t\t}\n\n\t\t_target.SetValue( value );\n\t}\n}\n"},{"Ident":"ali3nsystems.lib-metrify","Path":"Units.cs","FileName":"Units.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338943,"Code":"namespace Metrify;\npublic static class Units\n{\n\tpublic const float InchesToMeters = 0.0254f;\n\tpublic const float MetersToInches = 1f / InchesToMeters;\n\n\tpublic static float ToMeters( float inches ) =\u003E inches * InchesToMeters;\n\tpublic static float ToInches( float meters ) =\u003E meters * MetersToInches;\n\n\tpublic static Vector3 ToMeters( Vector3 inches ) =\u003E inches * InchesToMeters;\n\tpublic static Vector3 ToInches( Vector3 meters ) =\u003E meters * MetersToInches;\n}\n"},{"Ident":"ali3nsystems.lib-metrify","Path":"UnitExtensions.cs","FileName":"UnitExtensions.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338943,"Code":"using Sandbox;\n\nnamespace Metrify;\npublic static class UnitExtensions\n{\n\t/// \u003Csummary\u003EInches (world units) to metres.\u003C/summary\u003E\n\tpublic static float ToMeters( this float inches ) =\u003E Units.ToMeters( inches );\n\n\t/// \u003Csummary\u003EMetres to inches (world units).\u003C/summary\u003E\n\tpublic static float ToWorldUnits( this float meters ) =\u003E Units.ToInches( meters );\n\n\t/// \u003Csummary\u003EInches (world units) to metres.\u003C/summary\u003E\n\tpublic static Vector3 ToMeters( this Vector3 inches ) =\u003E Units.ToMeters( inches );\n\n\t/// \u003Csummary\u003EMetres to inches (world units).\u003C/summary\u003E\n\tpublic static Vector3 ToWorldUnits( this Vector3 meters ) =\u003E Units.ToInches( meters );\n\n\t/// \u003Csummary\u003EThis object\u0027s world position in metres.\u003C/summary\u003E\n\tpublic static Vector3 WorldPositionMeters( this GameObject go ) =\u003E Units.ToMeters( go.WorldPosition );\n\n\t/// \u003Csummary\u003EThis object\u0027s local position in metres.\u003C/summary\u003E\n\tpublic static Vector3 LocalPositionMeters( this GameObject go ) =\u003E Units.ToMeters( go.LocalPosition );\n\n\t/// \u003Csummary\u003ESet this object\u0027s world position from metres.\u003C/summary\u003E\n\tpublic static void SetWorldPositionMeters( this GameObject go, Vector3 meters )\n\t\t=\u003E go.WorldPosition = Units.ToInches( meters );\n\n\t/// \u003Csummary\u003ESet this object\u0027s local position from metres.\u003C/summary\u003E\n\tpublic static void SetLocalPositionMeters( this GameObject go, Vector3 meters )\n\t\t=\u003E go.LocalPosition = Units.ToInches( meters );\n}\n"},{"Ident":"ali3nsystems.lib-metrify","Path":"Code/Units.cs","FileName":"Units.cs","PackageType":"library","CodeKind":"Game","AssetVersionId":338943,"Code":"namespace Metrify;\npublic static class Units\n{\n\tpublic const float InchesToMeters = 0.0254f;\n\tpublic const float MetersToInches = 1f / InchesToMeters;\n\n\tpublic static float ToMeters( float inches ) =\u003E inches * InchesToMeters;\n\tpublic static float ToInches( float meters ) =\u003E meters * MetersToInches;\n\n\tpublic static Vector3 ToMeters( Vector3 inches ) =\u003E inches * InchesToMeters;\n\tpublic static Vector3 ToInches( Vector3 meters ) =\u003E meters * MetersToInches;\n}\n"},{"Ident":"ali3nsystems.lib-metrify","Path":"Editor/MeterControlWidget.cs","FileName":"MeterControlWidget.cs","PackageType":"library","CodeKind":"Editor","AssetVersionId":338943,"Code":"using Editor;\nusing Sandbox;\n\nnamespace Metrify;\n\n/// \u003Csummary\u003E\n/// Draws a [Meters] float in metres, with an \u0022m\u0022 suffix label.\n/// \u003C/summary\u003E\n[CustomEditor( typeof( float ), WithAllAttributes = new[] { typeof( MetersAttribute ) } )]\npublic sealed class MeterFloatControlWidget : ControlWidget\n{\n\tpublic override bool SupportsMultiEdit =\u003E true;\n\n\tprivate readonly FloatControlWidget _control;\n\n\tpublic MeterFloatControlWidget( SerializedProperty property ) : base( property )\n\t{\n\t\tHorizontalSizeMode = SizeMode.CanGrow | SizeMode.Expand;\n\n\t\tLayout = Layout.Row();\n\t\tLayout.Spacing = 2;\n\n\t\t_control = Layout.Add( new FloatControlWidget( new MeterFloatProperty( property ) )\n\t\t{\n\t\t\tLabel = \u0022m\u0022,\n\t\t\tHighlightColor = Theme.Green,\n\t\t\tToolTip = \u0022Metres \u2014 stored as inches (s\u0026box world units)\u0022\n\t\t}, 1 );\n\t}\n\n\tpublic override void StartEditing() =\u003E _control?.StartEditing();\n\n\tprotected override void OnPaint()\n\t{\n\t\t// child widget paints itself\n\t}\n\n\tprotected override void PaintUnder()\n\t{\n\t\t// nothing\n\t}\n}\n\n/// \u003Csummary\u003E\n/// Draws a [Meters] Vector3 with each axis in metres.\n/// \u003C/summary\u003E\n[CustomEditor( typeof( Vector3 ), WithAllAttributes = new[] { typeof( MetersAttribute ) } )]\npublic sealed class MeterVectorControlWidget : ControlWidget\n{\n\tpublic override bool SupportsMultiEdit =\u003E true;\n\n\tprivate FloatControlWidget _first;\n\n\tpublic MeterVectorControlWidget( SerializedProperty property ) : base( property )\n\t{\n\t\tHorizontalSizeMode = SizeMode.CanGrow | SizeMode.Expand;\n\n\t\tif ( !property.TryGetAsObject( out var obj ) )\n\t\t{\n\t\t\tLog.Warning( $\u0022[Meters] could not read {property.Name} as an object\u0022 );\n\t\t\treturn;\n\t\t}\n\n\t\tLayout = Layout.Row();\n\t\tLayout.Spacing = 2;\n\n\t\t_first = AddAxis( obj, \u0022x\u0022, Theme.Red, \u0022X\u0022 );\n\t\tAddAxis( obj, \u0022y\u0022, Theme.Green, \u0022Y\u0022 );\n\t\tAddAxis( obj, \u0022z\u0022, Theme.Blue, \u0022Z\u0022 );\n\t}\n\n\tprivate FloatControlWidget AddAxis( SerializedObject obj, string name, Color color, string label )\n\t{\n\t\tvar axis = obj.GetProperty( name );\n\t\tif ( axis is null ) return null;\n\n\t\tvar control = Layout.Add( new FloatControlWidget( new MeterFloatProperty( axis ) )\n\t\t{\n\t\t\tLabel = label,\n\t\t\tHighlightColor = color,\n\t\t\tToolTip = \u0022Metres \u2014 stored as inches (s\u0026box world units)\u0022\n\t\t}, 1 );\n\n\t\tcontrol.MinimumWidth = Theme.RowHeight;\n\t\tcontrol.HorizontalSizeMode = SizeMode.CanGrow | SizeMode.Expand;\n\n\t\treturn control;\n\t}\n\n\tpublic override void StartEditing() =\u003E _first?.StartEditing();\n\n\tprotected override void OnPaint()\n\t{\n\t\t// child widgets paint themselves\n\t}\n\n\tprotected override void PaintUnder()\n\t{\n\t\t// nothing\n\t}\n}\n"}]}