Subversion Repositories SoapBoxCore

Rev

Blame | Last modification | View Log | RSS feed

#region MIT License
/*
 * Copyright (c) 2005-2008 Jonathan Mark Porter. http://physics2d.googlepages.com/
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#endregion




#if UseDouble
using Scalar = System.Double;
#else
using Scalar = System.Single;
#endif
using System;
using System.Collections.Generic;

using AdvanceMath;
using AdvanceMath.Geometry2D;


namespace Physics2DDotNet.Shapes
{
    /// <summary>
    /// A shape that contains multiple polygons.
    /// </summary>
    [Serializable]
    public sealed class MultiPolygonShape : IShape, IRaySegmentsCollidable, ILineFluidAffectable, IExplosionAffectable
    {
        #region static
        private static Vector2D[] ConcatVertexes(Vector2D[][] polygons)
        {

            int totalLength = 0;
            Vector2D[] polygon;
            for (int index = 0; index < polygons.Length; ++index)
            {
                polygon = polygons[index];
                if (polygon == null) { throw new ArgumentNullException("polygons"); }
                if (polygon.Length < 3) { throw new ArgumentException("too few", "polygons"); }
                totalLength += polygon.Length;
            }
            Vector2D[] result = new Vector2D[totalLength];
            int offset = 0;
            for (int index = 0; index < polygons.Length; ++index)
            {
                polygon = polygons[index];
                polygon.CopyTo(result, offset);
                offset += polygon.Length;
            }
            return result;
        }
        #endregion
        #region fields
        private DistanceGrid grid;
        private Vector2D[][] polygons;
        private Vector2D[] vertexNormals;
        private Vector2D[] vertexes;
        private Vector2D centroid;
        private Scalar area;
        private Scalar inertia;
        private object tag;
        #endregion
        #region constructors
        public MultiPolygonShape(Vector2D[][] polygons, Scalar gridSpacing)
        {
            if (gridSpacing <= 0) { throw new ArgumentOutOfRangeException("gridSpacing"); }
            if (polygons == null) { throw new ArgumentNullException("polygons"); }
            if (polygons.Length == 0) { throw new ArgumentOutOfRangeException("polygons"); }
            this.polygons = polygons;
            this.vertexes = ConcatVertexes(polygons);
            this.vertexNormals = CalculateNormals();
            this.grid = new DistanceGrid(this, gridSpacing);
            VertexInfo info = VertexHelper.GetVertexInfoOfRange(polygons);
            this.inertia = info.Inertia;
            this.centroid = info.Centroid;
            this.area = info.Area;
        }
        public MultiPolygonShape(Vector2D[][] polygons, Scalar gridSpacing, Scalar inertia)
            : this(polygons, gridSpacing)
        {
            this.inertia = inertia;
        }
        #endregion
        #region properties
        public object Tag
        {
            get { return tag; }
            set { tag = value; }
        }
        public Vector2D[] Vertexes { get { return vertexes; } }
        public Vector2D[] VertexNormals { get { return vertexNormals; } }
        public Scalar Inertia
        {
            get { return inertia; }
        }
        public Vector2D Centroid
        {
            get { return centroid; }
        }
        public Scalar Area
        {
            get { return area; }
        }

        public Vector2D[][] Polygons
        {
            get { return polygons; }
        }

        public bool CanGetIntersection
        {
            get { return true; }
        }
        public bool CanGetCustomIntersection
        {
            get { return false; }
        }
        #endregion
        #region methods
        private Vector2D[] CalculateNormals()
        {
            Vector2D[] result = new Vector2D[Vertexes.Length];
            int offset = 0;
            for (int index = 0; index < polygons.Length; ++index)
            {
                Vector2D[] polygon = polygons[index];
                VertexHelper.CalculateNormals(polygon, result, offset);
                offset += polygon.Length;
            }
            return result;
        }

        public void CalcBoundingRectangle(ref Matrix2x3 matrix, out BoundingRectangle rectangle)
        {
            BoundingRectangle.FromVectors(ref matrix, Vertexes, out rectangle);
        }

        public bool TryGetIntersection(Vector2D point, out IntersectionInfo info)
        {
            return grid.TryGetIntersection(point, out info);
        }

        public void GetDistance(ref Vector2D point, out Scalar result)
        {
            result = Scalar.MaxValue;
            Scalar temp;
            for (int index = 0; index < polygons.Length; ++index)
            {
                BoundingPolygon.GetDistance(polygons[index], ref point, out temp);
                if (temp < result)
                {
                    result = temp;
                }
            }
        }
        public bool TryGetCustomIntersection(Body self, Body other, out object customIntersectionInfo)
        {
            throw new NotSupportedException();
        }

        bool IRaySegmentsCollidable.TryGetRayCollision(Body thisBody, Body raysBody, RaySegmentsShape raySegments, out RaySegmentIntersectionInfo info)
        {
            bool intersects = false;
            RaySegment[] segments = raySegments.Segments;
            Scalar[] result = new Scalar[segments.Length];
            Scalar temp;
            Vector2D[][] polygons = this.Polygons;
            for (int index = 0; index < segments.Length; ++index)
            {
                result[index] = -1;
            }
            Matrix2x3 matrix = raysBody.Matrices.ToBody * thisBody.Matrices.ToWorld;
            for (int polyIndex = 0; polyIndex < polygons.Length; ++polyIndex)
            {
                Vector2D[] unTrans = polygons[polyIndex];
                Vector2D[] polygon = new Vector2D[unTrans.Length];
                for (int index = 0; index < unTrans.Length; ++index)
                {
                    Vector2D.Transform(ref matrix, ref unTrans[index], out polygon[index]);
                }
                BoundingRectangle rect;
                BoundingRectangle.FromVectors(polygon, out rect);
                BoundingPolygon poly = new BoundingPolygon(polygon);
                for (int index = 0; index < segments.Length; ++index)
                {
                    RaySegment segment = segments[index];
                    rect.Intersects(ref segment.RayInstance, out temp);
                    if (temp >= 0 && temp <= segment.Length)
                    {
                        poly.Intersects(ref segment.RayInstance, out temp);
                        if (temp >= 0 && temp <= segment.Length)
                        {
                            if (result[index] == -1 || temp < result[index])
                            {
                                result[index] = temp;
                            }
                            intersects = true;
                        }
                    }
                }
            }
            if (intersects)
            {
                info = new RaySegmentIntersectionInfo(result);
            }
            else
            {
                info = null;
            }
            return intersects;
        }
        DragInfo IGlobalFluidAffectable.GetFluidInfo(Vector2D tangent)
        {
            Vector2D dragCenter;
            Scalar dragArea;
            ShapeHelper.GetFluidInfo(polygons, tangent, out dragCenter, out dragArea);
            return new DragInfo(dragCenter, dragArea);
        }
        FluidInfo ILineFluidAffectable.GetFluidInfo(GetTangentCallback callback, Line line)
        {
            if (polygons.Length == 1)
            {
                return ShapeHelper.GetFluidInfo(Vertexes, callback, line);
            }
            List<Vector2D[]> submerged = new List<Vector2D[]>(polygons.Length);
            for (int index = 0; index < polygons.Length; ++index)
            {
                Vector2D[] vertexes = VertexHelper.GetIntersection(polygons[index], line);
                if (vertexes.Length >= 3) { submerged.Add(vertexes); }
            }
            if (submerged.Count == 0) { return null; }
            Vector2D[][] newPolygons = submerged.ToArray();
            Vector2D centroid = VertexHelper.GetCentroidOfRange(newPolygons);
            Scalar area = VertexHelper.GetAreaOfRange(newPolygons);
            Vector2D tangent = callback(centroid);
            Vector2D dragCenter;
            Scalar dragArea;
            ShapeHelper.GetFluidInfo(newPolygons, tangent, out dragCenter, out dragArea);
            return new FluidInfo(dragCenter, dragArea, centroid, area);
        }

        DragInfo IExplosionAffectable.GetExplosionInfo(Matrix2x3 matrix, Scalar radius, GetTangentCallback callback)
        {
            //TODO: do this right!
            //TODO: do this right!
            Vector2D[] vertexes2 = new Vector2D[Vertexes.Length];
            for (int index = 0; index < vertexes2.Length; ++index)
            {
                vertexes2[index] = matrix * Vertexes[index];
            }
            Vector2D[] inter = VertexHelper.GetIntersection(vertexes2, radius);
            if (inter.Length < 3) { return null; }
            Vector2D centroid = VertexHelper.GetCentroid(inter);
            Vector2D tangent = callback(centroid);
            Scalar min, max;
            ShapeHelper.GetProjectedBounds(inter, tangent, out min, out max);
            Scalar avg = (max + min) / 2;
            return new DragInfo(tangent * avg, max - min);
        }
        #endregion
    }
}