public void BezierSegmentsToPath_WithConnectedSegments_ReturnsConnectedPath()
    {
        var segments = new BezierSegment[] {
            new BezierSegment()
            {
                P0 = new Vector2(0, 0), P1 = new Vector2(1, 1), P2 = new Vector2(2, 2), P3 = new Vector2(3, 3)
            },
            new BezierSegment()
            {
                P0 = new Vector2(3, 3), P1 = new Vector2(4, 4), P2 = new Vector2(5, 5), P3 = new Vector2(6, 6)
            }
        };

        var path = VectorUtils.BezierSegmentsToPath(segments);

        Assert.AreEqual(3, path.Length);

        var pathSeg = path[0];

        Assert.AreEqual(new Vector2(0, 0), pathSeg.P0);
        Assert.AreEqual(new Vector2(1, 1), pathSeg.P1);
        Assert.AreEqual(new Vector2(2, 2), pathSeg.P2);

        pathSeg = path[1];
        Assert.AreEqual(new Vector2(3, 3), pathSeg.P0);
        Assert.AreEqual(new Vector2(4, 4), pathSeg.P1);
        Assert.AreEqual(new Vector2(5, 5), pathSeg.P2);

        pathSeg = path[2];
        Assert.AreEqual(new Vector2(6, 6), pathSeg.P0);
    }
Beispiel #2
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 #3
0
    /// <summary>
    /// Tessellate the shape into geometry data.
    /// </summary>
    protected override void GenerateGeometry()
    {
        if ((shapeGeometry != null) && (!shapeDirty))
        {
            return;
        }

        Shape ellipse = new Shape();

        ellipse.PathProps = new PathProperties()
        {
            Stroke = new Stroke()
            {
                Color         = colorOutline,
                HalfThickness = penSize / 2f * penToMeshScale
            }
        };
        if (colorFill != Color.clear)
        {
            ellipse.Fill = new SolidFill()
            {
                Color = colorFill
            };
        }

        BezierSegment[] segments = GenerateSegments();

        ellipse.Contours             = new BezierContour[1];
        ellipse.Contours[0]          = new BezierContour();
        ellipse.Contours[0].Segments = VectorUtils.BezierSegmentsToPath(segments);

        shapeNode = new SceneNode()
        {
            Transform = matrixTransform,
            Shapes    = new List <Shape>
            {
                ellipse
            }
        };

        tessellationScene.Root = shapeNode;
        shapeGeometry          = VectorUtils.TessellateScene(tessellationScene, tessellationOptions);

        shapeDirty = false;
    }
    public void BezierSegmentsToPath_WithDisconnectedSegments_ReturnsConnectedPathWithStraightLine()
    {
        var segments = new BezierSegment[] {
            new BezierSegment()
            {
                P0 = new Vector2(0, 0), P1 = new Vector2(1, 1), P2 = new Vector2(2, 2), P3 = new Vector2(3, 3)
            },
            new BezierSegment()
            {
                P0 = new Vector2(6, 6), P1 = new Vector2(7, 7), P2 = new Vector2(8, 8), P3 = new Vector2(9, 9)
            }
        };

        var path = VectorUtils.BezierSegmentsToPath(segments);

        Assert.AreEqual(4, path.Length);

        var pathSeg = path[0];

        Assert.AreEqual(new Vector2(0, 0), pathSeg.P0);
        Assert.AreEqual(new Vector2(1, 1), pathSeg.P1);
        Assert.AreEqual(new Vector2(2, 2), pathSeg.P2);

        // The second segment should be a straight line between (3,3) and (6,6)
        pathSeg = path[1];
        Assert.AreEqual(new Vector2(3, 3), pathSeg.P0);
        Assert.AreEqual(new Vector2(4, 4), pathSeg.P1);
        Assert.AreEqual(new Vector2(5, 5), pathSeg.P2);

        pathSeg = path[2];
        Assert.AreEqual(new Vector2(6, 6), pathSeg.P0);
        Assert.AreEqual(new Vector2(7, 7), pathSeg.P1);
        Assert.AreEqual(new Vector2(8, 8), pathSeg.P2);

        pathSeg = path[3];
        Assert.AreEqual(new Vector2(9, 9), pathSeg.P0);
    }
    /// <summary>
    /// Tessellate the shape into geometry data.
    /// </summary>
    protected override void GenerateGeometry()
    {
        if ((shapeGeometry != null) && (!shapeDirty))
        {
            return;
        }

        Shape ellipse = new Shape();
        float theta   = Mathf.Atan2(MajorAxis.y, MajorAxis.x);

        VectorUtils.MakeEllipseShape(ellipse, Vector2.zero, MajorAxis.magnitude, MinorAxis.magnitude);

        ellipse.PathProps = new PathProperties()
        {
            Stroke = new Stroke()
            {
                Color         = colorOutline,
                HalfThickness = penSize / Screen.dpi
            }
        };
        if (colorFill != Color.clear)
        {
            ellipse.Fill = new SolidFill()
            {
                Color = colorFill
            };
        }

        int numCurves = 4;         // Supposed to calculate from max error

        BezierSegment[] segments   = new BezierSegment[numCurves];
        float           deltaAngle = sweepAngle / numCurves;
        float           sinTheta   = Mathf.Sin(theta);
        float           cosTheta   = Mathf.Cos(theta);
        float           angleB     = startAngle;
        float           a          = MajorAxis.magnitude;
        float           b          = MinorAxis.magnitude;
        float           t          = Mathf.Tan(0.5f * deltaAngle);
        float           alpha      = Mathf.Sin(deltaAngle) * (Mathf.Sqrt(4f + 3f * t * t) - 1f) / 3f;

        float   sinAngleB  = Mathf.Sin(angleB);
        float   cosAngleB  = Mathf.Cos(angleB);
        float   aSinAngleB = a * sinAngleB;
        float   aCosAngleB = a * cosAngleB;
        float   bSinAngleB = b * sinAngleB;
        float   bCosAngleB = b * cosAngleB;
        Vector2 ptB        = new Vector2();

        ptB.x = position.x + aCosAngleB * cosTheta - bSinAngleB * sinTheta;
        ptB.y = position.y + aCosAngleB * sinTheta + bSinAngleB * cosTheta;
        Vector2 dotB = new Vector2();

        dotB.x = -aSinAngleB * cosTheta - bCosAngleB * sinTheta;
        dotB.y = -aSinAngleB * sinTheta + bCosAngleB * cosTheta;

        for (int i = 0; i < numCurves; ++i)
        {
            float   angleA = angleB;
            Vector2 ptA    = ptB;
            Vector2 dotA   = dotB;

            angleB    += deltaAngle;
            sinAngleB  = Mathf.Sin(angleB);
            cosAngleB  = Mathf.Cos(angleB);
            aSinAngleB = a * sinAngleB;
            aCosAngleB = a * cosAngleB;
            bSinAngleB = b * sinAngleB;
            bCosAngleB = b * cosAngleB;
            ptB.x      = position.x + aCosAngleB * cosTheta - bSinAngleB * sinTheta;
            ptB.y      = position.y + aCosAngleB * sinTheta + bSinAngleB * cosTheta;
            dotB.x     = -aSinAngleB * cosTheta - bCosAngleB * sinTheta;
            dotB.y     = -aSinAngleB * sinTheta + bCosAngleB * cosTheta;

            segments[i].P0   = ptA;
            segments[i].P1.x = ptA.x + alpha * dotA.x;
            segments[i].P1.y = ptA.y + alpha * dotA.y;
            segments[i].P2.x = ptB.x - alpha * dotB.x;
            segments[i].P2.y = ptB.y - alpha * dotB.y;
            segments[i].P3   = ptB;
        }
        Shape testShape = new Shape();

        testShape.PathProps = new PathProperties()
        {
            Stroke = new Stroke()
            {
                Color         = Color.red,
                HalfThickness = penSize * 1.5f / Screen.dpi
            }
        };
        testShape.Contours             = new BezierContour[1];
        testShape.Contours[0]          = new BezierContour();
        testShape.Contours[0].Segments = VectorUtils.BezierSegmentsToPath(segments);

        shapeNode = new SceneNode()
        {
            Transform = Matrix2D.Translate(position) * Matrix2D.Rotate(-theta),
            Shapes    = new List <Shape>
            {
                ellipse
            }
        };

        SceneNode testNode = new SceneNode()
        {
            Transform = matrixTransform,
            Shapes    = new List <Shape>
            {
                testShape
            },
            Children = new List <SceneNode>
            {
                shapeNode
            }
        };

        tessellationScene.Root = testNode;
        shapeGeometry          = VectorUtils.TessellateScene(tessellationScene, tessellationOptions);

        shapeMesh  = null;
        shapeDirty = false;
    }