public void Bounds_ReturnsVerticesBoundingBox()
    {
        var bbox = VectorUtils.Bounds(new Vector2[] { Vector2.zero, Vector2.right, Vector2.one });

        Assert.AreEqual(Vector2.zero, bbox.min);
        Assert.AreEqual(Vector2.one, bbox.max);
    }
Beispiel #2
0
    /// <summary>
    /// Build a 2D bounding box for the shape.
    /// </summary>
    protected override void GenerateBounds()
    {
        // TO DO
        // http://www.iquilezles.org/www/articles/bezierbbox/bezierbbox.htm
        int            bezierSteps = VectorShapeUtils.bezierSteps;
        List <Vector2> pointList   = new List <Vector2>();
        float          step        = 1f / bezierSteps;

        for (int i = 0; i < vertices.Length; i++)
        {
            Vertex vert = vertices[i];

            pointList.Add(vert.position);
            if (vert.segmentCurves)
            {
                Vertex vertNext = vertices[NextIndex(i)];
                float  t        = step;
                for (int j = 1; j < bezierSteps; j++)
                {
                    pointList.Add(VectorShapeUtils.EvaluateCubicCurve(vert.position, vert.exitCP, vertNext.enterCP, vertNext.position, t));
                    t += step;
                }
            }
        }

        shapeBounds = VectorUtils.Bounds(pointList);
        boundsDirty = false;
    }
    public void Bounds_ComputesBezierPathBounds()
    {
        var path = VectorUtils.MakeArc(Vector2.zero, 0.0f, Mathf.PI / 2, 1.0f);
        var bbox = VectorUtils.Bounds(path);

        Assert.AreEqual(0.0f, bbox.min.x, VectorUtils.Epsilon);
        Assert.AreEqual(0.0f, bbox.min.y, VectorUtils.Epsilon);
        Assert.AreEqual(1.0f, bbox.max.x, VectorUtils.Epsilon);
        Assert.AreEqual(1.0f, bbox.max.y, VectorUtils.Epsilon);
    }
Beispiel #4
0
 public static Rect CalcBounds(string svg)
 {
     using (var reader = new StringReader(svg))
     {
         var sceneInfo   = SVGParser.ImportSVG(reader, ViewportOptions.DontPreserve);
         var tessOptions = SvgToPng.TessellationOptions;
         var geometry    = VectorUtils.TessellateScene(sceneInfo.Scene, tessOptions, sceneInfo.NodeOpacity);
         var vertices    = geometry.SelectMany(geom => geom.Vertices.Select(x => (geom.WorldTransform * x))).ToArray();
         var bounds      = VectorUtils.Bounds(vertices);
         return(bounds);
     }
 }
Beispiel #5
0
    private static VectorShape TryParseShapeToCircle(Shape shape, Matrix2D transform)
    {
        if (shape.Contours.Length > 1)
        {
            return(null);
        }

        BezierContour contour = shape.Contours[0];

        if (contour.Segments.Length < 5)
        {
            return(null);
        }
        if (!contour.Closed)
        {
            return(null);
        }

        BezierSegment[] segments = new BezierSegment[contour.Segments.Length - 1];
        for (int i = 0; i < segments.Length; i++)
        {
            segments[i].P0 = transform.MultiplyPoint(contour.Segments[i].P0);
            segments[i].P1 = transform.MultiplyPoint(contour.Segments[i].P1);
            segments[i].P2 = transform.MultiplyPoint(contour.Segments[i].P2);
            segments[i].P3 = transform.MultiplyPoint(contour.Segments[(i + 1)].P0);
        }

        Rect    shapeBounds = VectorUtils.Bounds(VectorUtils.BezierSegmentsToPath(segments));
        Vector2 center      = shapeBounds.center;
        float   radius      = (shapeBounds.width + shapeBounds.height) / 4f;
        float   error       = radius / 200f;

        for (int i = 0; i < segments.Length; i++)
        {
            if (Mathf.Abs(Vector2.Distance(center, segments[i].P0) - radius) > error)
            {
                return(null);
            }

            Vector2 midpoint = VectorUtils.Eval(segments[i], 0.5f);
            if (Mathf.Abs(Vector2.Distance(center, midpoint) - radius) > error)
            {
                return(null);
            }
        }

        CircleShape circle = new CircleShape(center, radius);

        circle.colorOutline = Color.red;
        return(circle);
    }
Beispiel #6
0
        List <Vector2[]> ISpritePhysicsOutlineDataProvider.GetOutlines(GUID guid)
        {
            if (GetSVGSpriteData().PhysicsOutlines.Count == 0)
            {
                // If no physics outline was set in the Sprite Editor, show the sprite's physics shape directly (if any)
                var sprite = GetSprite();
                if (sprite == null)
                {
                    return(null);
                }

                var importer = GetImporter();
                int width;
                int height;
                importer.TextureSizeForSpriteEditor(sprite, out width, out height);
                var size   = new Vector2(width, height);
                var offset = new Vector2(-width / 2.0f, -height / 2.0f);

                var storedShapes = new List <Vector2[]>(sprite.GetPhysicsShapeCount());
                var shape        = new List <Vector2>();
                for (int i = 0; i < sprite.GetPhysicsShapeCount(); ++i)
                {
                    shape.Clear();
                    sprite.GetPhysicsShape(i, shape);
                    var bounds = VectorUtils.Bounds(shape);
                    for (int j = 0; j < shape.Count; ++j)
                    {
                        var p = shape[j];
                        p       -= bounds.min;
                        p       /= bounds.size;
                        p       *= size;
                        p       += offset;
                        shape[j] = p;
                    }
                    storedShapes.Add(shape.ToArray());
                }

                return(storedShapes);
            }
            return(GetSVGSpriteData().PhysicsOutlines.Select(x => x.Vertices.ToArray()).ToList());
        }
Beispiel #7
0
    private static Sprite SpriteFromGeometry(List <VectorUtils.Geometry> geoms)
    {
        var vertices = new List <Vector2>();
        var indices  = new List <UInt16>();
        var colors   = new List <Color>();

        foreach (var geom in geoms)
        {
            if (geom.Indices.Length == 0)
            {
                continue;
            }

            indices.AddRange(geom.Indices.Select(x => (UInt16)(x + vertices.Count)));
            vertices.AddRange(geom.Vertices.Select(x => geom.WorldTransform * x));
            colors.AddRange(Enumerable.Repeat(geom.Color, geom.Vertices.Length));
        }

        var bbox = VectorUtils.Bounds(vertices);

        VectorUtils.RealignVerticesInBounds(vertices, bbox, true);
        var rect = new Rect(0, 0, bbox.width, bbox.height);

        // The Sprite.Create(Rect, Vector2, float, Texture2D) method is internal. Using reflection
        // until it becomes public.
        var spriteCreateMethod = typeof(Sprite).GetMethod("Create", BindingFlags.Static | BindingFlags.NonPublic, Type.DefaultBinder, new Type[] { typeof(Rect), typeof(Vector2), typeof(float), typeof(Texture2D) }, null);
        var sprite             = spriteCreateMethod.Invoke(null, new object[] { rect, Vector2.zero, 100.0f, null }) as Sprite;

        sprite.OverrideGeometry(vertices.ToArray(), indices.ToArray());

        var colors32 = colors.Select(c => (Color32)c);

        using (var nativeColors = new NativeArray <Color32>(colors32.ToArray(), Allocator.Temp))
            sprite.SetVertexAttribute <Color32>(VertexAttribute.Color, nativeColors);

        return(sprite);
    }
    /// <summary>
    /// Build a 2D bounding box for the shape.
    /// </summary>
    protected override void GenerateBounds()
    {
        List <Vector2> pointList = new List <Vector2>();
        float          step      = 1f / bezierSteps;

        for (int i = 0; i < vertices.Length; i++)
        {
            Vertex vert = vertices[i];

            pointList.Add(vert.position);
            if (vert.segmentCurves)
            {
                Vertex vertNext = vertices[NextIndex(i)];
                float  t        = step;
                for (int j = 1; j < bezierSteps; j++)
                {
                    pointList.Add(EvaluateCubicCurve(vert.position, vert.exitCP, vertNext.enterCP, vertNext.position, t));
                    t += step;
                }
            }
        }

        shapeBounds = VectorUtils.Bounds(pointList);
    }
        public Rect CalcSize(XdObjectJson xdObject)
        {
            var position      = Vector2.zero;
            var size          = new Vector2(xdObject.Shape.Width, xdObject.Shape.Height);
            var scaleBehavior = xdObject.Style?.Fill?.Pattern?.Meta?.Ux?.ScaleBehavior ?? "fill";
            var spriteUid     = xdObject.Style?.Fill?.Pattern?.Meta?.Ux?.Uid;

            var shapeType = xdObject.Shape?.Type;

            if (!string.IsNullOrWhiteSpace(spriteUid))
            {
                // nothing
            }
            else if (SvgUtil.Types.Contains(shapeType))
            {
                var svg = SvgUtil.CreateSvg(xdObject);
                using (var reader = new StringReader(svg))
                {
                    var sceneInfo   = SVGParser.ImportSVG(reader, ViewportOptions.DontPreserve);
                    var tessOptions = SvgToPng.TessellationOptions;
                    var geometry    = VectorUtils.TessellateScene(sceneInfo.Scene, tessOptions, sceneInfo.NodeOpacity);
                    var vertices    = geometry.SelectMany(geom => geom.Vertices.Select(x => (geom.WorldTransform * x))).ToArray();
                    var bounds      = VectorUtils.Bounds(vertices);
                    if (bounds.width > 0.0001f && bounds.height > 0.0001f)
                    {
                        size     = new Vector2(bounds.width, bounds.height);
                        position = new Vector2(bounds.x, bounds.y);
                    }
                }
            }

            if (scaleBehavior == "cover" && size.x > 0.0001f && size.y > 0.0001f)
            {
                var imageWidth     = xdObject.Style?.Fill?.Pattern?.Width ?? 0f;
                var imageHeight    = xdObject.Style?.Fill?.Pattern?.Height ?? 0f;
                var imageSize      = new Vector2(imageWidth, imageHeight);
                var imageAspect    = imageSize.x / imageSize.y;
                var instanceAspect = size.x / size.y;
                var offsetX        = xdObject.Style?.Fill?.Pattern?.Meta?.Ux?.OffsetX ?? 0f;
                var offsetY        = xdObject.Style?.Fill?.Pattern?.Meta?.Ux?.OffsetY ?? 0f;
                var scale          = xdObject.Style?.Fill?.Pattern?.Meta?.Ux?.Scale ?? 1.0f;

                if (imageAspect > instanceAspect)
                {
                    var prev = size.x;
                    size.x      = size.y * (imageSize.x / imageSize.y);
                    position.x -= (size.x - prev) / 2f;

                    position.x += offsetX * xdObject.Shape.Width * imageAspect / instanceAspect;
                    position.y += offsetY * xdObject.Shape.Height;
                }
                else
                {
                    var prev = size.y;
                    size.y      = size.x * (imageSize.y / imageSize.x);
                    position.y -= (size.y - prev) / 2f;

                    position.x += offsetX * xdObject.Shape.Width;
                    position.y += offsetY * xdObject.Shape.Height * imageAspect / instanceAspect;
                }

                {
                    var prev = size;
                    size     *= scale;
                    position -= (size - prev) / 2f;
                }
            }

            return(new Rect(position, size));
        }