/// <summary>Computes the inverse of the matrix.</summary> /// <returns>The inverse matrix</returns> public Matrix2D Inverse() { Matrix2D invMat = new Matrix2D(); float det = this[0, 0] * this[1, 1] - this[0, 1] * this[1, 0]; if (Mathf.Approximately(0.0f, det)) { return(zero); } float invDet = 1.0F / det; invMat[0, 0] = this[1, 1] * invDet; invMat[0, 1] = -this[0, 1] * invDet; invMat[1, 0] = -this[1, 0] * invDet; invMat[1, 1] = this[0, 0] * invDet; // Do the translation part invMat[0, 2] = -(this[0, 2] * invMat[0, 0] + this[1, 2] * invMat[0, 1]); invMat[1, 2] = -(this[0, 2] * invMat[1, 0] + this[1, 2] * invMat[1, 1]); return(invMat); }
static void GenerateTip(BezierSegment segment, bool atStart, float t, PathEnding ending, float halfThickness, TessellationOptions tessellateOptions, List <Vector2> verts, List <UInt16> inds) { // The tip includes the vertices at the end itself Vector2 tan, nrm; var pos = VectorUtils.EvalFull(segment, t, out tan, out nrm); int indexStart = verts.Count; switch (ending) { case PathEnding.Chop: if (atStart) { verts.Add(pos + nrm * halfThickness); verts.Add(pos - nrm * halfThickness); } else { // Not much, path segments are always expected to be generated perpendicular to the path // at the segment point location, so we don't have to do anything for the ending } break; case PathEnding.Square: if (atStart) { verts.Add(pos + nrm * halfThickness - tan * halfThickness); verts.Add(pos - nrm * halfThickness - tan * halfThickness); verts.Add(pos + nrm * halfThickness); verts.Add(pos - nrm * halfThickness); inds.Add((UInt16)(indexStart + 0)); inds.Add((UInt16)(indexStart + 3)); inds.Add((UInt16)(indexStart + 1)); inds.Add((UInt16)(indexStart + 0)); inds.Add((UInt16)(indexStart + 2)); inds.Add((UInt16)(indexStart + 3)); } else { // Relying on the last two vertices, and just adding two of our own here verts.Add(pos + nrm * halfThickness + tan * halfThickness); verts.Add(pos - nrm * halfThickness + tan * halfThickness); inds.Add((UInt16)(indexStart + 0 - 2)); inds.Add((UInt16)(indexStart + 3 - 2)); inds.Add((UInt16)(indexStart + 1 - 2)); inds.Add((UInt16)(indexStart + 0 - 2)); inds.Add((UInt16)(indexStart + 2 - 2)); inds.Add((UInt16)(indexStart + 3 - 2)); } break; case PathEnding.Round: float arcSign = atStart ? -1 : 1; int arcSegments = CalculateArcSteps(halfThickness, 0, Mathf.PI, tessellateOptions); for (int i = 1; i < arcSegments; i++) { float angle = Mathf.PI * (i / (float)arcSegments); verts.Add(pos + Matrix2D.RotateLH(angle) * nrm * halfThickness * arcSign); } if (atStart) { // Note how we maintain the last two vertices being setup for connection by the rest of the path vertices int indexTipStart = verts.Count; verts.Add(pos + nrm * halfThickness); verts.Add(pos - nrm * halfThickness); for (int i = 1; i < arcSegments; i++) { inds.Add((UInt16)(indexTipStart + 1)); inds.Add((UInt16)(indexStart + i - 1)); inds.Add((UInt16)(indexStart + i)); } } else { inds.Add((UInt16)(indexStart - 1)); inds.Add((UInt16)(indexStart - 2)); inds.Add((UInt16)(indexStart + 0)); for (int i = 1; i < arcSegments - 1; i++) { inds.Add((UInt16)(indexStart - 1)); inds.Add((UInt16)(indexStart + i - 1)); inds.Add((UInt16)(indexStart + i)); } } break; default: System.Diagnostics.Debug.Assert(false); // Joining has its own function break; } }
static void GenerateJoining(JoiningInfo joinInfo, PathCorner corner, float halfThickness, float tippedCornerLimit, TessellationOptions tessellateOptions, List <Vector2> verts, List <UInt16> inds) { // The joining generates the vertices at both ends as well as the joining itself if (verts.Count == 0) { // Starting a path with a joining (meaning a loop) verts.Add(joinInfo.RoundPosThickness ? joinInfo.PosThicknessEnd : joinInfo.InnerCornerVertex); verts.Add(joinInfo.RoundPosThickness ? joinInfo.InnerCornerVertex : joinInfo.NegThicknessEnd); } System.Diagnostics.Debug.Assert(verts.Count >= 2); int indexStart = verts.Count - 2; // Using the last two vertices // Convert a tipped corner to a beveled one if tippedCornerLimit ratio is reached if (corner == PathCorner.Tipped && tippedCornerLimit >= 1.0f) { var theta = Vector2.Angle(-joinInfo.TanAtEnd, joinInfo.TanAtStart) * Mathf.Deg2Rad; var ratio = 1.0f / Mathf.Sin(theta / 2.0f); if (ratio > tippedCornerLimit) { corner = PathCorner.Beveled; } } if (joinInfo.SimpleJoin) { // TODO } else if (corner == PathCorner.Tipped) { verts.Add(joinInfo.PosThicknessClosingPoint); verts.Add(joinInfo.NegThicknessClosingPoint); verts.Add(joinInfo.RoundPosThickness ? joinInfo.PosThicknessStart : joinInfo.InnerCornerVertex); verts.Add(joinInfo.RoundPosThickness ? joinInfo.InnerCornerVertex : joinInfo.NegThicknessStart); // Ending to tip inds.Add((UInt16)(indexStart + 0)); inds.Add((UInt16)(indexStart + 3)); inds.Add((UInt16)(indexStart + 1)); inds.Add((UInt16)(indexStart + 0)); inds.Add((UInt16)(indexStart + 2)); inds.Add((UInt16)(indexStart + 3)); // Tip to starting inds.Add((UInt16)(indexStart + 4)); inds.Add((UInt16)(indexStart + 3)); inds.Add((UInt16)(indexStart + 2)); inds.Add((UInt16)(indexStart + 4)); inds.Add((UInt16)(indexStart + 5)); inds.Add((UInt16)(indexStart + 3)); return; } else if (corner == PathCorner.Beveled) { verts.Add(joinInfo.RoundPosThickness ? joinInfo.PosThicknessEnd : joinInfo.InnerCornerVertex); // 2 verts.Add(joinInfo.RoundPosThickness ? joinInfo.InnerCornerVertex : joinInfo.NegThicknessEnd); // 3 verts.Add(joinInfo.RoundPosThickness ? joinInfo.PosThicknessStart : joinInfo.InnerCornerVertex); // 4 verts.Add(joinInfo.RoundPosThickness ? joinInfo.InnerCornerVertex : joinInfo.NegThicknessStart); // 5 // Ending to tip inds.Add((UInt16)(indexStart + 0)); inds.Add((UInt16)(indexStart + 2)); inds.Add((UInt16)(indexStart + 1)); inds.Add((UInt16)(indexStart + 1)); inds.Add((UInt16)(indexStart + 2)); inds.Add((UInt16)(indexStart + 3)); // Bevel if (joinInfo.RoundPosThickness) { inds.Add((UInt16)(indexStart + 2)); inds.Add((UInt16)(indexStart + 4)); inds.Add((UInt16)(indexStart + 3)); } else { inds.Add((UInt16)(indexStart + 3)); inds.Add((UInt16)(indexStart + 2)); inds.Add((UInt16)(indexStart + 5)); } return; } if (corner == PathCorner.Round) { float sweepAngle = Mathf.Acos(Vector2.Dot(joinInfo.NormAtEnd, joinInfo.NormAtStart)); bool flipArc = false; if (!PointOnTheLeftOfLine(Vector2.zero, joinInfo.NormAtEnd, joinInfo.NormAtStart)) { sweepAngle = -sweepAngle; flipArc = true; } UInt16 innerCornerVertexIndex = (UInt16)verts.Count; verts.Add(joinInfo.InnerCornerVertex); int arcSegments = CalculateArcSteps(halfThickness, 0, sweepAngle, tessellateOptions); for (int i = 0; i <= arcSegments; i++) { float angle = sweepAngle * (i / (float)arcSegments); Vector2 nrm = Matrix2D.RotateLH(angle) * joinInfo.NormAtEnd; if (flipArc) { nrm = -nrm; } verts.Add(nrm * halfThickness + joinInfo.JoinPos); if (i == 0) { inds.Add((UInt16)(indexStart + 0)); inds.Add((UInt16)(indexStart + 3)); inds.Add((UInt16)(indexStart + (joinInfo.RoundPosThickness ? 2 : 1))); inds.Add((UInt16)(indexStart + 0)); inds.Add((UInt16)(indexStart + 2)); inds.Add((UInt16)(indexStart + (joinInfo.RoundPosThickness ? 1 : 3))); } else { if (joinInfo.RoundPosThickness) { inds.Add((UInt16)(indexStart + i + (flipArc ? 3 : 2))); inds.Add((UInt16)(indexStart + i + (flipArc ? 2 : 3))); inds.Add(innerCornerVertexIndex); } else { inds.Add((UInt16)(indexStart + i + (flipArc ? 3 : 2))); inds.Add((UInt16)(indexStart + i + (flipArc ? 2 : 3))); inds.Add(innerCornerVertexIndex); } } } // Manually add the last segment, maintain the expected vertex positioning int endingVerticesIndex = verts.Count; if (joinInfo.RoundPosThickness) { verts.Add(joinInfo.PosThicknessStart); verts.Add(joinInfo.InnerCornerVertex); } else { verts.Add(joinInfo.InnerCornerVertex); verts.Add(joinInfo.NegThicknessStart); } inds.Add((UInt16)(endingVerticesIndex - 1)); inds.Add((UInt16)(endingVerticesIndex + 0)); inds.Add(innerCornerVertexIndex); } }
#pragma warning disable 612, 618 // Silence use of deprecated IDrawable private static List <Geometry> TessellateNodeHierarchyRecursive(SceneNode node, TessellationOptions tessellationOptions, Matrix2D worldTransform, float worldOpacity, Dictionary <SceneNode, float> nodeOpacities) { if (node.Clipper != null) { VectorClip.PushClip(TraceNodeHierarchyShapes(node.Clipper, tessellationOptions), worldTransform); } var geoms = new List <Geometry>(); if (node.Drawables != null) { // We process the drawables even though they are obsolete, until we remove the IDrawable interface entirely foreach (var drawable in node.Drawables) { var vectorShape = drawable as Shape; if (vectorShape != null) { bool isConvex = vectorShape.IsConvex && vectorShape.Contours.Length == 1; TessellateShape(vectorShape, geoms, tessellationOptions, isConvex); continue; } var vectorPath = drawable as Path; if (vectorPath != null) { TessellatePath(vectorPath.Contour, vectorPath.PathProps, geoms, tessellationOptions); continue; } var vectorRect = drawable as Rectangle; if (vectorRect != null) { TessellateRectangle(vectorRect, geoms, tessellationOptions); continue; } } } if (node.Shapes != null) { foreach (var shape in node.Shapes) { bool isConvex = shape.IsConvex && shape.Contours.Length == 1; TessellateShape(shape, geoms, tessellationOptions, isConvex); } } foreach (var g in geoms) { g.Color.a *= worldOpacity; g.WorldTransform = worldTransform; g.UnclippedBounds = Bounds(g.Vertices); VectorClip.ClipGeometry(g); } if (node.Children != null) { foreach (var child in node.Children) { var childOpacity = 1.0f; if (nodeOpacities == null || !nodeOpacities.TryGetValue(child, out childOpacity)) { childOpacity = 1.0f; } var transform = worldTransform * child.Transform; var opacity = worldOpacity * childOpacity; var childGeoms = TessellateNodeHierarchyRecursive(child, tessellationOptions, transform, opacity, nodeOpacities); geoms.AddRange(childGeoms); } } if (node.Clipper != null) { VectorClip.PopClip(); } return(geoms); }
internal static Vector2[] GenerateShapeUVs(Vector2[] verts, Rect bounds, Matrix2D uvTransform) { UnityEngine.Profiling.Profiler.BeginSample("GenerateShapeUVs"); uvTransform = Matrix2D.Translate(new Vector2(0, 1)) * Matrix2D.Scale(new Vector2(1.0f, -1.0f)) * // Do 1-uv.y uvTransform * Matrix2D.Scale(new Vector2(1.0f / bounds.width, 1.0f / bounds.height)) * Matrix2D.Translate(-bounds.position); var uvs = new Vector2[verts.Length]; int vertCount = verts.Length; for (int i = 0; i < vertCount; i++) { uvs[i] = uvTransform * verts[i]; } UnityEngine.Profiling.Profiler.EndSample(); return(uvs); }
#pragma warning disable 612, 618 // Silence use of deprecated IDrawable private static List <Geometry> TessellateNodeHierarchyRecursive(SceneNode node, TessellationOptions tessellationOptions, Matrix2D worldTransform, float worldOpacity, Dictionary <SceneNode, float> nodeOpacities) { if (node.Clipper != null) { VectorClip.PushClip(TraceNodeHierarchyShapes(node.Clipper, tessellationOptions), worldTransform); } var geoms = new List <Geometry>(); if (node.Shapes != null) { foreach (var shape in node.Shapes) { bool isConvex = shape.IsConvex && shape.Contours.Length == 1; TessellateShape(shape, geoms, tessellationOptions, isConvex); } } foreach (var g in geoms) { g.Color.a *= worldOpacity; g.WorldTransform = worldTransform; g.UnclippedBounds = Bounds(g.Vertices); VectorClip.ClipGeometry(g); } if (node.Children != null) { foreach (var child in node.Children) { var childOpacity = 1.0f; if (nodeOpacities == null || !nodeOpacities.TryGetValue(child, out childOpacity)) { childOpacity = 1.0f; } var transform = worldTransform * child.Transform; var opacity = worldOpacity * childOpacity; var childGeoms = TessellateNodeHierarchyRecursive(child, tessellationOptions, transform, opacity, nodeOpacities); geoms.AddRange(childGeoms); } } if (node.Clipper != null) { VectorClip.PopClip(); } return(geoms); }