/// <summary> /// New PolyShape from Unity contour data. /// </summary> /// <param name="contour">Contour data</param> public static PolyShape Create(BezierContour contour) { PolyShape shape = Create(); int vertexCount = contour.Segments.Length; shape.vertices = new Vertex[vertexCount]; for (int i = 0; i < vertexCount; i++) { shape.vertices[i] = new Vertex(); } for (int i = 0; i < vertexCount; i++) { BezierPathSegment segment = contour.Segments[i]; shape.vertices[i].position = segment.P0; shape.vertices[i].exitCP = segment.P1; shape.vertices[shape.NextIndex(i)].enterCP = segment.P2; shape.vertices[i].segmentCurves = true; } shape.closed = contour.Closed; return(shape); }
private static VectorShape ParseContour(BezierContour contour, Matrix2D transform) { VectorShape vectorShape = new PolyShape(contour); vectorShape.TransformBy(transform); return(vectorShape); }
private BezierContour[] BuildCircleContourWithMask(Rect rect, Vector2 rad, Rect maskRect, Vector2 maskRad) { var contours = new BezierContour[2]; contours[0] = VectorUtils.BuildRectangleContour(rect, rad, rad, rad, rad); contours[1] = VectorUtils.BuildRectangleContour(maskRect, maskRad, maskRad, maskRad, maskRad); return(contours); }
public void CubicTo(float x1, float y1, float x2, float y2, float x3, float y3) { var bezier = new BezierContour( new Vector2(x1, y1), new Vector2(x2, y2), new Vector2(x3, y3) ); Contours.Add(bezier); }
public void CubicTo(double x1, double y1, double x2, double y2, double x3, double y3) { var bezier = new BezierContour( new Vector((float)x1, (float)y1), new Vector((float)x2, (float)y2), new Vector((float)x3, (float)y3) ); Contours.Add(bezier); }
private BezierContour[] BuildRectangleContourWithMask(Rect rect, Rect maskRect) { // ToDo: Clamp [maskRect] by [rect] var contours = new BezierContour[2]; contours[0] = VectorUtils.BuildRectangleContour(rect, Vector2Zero, Vector2Zero, Vector2Zero, Vector2Zero); contours[1] = VectorUtils.BuildRectangleContour(maskRect, Vector2Zero, Vector2Zero, Vector2Zero, Vector2Zero); return(contours); }
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); }
/// <summary> /// Tessellate the shape into geometry data. /// </summary> protected override void GenerateGeometry() { if ((shapeGeometry != null) && (!shapeDirty)) { return; } Shape circle = new Shape(); if (closed) { VectorUtils.MakeCircleShape(circle, position, radius); } else { BezierContour contour = new BezierContour(); contour.Segments = VectorUtils.MakeArc(position, startAngle, sweepAngle, radius); circle.Contours = new BezierContour[] { contour }; } circle.PathProps = new PathProperties() { Stroke = new Stroke() { Color = colorOutline, HalfThickness = penSize / Screen.dpi } }; if (colorFill != Color.clear) { circle.Fill = new SolidFill() { Color = colorFill }; } shapeNode = new SceneNode() { Transform = matrixTransform, Shapes = new List <Shape> { circle } }; tessellationScene.Root = shapeNode; shapeGeometry = VectorUtils.TessellateScene(tessellationScene, tessellationOptions); shapeMesh = null; shapeDirty = false; }
private static void TessellatePath(BezierContour contour, PathProperties pathProps, List <VectorUtils.Geometry> geoms, VectorUtils.TessellationOptions options) { if (pathProps.Stroke != null) { Vector2[] vertices; UInt16[] indices; VectorUtils.TessellatePath(contour, pathProps, options, out vertices, out indices); var color = pathProps.Stroke.Color; geoms.Add(new VectorUtils.Geometry() { Vertices = vertices, Indices = indices, Color = color }); } }
void ApplySVGPath() { string svgPath = "Assets/RouteData/drawsvg.svg"; SVGParser.SceneInfo sceneInfo = SVGParser.ImportSVG(new StreamReader(svgPath)); Shape path = sceneInfo.NodeIDs["e1_polyline"].Shapes[0]; Debug.Log(path); BezierContour[] cs = path.Contours; BezierContour c = cs[0]; //Debug.Log(c); BezierPathSegment[] ss = c.Segments; Debug.Log($"SVGRoute segments count: {ss.Length}"); for (int i = 0; i < ss.Length; i++) { BezierPathSegment s = ss[i]; Debug.Log($"SVGRoute Segment points: {s.P0} -> {s.P1} -> {s.P2}"); var debug1 = GameObject.Find($"SVGTarget{(i * 3) + 1}"); var debug2 = GameObject.Find($"SVGTarget{(i * 3) + 2}"); var debug3 = GameObject.Find($"SVGTarget{(i * 3) + 3}"); debug1.transform.localPosition = s.P0; debug2.transform.localPosition = s.P1; debug3.transform.localPosition = s.P2; Debug.Log(debug3); } // debug1.transform.position = new Vector3(s.P0.x, 0.1f, s.P0.y); // //(s.P0.x / 10) - 10f, 0.1f, (s.P0.y / 10) + 4.3f); // debug2.transform.position = new Vector3(s.P1.x, 0.1f, s.P1.y); // debug3.transform.position = new Vector3(s.P2.x, 0.1f, s.P2.y); var debug0 = GameObject.Find("SVGTarget0"); debug0.transform.localPosition = Vector3.zero; //path. //var fill = shape.Fill as SolidFill; //fill.Color = Color.red; // ... //var geoms = VectorUtils.TessellateScene(sceneInfo.Scene, tessOptions); //var sprite = VectorUtils.BuildSprite(geoms, 100.0f, VectorUtils.Alignment.Center, Vector2.zero, 128, true); }
public void CloseContour() { if (m_Segments.Count > 0) { var contour = new BezierContour() { Segments = m_Segments.ToArray() }; m_Contours.Add(contour); m_Segments.Clear(); if (VectorUtils.PathEndsPerfectlyMatch(contour.Segments)) { contour.Closed = true; } } }
/// <summary> /// New PolyShape from Unity contour data. /// </summary> /// <param name="contour">Contour data</param> public PolyShape(BezierContour contour) { int vertexCount = contour.Segments.Length; vertices = new Vertex[vertexCount]; for (int i = 0; i < vertexCount; i++) { vertices[i] = new Vertex(); } for (int i = 0; i < vertexCount; i++) { BezierPathSegment segment = contour.Segments[i]; vertices[i].position = segment.P0; vertices[i].exitCP = segment.P1; vertices[NextIndex(i)].enterCP = segment.P2; vertices[i].segmentCurves = true; } closed = contour.Closed; }
private static void TessellatePath(BezierContour contour, PathProperties pathProps, List <Geometry> geoms, TessellationOptions tessellationOptions) { UnityEngine.Profiling.Profiler.BeginSample("TessellatePath"); if (pathProps.Stroke != null) { Vector2[] vertices; UInt16[] indices; Vector2[][] paths; ShapeUtils.TessellatePath(contour, pathProps, tessellationOptions, out vertices, out indices, out paths); var color = pathProps.Stroke.Color; if (indices.Length > 0) { geoms.Add(new Geometry() { Vertices = vertices, Indices = indices, Paths = paths, Color = color }); } } UnityEngine.Profiling.Profiler.EndSample(); }
static Vector2[] TraceShape(BezierContour contour, Stroke stroke, TessellationOptions tessellateOptions) { if (tessellateOptions.StepDistance < Epsilon) { throw new Exception("stepDistance too small"); } if (contour.Segments.Length < 2) { return(new Vector2[0]); } float[] segmentLengths = ShapeUtils.SegmentsLengths(contour.Segments, contour.Closed); // Approximate the number of vertices/indices we need to store the results so we reduce memory reallocations during work float approxTotalLength = 0.0f; foreach (var s in segmentLengths) { approxTotalLength += s; } int approxStepCount = Math.Max((int)(approxTotalLength / tessellateOptions.StepDistance + 0.5f), 2); var strokePattern = stroke != null ? stroke.Pattern : null; var strokePatternOffset = stroke != null ? stroke.PatternOffset : 0.0f; if (strokePattern != null) { approxStepCount += strokePattern.Length * 2; } List <Vector2> verts = new List <Vector2>(approxStepCount); // A little bit possibly for the endings var patternIt = new PathPatternIterator(strokePattern, strokePatternOffset); var pathIt = new PathDistanceForwardIterator(contour.Segments, true, tessellateOptions.MaxCordDeviationSquared, tessellateOptions.MaxTanAngleDeviationCosine, tessellateOptions.SamplingStepSize); verts.Add(pathIt.EvalCurrent()); while (!pathIt.Ended) { float distance = patternIt.SegmentLength; float startingLength = pathIt.LengthSoFar; float unitsRemaining = Mathf.Min(tessellateOptions.StepDistance, distance); bool endedEntirePath = false; for (;;) { var result = pathIt.AdvanceBy(unitsRemaining, out unitsRemaining); if (result == PathDistanceForwardIterator.Result.Ended) { endedEntirePath = true; break; } else if (result == PathDistanceForwardIterator.Result.NewSegment) { verts.Add(pathIt.EvalCurrent()); } if ((unitsRemaining <= Epsilon) && !TryGetMoreRemainingUnits(ref unitsRemaining, pathIt, startingLength, distance, tessellateOptions.StepDistance)) { break; } if (result == PathDistanceForwardIterator.Result.Stepped) { verts.Add(pathIt.EvalCurrent()); } } // Ending if (endedEntirePath) { break; } else { verts.Add(pathIt.EvalCurrent()); } patternIt.Advance(); } if ((verts[0] - verts[verts.Count - 1]).sqrMagnitude < Epsilon) { verts.RemoveAt(verts.Count - 1); } return(verts.ToArray()); // Why not return verts itself? }
public virtual bool CreateMetaball(float radius1, Vector2 center1) { var distance = Vector2.Distance(center1, Center); float u1; float u2; // Check if balls are intersecting if (distance > (Attached ? 100 : distanceBeforeDissolve) || distance <= Mathf.Abs(radius1 - ballRadius)) { return(false); } if (distance < radius1 + ballRadius) { // case circles are overlapping u1 = Mathf.Acos((radius1 * radius1 + distance * distance - ballRadius * ballRadius) / (2 * radius1 * distance)); u2 = Mathf.Acos((ballRadius * ballRadius + distance * distance - radius1 * radius1) / (2 * ballRadius * distance)); } else { u1 = 0; u2 = 0; } // Calculate all angles needed var angleBetweenCenters = AngleBetweenCenters(Center, center1); var maxSpread = Mathf.Acos((radius1 - ballRadius) / distance); // Circle 1 (left) var angle1 = angleBetweenCenters + u1 + (maxSpread - u1) * v; var angle2 = angleBetweenCenters - (u1 + (maxSpread - u1) * v); // Circle 2 (right) var angle3 = angleBetweenCenters + Mathf.PI - u2 - (Mathf.PI - u2 - maxSpread) * v; var angle4 = angleBetweenCenters - (Mathf.PI - u2 - (Mathf.PI - u2 - maxSpread) * v); // Calculate the four bezier points var point1 = GetPoint(center1, angle1, radius1); var point2 = GetPoint(center1, angle2, radius1); var point3 = GetPoint(Center, angle3, ballRadius); var point4 = GetPoint(Center, angle4, ballRadius); // Calculate the four handles var totalRadius = radius1 + ballRadius; var d2 = Mathf.Min(v * 10, Vector2.Distance(point1, point3) / totalRadius); var r1 = radius1 * d2; var r2 = ballRadius * d2; var handle1 = GetPoint(point1, angle1 - Mathf.PI / 2, r1); var handle2 = GetPoint(point2, angle2 + Mathf.PI / 2, r1); var handle3 = GetPoint(point3, angle3 + Mathf.PI / 2, r2); var handle4 = GetPoint(point4, angle4 - Mathf.PI / 2, r2); // Define the bezier segments if (BezierCurveUtils.CheckIBezierCurveIntersection( new BezierSegment { P0 = point1, P1 = handle1, P2 = handle3, P3 = point3 }, new BezierSegment { P0 = point2, P1 = handle2, P2 = handle4, P3 = point4 })) { return(false); } var bezierSegments = new[] { new BezierPathSegment { P0 = point1 - (Vector2)transform.parent.position, P1 = handle1 - (Vector2)transform.parent.position, P2 = handle3 - (Vector2)transform.parent.position }, new BezierPathSegment { P0 = point3 - (Vector2)transform.parent.position, P1 = point3 - (Vector2)transform.parent.position, P2 = point4 - (Vector2)transform.parent.position }, new BezierPathSegment { P0 = point4 - (Vector2)transform.parent.position, P1 = handle4 - (Vector2)transform.parent.position, P2 = handle2 - (Vector2)transform.parent.position }, new BezierPathSegment { P0 = point2 - (Vector2)transform.parent.position, P1 = point2 - (Vector2)transform.parent.position, P2 = point1 - (Vector2)transform.parent.position } }; // Define the bezier contour for the shape var bezierContour = new BezierContour { Segments = bezierSegments, Closed = false }; // Unite everything together GenerateBezierCurve(new[] { bezierContour }); return(true); }
/// <summary> /// Tessellates a path. /// </summary> /// <param name="contour">The path to tessellate</param> /// <param name="pathProps">The path properties</param> /// <param name="tessellateOptions">The tessellation options</param> /// <param name="vertices">The resulting vertices</param> /// <param name="indices">The resulting triangles</param> /// <remarks> /// The individual line segments generated during tessellation are made out of a set of ordered vertices. It is important /// to honor this ordering so joining and and capping connect properly with the existing vertices without generating dupes. /// The ordering assumed is as follows: /// The last two vertices of a piece must be such that the first is generated at the end with a positive half-thickness /// while the second vertex is at the end too but at a negative half-thickness. /// No assumptions are enforced for other vertices before the two last vertices. /// </remarks> public static void TessellatePath(BezierContour contour, PathProperties pathProps, TessellationOptions tessellateOptions, out Vector2[] vertices, out UInt16[] indices, out Vector2[][] paths) { if (tessellateOptions.StepDistance < Epsilon) { throw new Exception("stepDistance too small"); } if (contour.Segments.Length < 2) { vertices = new Vector2[0]; indices = new UInt16[0]; paths = new Vector2[0][]; return; } UnityEngine.Profiling.Profiler.BeginSample("TessellatePath"); float[] segmentLengths = ShapeUtils.SegmentsLengths(contour.Segments, contour.Closed); // Approximate the number of vertices/indices we need to store the results so we reduce memory reallocations during work float approxTotalLength = 0.0f; foreach (var s in segmentLengths) { approxTotalLength += s; } int approxStepCount = Math.Max((int)(approxTotalLength / tessellateOptions.StepDistance + 0.5f), 2); if (pathProps.Stroke.Pattern != null) { approxStepCount += pathProps.Stroke.Pattern.Length * 2; } List <Vector2> verts = new List <Vector2>(approxStepCount * 2 + 32); // A little bit possibly for the endings List <UInt16> inds = new List <UInt16>((int)(verts.Capacity * 1.5f)); // Usually every 4 verts represent a quad that uses 6 indices List <Vector2[]> pats = new List <Vector2[]>(); var patternIt = new PathPatternIterator(pathProps.Stroke.Pattern, pathProps.Stroke.PatternOffset); var pathIt = new PathDistanceForwardIterator(contour.Segments, contour.Closed, tessellateOptions.MaxCordDeviationSquared, tessellateOptions.MaxTanAngleDeviationCosine, tessellateOptions.SamplingStepSize); JoiningInfo[] joiningInfo = new JoiningInfo[2]; HandleNewSegmentJoining(pathIt, patternIt, joiningInfo, pathProps.Stroke.HalfThickness, segmentLengths); int rangeIndex = 0; while (!pathIt.Ended) { if (patternIt.IsSolid) { TessellateRange(patternIt.SegmentLength, pathIt, patternIt, pathProps, tessellateOptions, joiningInfo, segmentLengths, approxTotalLength, rangeIndex++, verts, inds, pats); } else { SkipRange(patternIt.SegmentLength, pathIt, patternIt, pathProps, joiningInfo, segmentLengths); } patternIt.Advance(); } vertices = verts.ToArray(); indices = inds.ToArray(); paths = pats.ToArray(); UnityEngine.Profiling.Profiler.EndSample(); }
public override bool CreateMetaball(float radius1, Vector2 center1) { var distance = Vector2.Distance(center1, center); float u1; var point1 = GameFieldManager.Instance.GetPointOnWall(point1Index); var point2 = GameFieldManager.Instance.GetPointOnWall(point2Index); var perpendicularLine = Vector2.Perpendicular(point1 - point2); var point5 = center1 + perpendicularLine.normalized * radius1; // Check if balls are intersecting if (distance > (Attached ? 100 : distanceBeforeDissolve)) { return(false); } if (distance < radius1 + ballRadius) { if (CheckSide(point2, point1, point5) > 0) { return(false); } // case circles are overlapping u1 = Mathf.Acos((radius1 * radius1 + distance * distance - ballRadius * ballRadius) / (2 * radius1 * distance)); } else { if (CheckSide(point2, point1, point5) > 0) { return(false); } u1 = 0; } // Calculate all angles needed var angleBetweenCenters = AngleBetweenCenters(center, center1); var maxSpread = Mathf.Acos((radius1 - ballRadius) / distance); // Circle 1 (left) var angle1 = angleBetweenCenters + u1 + (maxSpread - u1) * v; var angle2 = angleBetweenCenters - (u1 + (maxSpread - u1) * v); // Calculate the four bezier points var point3 = GetPoint(center1, angle1, radius1); var point4 = GetPoint(center1, angle2, radius1); var tangentPoint1 = GameFieldManager.Instance.GetPointOnWall(point1Index - 1); var tangentPoint2 = GameFieldManager.Instance.GetPointOnWall(point2Index + 1); // Calculate the four handles var totalRadius = radius1 + ballRadius; var d2 = Mathf.Min(v * 10f, Vector2.Distance(point1, point3) / totalRadius); if (float.IsNaN(d2)) { d2 = lastD2; } lastD2 = d2; var r1 = radius1 * d2; var r2 = ballRadius * d2; // Handle point 1 Right surface var handle1 = GetPoint(point1, AngleBetweenCenters(tangentPoint1, point1), r1); // Handle point 2 Left surface var handle2 = GetPoint(point2, AngleBetweenCenters(tangentPoint2, point2), r1); // Handle point 3 Right Ball var handle3 = GetPoint(point3, angle1 - Mathf.PI / 2, r2); // Handle point 4 Left Ball var handle4 = GetPoint(point4, angle2 + Mathf.PI / 2, r2); // Handle point 5 Right var handle5 = point5 + Vector2.Perpendicular(point5).normalized *radius1; // Handle point 5 Left var handle6 = point5 - Vector2.Perpendicular(point5).normalized *radius1; // Define the bezier segments var numberOfPoints = point1Index - point2Index; int index; BezierPathSegment[] bezierSegments; if (distance <= Mathf.Abs(radius1 - ballRadius)) { bezierSegments = new BezierPathSegment[2 + numberOfPoints]; // return true; bezierSegments[0] = new BezierPathSegment { P0 = point1, P1 = handle1, P2 = handle5 }; bezierSegments[1] = new BezierPathSegment { P0 = point5, P1 = handle6, P2 = handle2 }; index = 2; } else { if (BezierCurveUtils.CheckIBezierCurveIntersection( new BezierSegment { P0 = point1, P1 = handle1, P2 = handle3, P3 = point3 }, new BezierSegment { P0 = point2, P1 = handle2, P2 = handle4, P3 = point4 })) { return(false); } bezierSegments = new BezierPathSegment[3 + numberOfPoints]; bezierSegments[0] = new BezierPathSegment { P0 = point1, P1 = handle1, P2 = handle3 }; bezierSegments[1] = new BezierPathSegment { P0 = point3, P1 = point3, P2 = point4 }; bezierSegments[2] = new BezierPathSegment { P0 = point4, P1 = handle4, P2 = handle2 }; index = 3; } for (var i = 0; i < numberOfPoints; i++) { var bezierPointToAdd = GameFieldManager.Instance.GetPointOnWall(point2Index + i); var nextBezierPoint = GameFieldManager.Instance.GetPointOnWall(point2Index + i + 1); bezierSegments[index++] = new BezierPathSegment { P0 = bezierPointToAdd, P1 = bezierPointToAdd, P2 = nextBezierPoint }; } var bezierContour = new BezierContour { Segments = bezierSegments, Closed = false }; // Draw the bezier curve GenerateBezierCurve(new[] { bezierContour }); return(true); }