Compare commits
	
		
			12 Commits
		
	
	
		
			ceebe21041
			...
			85bad951ff
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 85bad951ff | |||
| 6a84c3ec1a | |||
| ceb29cc42f | |||
| c6d2bad23e | |||
| 2bfd391286 | |||
| ac09b78edd | |||
| 4607955d55 | |||
| 271a9a244b | |||
| f980546d37 | |||
| dfcc877e58 | |||
| 8ebde9dedf | |||
| 238bf2d574 | 
@@ -19,6 +19,7 @@ public static class Vector2DExtensions
 | 
			
		||||
    public static Vector2D FromTo(this Vector2D from, Vector2D to) => Vector2D.FromTo(from, to);
 | 
			
		||||
    public static Vector2D Scale(this Vector2D vector, Vector2D scale) => Vector2D.Scale(vector, scale);
 | 
			
		||||
 | 
			
		||||
    public static Vector2D Perpendicular(this Vector2D vector) => Vector2D.Perpendicular(vector);
 | 
			
		||||
    public static Vector2D Rotate(this Vector2D vector, float angleInRadian) => Vector2D.Rotate(vector, angleInRadian);
 | 
			
		||||
    public static Vector2D Min(this Vector2D left, Vector2D right) => Vector2D.Min(left, right);
 | 
			
		||||
    public static Vector2D Max(this Vector2D left, Vector2D right) => Vector2D.Max(left, right);
 | 
			
		||||
 
 | 
			
		||||
@@ -40,6 +40,7 @@ public record Vector2D(float X, float Y)
 | 
			
		||||
    public static Vector2D FromTo(Vector2D from, Vector2D to) => to - from;
 | 
			
		||||
    public static Vector2D Scale(Vector2D vector, Vector2D scale) => new(vector.X * scale.X, vector.Y * scale.Y);
 | 
			
		||||
 | 
			
		||||
    public static Vector2D Perpendicular(Vector2D vector) => new(-vector.Y, vector.X);
 | 
			
		||||
    public static Vector2D Rotate(Vector2D vector, float angleInRadian) => new(MathF.Cos(angleInRadian) * vector.X - MathF.Sin(angleInRadian) * vector.Y, MathF.Sin(angleInRadian) * vector.X + MathF.Cos(angleInRadian) * vector.Y);
 | 
			
		||||
    public static Vector2D Min(Vector2D left, Vector2D right) => new((left.X < right.X) ? left.X : right.X, (left.Y < right.Y) ? left.Y : right.Y);
 | 
			
		||||
    public static Vector2D Max(Vector2D left, Vector2D right) => new((left.X > right.X) ? left.X : right.X, (left.Y > right.Y) ? left.Y : right.Y);
 | 
			
		||||
 
 | 
			
		||||
@@ -39,13 +39,15 @@ public abstract class Collider2DBehaviourBase : BehaviourOverride, ICollider2D
 | 
			
		||||
        BehaviourController.OnBehaviourAdded += OnBehaviourAddedToController;
 | 
			
		||||
        BehaviourController.OnBehaviourRemoved += OnBehaviourRemovedFromController;
 | 
			
		||||
 | 
			
		||||
        Transform.OnPositionChanged += OnPositionChanged;
 | 
			
		||||
        Transform.OnPositionChanged += SetNeedsRecalculation;
 | 
			
		||||
        Transform.OnRotationChanged += SetNeedsRecalculation;
 | 
			
		||||
        Transform.OnScaleChanged += SetNeedsRecalculation;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void OnBehaviourAddedToController(IBehaviourController _, IBehaviour behaviour)
 | 
			
		||||
    {
 | 
			
		||||
        if (behaviour is IRigidBody2D rigidbody)
 | 
			
		||||
            _rigidBody2D = rigidbody;
 | 
			
		||||
        if (behaviour is IRigidBody2D rigidBody)
 | 
			
		||||
            _rigidBody2D = rigidBody;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void OnBehaviourRemovedFromController(IBehaviourController _, IBehaviour behaviour)
 | 
			
		||||
@@ -54,13 +56,15 @@ public abstract class Collider2DBehaviourBase : BehaviourOverride, ICollider2D
 | 
			
		||||
            _rigidBody2D = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void OnPositionChanged(ITransform transform) => NeedsRecalculation = true;
 | 
			
		||||
    private void SetNeedsRecalculation(ITransform transform) => NeedsRecalculation = true;
 | 
			
		||||
 | 
			
		||||
    protected override void OnFinalize()
 | 
			
		||||
    {
 | 
			
		||||
        BehaviourController.OnBehaviourAdded -= OnBehaviourAddedToController;
 | 
			
		||||
        BehaviourController.OnBehaviourRemoved -= OnBehaviourRemovedFromController;
 | 
			
		||||
        Transform.OnScaleChanged -= SetNeedsRecalculation;
 | 
			
		||||
 | 
			
		||||
        Transform.OnPositionChanged -= OnPositionChanged;
 | 
			
		||||
        Transform.OnPositionChanged -= SetNeedsRecalculation;
 | 
			
		||||
        Transform.OnRotationChanged -= SetNeedsRecalculation;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis;
 | 
			
		||||
 | 
			
		||||
using Syntriax.Engine.Core;
 | 
			
		||||
using Syntriax.Engine.Physics2D.Abstract;
 | 
			
		||||
using Syntriax.Engine.Physics2D.Primitives;
 | 
			
		||||
 | 
			
		||||
namespace Syntriax.Engine.Physics2D;
 | 
			
		||||
 | 
			
		||||
@@ -30,36 +31,104 @@ public class CollisionDetector : ICollisionDetector
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private bool DetectCircleShape(ICircleCollider2D circleCollider, IShapeCollider2D shapeCollider, out CollisionDetectionInformation? collisionInformation)
 | 
			
		||||
    private static bool DetectCircleShape(ICircleCollider2D circleCollider, IShapeCollider2D shapeCollider, out CollisionDetectionInformation? collisionInformation)
 | 
			
		||||
    {
 | 
			
		||||
        throw new System.NotImplementedException();
 | 
			
		||||
        return DetectShapeCircle(shapeCollider, circleCollider, out collisionInformation);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static bool DetectShapeShape(IShapeCollider2D left, IShapeCollider2D right, out CollisionDetectionInformation? collisionInformation)
 | 
			
		||||
    {
 | 
			
		||||
        throw new System.NotImplementedException();
 | 
			
		||||
        collisionInformation = default;
 | 
			
		||||
 | 
			
		||||
        var vertices = left.ShapeWorld.Vertices;
 | 
			
		||||
        int count = vertices.Count;
 | 
			
		||||
 | 
			
		||||
        for (int indexProjection = 0; indexProjection < count; indexProjection++)
 | 
			
		||||
        {
 | 
			
		||||
            Vector2D projectionVector = vertices[indexProjection].FromTo(vertices[(indexProjection + 1) % count]).Perpendicular().Normalized;
 | 
			
		||||
 | 
			
		||||
            Projection leftProjection = left.ShapeWorld.ToProjection(projectionVector);
 | 
			
		||||
            Projection rightProjection = right.ShapeWorld.ToProjection(projectionVector);
 | 
			
		||||
 | 
			
		||||
            if (!leftProjection.Overlaps(rightProjection, out float depth))
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            if (collisionInformation == default || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth))
 | 
			
		||||
                collisionInformation = new(left, right, projectionVector, depth);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static bool DetectShapeCircle(IShapeCollider2D shapeCollider, ICircleCollider2D circleCollider, out CollisionDetectionInformation? collisionInformation)
 | 
			
		||||
    {
 | 
			
		||||
        throw new System.NotImplementedException();
 | 
			
		||||
        collisionInformation = default;
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            Vector2D shapeToCircleProjectionVector = shapeCollider.Transform.Position.FromTo(circleCollider.CircleWorld.Center).Normalized;
 | 
			
		||||
 | 
			
		||||
            Projection shapeProjection = shapeCollider.ShapeWorld.ToProjection(shapeToCircleProjectionVector);
 | 
			
		||||
            Projection circleProjection = circleCollider.CircleWorld.ToProjection(shapeToCircleProjectionVector);
 | 
			
		||||
 | 
			
		||||
            if (!shapeProjection.Overlaps(circleProjection, out float depth))
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            if (collisionInformation == default || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth))
 | 
			
		||||
                collisionInformation = new(shapeCollider, circleCollider, shapeToCircleProjectionVector, depth);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var vertices = shapeCollider.ShapeWorld.Vertices;
 | 
			
		||||
        int count = vertices.Count;
 | 
			
		||||
 | 
			
		||||
        for (int indexProjection = 0; indexProjection < count; indexProjection++)
 | 
			
		||||
        {
 | 
			
		||||
            Vector2D projectionVector = vertices[indexProjection].FromTo(vertices[(indexProjection + 1) % count]).Perpendicular().Normalized;
 | 
			
		||||
 | 
			
		||||
            Projection shapeProjection = shapeCollider.ShapeWorld.ToProjection(projectionVector);
 | 
			
		||||
            Projection circleProjection = circleCollider.CircleWorld.ToProjection(projectionVector);
 | 
			
		||||
 | 
			
		||||
            if (!shapeProjection.Overlaps(circleProjection, out float depth))
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            if (collisionInformation == default || Math.Abs(collisionInformation.Penetration) > Math.Abs(depth))
 | 
			
		||||
                collisionInformation = new(shapeCollider, circleCollider, projectionVector, depth);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static bool DetectCircleCircle(ICircleCollider2D left, ICircleCollider2D right, out CollisionDetectionInformation? collisionInformation)
 | 
			
		||||
    {
 | 
			
		||||
        collisionInformation = default;
 | 
			
		||||
 | 
			
		||||
        Vector2D leftToRightCenter = left.CircleWorld.Center.FromTo(right.CircleWorld.Center);
 | 
			
		||||
        float distanceCircleCenter = leftToRightCenter.Magnitude;
 | 
			
		||||
        float radiusSum = left.CircleWorld.Radius + right.CircleWorld.Radius;
 | 
			
		||||
        Vector2D leftToRightCenterProjectionVector = left.CircleWorld.Center.FromTo(right.CircleWorld.Center).Normalized;
 | 
			
		||||
 | 
			
		||||
        float circleSurfaceDistance = distanceCircleCenter - radiusSum;
 | 
			
		||||
        Projection leftProjection = left.CircleWorld.ToProjection(leftToRightCenterProjectionVector);
 | 
			
		||||
        Projection rightProjection = right.CircleWorld.ToProjection(leftToRightCenterProjectionVector);
 | 
			
		||||
 | 
			
		||||
        bool collision = circleSurfaceDistance <= 0f;
 | 
			
		||||
        bool collision = leftProjection.Overlaps(rightProjection, out float depth);
 | 
			
		||||
 | 
			
		||||
        if (collision)
 | 
			
		||||
            collisionInformation = new(left, right, leftToRightCenter.Normalized, -circleSurfaceDistance);
 | 
			
		||||
            collisionInformation = new(left, right, leftToRightCenterProjectionVector, depth);
 | 
			
		||||
 | 
			
		||||
        return collision;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // private static bool DetectCircleCircle(ICircleCollider2D left, ICircleCollider2D right, out CollisionDetectionInformation? collisionInformation)
 | 
			
		||||
    // {
 | 
			
		||||
    //     collisionInformation = default;
 | 
			
		||||
 | 
			
		||||
    //     Vector2D leftToRightCenter = left.CircleWorld.Center.FromTo(right.CircleWorld.Center);
 | 
			
		||||
    //     float distanceCircleCenter = leftToRightCenter.Magnitude;
 | 
			
		||||
    //     float radiusSum = left.CircleWorld.Radius + right.CircleWorld.Radius;
 | 
			
		||||
 | 
			
		||||
    //     float circleSurfaceDistance = distanceCircleCenter - radiusSum;
 | 
			
		||||
 | 
			
		||||
    //     bool collision = circleSurfaceDistance <= 0f;
 | 
			
		||||
 | 
			
		||||
    //     if (collision)
 | 
			
		||||
    //         collisionInformation = new(left, right, leftToRightCenter.Normalized, -circleSurfaceDistance);
 | 
			
		||||
 | 
			
		||||
    //     return collision;
 | 
			
		||||
    // }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,12 @@ public record Circle(Vector2D Center, float Radius)
 | 
			
		||||
 | 
			
		||||
    public static Circle Displace(Circle circle, Vector2D displaceVector) => new(circle.Center + displaceVector, circle.Radius);
 | 
			
		||||
 | 
			
		||||
    public static Projection Project(Circle circle, Vector2D projectionVector)
 | 
			
		||||
    {
 | 
			
		||||
        float projectedCenter = circle.Center.Dot(projectionVector);
 | 
			
		||||
        return new(projectedCenter - circle.Radius, projectedCenter + circle.Radius);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Circle TransformCircle(ITransform transform, Circle circle)
 | 
			
		||||
        => new(transform.TransformVector2D(circle.Center), circle.Radius * transform.Scale.Magnitude);
 | 
			
		||||
 | 
			
		||||
@@ -27,6 +33,8 @@ public static class CircleExtensions
 | 
			
		||||
 | 
			
		||||
    public static Circle Displace(this Circle circle, Vector2D displaceVector) => Circle.Displace(circle, displaceVector);
 | 
			
		||||
 | 
			
		||||
    public static Projection ToProjection(this Circle circle, Vector2D projectionVector) => Circle.Project(circle, projectionVector);
 | 
			
		||||
 | 
			
		||||
    public static Circle TransformCircle(this ITransform transform, Circle circle) => Circle.TransformCircle(transform, circle);
 | 
			
		||||
 | 
			
		||||
    public static bool ApproximatelyEquals(this Circle left, Circle right) => Circle.ApproximatelyEquals(left, right);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										45
									
								
								Engine.Physics2D/Primitives/Projection.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								Engine.Physics2D/Primitives/Projection.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
namespace Syntriax.Engine.Physics2D.Primitives;
 | 
			
		||||
 | 
			
		||||
public record Projection(float Min, float Max)
 | 
			
		||||
{
 | 
			
		||||
    public static bool Overlaps(Projection left, Projection right) => Overlaps(left, right, out var _);
 | 
			
		||||
    public static bool Overlaps(Projection left, Projection right, out float depth)
 | 
			
		||||
    {
 | 
			
		||||
        // TODO Try to improve this
 | 
			
		||||
        bool rightMinInLeft = right.Min > left.Min && right.Min < left.Max;
 | 
			
		||||
        if (rightMinInLeft)
 | 
			
		||||
        {
 | 
			
		||||
            depth = left.Max - right.Min;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bool rightMaxInLeft = right.Max < left.Max && right.Max > left.Min;
 | 
			
		||||
        if (rightMaxInLeft)
 | 
			
		||||
        {
 | 
			
		||||
            depth = left.Min - right.Max;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bool leftMinInRight = left.Min > right.Min && left.Min < right.Max;
 | 
			
		||||
        if (leftMinInRight)
 | 
			
		||||
        {
 | 
			
		||||
            depth = right.Max - left.Min;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        bool leftMaxInRight = left.Max < right.Max && left.Max > right.Min;
 | 
			
		||||
        if (leftMaxInRight)
 | 
			
		||||
        {
 | 
			
		||||
            depth = right.Min - left.Max;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        depth = 0f;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
public static class ProjectionExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static bool Overlaps(this Projection left, Projection right) => Projection.Overlaps(left, right);
 | 
			
		||||
    public static bool Overlaps(this Projection left, Projection right, out float depth) => Projection.Overlaps(left, right, out depth);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
@@ -9,7 +8,31 @@ namespace Syntriax.Engine.Physics2D.Primitives;
 | 
			
		||||
 | 
			
		||||
public record Shape(IList<Vector2D> Vertices) : IEnumerable<Vector2D>
 | 
			
		||||
{
 | 
			
		||||
    public Vector2D this[Index index] => Vertices[index];
 | 
			
		||||
    public static readonly Shape Triangle = CreateNgon(3, Vector2D.Up);
 | 
			
		||||
    public static readonly Shape Box = CreateNgon(4, Vector2D.One);
 | 
			
		||||
    public static readonly Shape Pentagon = CreateNgon(5, Vector2D.Up);
 | 
			
		||||
    public static readonly Shape Hexagon = CreateNgon(6, Vector2D.Right);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public Vector2D this[System.Index index] => Vertices[index];
 | 
			
		||||
 | 
			
		||||
    public static Shape CreateCopy(Shape shape) => new(new List<Vector2D>(shape.Vertices));
 | 
			
		||||
 | 
			
		||||
    public static Shape CreateNgon(int vertexCount) => CreateNgon(vertexCount, Vector2D.Up);
 | 
			
		||||
    public static Shape CreateNgon(int vertexCount, Vector2D positionToRotate)
 | 
			
		||||
    {
 | 
			
		||||
        if (vertexCount < 3)
 | 
			
		||||
            throw new System.ArgumentException($"{nameof(vertexCount)} must have a value of more than 2.");
 | 
			
		||||
 | 
			
		||||
        List<Vector2D> vertices = new(vertexCount);
 | 
			
		||||
 | 
			
		||||
        float radiansPerVertex = 360f / vertexCount * Math.DegreeToRadian;
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < vertexCount; i++)
 | 
			
		||||
            vertices.Add(positionToRotate.Rotate(i * radiansPerVertex));
 | 
			
		||||
 | 
			
		||||
        return new(vertices);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Triangle GetSuperTriangle(Shape shape)
 | 
			
		||||
    {
 | 
			
		||||
@@ -18,15 +41,15 @@ public record Shape(IList<Vector2D> Vertices) : IEnumerable<Vector2D>
 | 
			
		||||
 | 
			
		||||
        foreach (Vector2D point in shape.Vertices)
 | 
			
		||||
        {
 | 
			
		||||
            minX = MathF.Min(minX, point.X);
 | 
			
		||||
            minY = MathF.Min(minY, point.Y);
 | 
			
		||||
            maxX = MathF.Max(maxX, point.X);
 | 
			
		||||
            maxY = MathF.Max(maxY, point.Y);
 | 
			
		||||
            minX = Math.Min(minX, point.X);
 | 
			
		||||
            minY = Math.Min(minY, point.Y);
 | 
			
		||||
            maxX = Math.Max(maxX, point.X);
 | 
			
		||||
            maxY = Math.Max(maxY, point.Y);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        float dx = maxX - minX;
 | 
			
		||||
        float dy = maxY - minY;
 | 
			
		||||
        float deltaMax = MathF.Max(dx, dy);
 | 
			
		||||
        float deltaMax = Math.Max(dx, dy);
 | 
			
		||||
        float midX = (minX + maxX) / 2;
 | 
			
		||||
        float midY = (minY + maxY) / 2;
 | 
			
		||||
 | 
			
		||||
@@ -52,6 +75,30 @@ public record Shape(IList<Vector2D> Vertices) : IEnumerable<Vector2D>
 | 
			
		||||
        return lines;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void Project(Shape shape, Vector2D projectionVector, IList<float> list)
 | 
			
		||||
    {
 | 
			
		||||
        list.Clear();
 | 
			
		||||
 | 
			
		||||
        int count = shape.Vertices.Count;
 | 
			
		||||
        for (int i = 0; i < count; i++)
 | 
			
		||||
            list.Add(projectionVector.Dot(shape[i]));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Projection Project(Shape shape, Vector2D projectionVector)
 | 
			
		||||
    {
 | 
			
		||||
        float min = float.MaxValue;
 | 
			
		||||
        float max = float.MinValue;
 | 
			
		||||
 | 
			
		||||
        foreach (var vertex in shape)
 | 
			
		||||
        {
 | 
			
		||||
            float projectedLength = projectionVector.Dot(vertex);
 | 
			
		||||
            min = Math.Min(projectedLength, min);
 | 
			
		||||
            max = Math.Max(projectedLength, max);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return new(min, max);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Shape TransformShape(Shape shape, ITransform transform)
 | 
			
		||||
    {
 | 
			
		||||
        List<Vector2D> vertices = new(shape.Vertices.Count);
 | 
			
		||||
@@ -90,10 +137,14 @@ public record Shape(IList<Vector2D> Vertices) : IEnumerable<Vector2D>
 | 
			
		||||
 | 
			
		||||
public static class ShapeExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static Shape CreateCopy(Shape shape) => Shape.CreateCopy(shape);
 | 
			
		||||
    public static Triangle ToSuperTriangle(this Shape shape) => Shape.GetSuperTriangle(shape);
 | 
			
		||||
    public static void ToLines(this Shape shape, IList<Line> lines) => Shape.GetLines(shape, lines);
 | 
			
		||||
    public static List<Line> ToLines(this Shape shape) => Shape.GetLines(shape);
 | 
			
		||||
 | 
			
		||||
    public static void ToProjection(this Shape shape, Vector2D projectionVector, IList<float> list) => Shape.Project(shape, projectionVector, list);
 | 
			
		||||
    public static Projection ToProjection(this Shape shape, Vector2D projectionVector) => Shape.Project(shape, projectionVector);
 | 
			
		||||
 | 
			
		||||
    public static Shape TransformShape(this ITransform transform, Shape shape) => Shape.TransformShape(shape, transform);
 | 
			
		||||
    public static void TransformShape(this ITransform transform, Shape from, ref Shape to) => Shape.TransformShape(from, transform, ref to);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user