🔍 s&box Package Code Search

Search C# source code, UI razor templates, shaders, and configs across s&box packages.

Showing code results for query: * (10 total matches found)
ali3nsystems.lib-metrify / MetersAttribute.cs
Game library
using System;

namespace Metrify;

[AttributeUsage( AttributeTargets.Property | AttributeTargets.Field )]
public class MetersAttribute : Attribute
{
}
ali3nsystems.lib-metrify / Editor/MetricRowInjector.cs
Editor library
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using Editor;
using Editor.Inspectors;
using Sandbox;

namespace Metrify;

public static class MetricRowInjector
{
	private static readonly ConditionalWeakTable<Widget, object> _injected = new();
	private static RealTimeSince _sinceLastScan;

	[EditorEvent.Frame]
	public static void OnFrame()
	{
		// The UI tree only changes on selection/edit; a 10Hz scan is plenty and keeps the
		// recursive traversal off the per-frame cost.
		if ( _sinceLastScan < 0.1f ) return;
		_sinceLastScan = 0;

		var root = EditorWindow;
		if ( !root.IsValid() ) return;

		foreach ( var inspector in FindDescendants<GameObjectInspector>( root ) )
		{
			var transformSheet = FindDescendants<ComponentSheet>( inspector )
				.FirstOrDefault( s => s.Header?.Title == "Transform" );
			if ( !transformSheet.IsValid() ) continue;

			var editorWidget = FindDescendants<ComponentEditorWidget>( transformSheet ).FirstOrDefault();
			if ( !editorWidget.IsValid() || editorWidget.Layout is null ) continue;

			if ( _injected.TryGetValue( editorWidget, out _ ) ) continue;

			var metric = BuildMetricProperty( inspector.SerializedObject );
			if ( metric is null ) continue;

			var sheet = new ControlSheet();
			sheet.IncludePropertyNames = true;
			sheet.AddRow( metric );

			editorWidget.Layout.Add( sheet );
			_injected.Add( editorWidget, null );
		}
	}

	private static SerializedProperty BuildMetricProperty( SerializedObject inspectorObject )
	{
		var transformProp = inspectorObject?.GetProperty( nameof( GameObject.Transform ) );
		if ( transformProp is null ) return null;
		if ( !transformProp.TryGetAsObject( out var transform ) ) return null;

		var localPosition = transform.GetProperty( "LocalPosition" );
		if ( localPosition is null ) return null;

		var customizable = localPosition.GetCustomizable();
		customizable.SetDisplayName( "Local Position (meters)" );
		customizable.AddAttribute( new MetersAttribute() );

		return customizable;
	}

	private static IEnumerable<T> FindDescendants<T>( Widget root ) where T : Widget
	{
		foreach ( var child in root.Children )
		{
			if ( !child.IsValid() ) continue;

			if ( child is T match )
				yield return match;

			foreach ( var descendant in FindDescendants<T>( child ) )
				yield return descendant;
		}
	}
}
ali3nsystems.lib-metrify / Code/MetersAttribute.cs
Game library
using System;

namespace Metrify;

[AttributeUsage( AttributeTargets.Property | AttributeTargets.Field )]
public class MetersAttribute : Attribute
{
}
ali3nsystems.lib-metrify / .obj/__compiler_extra.cs
Game library
global using static Sandbox.Internal.GlobalGameNamespace;
global using Microsoft.AspNetCore.Components;
global using Microsoft.AspNetCore.Components.Rendering;
[assembly: global::System.Reflection.AssemblyMetadata( "AddonTitle", "Metrify" )]
[assembly: global::System.Reflection.AssemblyMetadata( "AddonIdent", "lib-metrify" )]
[assembly: global::System.Reflection.AssemblyMetadata( "OrgIdent", "ali3nsystems" )]
[assembly: global::System.Reflection.AssemblyMetadata( "Ident", "ali3nsystems.lib-metrify" )]
[assembly: global::System.Reflection.AssemblyMetadata( "EngineVersion", "28" )]
[assembly: global::System.Reflection.AssemblyMetadata( "EngineMinorVersion", "1" )]

[assembly: System.Runtime.Versioning.TargetFramework( ".NETCoreApp,Version=v9.0", FrameworkDisplayName = ".NET 9.0" )]
[assembly: global::System.Reflection.AssemblyMetadata( "CompileTime", "2026-08-05T13:23:28.7534796Z" )]
[assembly: global::System.Reflection.AssemblyVersion("0.0.117.0")]
[assembly: global::System.Reflection.AssemblyFileVersion("0.0.117.0")]
ali3nsystems.lib-metrify / Code/UnitExtensions.cs
Game library
using Sandbox;

namespace Metrify;
public static class UnitExtensions
{
	/// <summary>Inches (world units) to metres.</summary>
	public static float ToMeters( this float inches ) => Units.ToMeters( inches );

	/// <summary>Metres to inches (world units).</summary>
	public static float ToWorldUnits( this float meters ) => Units.ToInches( meters );

	/// <summary>Inches (world units) to metres.</summary>
	public static Vector3 ToMeters( this Vector3 inches ) => Units.ToMeters( inches );

	/// <summary>Metres to inches (world units).</summary>
	public static Vector3 ToWorldUnits( this Vector3 meters ) => Units.ToInches( meters );

	/// <summary>This object's world position in metres.</summary>
	public static Vector3 WorldPositionMeters( this GameObject go ) => Units.ToMeters( go.WorldPosition );

	/// <summary>This object's local position in metres.</summary>
	public static Vector3 LocalPositionMeters( this GameObject go ) => Units.ToMeters( go.LocalPosition );

	/// <summary>Set this object's world position from metres.</summary>
	public static void SetWorldPositionMeters( this GameObject go, Vector3 meters )
		=> go.WorldPosition = Units.ToInches( meters );

	/// <summary>Set this object's local position from metres.</summary>
	public static void SetLocalPositionMeters( this GameObject go, Vector3 meters )
		=> go.LocalPosition = Units.ToInches( meters );
}
ali3nsystems.lib-metrify / Editor/MeterProperty.cs
Editor library
using Sandbox;

namespace Metrify;

internal sealed class MeterFloatProperty : SerializedProperty.Proxy
{
	private readonly SerializedProperty _target;
	protected override SerializedProperty ProxyTarget => _target;

	public MeterFloatProperty( SerializedProperty target )
	{
		_target = target;
	}

	public override T GetValue<T>( T defaultValue = default )
	{
		if ( typeof( T ) == typeof( float ) || typeof( T ) == typeof( double ) ||
		     typeof( T ) == typeof( string ) || typeof( T ) == typeof( object ) )
		{
			var meters = Units.ToMeters( _target.GetValue<float>() );

			if ( typeof( T ) == typeof( float ) ) return (T)(object)meters;
			if ( typeof( T ) == typeof( double ) ) return (T)(object)(double)meters;
			if ( typeof( T ) == typeof( string ) ) return (T)(object)meters.ToString( "0.###" );
			return (T)(object)meters;
		}

		return _target.GetValue( defaultValue );
	}

	public override void SetValue<T>( T value )
	{
		switch ( value )
		{
			case float meters:
				_target.SetValue( Units.ToInches( meters ) );
				return;

			case double meters:
				_target.SetValue( Units.ToInches( (float)meters ) );
				return;

			case string text when float.TryParse( text, out var meters ):
				_target.SetValue( Units.ToInches( meters ) );
				return;
		}

		_target.SetValue( value );
	}
}
ali3nsystems.lib-metrify / Units.cs
Game library
namespace Metrify;
public static class Units
{
	public const float InchesToMeters = 0.0254f;
	public const float MetersToInches = 1f / InchesToMeters;

	public static float ToMeters( float inches ) => inches * InchesToMeters;
	public static float ToInches( float meters ) => meters * MetersToInches;

	public static Vector3 ToMeters( Vector3 inches ) => inches * InchesToMeters;
	public static Vector3 ToInches( Vector3 meters ) => meters * MetersToInches;
}
ali3nsystems.lib-metrify / UnitExtensions.cs
Game library
using Sandbox;

namespace Metrify;
public static class UnitExtensions
{
	/// <summary>Inches (world units) to metres.</summary>
	public static float ToMeters( this float inches ) => Units.ToMeters( inches );

	/// <summary>Metres to inches (world units).</summary>
	public static float ToWorldUnits( this float meters ) => Units.ToInches( meters );

	/// <summary>Inches (world units) to metres.</summary>
	public static Vector3 ToMeters( this Vector3 inches ) => Units.ToMeters( inches );

	/// <summary>Metres to inches (world units).</summary>
	public static Vector3 ToWorldUnits( this Vector3 meters ) => Units.ToInches( meters );

	/// <summary>This object's world position in metres.</summary>
	public static Vector3 WorldPositionMeters( this GameObject go ) => Units.ToMeters( go.WorldPosition );

	/// <summary>This object's local position in metres.</summary>
	public static Vector3 LocalPositionMeters( this GameObject go ) => Units.ToMeters( go.LocalPosition );

	/// <summary>Set this object's world position from metres.</summary>
	public static void SetWorldPositionMeters( this GameObject go, Vector3 meters )
		=> go.WorldPosition = Units.ToInches( meters );

	/// <summary>Set this object's local position from metres.</summary>
	public static void SetLocalPositionMeters( this GameObject go, Vector3 meters )
		=> go.LocalPosition = Units.ToInches( meters );
}
ali3nsystems.lib-metrify / Code/Units.cs
Game library
namespace Metrify;
public static class Units
{
	public const float InchesToMeters = 0.0254f;
	public const float MetersToInches = 1f / InchesToMeters;

	public static float ToMeters( float inches ) => inches * InchesToMeters;
	public static float ToInches( float meters ) => meters * MetersToInches;

	public static Vector3 ToMeters( Vector3 inches ) => inches * InchesToMeters;
	public static Vector3 ToInches( Vector3 meters ) => meters * MetersToInches;
}
ali3nsystems.lib-metrify / Editor/MeterControlWidget.cs
Editor library
using Editor;
using Sandbox;

namespace Metrify;

/// <summary>
/// Draws a [Meters] float in metres, with an "m" suffix label.
/// </summary>
[CustomEditor( typeof( float ), WithAllAttributes = new[] { typeof( MetersAttribute ) } )]
public sealed class MeterFloatControlWidget : ControlWidget
{
	public override bool SupportsMultiEdit => true;

	private readonly FloatControlWidget _control;

	public MeterFloatControlWidget( SerializedProperty property ) : base( property )
	{
		HorizontalSizeMode = SizeMode.CanGrow | SizeMode.Expand;

		Layout = Layout.Row();
		Layout.Spacing = 2;

		_control = Layout.Add( new FloatControlWidget( new MeterFloatProperty( property ) )
		{
			Label = "m",
			HighlightColor = Theme.Green,
			ToolTip = "Metres — stored as inches (s&box world units)"
		}, 1 );
	}

	public override void StartEditing() => _control?.StartEditing();

	protected override void OnPaint()
	{
		// child widget paints itself
	}

	protected override void PaintUnder()
	{
		// nothing
	}
}

/// <summary>
/// Draws a [Meters] Vector3 with each axis in metres.
/// </summary>
[CustomEditor( typeof( Vector3 ), WithAllAttributes = new[] { typeof( MetersAttribute ) } )]
public sealed class MeterVectorControlWidget : ControlWidget
{
	public override bool SupportsMultiEdit => true;

	private FloatControlWidget _first;

	public MeterVectorControlWidget( SerializedProperty property ) : base( property )
	{
		HorizontalSizeMode = SizeMode.CanGrow | SizeMode.Expand;

		if ( !property.TryGetAsObject( out var obj ) )
		{
			Log.Warning( $"[Meters] could not read {property.Name} as an object" );
			return;
		}

		Layout = Layout.Row();
		Layout.Spacing = 2;

		_first = AddAxis( obj, "x", Theme.Red, "X" );
		AddAxis( obj, "y", Theme.Green, "Y" );
		AddAxis( obj, "z", Theme.Blue, "Z" );
	}

	private FloatControlWidget AddAxis( SerializedObject obj, string name, Color color, string label )
	{
		var axis = obj.GetProperty( name );
		if ( axis is null ) return null;

		var control = Layout.Add( new FloatControlWidget( new MeterFloatProperty( axis ) )
		{
			Label = label,
			HighlightColor = color,
			ToolTip = "Metres — stored as inches (s&box world units)"
		}, 1 );

		control.MinimumWidth = Theme.RowHeight;
		control.HorizontalSizeMode = SizeMode.CanGrow | SizeMode.Expand;

		return control;
	}

	public override void StartEditing() => _first?.StartEditing();

	protected override void OnPaint()
	{
		// child widgets paint themselves
	}

	protected override void PaintUnder()
	{
		// nothing
	}
}
Debug: View Raw JSON Response
{
    "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<Widget, object> _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 < 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<GameObjectInspector>( root ) )\n\t\t{\n\t\t\tvar transformSheet = FindDescendants<ComponentSheet>( inspector )\n\t\t\t\t.FirstOrDefault( s => s.Header?.Title == \"Transform\" );\n\t\t\tif ( !transformSheet.IsValid() ) continue;\n\n\t\t\tvar editorWidget = FindDescendants<ComponentEditorWidget>( 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( \"LocalPosition\" );\n\t\tif ( localPosition is null ) return null;\n\n\t\tvar customizable = localPosition.GetCustomizable();\n\t\tcustomizable.SetDisplayName( \"Local Position (meters)\" );\n\t\tcustomizable.AddAttribute( new MetersAttribute() );\n\n\t\treturn customizable;\n\t}\n\n\tprivate static IEnumerable<T> FindDescendants<T>( 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<T>( 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( \"AddonTitle\", \"Metrify\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"AddonIdent\", \"lib-metrify\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"OrgIdent\", \"ali3nsystems\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"Ident\", \"ali3nsystems.lib-metrify\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"EngineVersion\", \"28\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"EngineMinorVersion\", \"1\" )]\r\n\r\n[assembly: System.Runtime.Versioning.TargetFramework( \".NETCoreApp,Version=v9.0\", FrameworkDisplayName = \".NET 9.0\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"CompileTime\", \"2026-08-05T13:23:28.7534796Z\" )]\r\n[assembly: global::System.Reflection.AssemblyVersion(\"0.0.117.0\")]\r\n[assembly: global::System.Reflection.AssemblyFileVersion(\"0.0.117.0\")]"
        },
        {
            "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/// <summary>Inches (world units) to metres.</summary>\n\tpublic static float ToMeters( this float inches ) => Units.ToMeters( inches );\n\n\t/// <summary>Metres to inches (world units).</summary>\n\tpublic static float ToWorldUnits( this float meters ) => Units.ToInches( meters );\n\n\t/// <summary>Inches (world units) to metres.</summary>\n\tpublic static Vector3 ToMeters( this Vector3 inches ) => Units.ToMeters( inches );\n\n\t/// <summary>Metres to inches (world units).</summary>\n\tpublic static Vector3 ToWorldUnits( this Vector3 meters ) => Units.ToInches( meters );\n\n\t/// <summary>This object's world position in metres.</summary>\n\tpublic static Vector3 WorldPositionMeters( this GameObject go ) => Units.ToMeters( go.WorldPosition );\n\n\t/// <summary>This object's local position in metres.</summary>\n\tpublic static Vector3 LocalPositionMeters( this GameObject go ) => Units.ToMeters( go.LocalPosition );\n\n\t/// <summary>Set this object's world position from metres.</summary>\n\tpublic static void SetWorldPositionMeters( this GameObject go, Vector3 meters )\n\t\t=> go.WorldPosition = Units.ToInches( meters );\n\n\t/// <summary>Set this object's local position from metres.</summary>\n\tpublic static void SetLocalPositionMeters( this GameObject go, Vector3 meters )\n\t\t=> 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 => _target;\n\n\tpublic MeterFloatProperty( SerializedProperty target )\n\t{\n\t\t_target = target;\n\t}\n\n\tpublic override T GetValue<T>( 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<float>() );\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( \"0.###\" );\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<T>( 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 ) => inches * InchesToMeters;\n\tpublic static float ToInches( float meters ) => meters * MetersToInches;\n\n\tpublic static Vector3 ToMeters( Vector3 inches ) => inches * InchesToMeters;\n\tpublic static Vector3 ToInches( Vector3 meters ) => 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/// <summary>Inches (world units) to metres.</summary>\n\tpublic static float ToMeters( this float inches ) => Units.ToMeters( inches );\n\n\t/// <summary>Metres to inches (world units).</summary>\n\tpublic static float ToWorldUnits( this float meters ) => Units.ToInches( meters );\n\n\t/// <summary>Inches (world units) to metres.</summary>\n\tpublic static Vector3 ToMeters( this Vector3 inches ) => Units.ToMeters( inches );\n\n\t/// <summary>Metres to inches (world units).</summary>\n\tpublic static Vector3 ToWorldUnits( this Vector3 meters ) => Units.ToInches( meters );\n\n\t/// <summary>This object's world position in metres.</summary>\n\tpublic static Vector3 WorldPositionMeters( this GameObject go ) => Units.ToMeters( go.WorldPosition );\n\n\t/// <summary>This object's local position in metres.</summary>\n\tpublic static Vector3 LocalPositionMeters( this GameObject go ) => Units.ToMeters( go.LocalPosition );\n\n\t/// <summary>Set this object's world position from metres.</summary>\n\tpublic static void SetWorldPositionMeters( this GameObject go, Vector3 meters )\n\t\t=> go.WorldPosition = Units.ToInches( meters );\n\n\t/// <summary>Set this object's local position from metres.</summary>\n\tpublic static void SetLocalPositionMeters( this GameObject go, Vector3 meters )\n\t\t=> 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 ) => inches * InchesToMeters;\n\tpublic static float ToInches( float meters ) => meters * MetersToInches;\n\n\tpublic static Vector3 ToMeters( Vector3 inches ) => inches * InchesToMeters;\n\tpublic static Vector3 ToInches( Vector3 meters ) => 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/// <summary>\n/// Draws a [Meters] float in metres, with an \"m\" suffix label.\n/// </summary>\n[CustomEditor( typeof( float ), WithAllAttributes = new[] { typeof( MetersAttribute ) } )]\npublic sealed class MeterFloatControlWidget : ControlWidget\n{\n\tpublic override bool SupportsMultiEdit => 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 = \"m\",\n\t\t\tHighlightColor = Theme.Green,\n\t\t\tToolTip = \"Metres \u2014 stored as inches (s&box world units)\"\n\t\t}, 1 );\n\t}\n\n\tpublic override void StartEditing() => _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/// <summary>\n/// Draws a [Meters] Vector3 with each axis in metres.\n/// </summary>\n[CustomEditor( typeof( Vector3 ), WithAllAttributes = new[] { typeof( MetersAttribute ) } )]\npublic sealed class MeterVectorControlWidget : ControlWidget\n{\n\tpublic override bool SupportsMultiEdit => 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( $\"[Meters] could not read {property.Name} as an object\" );\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, \"x\", Theme.Red, \"X\" );\n\t\tAddAxis( obj, \"y\", Theme.Green, \"Y\" );\n\t\tAddAxis( obj, \"z\", Theme.Blue, \"Z\" );\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 = \"Metres \u2014 stored as inches (s&box world units)\"\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() => _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"
        }
    ]
}