/// <summary> /// Creates a new rectangle shaped sub-path. /// </summary> public static void Rect(this Nvg nvg, Rectangle <float> rect) { InstructionQueue queue = nvg.instructionQueue; queue.AddMoveTo(rect.Origin); queue.AddLineTo(new(rect.Origin.X, rect.Max.Y)); queue.AddLineTo(rect.Max); queue.AddLineTo(new(rect.Max.X, rect.Origin.Y)); queue.AddClose(); }
private Nvg(INvgRenderer renderer) { this.renderer = renderer; instructionQueue = new InstructionQueue(this); pathCache = new PathCache(this); stateStack = new StateStack(); pixelRatio = new PixelRatio(); if (!this.renderer.Create()) { Dispose(); throw new InvalidOperationException("Failed to initialize the renderer!"); } fontManager = new FontManager(this); }
/// <summary> /// Creates a new rounded rectangle shaped sub-path with varying radii for each corner. /// </summary> public static void RoundedRectVarying(this Nvg nvg, Rectangle <float> rect, float radTopLeft, float radTopRight, float radBottomRight, float radBottomLeft) { if (radTopLeft < 0.1f && radTopRight < 0.1f && radBottomRight < 0.1f && radBottomLeft < 0.1f) { Rect(nvg, rect); } else { InstructionQueue queue = nvg.instructionQueue; float factor = 1 - KAPPA90; Vector2D <float> half = Vector2D.Abs(rect.Size) * 0.5f; Vector2D <float> rBL = new(MathF.Min(radBottomLeft, half.X) * Maths.Sign(rect.Size.X), MathF.Min(radBottomLeft, half.Y) * Maths.Sign(rect.Size.Y)); Vector2D <float> rBR = new(MathF.Min(radBottomRight, half.X) * Maths.Sign(rect.Size.X), MathF.Min(radBottomRight, half.Y) * Maths.Sign(rect.Size.Y)); Vector2D <float> rTR = new(MathF.Min(radTopRight, half.X) * Maths.Sign(rect.Size.X), MathF.Min(radTopRight, half.Y) * Maths.Sign(rect.Size.Y)); Vector2D <float> rTL = new(MathF.Min(radTopLeft, half.X) * Maths.Sign(rect.Size.X), MathF.Min(radTopLeft, half.Y) * Maths.Sign(rect.Size.Y)); queue.AddMoveTo(new(rect.Origin.X, rect.Origin.Y + rTL.Y)); queue.AddLineTo(new(rect.Origin.X, rect.Origin.Y + rect.Size.Y - rBL.Y)); queue.AddBezierTo( new(rect.Origin.X, rect.Origin.Y + rect.Size.Y - rBL.Y * factor), new(rect.Origin.X + rBL.X * factor, rect.Origin.Y + rect.Size.Y), new(rect.Origin.X + rBL.X, rect.Origin.Y + rect.Size.Y) ); queue.AddLineTo(new(rect.Origin.X + rect.Size.X - rBR.X, rect.Origin.Y + rect.Size.Y)); queue.AddBezierTo( new(rect.Origin.X + rect.Size.X - rBR.X * factor, rect.Origin.Y + rect.Size.Y), new(rect.Origin.X + rect.Size.X, rect.Origin.Y + rect.Size.Y - rBR.Y * factor), new(rect.Origin.X + rect.Size.X, rect.Origin.Y + rect.Size.Y - rBR.Y) ); queue.AddLineTo(new(rect.Origin.X + rect.Size.X, rect.Origin.Y + rTR.Y)); queue.AddBezierTo( new(rect.Origin.X + rect.Size.X, rect.Origin.Y + rTR.Y * factor), new(rect.Origin.X + rect.Size.X - rTR.X * factor, rect.Origin.Y), new(rect.Origin.X + rect.Size.X - rTR.X, rect.Origin.Y) ); queue.AddLineTo(new(rect.Origin.X + rTL.X, rect.Origin.Y)); queue.AddBezierTo( new(rect.Origin.X + rTL.X * factor, rect.Origin.Y), new(rect.Origin.X, rect.Origin.Y + rTL.Y * factor), new(rect.Origin.X, rect.Origin.Y + rTL.Y) ); queue.AddClose(); } }
/// <summary> /// Creates a new ellipse shaped sub-path. /// </summary> public static void Ellipse(this Nvg nvg, Vector2D <float> c, float rx, float ry) { InstructionQueue queue = nvg.instructionQueue; queue.AddMoveTo(new(c.X - rx, c.Y)); queue.AddBezierTo( new(c.X - rx, c.Y + ry * KAPPA90), new(c.X - rx * KAPPA90, c.Y + ry), new(c.X, c.Y + ry)); queue.AddBezierTo( new(c.X + rx * KAPPA90, c.Y + ry), new(c.X + rx, c.Y + ry * KAPPA90), new(c.X + rx, c.Y)); queue.AddBezierTo( new(c.X + rx, c.Y - ry * KAPPA90), new(c.X + rx * KAPPA90, c.Y - ry), new(c.X, c.Y - ry)); queue.AddBezierTo( new(c.X - rx * KAPPA90, c.Y - ry), new(c.X - rx, c.Y - ry * KAPPA90), new(c.X - rx, c.Y)); queue.AddClose(); }
/// <summary> /// Creates new circle arc shaped sub-path. /// The arc is drawn from angle a0 to a1. /// </summary> /// <param name="c">The arc center.</param> /// <param name="r">The arc radius.</param> /// <param name="dir">The direction the arc is swept in.</param> public static void Arc(this Nvg nvg, Vector2D <float> c, float r, float a0, float a1, Winding dir) { Vector2D <float> pPos = default; Vector2D <float> pTan = default; InstructionQueue queue = nvg.instructionQueue; bool line = queue.Count > 0; float da = a1 - a0; if (dir == Winding.Cw) { if (MathF.Abs(da) >= MathF.PI * 2.0f) { da = MathF.PI * 2.0f; } else { while (da < 0.0f) { da += MathF.PI * 2.0f; } } } else { if (MathF.Abs(da) >= MathF.PI * 2.0f) { da = -MathF.PI * 2.0f; } else { while (da > 0.0f) { da -= MathF.PI * 2.0f; } } } int ndivs = Math.Max(1, Math.Min((int)(MathF.Abs(da) / (MathF.PI * 0.5f) + 0.5f), 5)); float hda = (da / (float)ndivs) / 2.0f; float kappa = MathF.Abs(4.0f / 3.0f * (1.0f - MathF.Cos(hda)) / MathF.Sin(hda)); if (dir == Winding.Ccw) { kappa *= -1.0f; } for (int i = 0; i <= ndivs; i++) { float alpha = a0 + da * ((float)i / (float)ndivs); Vector2D <float> d = new(MathF.Cos(alpha), MathF.Sin(alpha)); Vector2D <float> pos = new(c.X + d.X * r, c.Y + d.Y * r); Vector2D <float> tan = new(-d.Y * r * kappa, d.X *r *kappa); if (i == 0) { if (line) { queue.AddLineTo(pos); } else { queue.AddMoveTo(pos); } } else { queue.AddBezierTo(pPos + pTan, pos - tan, pos); } pPos = pos; pTan = tan; } }