using UnityEngine;
// Shapes © Freya Holmér - https://twitter.com/FreyaHolmer/
// Website & Documentation - https://acegikmo.com/shapes/
namespace Shapes {
/// A Disc/Ring/Pie/Arc shape component
[ExecuteAlways]
[AddComponentMenu( "Shapes/Disc" )]
public partial class Disc : ShapeRenderer, IDashable {
/// Various color modes for disc shapes
public enum DiscColorMode {
/// A single color across the entire disc
Single,
/// A color gradient with one color in the center, and one color on the outer perimeter
Radial,
/// An angular color gradient
Angular,
/// A color gradient that is both angular and radial
Bilinear
}
/// Returns true if this is a ring or an arc
public bool HasThickness => type.HasThickness();
/// Returns true if this is a pie or an arc
public bool HasSector => type.HasSector();
[SerializeField] DiscType type = DiscType.Disc;
/// Get or set the type of this disc. Either a solid disc, ring, pie or an arc
public DiscType Type {
get => type;
set {
type = value;
UpdateMaterial();
ApplyProperties();
}
}
[SerializeField] DiscColorMode colorMode = DiscColorMode.Single;
/// The color gradient mode to use for this shape
public DiscColorMode ColorMode {
get => colorMode;
set {
colorMode = value;
ApplyProperties();
}
}
/// The color of this shape. The alpha channel is used for opacity/intensity in all blend modes
public override Color Color {
get => color;
set {
SetColor( ShapesMaterialUtils.propColor, color = value );
SetColor( ShapesMaterialUtils.propColorOuterStart, colorOuterStart = value );
SetColor( ShapesMaterialUtils.propColorInnerEnd, colorInnerEnd = value );
SetColorNow( ShapesMaterialUtils.propColorOuterEnd, colorOuterEnd = value );
}
}
/// Get or set the inner color at the start angle of the bilinear color mode
public Color ColorInnerStart {
get => color;
set => SetColorNow( ShapesMaterialUtils.propColor, color = value );
}
[SerializeField] [ShapesColorField( true )] Color colorOuterStart = Color.white;
/// Get or set the outer color at the start angle of the bilinear color mode
public Color ColorOuterStart {
get => colorOuterStart;
set => SetColorNow( ShapesMaterialUtils.propColorOuterStart, colorOuterStart = value );
}
[SerializeField] [ShapesColorField( true )] Color colorInnerEnd = Color.white;
/// Get or set the outer color at the end angle of the bilinear color mode
public Color ColorInnerEnd {
get => colorInnerEnd;
set => SetColorNow( ShapesMaterialUtils.propColorInnerEnd, colorInnerEnd = value );
}
[SerializeField] [ShapesColorField( true )] Color colorOuterEnd = Color.white;
/// Get or set the outer color at the end angle of the bilinear color mode
public Color ColorOuterEnd {
get => colorOuterEnd;
set => SetColorNow( ShapesMaterialUtils.propColorOuterEnd, colorOuterEnd = value );
}
/// Get or set the outer color when using the radial color mode
public Color ColorOuter {
get => ColorOuterStart;
set {
SetColor( ShapesMaterialUtils.propColorOuterStart, colorOuterStart = value );
SetColorNow( ShapesMaterialUtils.propColorOuterEnd, colorOuterEnd = value );
}
}
/// Get or set the inner color when using the radial color mode
public Color ColorInner {
get => color;
set {
SetColor( ShapesMaterialUtils.propColor, color = value );
SetColorNow( ShapesMaterialUtils.propColorInnerEnd, colorInnerEnd = value );
}
}
/// Get or set the start angle color when using the angular color mode
public Color ColorStart {
get => base.Color;
set {
SetColor( ShapesMaterialUtils.propColor, color = value );
SetColorNow( ShapesMaterialUtils.propColorOuterStart, colorOuterStart = value );
}
}
/// Get or set the end angle color when using the angular color mode
public Color ColorEnd {
get => colorInnerEnd;
set {
SetColor( ShapesMaterialUtils.propColorInnerEnd, colorInnerEnd = value );
SetColorNow( ShapesMaterialUtils.propColorOuterEnd, colorOuterEnd = value );
}
}
[SerializeField] DiscGeometry geometry = DiscGeometry.Flat2D;
/// Get or set the type of geometry used (flat or billboarded)
public DiscGeometry Geometry {
get => geometry;
set => SetIntNow( ShapesMaterialUtils.propAlignment, (int)( geometry = value ) );
}
// in-editor serialized field, suppressing "assigned but unused field" warning
#pragma warning disable CS0414
[SerializeField] AngularUnit angUnitInput = AngularUnit.Degrees;
#pragma warning restore CS0414
[SerializeField] float angRadiansStart = 0;
/// Get or set the start angle (in radians) of pies and arcs
public float AngRadiansStart {
get => angRadiansStart;
set => SetFloatNow( ShapesMaterialUtils.propAngStart, angRadiansStart = value );
}
[SerializeField] float angRadiansEnd = ShapesMath.TAU * ( 3 / 8f );
/// Get or set the end angle (in radians) of pies and arcs
public float AngRadiansEnd {
get => angRadiansEnd;
set => SetFloatNow( ShapesMaterialUtils.propAngEnd, angRadiansEnd = value );
}
[SerializeField] float radius = 1;
/// Get or set the radius of this shape in the given RadiusSpace. For arcs and rings, this radius is the distance from the origin of the ring to the center of the thickness of the arc/ring
public float Radius {
get => radius;
set => SetFloatNow( ShapesMaterialUtils.propRadius, radius = Mathf.Max( 0f, value ) );
}
[SerializeField] ThicknessSpace radiusSpace = Shapes.ThicknessSpace.Meters;
/// Get or set the space in which radius is defined
public ThicknessSpace RadiusSpace {
get => radiusSpace;
set => SetIntNow( ShapesMaterialUtils.propRadiusSpace, (int)( radiusSpace = value ) );
}
[SerializeField] float thickness = 0.5f;
/// this property is obsolete I'm sorry, this was a typo! please use Thickness instead!
[System.Obsolete( "this property is obsolete, this was a typo! please use Thickness instead!", true )]
public float RadiusInner {
get => Thickness;
set => Thickness = value;
}
/// Get or set the thickness or rings and arcs, in the given ThicknessSpace
public float Thickness {
get => thickness;
set {
SetFloatNow( ShapesMaterialUtils.propThickness, thickness = Mathf.Max( 0f, value ) );
if( HasThickness && dashed && dashStyle.space == DashSpace.Relative )
SetAllDashValues( now: true );
}
}
[SerializeField] ThicknessSpace thicknessSpace = Shapes.ThicknessSpace.Meters;
/// Get or set the space in which thickness is defined for rings and arcs
public ThicknessSpace ThicknessSpace {
get => thicknessSpace;
set => SetIntNow( ShapesMaterialUtils.propThicknessSpace, (int)( thicknessSpace = value ) );
}
[SerializeField] ArcEndCap arcEndCaps = ArcEndCap.None;
/// Get or set the type of end caps to use for arcs
public ArcEndCap ArcEndCaps {
get => arcEndCaps;
set => SetIntNow( ShapesMaterialUtils.propRoundCaps, (int)( arcEndCaps = value ) );
}
private protected override void SetAllMaterialProperties() {
SetInt( ShapesMaterialUtils.propAlignment, (int)geometry );
SetFloat( ShapesMaterialUtils.propRadius, radius );
SetInt( ShapesMaterialUtils.propRadiusSpace, (int)radiusSpace );
SetFloat( ShapesMaterialUtils.propThickness, thickness );
SetInt( ShapesMaterialUtils.propThicknessSpace, (int)thicknessSpace );
SetInt( ShapesMaterialUtils.propRoundCaps, (int)arcEndCaps );
SetFloat( ShapesMaterialUtils.propAngStart, angRadiansStart );
SetFloat( ShapesMaterialUtils.propAngEnd, angRadiansEnd );
switch( ColorMode ) {
case DiscColorMode.Single:
SetColor( ShapesMaterialUtils.propColorOuterStart, base.Color );
SetColor( ShapesMaterialUtils.propColorInnerEnd, base.Color );
SetColor( ShapesMaterialUtils.propColorOuterEnd, base.Color );
break;
case DiscColorMode.Radial:
SetColor( ShapesMaterialUtils.propColorOuterStart, ColorOuterStart );
SetColor( ShapesMaterialUtils.propColorInnerEnd, base.Color );
SetColor( ShapesMaterialUtils.propColorOuterEnd, ColorOuterStart );
break;
case DiscColorMode.Angular:
SetColor( ShapesMaterialUtils.propColorOuterStart, base.Color );
SetColor( ShapesMaterialUtils.propColorInnerEnd, ColorInnerEnd );
SetColor( ShapesMaterialUtils.propColorOuterEnd, ColorInnerEnd );
break;
case DiscColorMode.Bilinear:
SetColor( ShapesMaterialUtils.propColorOuterStart, ColorOuterStart );
SetColor( ShapesMaterialUtils.propColorInnerEnd, ColorInnerEnd );
SetColor( ShapesMaterialUtils.propColorOuterEnd, ColorOuterEnd );
break;
}
SetAllDashValues( now: false );
}
internal override bool HasDetailLevels => false;
#if UNITY_EDITOR
private protected override void ShapeClampRanges() {
radius = Mathf.Max( 0f, radius ); // disallow negative radius
thickness = Mathf.Max( 0f, thickness ); // disallow negative inner radius
if( matchDashSpacingToSize == false ) // this is a lil scary but I think it's okay it's going to be okay
DashSpacing = DashSpace == DashSpace.FixedCount ? Mathf.Clamp01( DashSpacing ) : Mathf.Max( 0f, DashSpacing );
}
#endif
private protected override Material[] GetMaterials() {
return new[] { ShapesMaterialUtils.GetDiscMaterial( type )[BlendMode] };
}
private protected override Bounds GetBounds_Internal() {
if( radiusSpace != ThicknessSpace.Meters )
return new Bounds( Vector3.zero, Vector3.one );
// presume 0 world space padding when pixels or noots are used
float padding = thicknessSpace == ThicknessSpace.Meters ? thickness * .5f : 0f;
float apothem = HasThickness ? radius + padding : radius;
float size = apothem * 2;
return new Bounds( Vector3.zero, new Vector3( size, size, 0f ) );
}
}
}