diff --git a/Runtime/Graphs/GraphWalker.cs b/Runtime/Graphs/GraphWalker.cs new file mode 100644 index 0000000..cb4937e --- /dev/null +++ b/Runtime/Graphs/GraphWalker.cs @@ -0,0 +1,89 @@ +using System.Collections; +using System.Collections.Generic; +using System; + +namespace Rokojori +{ + public abstract class GraphWalker where N:class + { + public virtual int NumConnectedFrom( N node ){ return 0;} + public virtual N GetConnectedFrom( N node, int index ){ return default(N);} + + public abstract int NumConnectedTo( N node ); + public abstract N GetConnectedTo( N node, int index ); + + public int NumConnectedAll( N node ) + { + return NumConnectedTo( node ) + NumConnectedFrom( node ); + } + + public N GetConnected( N node, int index ) + { + var fromConnections = NumConnectedFrom( node ); + + return index < fromConnections ? GetConnectedFrom( node, index ) : GetConnectedTo( node, index - fromConnections ); + } + + + public bool IsConnectedTo( N source, N destination ) + { + var processed = new HashSet(); + var toDo = new List(); + + toDo.Add( source ); + + while ( toDo.Count > 0 ) + { + var current = Lists.RemoveFirst( toDo ); + + if ( processed.Contains( current ) ) + { + continue; + } + + processed.Add( current ); + + var numConnected = NumConnectedTo( current ); + + for ( int i = 0; i < numConnected; i++ ) + { + var connected = GetConnected( current, i ); + + if ( connected == destination ) + { + return true; + } + + if ( ! processed.Contains( connected ) ) + { + toDo.Add( connected ); + } + } + } + + return false; + } + + + + public bool IsDirectlyConnectedTo( N node, N other ) + { + if ( node == null || other == null ) + { + return false; + } + + var connected = NumConnectedTo( node ); + + for ( int i = 0; i < connected; i++ ) + { + if ( GetConnected( node, i ) == other ) + { + return true; + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/Runtime/Graphs/Trees/TreeWalker.cs b/Runtime/Graphs/Trees/TreeWalker.cs index 5028246..d0fd564 100644 --- a/Runtime/Graphs/Trees/TreeWalker.cs +++ b/Runtime/Graphs/Trees/TreeWalker.cs @@ -240,7 +240,7 @@ namespace Rokojori return NextNonChild( node ); } - public N GetParent( N node, Func predicate ) + public N GetInParents( N node, Func predicate ) { var p = Parent( node ); diff --git a/Runtime/Logging/RJLog.cs b/Runtime/Logging/RJLog.cs index 75eb009..997c777 100644 --- a/Runtime/Logging/RJLog.cs +++ b/Runtime/Logging/RJLog.cs @@ -42,6 +42,13 @@ namespace Rokojori GD.PrintRich( trace ); } + static void LogMessageWithFullTrace( string message ) + { + var trace = GetFullTrace(); + GD.PrintRich("\n[b]" + message + "[/b]"); + GD.PrintRich( trace ); + } + static void LogErrorMessage( string message ) { var trace = GetTrace(); @@ -49,6 +56,11 @@ namespace Rokojori GD.PrintRich( trace ); } + static string GetFullTrace() + { + return ( new System.Diagnostics.StackTrace( true ) ).ToString(); + } + static string GetTrace() { var stackTrace = new System.Diagnostics.StackTrace( true ); @@ -86,6 +98,23 @@ namespace Rokojori return "[color=gray] " + trace + "[/color]" ; } + public static void LogWithFullTrace( params object[] objects) + { + var sb = new StringBuilder(); + + for ( int i = 0; i < objects.Length; i++ ) + { + if ( i != 0 ) + { + sb.Append( " " ); + } + + Stringify( objects[ i ], sb ); + } + + LogMessageWithFullTrace( sb.ToString() ); + } + public static void Log( params object[] objects) { var sb = new StringBuilder(); diff --git a/Runtime/Math/Geometry/Capsule3.cs b/Runtime/Math/Geometry/Capsule3.cs new file mode 100644 index 0000000..b8a177e --- /dev/null +++ b/Runtime/Math/Geometry/Capsule3.cs @@ -0,0 +1,44 @@ +using Godot; +using System.Collections; +using System.Collections.Generic; + +namespace Rokojori +{ + public class Capsule3 + { + public Vector3 start = Vector3.Zero; + public Vector3 end = Vector3.Zero; + public float radius = 1; + + public Capsule3( Vector3 start, Vector3 end, float radius ) + { + this.start = start; + this.end = end; + this.radius = radius; + } + + public Capsule3 Copy() + { + return new Capsule3( start, end, radius ); + } + + public Vector3 center + { + get { return start.Lerp( end, 0.5f ); } + } + + public Line3 centerLine + { + get { return new Line3( start, end ); } + } + + public float DistanceToPoint( Vector3 p ) + { + var lineDistance = centerLine.DistanceToPoint( p ); + return Mathf.Max( 0, lineDistance - radius ); + } + + + } +} + diff --git a/Runtime/Math/Geometry/Curve3.cs b/Runtime/Math/Geometry/Curve3.cs new file mode 100644 index 0000000..6b2cbc6 --- /dev/null +++ b/Runtime/Math/Geometry/Curve3.cs @@ -0,0 +1,77 @@ +using Godot; +using System.Collections; +using System.Collections.Generic; + +namespace Rokojori +{ + public abstract class Curve3 + { + float _gradientSamplingRange = Mathf.Pow( 10f, -13f ); + public abstract Vector3 SampleAt( float t ); + + public virtual void SampleMultiple( int numSamples, List output ) + { + SampleMultiple( new Range( 0, 1 ), numSamples, output ); + } + + public virtual void SampleMultiple( Range range, int numSamples, List output ) + { + + var diff = range.length / ( numSamples - 1 ) ; + + for ( var i = 0 ; i < numSamples; i++ ) + { + var t = range.min + i * diff; + output.Add( SampleAt( t ) ); + } + } + + public virtual Vector3 GradientAt( float t ) + { + + return SampleAt( t + _gradientSamplingRange ) - SampleAt( t - _gradientSamplingRange ); + } + + public virtual float ComputeLength( int numSamples ) + { + return ComputeSubLength( Range.Of_01 , numSamples ); + } + + public virtual float ComputeSubLength( Range range, int numSamples ) + { + var diff = range.length / ( numSamples - 1 ) ; + var it = SampleAt( range.min ); + var length = 0f; + + for ( var i = 1 ; i < numSamples; i++ ) + { + var t = range.min + i * diff; + var next = SampleAt( t ); + length += ( next - it ).Length(); + it = next; + } + + return length; + } + + public virtual int ComputeNumSamplesFor( Range range, + float minimumDistance, int minSamples = 2, + int numSamplesForLengthComputation = -1 ) + { + if ( minSamples < 2 ) + { minSamples = 2; } + + if ( numSamplesForLengthComputation <= 0 ) + { numSamplesForLengthComputation = minSamples * 4; } + + float distance = ComputeSubLength( range, numSamplesForLengthComputation ); + float numFloatingCuts = distance / minimumDistance; + int numSamples = Mathf.CeilToInt( numFloatingCuts ); + + return Mathf.Max( minSamples, numSamples ); + } + + + + } +} \ No newline at end of file diff --git a/Runtime/Math/Geometry/Distance3.cs b/Runtime/Math/Geometry/Distance3.cs new file mode 100644 index 0000000..53e6590 --- /dev/null +++ b/Runtime/Math/Geometry/Distance3.cs @@ -0,0 +1,117 @@ +using Godot; +using System.Collections; +using System.Collections.Generic; + +namespace Rokojori +{ + public class Distance + { + + /* + + P = Point, + S = Sphere + L = Line, + C = Capsule + + + P S L C + P y y y y + S y y y + L y y + C y + + */ + + public static float Of( Vector3 a, Vector3 b ) + { + return ( a - b ).Length(); + } + + public static float Of( Vector3 a, Sphere b ) + { + return b.DistanceToPoint( a ); + } + + public static float Of( Sphere a, Vector3 b ) + { + return Of( b, a ); + } + + public static float Of( Vector3 a, Line3 b ) + { + return b.DistanceToPoint( a ); + } + + public static float Of( Line3 b, Vector3 a ) + { + return Of( a, b ); + } + + public static float Of( Vector3 a, Capsule3 b ) + { + return b.DistanceToPoint( a ); + } + + public static float Of( Capsule3 a, Vector3 b ) + { + return Of( b, a ); + } + + public static float Of( Sphere a, Sphere b ) + { + return a.DistanceToSphere( b ); + } + + public static float Of( Sphere a, Line3 b ) + { + var centerDistance = Of( a.center, b ); + return Mathf.Max( centerDistance - a.radius, 0 ); + } + + public static float Of( Line3 a, Sphere b ) + { + return Of( b, a ); + } + + public static float Of( Sphere a, Capsule3 b ) + { + var centerDistance = Of( a.center, b ); + return Mathf.Max( centerDistance - a.radius, 0 ); + } + + public static float Of( Capsule3 a, Sphere b ) + { + return Of( b, a ); + } + + public static float Of( Line3 a, Line3 b ) + { + return a.DistanceToLine( b ); + } + + public static float Of( Line3 a, Capsule3 b ) + { + var lineDistance = Of( a, b.centerLine ); + return Mathf.Max( 0, lineDistance - b.radius ); + } + + public static float Of( Capsule3 a, Line3 b ) + { + return Of( b, a ); + } + + public static float Of( Capsule3 a, Capsule3 b ) + { + var lineDistance = Of( a.centerLine, b.centerLine ); + return Mathf.Max( 0, lineDistance - ( a.radius + b.radius ) ); + } + + + + + + + } +} + diff --git a/Runtime/Math/Geometry/LerpCurve3.cs b/Runtime/Math/Geometry/LerpCurve3.cs new file mode 100644 index 0000000..1238f9d --- /dev/null +++ b/Runtime/Math/Geometry/LerpCurve3.cs @@ -0,0 +1,249 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; + + +namespace Rokojori +{ + + public class SubdivisionData + { + public Vector3 point; + public bool isCorner; + public Vector3 direction; + + public SubdivisionData( Vector3 p, Vector3 d, bool corner = false ) + { + point = p; + direction = d; + isCorner = corner; + } + + } + + public class LerpCurveClosestToPointResult + { + public float parameter; + public float distance; + public Vector3 point; + } + + public class LerpCurve3:Curve3 + { + List list; + + public LerpCurve3( List list ) + { + this.list = list; + + if ( list.Count == 0 ) + { + RJLog.Log( "LERP CURVE WITH EMPTY LIST" ); + } + } + + public override float ComputeLength( int numSamples ) + { + var length = 0f; + var end = list.Count - 1; + + for ( int i = 0; i < end; i++ ) + { + var p0 = list[ i ]; + var p1 = list[ i + 1 ]; + + length += p0.DistanceTo( p1 ); + } + + return length; + } + + public float UndistortParameter( float parameter ) + { + var completeLength = ComputeLength( 0 ); + + var end = list.Count - 1; + var normalizer = 1f / ( list.Count - 1 ); + + var length = 0f; + + for ( int i = 0; i < end; i++ ) + { + var p0 = list[ i ]; + var p1 = list[ i + 1 ]; + var t0 = i * normalizer; + var t1 = ( i + 1 ) * normalizer; + var distance = p0.DistanceTo( p1 ); + + if ( t0 <= parameter && parameter <= t1 ) + { + length += distance * ( parameter - t0 ); + return length / completeLength; + } + + length += distance; + + } + + return 1; + + } + + public List Subdivide( float distance, bool close ) + { + var output = new List(); + + var num = list.Count - 1; + + if ( close ) + { + num ++; + } + + for ( var i = 0; i < num; i++ ) + { + var start = list[ i ]; + var end = list[ i == list.Count ? 0 : i + 1 ]; + + var dir = ( end - start ); + var length = dir.Length(); + var samples = Mathf.CeilToInt( length / distance ); + + output.Add( new SubdivisionData( start, dir, true ) ); + + for ( int j = 1; j < samples; j++ ) + { + var samplePoint = j / (float) samples; + var point = start.Lerp( end, samplePoint ); + output.Add( new SubdivisionData( point, dir ) ); + } + + } + + + if ( ! close ) + { + var start = list[ list.Count - 2]; + var end = list[ list.Count - 1]; + + var dir = ( end - start ); + + output.Add( new SubdivisionData( end, dir, true ) ); + + } + + + var dirCount = close ? output.Count : ( output.Count - 1 ); + + for ( int i = 0; i < dirCount; i++ ) + { + var next = i == output.Count ? 0 : i + 1; + output[ i ].direction = output[ next ].point - output[ i ].point; + } + + + + + return output; + } + + public override Vector3 SampleAt( float t ) + { + if ( t < 0 ) + { + return list[ 0 ]; + } + + if ( t >= 1 ) + { + return list[ list.Count - 1 ]; + } + + if ( list.Count == 1 ) + { + return list[ 0 ]; + } + + + + var listIndex = t * ( list.Count - 1 ); + var flooredIndex = (int) Mathf.Floor( listIndex ); + + var lerpAmount = listIndex - flooredIndex; + + if ( flooredIndex < 0 || ( flooredIndex >= list.Count - 1 ) ) + { + RJLog.Log( "Out of range index:",flooredIndex, " t:", t, " listIndex", listIndex, "Count:", list.Count ); + } + + var lower = list[ flooredIndex ]; + var upper = list[ flooredIndex + 1 ]; + + return Math3D.LerpUnclamped( lower, upper, lerpAmount ); + + } + + public LerpCurve3 FromRange( Curve3 curve, Range range, int numSamples ) + { + var points = new List(); + curve.SampleMultiple( range, numSamples, points ); + return new LerpCurve3( points ); + } + + public LerpCurve3 From( Curve3 curve, int numSamples ) + { + return FromRange( curve, Range.Of_01, numSamples ); + } + + public static LerpCurve3 FromPoints( Vector3 first, Vector3 second, params Vector3[] others ) + { + var points = new List(); + points.Add( first ); + points.Add( second ); + points.AddRange( others ); + + return new LerpCurve3( points ); + } + + Line3 line; + + public void GetClosest( Vector3 point, LerpCurveClosestToPointResult result ) + { + var closestDistance = float.MaxValue; + Vector3 closestPoint = Vector3.Zero; + var closestParameter = 0f; + + if ( line == null ) + { + line = new Line3( Vector3.Zero, Vector3.Zero ); + } + + var end = list.Count - 1; + + var parameterNormalizer = 1f / ( list.Count - 1f ); + + for ( int i = 0; i < end; i++ ) + { + line.start = list[ i ]; + line.end = list[ i + 1 ]; + + var currentParameter = line.ClostestParameterToPoint( point ); + var currentClosestPoint = line.GetPointAtParameter( currentParameter ); + var currentDistance = ( point - currentClosestPoint ).LengthSquared(); + + if ( currentDistance < closestDistance ) + { + closestDistance = currentDistance; + closestPoint = currentClosestPoint; + closestParameter = ( currentParameter + i ) * parameterNormalizer; + } + } + + result.distance = Mathf.Sqrt( closestDistance ); + result.point = closestPoint; + result.parameter = closestParameter; + + } + + } +} \ No newline at end of file diff --git a/Runtime/Math/Geometry/Line3.cs b/Runtime/Math/Geometry/Line3.cs new file mode 100644 index 0000000..b6f4296 --- /dev/null +++ b/Runtime/Math/Geometry/Line3.cs @@ -0,0 +1,198 @@ +using Godot; +using System.Collections; +using System.Collections.Generic; + +namespace Rokojori +{ + public class Line3:Curve3 + { + public Vector3 start = Vector3.Zero; + public Vector3 end = Vector3.Zero; + + public Line3( Vector3 start, Vector3 end ) + { + this.start = start; + this.end = end; + } + + public override Vector3 SampleAt( float t ) + { + return start.Lerp( end, t ); + } + + public Line3 Copy( ) + { + return new Line3( start, end ); + } + + public Vector3 direction { get { return end - start; } } + + public float length { get { return direction.Length(); } } + + public Vector3 GetPointAtParameter( float t ) + { + return start + direction * t; + } + + public Vector3 center{ get { return ( start + end ) * 0.5f; } } + + public Line3 ScaleFromCenter( float scale ) + { + var lineCenter = center; + var halfDirection = direction * ( 0.5f * scale ); + + var scaledStart = lineCenter - halfDirection; + var scaledEnd = lineCenter + halfDirection; + + return new Line3( scaledStart, scaledEnd ); + } + + public Line3 ChangeLengthFromCenter( float lengthChange ) + { + var lineCenter = center; + var halfDirection = direction * 0.5f; + var length = halfDirection.Length(); + length += lengthChange * 0.5f; + + halfDirection = halfDirection.Normalized() * length; + + var scaledStart = lineCenter - halfDirection; + var scaledEnd = lineCenter + halfDirection; + + return new Line3( scaledStart, scaledEnd ); + } + + public Line3 SetLengthFromCenter( float length ) + { + var lineCenter = center; + var halfDirection = direction.Normalized() * ( length * 0.5f ); + + var scaledStart = lineCenter - halfDirection; + var scaledEnd = lineCenter + halfDirection; + + return new Line3( scaledStart, scaledEnd ); + } + + + public Vector3 ClosestPointToPoint( Vector3 point ) + { + var parameter = MathX.Clamp01( ClostestParameterToPoint( point ) ); + return GetPointAtParameter( parameter ); + } + + public float ClostestParameterToPoint( Vector3 point ) + { + var startP = point - start; + var startEnd = end - start; + var startEnd2 = startEnd.Dot( startEnd ); + var startEnd_startP = startEnd.Dot( startP ); + + if ( startEnd2 == 0 ) + { + return 0; + } + + var t = startEnd_startP / startEnd2; + + return t; + + } + + public float DistanceToPoint( Vector3 point ) + { + return ( point - ClosestPointToPoint( point ) ).Length(); + } + + + public float DistanceToLine( Line3 s ) + { + var points = ClosestPointsToLine( s ); + return ( points[ 1 ] - points[ 0 ] ).Length(); + } + + public Vector3[] ClosestPointsToLine( Line3 s ) + { + var parameters = ClosestParametersToLine( s ); + var x = this.GetPointAtParameter( parameters.X ); + var y = s.GetPointAtParameter( parameters.Y ); + + return new Vector3[]{ x, y }; + } + + public Vector2 ClosestParametersToLine( Line3 s ) + { + float epsilon = 0.00000001f; + + var u = this.direction; + var v = s.direction; + var w = start - s.start; + + var a = u.Dot( u ); + var b = u.Dot( v ); + var c = v.Dot( v ); + var d = u.Dot( w ); + var e = v.Dot( w ); + + var DD = a * c - b * b; + + var sc = 0f; + var sN = 0f; + var sD = 0f; + + var tc = 0f; + var tN = 0f; + var tD = 0f; + + sD = DD; + tD = DD; + + if ( DD < epsilon ) + { + sN = 0.0f; + sD = 1.0f; + tN = e; + tD = c; + } + else + { + sN = b * e - c * d ; + tN = a * e - b * d ; + + if ( sN < 0.0f ) + { + sN = 0.0f; + tN = e; + tD = c; + } + else if ( sN > sD ) + { + sN = sD; + tN = e + b; + tD = c; + } + } + + if ( tN < 0f || tN > tD ) + { + var tN_bigger_tD = tN >= 0f && tN > tD; + + tN = tN_bigger_tD ? tD : 0f; + + var inbetween = tN_bigger_tD ? ( -d + b ) : ( -d ); + var isSmaller = inbetween < 0; + var isBigger = inbetween > a; + + sN = isSmaller ? 0f : isBigger ? sD : inbetween; + sD = ! isSmaller && ! isBigger ? a : sD; + } + + sc = Mathf.Abs( sN ) < epsilon ? 0f : sN / sD; + tc = Mathf.Abs( tN ) < epsilon ? 0f : tN / tD; + + return new Vector2( sc, tc ); + + } + + } +} + diff --git a/Runtime/Math/Geometry/Sphere.cs b/Runtime/Math/Geometry/Sphere.cs new file mode 100644 index 0000000..74aac8b --- /dev/null +++ b/Runtime/Math/Geometry/Sphere.cs @@ -0,0 +1,53 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; + +namespace Rokojori +{ + public class Sphere + { + public Vector3 center = Vector3.Zero; + public float radius = 1; + + public Sphere( Vector3 center, float radius ) + { + this.center = center; + this.radius = radius; + } + + public Sphere Copy() + { + return new Sphere( center, radius ); + } + + public float DistanceToPoint( Vector3 point ) + { + return Mathf.Max( 0, ( point - center ).Length() - radius ); + } + + public bool ContainsPoint( Vector3 point ) + { + return DistanceToPoint( point ) <= 0; + } + + public float DistanceToSphere( Sphere sphere ) + { + var distance = sphere.center - center; + var radiusBoth = sphere.radius + radius; + return Mathf.Max( 0, distance.Length() - radiusBoth ); + } + + public bool IntersectsSphere( Sphere sphere ) + { + return DistanceToSphere( sphere ) <= 0; + } + + public Box3 CreateBox() + { + var offset = radius * Vector3.One; + return Box3.Create( center -offset, center + offset ); + } + + } +} + diff --git a/Runtime/Math/Geometry/Triangle3.cs b/Runtime/Math/Geometry/Triangle3.cs new file mode 100644 index 0000000..bbd565a --- /dev/null +++ b/Runtime/Math/Geometry/Triangle3.cs @@ -0,0 +1,96 @@ +using Godot; +using System.Collections; +using System.Collections.Generic; + + +namespace Rokojori +{ + public enum TriangleSide + { + AB, + BC, + CA + } + + public class TriangleSides + { + public static readonly TriangleSide[] ALL = + { + TriangleSide.AB, + TriangleSide.BC, + TriangleSide.CA + }; + } + + public class Triangle3 + { + public Vector3 a; + public Vector3 b; + public Vector3 c; + + public Triangle3( Vector3 a, Vector3 b, Vector3 c ) + { + this.a = a; + this.b = b; + this.c = c; + } + + public float perimeter + { + get + { + var sideA = ( b - a ).Length(); + var sideB = ( c - b ).Length(); + var sideC = ( a - c ).Length(); + + return sideA + sideB + sideC; + } + } + + public float semiperimeter + { + get + { + return perimeter * 0.5f; + } + } + + public static float ComputeTriangleArea( Vector3 a, Vector3 b, Vector3 c ) + { + var sideA = ( b - a ).Length(); + var sideB = ( c - b ).Length(); + var sideC = ( a - c ).Length(); + + + var perimeter = sideA + sideB + sideC; + var semiperimeter = 0.5f * perimeter; + var areaValue = Mathf.Sqrt( semiperimeter * ( semiperimeter - sideA ) + * ( semiperimeter - sideB ) + * ( semiperimeter - sideC )) ; + + return areaValue; + } + + public float area + { + get + { + return ComputeTriangleArea( a, b, c ); + } + } + + + public Line3 GetSide( TriangleSide side ) + { + switch ( side ) + { + case TriangleSide.AB: return new Line3( a, b ); + case TriangleSide.BC: return new Line3( b, c ); + case TriangleSide.CA: return new Line3( c, a ); + } + + return null; + } + } + +} \ No newline at end of file diff --git a/Runtime/Math/Math3D.cs b/Runtime/Math/Math3D.cs index d22f488..3d24c13 100644 --- a/Runtime/Math/Math3D.cs +++ b/Runtime/Math/Math3D.cs @@ -8,6 +8,16 @@ namespace Rokojori { public class Math3D { + public static Vector3 LerpUnclamped( Vector3 a, Vector3 b, float amount ) + { + return a + amount * ( b - a ); + } + + public static Vector3 LerpClamped( Vector3 a, Vector3 b, float amount ) + { + return LerpUnclamped( a, b, MathX.Clamp01( amount ) ); + } + public static Quaternion GetGlobalRotation( Node3D node ) { return node.GlobalBasis.GetRotationQuaternion(); diff --git a/Runtime/Math/RationalCubicBezier.cs b/Runtime/Math/RationalCubicBezier.cs new file mode 100644 index 0000000..945bd2e --- /dev/null +++ b/Runtime/Math/RationalCubicBezier.cs @@ -0,0 +1,104 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; + +namespace Rokojori +{ + + public class RationalCubicBezier + { + public static float Compute( + float t, + float p0, float p1, float p2, float p3, + float w0, float w1, float w2, float w3 + ) + { + var bp0 = ( 1 - t ) * ( 1 - t ) * ( 1 - t ) * w0; + var bp1 = 3 * ( 1 - t ) * ( 1 - t ) * t * w1; + var bp2 = 3 * ( 1 - t ) * t * t * w2; + var bp3 = t * t * t * w3; + + var weighted = p0 * bp0 + p1 * bp1 + p2 * bp2 + p3 * bp3; + + var projection = bp0 + bp1 + bp2 + bp3; + + return weighted / projection; + } + + public static Vector2 Compute( + float t, + Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, + float w0, float w1, float w2, float w3 + ) + { + var bp0 = ( 1 - t ) * ( 1 - t ) * ( 1 - t ) * w0; + var bp1 = 3 * ( 1 - t ) * ( 1 - t ) * t * w1; + var bp2 = 3 * ( 1 - t ) * t * t * w2; + var bp3 = t * t * t * w3; + + var weighted = p0 * bp0 + p1 * bp1 + p2 * bp2 + p3 * bp3; + + var projection = bp0 + bp1 + bp2 + bp3; + + return weighted / projection; + } + + public static Vector3 Compute( + float t, + Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, + float w0, float w1, float w2, float w3 + ) + { + var bp0 = ( 1 - t ) * ( 1 - t ) * ( 1 - t ) * w0; + var bp1 = 3 * ( 1 - t ) * ( 1 - t ) * t * w1; + var bp2 = 3 * ( 1 - t ) * t * t * w2; + var bp3 = t * t * t * w3; + + var weighted = p0 * bp0 + p1 * bp1 + p2 * bp2 + p3 * bp3; + + var projection = bp0 + bp1 + bp2 + bp3; + + return weighted / projection; + } + + + public static Vector2 Compute( float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3 ) + { + var bp0 = ( 1 - t ) * ( 1 - t ) * ( 1 - t ) * p0.Z; + var bp1 = 3 * ( 1 - t ) * ( 1 - t ) * t * p1.Z; + var bp2 = 3 * ( 1 - t ) * t * t * p2.Z; + var bp3 = t * t * t * p3.Z; + + var weighted = p0 * bp0 + p1 * bp1 + p2 * bp2 + p3 * bp3; + + var projection = bp0 + bp1 + bp2 + bp3; + + var result = weighted / projection; + + return new Vector2( result.X, result.Y ); + } + + public static Vector3 Compute( float t, Vector4 p0, Vector4 p1, Vector4 p2, Vector4 p3 ) + { + var bp0 = ( 1 - t ) * ( 1 - t ) * ( 1 - t ) * p0.W; + var bp1 = 3 * ( 1 - t ) * ( 1 - t ) * t * p1.W; + var bp2 = 3 * ( 1 - t ) * t * t * p2.W; + var bp3 = t * t * t * p3.W; + + var weighted = p0 * bp0 + p1 * bp1 + p2 * bp2 + p3 * bp3; + + var projection = bp0 + bp1 + bp2 + bp3; + + var result = weighted / projection; + + return new Vector3( result.X, result.Y, result.Z ); + } + + + + + + } + + +} \ No newline at end of file diff --git a/Runtime/Math/_Unity_To_Godot_.txt b/Runtime/Math/_Unity_To_Godot_.txt new file mode 100644 index 0000000..44bd81c --- /dev/null +++ b/Runtime/Math/_Unity_To_Godot_.txt @@ -0,0 +1,20 @@ +Match: \.magnitude +Replace: .Length() + +Match: \.normalized +Replace: .Normalized() + +Match: Vector3\.(Lerp|Dot)\(\s*(\w+)\s*,\s* +Replace: $2.$1( + +Match: Vector3\.Distance\(\s*(\w+)\s*,\s* +Replace: $1.DistanceTo( + +Match: \.x +Replace: .X + +Match: \.Y +Replace: .Y + +Match: \.z +Replace: .Z \ No newline at end of file diff --git a/Runtime/Procedural/MeshGeometry.cs b/Runtime/Procedural/MeshGeometry.cs new file mode 100644 index 0000000..97561c6 --- /dev/null +++ b/Runtime/Procedural/MeshGeometry.cs @@ -0,0 +1,138 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + + + +namespace Rokojori +{ + public class MeshGeometry + { + public List vertices; + public List indices; + + public List normals; + public List uvs; + public List uv2s; + public List colors; + + + public void Initialize( bool normals = true, bool uvs = true, bool colors = false, bool uvs2 = false) + { + this.vertices = new List(); + this.indices = new List(); + + this.normals = normals ? new List() : null; + this.uvs = uvs ? new List() : null; + this.uv2s = uvs2 ? new List() : null; + this.colors = colors ? new List() : null; + } + + public void AddTriangle( + Vector3 va, Vector3 vb, Vector3 vc, + Vector3 na, Vector3 nb, Vector3 nc, + Vector2 uva, Vector2 uvb, Vector2 uvc + ) + { + var index = vertices.Count; + Lists.Add( vertices, va, vb, vc ); + Lists.Add( normals, na, nb, nc ); + Lists.Add( uvs, uva, uvb, uvc ); + Lists.Add( indices, index, index + 1, index + 2 ); + } + + public void AddQuad( + Vector3 va, Vector3 vb, Vector3 vc, Vector3 vd, + Vector3 na, Vector3 nb, Vector3 nc, Vector3 nd, + Vector2 uva, Vector2 uvb, Vector2 uvc, Vector2 uvd + ) + { + AddTriangle( va, vb, vc, na, nb, nc, uva, uvb, uvc ); + AddTriangle( vc, vd, va, nc, nd, na, uvc, uvd, uva ); + } + + public void AddTriangle( + Vector3 va, Vector3 vb, Vector3 vc, + Vector2 uva, Vector2 uvb, Vector2 uvc + ) + { + var n = Vector3.Up; + + AddTriangle( va, vb, vc, + n, n, n, + uva, uvb, uvc ); + } + + public void AddQuad( + Vector3 va, Vector3 vb, Vector3 vc, Vector3 vd, + Vector2 uva, Vector2 uvb, Vector2 uvc, Vector2 uvd + ) + { + + AddTriangle( va, vb, vc, uva, uvb, uvc ); + AddTriangle( vc, vd, va, uvc, uvd, uva ); + } + + public void AddTriangle( Vector3 va, Vector3 vb, Vector3 vc ) + { + var n = Vector3.Up; + var uv = Vector2.Zero; + + AddTriangle( va, vb, vc, n, n, n, uv, uv, uv ); + } + + public void AddQuad( Vector3 va, Vector3 vb, Vector3 vc, Vector3 vd ) + { + AddTriangle( va, vb, vc ); + AddTriangle( vc, vd, va ); + } + + + public ArrayMesh Generate( Mesh.PrimitiveType type = Mesh.PrimitiveType.Triangles, ArrayMesh arrayMesh = null ) + { + if ( arrayMesh == null ) + { + arrayMesh = new ArrayMesh(); + } + + var surfaceArray = new Godot.Collections.Array(); + surfaceArray.Resize( (int) Mesh.ArrayType.Max ); + + if ( vertices != null ) + { + surfaceArray[ (int) Mesh.ArrayType.Vertex ] = vertices.ToArray(); + } + + if ( normals != null ) + { + surfaceArray[ (int) Mesh.ArrayType.Normal ] = normals.ToArray(); + } + + if ( uvs != null ) + { + surfaceArray[ (int) Mesh.ArrayType.TexUV ] = uvs.ToArray(); + } + + if ( uv2s != null ) + { + surfaceArray[ (int) Mesh.ArrayType.TexUV2 ] = uv2s.ToArray(); + } + + if ( colors != null ) + { + surfaceArray[ (int) Mesh.ArrayType.Color ] = colors.ToArray(); + } + + if ( indices != null ) + { + surfaceArray[ (int) Mesh.ArrayType.Index ] = indices.ToArray(); + } + + arrayMesh.AddSurfaceFromArrays( Mesh.PrimitiveType.Triangles, surfaceArray ); + + return arrayMesh; + } + + } +} \ No newline at end of file diff --git a/Runtime/Random/RandomEngine.cs b/Runtime/Random/RandomEngine.cs index 91af38f..8db51bd 100644 --- a/Runtime/Random/RandomEngine.cs +++ b/Runtime/Random/RandomEngine.cs @@ -7,7 +7,7 @@ using System; namespace Rokojori { - public abstract class RandomEngine + public abstract class RandomEngine { public abstract float Next(); @@ -208,6 +208,106 @@ namespace Rokojori return list[ index ]; } + + public T RemoveFrom( List list ) + { + if ( list.Count == 0 ) + { + return default ( T ); + } + + var index = this.IntegerExclusive( 0, list.Count ); + + var item = list[ index ]; + + list.RemoveAt( index ); + return item; + } + + public List RemoveMutlipleFrom( List list, int amount ) + { + if ( amount >= list.Count ) + { + return list; + } + + var items = new List(); + + for ( int i = 0; i < amount; i++ ) + { + var itemIndex = this.IntegerExclusive( 0, list.Count ); + var item = list[ itemIndex ]; + list.RemoveAt( itemIndex ); + items.Add( item ); + } + + return items; + } + + public T RemoveWeightedFrom( List list, List weights ) + { + if ( list == null || list.Count == 0 ) + { + return default(T); + } + + if ( list.Count == 1 ) + { + return list[ 0 ]; + } + + var weightedSelection = RemoveMultipleWeightedFrom( list, weights, 1 ); + + + return weightedSelection[ 0 ]; + } + + public List RemoveMultipleWeightedFrom( List list, List weights, int amount ) + { + if ( amount >= list.Count ) + { + return list; + } + + var sumWeights = 0f; + + weights.ForEach( w => sumWeights += w ); + + var selection = new List(); + + for ( int i = 0; i < amount; i++ ) + { + var number = Range( 0, sumWeights ); + + var index = _FindElementIndexWithWeights( weights, Range( 0, sumWeights ) ); + var element = list[ index ]; + var weight = weights[ index ]; + list.RemoveAt( index ); + sumWeights -= weight; + selection.Add( element ); + } + + return selection; + } + + int _FindElementIndexWithWeights( List weights, float value ) + { + var limit = 0f; + + for ( int i = 0; i < weights.Count; i++ ) + { + var before = limit; + limit += weights[ i ]; + + if ( before <= value && value < limit ) + { + return i; + } + } + + return weights.Count - 1; + } + } public abstract class SeedableRandomEngine:RandomEngine diff --git a/Runtime/Shading/Library/Transform.gdshaderinc b/Runtime/Shading/Library/Transform.gdshaderinc new file mode 100644 index 0000000..e936799 --- /dev/null +++ b/Runtime/Shading/Library/Transform.gdshaderinc @@ -0,0 +1,10 @@ + +vec3 localToWorld( vec3 _VERTEX, mat4 _MODEL_MATRIX ) +{ + return ( _MODEL_MATRIX * vec4( _VERTEX, 1.0 ) ).xyz; +} + +vec3 worldToLocal( vec3 _VERTEX, mat4 _MODEL_MATRIX ) +{ + return ( inverse( _MODEL_MATRIX ) * vec4( _VERTEX, 1.0 ) ).xyz; +} \ No newline at end of file diff --git a/Runtime/Tools/Lists.cs b/Runtime/Tools/Lists.cs index e2a213d..3869349 100644 --- a/Runtime/Tools/Lists.cs +++ b/Runtime/Tools/Lists.cs @@ -36,6 +36,28 @@ namespace Rokojori return list.IndexOf( item ) != - 1; } + public static T RemoveAt( List list, int index ) + { + if ( list.Count == 0 || index < 0 || ( index > ( list.Count - 1 ) ) ) + { + return default(T); + } + + var first = list[ index ]; + list.RemoveAt( index ); + return first; + } + + public static T RemoveFirst( List list ) + { + return RemoveAt( list, 0 ); + } + + public static T RemoveLast( List list ) + { + return RemoveAt( list, list.Count - 1 ); + } + public static T Pop( List list ) { if ( list.Count == 0 ){ return default(T); } @@ -143,6 +165,23 @@ namespace Rokojori return copy; } + public static List FilterAndMap( List list, Func filter, Func map) + { + var mapped = new List(); + + for ( int i = 0; i < list.Count; i++ ) + { + if ( ! filter( list[ i ], i ) ) + { + continue; + } + + mapped.Add( map( list[ i ] ) ); + } + + return mapped; + } + public static void Filter( List inputList, List list, Func filter ) { for ( int i = 0; i < inputList.Count; i++ ) @@ -164,7 +203,7 @@ namespace Rokojori } } } - + public static List Filter( List inputList, Func filter ) { var list = new List(); @@ -198,6 +237,24 @@ namespace Rokojori ); return list; + } + + public static void Add( List list, params T[] entries ) + { + list.AddRange( entries ); + } + + public static void Insert( List list, T item, int index ) + { + if ( index >= list.Count ) + { + list.Add( item ); + return; + } + + index = index < 0 ? 0 : index; + + list.Insert( index, item ); } public static T Find( List list, Func callback ) diff --git a/Runtime/Tools/Safe.cs b/Runtime/Tools/Safe.cs new file mode 100644 index 0000000..63a586b --- /dev/null +++ b/Runtime/Tools/Safe.cs @@ -0,0 +1,36 @@ +using System.Collections; +using System.Collections.Generic; +using System; +using System.Reflection; +using System.Text.RegularExpressions; + +namespace Rokojori +{ + public class Safe + { + static readonly int maxWhileIterations = 1000 * 1000; + + public static void While( Func condition, Action action, Action onTooManyIterations = null ) + { + var it = 0; + + while ( it < Safe.maxWhileIterations && condition() ) + { + action(); + it++; + } + + if ( it >= Safe.maxWhileIterations ) + { + RJLog.LogWithFullTrace( "Loop with too many iterations" ); + + if ( onTooManyIterations != null ) + { + onTooManyIterations(); + } + } + + + } + } +} \ No newline at end of file diff --git a/Runtime/UI/Layouts/UIFlowLayout.cs b/Runtime/UI/Layouts/UIFlowLayout.cs index db9f92a..5258ecf 100644 --- a/Runtime/UI/Layouts/UIFlowLayout.cs +++ b/Runtime/UI/Layouts/UIFlowLayout.cs @@ -67,13 +67,13 @@ namespace Rokojori var maxWidth = 0f; var maxHeight = 0f; - if ( UINumber.IsNullOrNone( region.width ) ) + if ( UINumber.IsNullOrNone( UIStyle.Width( region ) ) ) { lines.ForEach( l => { maxWidth = Mathf.Max( maxWidth, l.maxX ); } ); } else { - maxWidth = UINumber.Compute( region, region.width ); + maxWidth = UINumber.Compute( region, UIStyle.Width( region ) ); } if ( lines.Count > 0 ) @@ -81,12 +81,12 @@ namespace Rokojori maxHeight = lines[ lines.Count - 1 ].maxY; } - var margin = UINumber.Compute( region, region.margin, 0 ); - var marginLeft = margin + UINumber.Compute( region, region.marginLeft, 0 ); - var marginTop = margin + UINumber.Compute( region, region.marginTop, 0 ); + var margin = UINumber.Compute( region, UIStyle.Margin( region ), 0 ); + var marginLeft = margin + UINumber.Compute( region, UIStyle.MarginLeft( region ), 0 ); + var marginTop = margin + UINumber.Compute( region, UIStyle.MarginTop( region ), 0 ); - var marginRight = margin + UINumber.Compute( region, region.marginRight, 0 ); - var marginBottom = margin + UINumber.Compute( region, region.marginBottom, 0 ); + var marginRight = margin + UINumber.Compute( region, UIStyle.MarginRight( region ), 0 ); + var marginBottom = margin + UINumber.Compute( region, UIStyle.MarginBottom( region ), 0 ); var marginOffset = new Vector2( marginLeft, marginTop ); @@ -98,46 +98,38 @@ namespace Rokojori region.Size = new Vector2( maxWidth + horizontalMargins, maxHeight + verticalMargins ); - if ( region.position == UIPosition.Parent_Anchor ) + if ( UIStyle.Position( region ) == UIPosition.Parent_Anchor ) { - var p = NodesWalker.Get().Parent( region ) as Control; - - if ( p != null ) - { - var pWidth = UILayouting.GetWidth( p ); - var pHeight = UILayouting.GetHeight( p ); - - var x = p.Position.X; - var y = p.Position.Y; - - - if ( ! UINumber.IsNullOrNone( region.left )) - { - var left = UINumber.Compute( region, region.left, 0 ); - x = left; - } - else if ( ! UINumber.IsNullOrNone( region.right ) ) - { - var right = UINumber.Compute( region, region.right, 0 ); - x = ( pWidth - UILayouting.GetWidth( region ) ) - right; - } - - UILayouting.SetPosition( region, new Vector2( x, y ) ); - - } - + UILayouting.SetPositionInParentAnchor( region ); } + + Nodes.ForEachDirectChild( region, + child => + { + + var styleContainer = child as UIStylePropertyContainer; + + if ( styleContainer == null || UIStyle.Position( styleContainer ) != UIPosition.Parent_Anchor ) + { + return; + } + + UILayouting.UpdateChild( child ); + UILayouting.SetPositionInParentAnchor( styleContainer ); + + } + ); } static List CreateLines( UIRegion region ) { var x = 0f; - var width = UINumber.Compute( region, region.width, 100000000f ); + var width = UINumber.Compute( region, UIStyle.Width( region ), 100000000f ); var lines = new List(); var currentLine = new Line(); - var elementSpacing = UINumber.Compute( region, region.elementSpacing, 0f ); + var elementSpacing = UINumber.Compute( region, UIStyle.ElementSpacing( region ), 0f ); Nodes.ForEachDirectChild( region, c => UILayouting.UpdateChild( c ) ); @@ -146,6 +138,13 @@ namespace Rokojori child => { + var styleContainer = child as UIStylePropertyContainer; + + if ( styleContainer != null && UIStyle.Position( styleContainer ) == UIPosition.Parent_Anchor ) + { + return; + } + var cWidth = UILayouting.GetWidth( child ); var cHeight = UILayouting.GetHeight( child ); @@ -193,13 +192,13 @@ namespace Rokojori lines.ForEach( line => AdjustVerticalAlignment( region, line, verticalAlignment ) ); - if ( UINumber.IsNullOrNone( region.width ) ) + if ( UINumber.IsNullOrNone( UIStyle.Width( region ) ) ) { return; } var horizontalAlignment = UINumber.Compute( region, region.horizontalAlignment, 0.5f, 1 ); - var maxWidth = UINumber.Compute( region, region.width, 0 ); + var maxWidth = UINumber.Compute( region, UIStyle.Width( region ), 0 ); lines.ForEach( line => AdjustHorizontalAlignment( region, line, horizontalAlignment, maxWidth ) ); diff --git a/Runtime/UI/Layouts/UILayouting.cs b/Runtime/UI/Layouts/UILayouting.cs index bfef6f9..d41ac46 100644 --- a/Runtime/UI/Layouts/UILayouting.cs +++ b/Runtime/UI/Layouts/UILayouting.cs @@ -40,6 +40,45 @@ namespace Rokojori UILayouting.UpdatePivot( control ); } + public static void SetPositionInParentAnchor( UIStylePropertyContainer region ) + { + var control = (Control) region; + var p = NodesWalker.Get().Parent( control ) as Control; + + var pWidth = p == null ? UI.GetWindowWidth( control ) : UILayouting.GetWidth( p ); + var pHeight = p == null ? UI.GetWindowHeight( control ) : UILayouting.GetHeight( p ); + + var x = p.Position.X; + var y = p.Position.Y; + + + if ( ! UINumber.IsNullOrNone( UIStyle.Left( region ) )) + { + var left = UINumber.Compute( control, UIStyle.Left( region ), 0 ); + x = left; + } + else if ( ! UINumber.IsNullOrNone( UIStyle.Right( region ) ) ) + { + var right = UINumber.Compute( control, UIStyle.Right( region ), 0 ); + x = ( pWidth - UILayouting.GetWidth( control ) ) - right; + } + + if ( ! UINumber.IsNullOrNone( UIStyle.Top( region ) )) + { + var top = UINumber.Compute( control, UIStyle.Top( region ), 0 ); + y = top; + } + else if ( ! UINumber.IsNullOrNone( UIStyle.Bottom( region ) ) ) + { + var bottom = UINumber.Compute( control, UIStyle.Bottom( region ), 0 ); + y = ( pHeight - UILayouting.GetHeight( control ) ) - bottom; + } + + UILayouting.SetPosition( control, new Vector2( x, y ) ); + + + } + public static void UpdatePivot( Control c ) { if ( ! ( c is UIImage || c is UIRegion || c is UIText ) ) @@ -49,8 +88,8 @@ namespace Rokojori var container = c as UIStylePropertyContainer; - var pivotX = UINumber.Compute( c, UIStyle.PivotX( container ), 0, c.Size.X ); - var pivotY = UINumber.Compute( c, UIStyle.PivotY( container ), 0, c.Size.Y ); + var pivotX = UINumber.Compute( c, UIStyle.PivotX( container ), 0.5f * c.Size.X, c.Size.X ); + var pivotY = UINumber.Compute( c, UIStyle.PivotY( container ), 0.5f * c.Size.Y, c.Size.Y ); c.PivotOffset = new Vector2( pivotX, pivotY ); diff --git a/Runtime/UI/Nodes/UIImage.cs b/Runtime/UI/Nodes/UIImage.cs index ce0ce70..d078661 100644 --- a/Runtime/UI/Nodes/UIImage.cs +++ b/Runtime/UI/Nodes/UIImage.cs @@ -8,6 +8,9 @@ namespace Rokojori [GlobalClass] public partial class UIImage:TextureRect, UIStylePropertyContainer { + [Export] + public UIStyle parentStyle; + [ExportCategory("Size & Margins")] [Export] public UINumber width; @@ -60,6 +63,16 @@ namespace Rokojori public UINumber scaleY; + public UIStyle GetUIStyleParent() + { + return parentStyle; + } + + public UIPosition GetUIPosition() + { + return position; + } + public UINumber GetUIStyleNumberProperty( UIStyleNumberProperty property ) { switch ( property ) diff --git a/Runtime/UI/Nodes/UIRegion.cs b/Runtime/UI/Nodes/UIRegion.cs index 4ff9f4c..6b66557 100644 --- a/Runtime/UI/Nodes/UIRegion.cs +++ b/Runtime/UI/Nodes/UIRegion.cs @@ -8,9 +8,9 @@ namespace Rokojori public partial class UIRegion : Control, UIStylePropertyContainer { [Export] - public UIStyle parent; + public UIStyle parentStyle; - [ExportCategory("Layout")] + [ExportCategory( "Layout" )] [Export] public UILayout layout; @@ -25,7 +25,7 @@ namespace Rokojori public UINumber lineSpacing; - [ExportCategory("Size & Margins")] + [ExportCategory( "Size & Margins" )] [Export] public UINumber width; [Export] @@ -44,7 +44,27 @@ namespace Rokojori [Export] public UINumber marginBottom; - [ExportCategory("Position")] + [ExportCategory( "Font" )] + [Export] + public Font font; + [Export] + public UINumber fontSize; + [Export] + public UIColor fontColor; + + [Export] + public UINumber outlineSize; + [Export] + public UIColor outlineColor; + + [Export] + public UINumber shadowSize; + [Export] + public UIColor shadowColor; + + + + [ExportCategory( "Position" )] [Export] public UIPosition position; [Export] @@ -57,6 +77,16 @@ namespace Rokojori public UINumber bottom; + public UIStyle GetUIStyleParent() + { + return parentStyle; + } + + public UIPosition GetUIPosition() + { + return position; + } + public UINumber GetUIStyleNumberProperty( UIStyleNumberProperty property ) { switch ( property ) @@ -69,6 +99,10 @@ namespace Rokojori case UIStyleNumberProperty.Width: return width; case UIStyleNumberProperty.Height: return height; + + case UIStyleNumberProperty.ElementSpacing: return elementSpacing; + case UIStyleNumberProperty.LineSpacing: return lineSpacing; + case UIStyleNumberProperty.Margin: return margin; case UIStyleNumberProperty.MarginLeft: return marginLeft; diff --git a/Runtime/UI/Nodes/UIText.cs b/Runtime/UI/Nodes/UIText.cs index 4ed0fe9..cb55277 100644 --- a/Runtime/UI/Nodes/UIText.cs +++ b/Runtime/UI/Nodes/UIText.cs @@ -8,6 +8,12 @@ namespace Rokojori [GlobalClass] public partial class UIText:Label,UIStylePropertyContainer { + [Export] + public UIStyle parentStyle; + + [Export] + public UINumber fontSize; + [ExportCategory("Size & Margins")] [Export] public UINumber width; @@ -59,6 +65,15 @@ namespace Rokojori [Export] public UINumber scaleY; + public UIStyle GetUIStyleParent() + { + return parentStyle; + } + + public UIPosition GetUIPosition() + { + return position; + } public UINumber GetUIStyleNumberProperty( UIStyleNumberProperty property ) { diff --git a/Runtime/UI/Styling/UIColor.cs b/Runtime/UI/Styling/UIColor.cs new file mode 100644 index 0000000..f714304 --- /dev/null +++ b/Runtime/UI/Styling/UIColor.cs @@ -0,0 +1,15 @@ + +using Godot; +using Rokojori; + +namespace Rokojori +{ + + [Tool] + [GlobalClass] + public partial class UIColor : Resource + { + [Export] + public Color color; + } +} \ No newline at end of file diff --git a/Runtime/UI/Styling/UINumber.cs b/Runtime/UI/Styling/UINumber.cs index 96e718d..38429d1 100644 --- a/Runtime/UI/Styling/UINumber.cs +++ b/Runtime/UI/Styling/UINumber.cs @@ -34,6 +34,7 @@ namespace Rokojori return number.IsNone; } + public static float Compute( Control control, UINumber number, float alternative = 0, float relative = 100 ) { if ( number == null || control == null || number.IsNone ) @@ -41,19 +42,9 @@ namespace Rokojori return alternative; } - var width = 0; - var height = 0; + var width = UI.GetWindowWidth( control ); + var height = UI.GetWindowHeight( control ); - if ( Engine.IsEditorHint() ) - { - width = ProjectSettings.GetSetting( "display/window/size/viewport_width" ).AsInt32(); - height = ProjectSettings.GetSetting( "display/window/size/viewport_height" ).AsInt32(); - } - else - { - width = control.GetWindow().Size.X; - height = control.GetWindow().Size.Y; - } return Compute( control, number, width, height, relative ); @@ -75,7 +66,7 @@ namespace Rokojori if ( _ui == null ) { - // _ui = NodesWalker.Get().GetParent( control, n => n is UI ) as UI; + _ui = NodesWalker.Get().GetInParents( control, n => n is UI ) as UI; } switch ( number.unit ) @@ -95,6 +86,18 @@ namespace Rokojori return number.value * height / 100f; } + case "pw": + { + var parent = control.GetParent(); + return number.value / 100f * ( parent == null ? width : parent.Size.X ); + } + + case "ph": + { + var parent = control.GetParent(); + return number.value / 100f * ( parent == null ? height : parent.Size.Y ); + } + case "px": case "": { return number.value; @@ -110,17 +113,25 @@ namespace Rokojori var expressionText = number.unit == null ? "" : RegexUtility.Replace( number.unit, "%", " * relative " ); - var parseResult = expression.Parse( expressionText, new string[]{ "em","vw", "vh", "px", "relative", "value" } ); + var parseResult = expression.Parse( expressionText, + new string[] + { + "em","vw", "vh", "pw", "ph", "px", "relative", "value" } + ); if ( Error.Ok != parseResult ) { return 0; } + var parentControl = control.GetParent(); + var inputs = new Godot.Collections.Array(); inputs.Add( em() ); inputs.Add( width / 100f ); inputs.Add( height / 100f ); + inputs.Add( ( parentControl == null ? width : parentControl.Size.X ) / 100f ); + inputs.Add( ( parentControl == null ? height : parentControl.Size.Y ) / 100f ); inputs.Add( 1 ); inputs.Add( relative / 100f ); diff --git a/Runtime/UI/Styling/UIPosition.cs b/Runtime/UI/Styling/UIPosition.cs index 353dc94..65f958f 100644 --- a/Runtime/UI/Styling/UIPosition.cs +++ b/Runtime/UI/Styling/UIPosition.cs @@ -6,6 +6,7 @@ namespace Rokojori { public enum UIPosition { + ___, From_Layout, Parent_Anchor } diff --git a/Runtime/UI/Styling/UIStyle.cs b/Runtime/UI/Styling/UIStyle.cs index 8f8ac19..6e21252 100644 --- a/Runtime/UI/Styling/UIStyle.cs +++ b/Runtime/UI/Styling/UIStyle.cs @@ -7,106 +7,236 @@ namespace Rokojori [Tool] [GlobalClass] - public partial class UIStyle:Resource + public partial class UIStyle:Resource, UIStylePropertyContainer { [Export] - public UIStyle parent; - - [Export] - public UILayout layoutType; + public UIStyle parentStyle; - [ExportCategory("Width")] + [ExportCategory( "Layout" )] [Export] - public float widthValue = 1; - [Export] - public string widthUnit = "vw"; + public UILayout layout; - [ExportCategory("Height")] [Export] - public float heightValue = 1; + public UINumber horizontalAlignment; [Export] - public string heightUnit = "vw"; + public UINumber verticalAlignment; - [ExportCategory("LineSpacing")] [Export] - public float lineSpacingValue = 1; + public UINumber elementSpacing; [Export] - public string ineSpacingtUnit = "vw"; + public UINumber lineSpacing; + + + [ExportCategory( "Size & Margins" )] + [Export] + public UINumber width; + [Export] + public UINumber height; + + + [Export] + public UINumber margin; + + [Export] + public UINumber marginLeft; + [Export] + public UINumber marginTop; + [Export] + public UINumber marginRight; + [Export] + public UINumber marginBottom; + + [ExportCategory( "Font" )] + [Export] + public Font font; + [Export] + public UINumber fontSize; + [Export] + public UIColor fontColor; + + [Export] + public UINumber outlineSize; + [Export] + public UIColor outlineColor; + + [Export] + public UINumber shadowSize; + [Export] + public UIColor shadowColor; + + [ExportCategory( "Position" )] + [Export] + public UIPosition position; + [Export] + public UINumber left; + [Export] + public UINumber top; + [Export] + public UINumber right; + [Export] + public UINumber bottom; + public UIStyle GetUIStyleParent() + { + return parentStyle; + } + + public UIPosition GetUIPosition() + { + return position; + } + + public UINumber GetUIStyleNumberProperty( UIStyleNumberProperty property ) + { + switch ( property ) + { + case UIStyleNumberProperty.Left: return left; + case UIStyleNumberProperty.Right: return right; + case UIStyleNumberProperty.Top: return top; + case UIStyleNumberProperty.Bottom: return bottom; + + case UIStyleNumberProperty.ElementSpacing: return elementSpacing; + case UIStyleNumberProperty.LineSpacing: return lineSpacing; + + + case UIStyleNumberProperty.Width: return width; + case UIStyleNumberProperty.Height: return height; + + case UIStyleNumberProperty.Margin: return margin; + + case UIStyleNumberProperty.MarginLeft: return marginLeft; + case UIStyleNumberProperty.MarginRight: return marginRight; + case UIStyleNumberProperty.MarginTop: return marginTop; + case UIStyleNumberProperty.MarginBottom: return marginBottom; + } + + return null; + } + + public static UINumber GetReferenceableNumberProperty( UIStylePropertyContainer container, UIStyleNumberProperty property ) + { + if ( container == null ) + { + return null; + } + + var ownProperty = container.GetUIStyleNumberProperty( property ); + + if ( ownProperty != null ) + { + return ownProperty; + } + + var style = container.GetUIStyleParent(); + + return GetReferenceableNumberProperty( style, property ); + } + + public static UIPosition Position( UIStylePropertyContainer container ) + { + var ownProperty = container.GetUIPosition(); + + if ( ownProperty != UIPosition.___ ) + { + return ownProperty; + } + + return container.GetUIPosition(); + } + + public static UINumber Width( UIStylePropertyContainer container ) + { + return GetReferenceableNumberProperty( container, UIStyleNumberProperty.Width ); + } + + public static UINumber Height( UIStylePropertyContainer container ) + { + return GetReferenceableNumberProperty( container, UIStyleNumberProperty.Height ); + } public static UINumber Left( UIStylePropertyContainer container ) { - return container.GetUIStyleNumberProperty( UIStyleNumberProperty.Left ); + return GetReferenceableNumberProperty( container, UIStyleNumberProperty.Left ); } public static UINumber Right( UIStylePropertyContainer container ) { - return container.GetUIStyleNumberProperty( UIStyleNumberProperty.Right ); + return GetReferenceableNumberProperty( container, UIStyleNumberProperty.Right ); } public static UINumber Top( UIStylePropertyContainer container ) { - return container.GetUIStyleNumberProperty( UIStyleNumberProperty.Top ); + return GetReferenceableNumberProperty( container, UIStyleNumberProperty.Top ); } public static UINumber Bottom( UIStylePropertyContainer container ) { - return container.GetUIStyleNumberProperty( UIStyleNumberProperty.Bottom ); + return GetReferenceableNumberProperty( container, UIStyleNumberProperty.Bottom ); } public static UINumber Margin( UIStylePropertyContainer container ) { - return container.GetUIStyleNumberProperty( UIStyleNumberProperty.Margin ); + return GetReferenceableNumberProperty( container, UIStyleNumberProperty.Margin ); } public static UINumber MarginLeft( UIStylePropertyContainer container ) { - return container.GetUIStyleNumberProperty( UIStyleNumberProperty.MarginLeft ); + return GetReferenceableNumberProperty( container, UIStyleNumberProperty.MarginLeft ); } public static UINumber MarginRight( UIStylePropertyContainer container ) { - return container.GetUIStyleNumberProperty( UIStyleNumberProperty.MarginRight ); + return GetReferenceableNumberProperty( container, UIStyleNumberProperty.MarginRight ); } public static UINumber MarginTop( UIStylePropertyContainer container ) { - return container.GetUIStyleNumberProperty( UIStyleNumberProperty.MarginTop ); + return GetReferenceableNumberProperty( container, UIStyleNumberProperty.MarginTop ); } public static UINumber MarginBottom( UIStylePropertyContainer container ) { - return container.GetUIStyleNumberProperty( UIStyleNumberProperty.MarginBottom ); + return GetReferenceableNumberProperty( container, UIStyleNumberProperty.MarginBottom ); } public static UINumber PivotX( UIStylePropertyContainer container ) { - return container.GetUIStyleNumberProperty( UIStyleNumberProperty.PivotX ); + return GetReferenceableNumberProperty( container, UIStyleNumberProperty.PivotX ); } public static UINumber PivotY( UIStylePropertyContainer container ) { - return container.GetUIStyleNumberProperty( UIStyleNumberProperty.PivotY ); + return GetReferenceableNumberProperty( container, UIStyleNumberProperty.PivotY ); } public static UINumber Rotation( UIStylePropertyContainer container ) { - return container.GetUIStyleNumberProperty( UIStyleNumberProperty.Rotation ); + return GetReferenceableNumberProperty( container, UIStyleNumberProperty.Rotation ); } public static UINumber Scale( UIStylePropertyContainer container ) { - return container.GetUIStyleNumberProperty( UIStyleNumberProperty.Scale ); + return GetReferenceableNumberProperty( container, UIStyleNumberProperty.Scale ); } public static UINumber ScaleX( UIStylePropertyContainer container ) { - return container.GetUIStyleNumberProperty( UIStyleNumberProperty.ScaleX ); + return GetReferenceableNumberProperty( container, UIStyleNumberProperty.ScaleX ); } public static UINumber ScaleY( UIStylePropertyContainer container ) { - return container.GetUIStyleNumberProperty( UIStyleNumberProperty.ScaleY ); + return GetReferenceableNumberProperty( container, UIStyleNumberProperty.ScaleY ); + } + + public static UINumber ElementSpacing( UIStylePropertyContainer container ) + { + return GetReferenceableNumberProperty( container, UIStyleNumberProperty.ElementSpacing ); + } + + public static UINumber LineSpacing( UIStylePropertyContainer container ) + { + return GetReferenceableNumberProperty( container, UIStyleNumberProperty.LineSpacing ); } } diff --git a/Runtime/UI/Styling/UIStyleProperty.cs b/Runtime/UI/Styling/UIStyleProperty.cs index 2f03ec8..ade5289 100644 --- a/Runtime/UI/Styling/UIStyleProperty.cs +++ b/Runtime/UI/Styling/UIStyleProperty.cs @@ -14,6 +14,9 @@ namespace Rokojori Width, Height, + ElementSpacing, + LineSpacing, + Margin, MarginLeft, MarginRight, @@ -27,6 +30,17 @@ namespace Rokojori Scale, ScaleX, - ScaleY + ScaleY, + + FontSize, + FontOutlineSize, + FontShadowSize + } + + public enum UIStyleColorProperty + { + FontColor, + FontOutlineColor, + FontShadowColor } } \ No newline at end of file diff --git a/Runtime/UI/Styling/UIStylePropertyContainer.cs b/Runtime/UI/Styling/UIStylePropertyContainer.cs index c54e37f..c17e19c 100644 --- a/Runtime/UI/Styling/UIStylePropertyContainer.cs +++ b/Runtime/UI/Styling/UIStylePropertyContainer.cs @@ -6,6 +6,9 @@ namespace Rokojori { public interface UIStylePropertyContainer { + UIStyle GetUIStyleParent(); + + UIPosition GetUIPosition(); UINumber GetUIStyleNumberProperty( UIStyleNumberProperty property ); } } \ No newline at end of file diff --git a/Runtime/UI/UI.cs b/Runtime/UI/UI.cs index 82fce08..f90a0b8 100644 --- a/Runtime/UI/UI.cs +++ b/Runtime/UI/UI.cs @@ -29,7 +29,7 @@ namespace Rokojori UpdateUIElements(); } - + void UpdateFontSize() { @@ -53,6 +53,30 @@ namespace Rokojori Nodes.ForEachDirectChild( this, r => r.Layout() ); } + + public static float GetWindowWidth( Control control ) + { + if ( Engine.IsEditorHint() ) + { + return ProjectSettings.GetSetting( "display/window/size/viewport_width" ).AsInt32(); + } + else + { + return control.GetWindow().Size.X; + } + } + + public static float GetWindowHeight( Control control ) + { + if ( Engine.IsEditorHint() ) + { + return ProjectSettings.GetSetting( "display/window/size/viewport_height" ).AsInt32(); + } + else + { + return control.GetWindow().Size.Y; + } + } }