DiamondPanel.Perimeters.cs
using System;
using Sandbox;
namespace Diamonds;
public sealed partial class DiamondPanel
{
// Editor A/B comparison; never affects gameplay.
bool drawingReferencePerimeters = false;
Vector2[] borderPoints, borderMiters;
static Vector2 PerimeterDirection( Vector2 delta )
{
// Rigid translation can round world-space subtraction differently. Use the
// exact diamond slope so rebuilt and translated caches retain identical joins.
if ( delta.x != 0 && delta.y != 0 &&
MathF.Abs( MathF.Abs( delta.x ) * DiamondBoard.Height - MathF.Abs( delta.y ) * DiamondBoard.Width ) < 0.05f )
return new Vector2( MathF.CopySign( DiamondBoard.Width, delta.x ), MathF.CopySign( DiamondBoard.Height, delta.y ) ).Normal;
return delta.Normal;
}
static Vector2[] BuildPerimeterMiters( ReadOnlySpan<Vector2> points, bool closed )
{
if ( points.Length < 2 ) return null;
var offsets = new Vector2[points.Length];
for ( int i = 0; i < points.Length; i++ )
{
var incoming = PerimeterDirection( points[i] - points[(i + points.Length - 1) % points.Length] );
var outgoing = PerimeterDirection( points[(i + 1) % points.Length] - points[i] );
if ( !closed && i == 0 ) incoming = outgoing;
if ( !closed && i == points.Length - 1 ) outgoing = incoming;
if ( incoming.LengthSquared == 0 || outgoing.LengthSquared == 0 ) return null;
float denominator = 1 + Vector2.Dot( incoming, outgoing );
if ( denominator <= 0.00001f ) return null;
var miter = new Vector2( -incoming.y - outgoing.y, incoming.x + outgoing.x ) / denominator;
// Keep Painter's original bevel fallback at extreme or degenerate corners.
if ( miter.Length > 4 ) return null;
offsets[i] = miter;
}
return offsets;
}
void DrawPerimeter( Painter p, ReadOnlySpan<Vector2> points, Vector2[] miters, Color color, float width, bool closed )
{
if ( drawingReferencePerimeters || miters is null )
{
p.Fill = Fill.None;
p.Stroke = Stroke.Solid( color, width ) with { Join = Stroke.LineJoin.Miter };
if ( closed ) p.Polygon( points ); else p.Line( points );
return;
}
p.Fill = color; p.Stroke = Stroke.None;
float half = width * 0.5f;
for ( int i = 0; i < points.Length - (closed ? 0 : 1); i++ )
{
int next = (i + 1) % points.Length;
p.Quad( points[i] + miters[i] * half, points[next] + miters[next] * half,
points[next] - miters[next] * half, points[i] - miters[i] * half );
}
}
}