ModelingEvolution.Drawing 1.4.0.55

dotnet add package ModelingEvolution.Drawing --version 1.4.0.55
                    
NuGet\Install-Package ModelingEvolution.Drawing -Version 1.4.0.55
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ModelingEvolution.Drawing" Version="1.4.0.55" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ModelingEvolution.Drawing" Version="1.4.0.55" />
                    
Directory.Packages.props
<PackageReference Include="ModelingEvolution.Drawing" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ModelingEvolution.Drawing --version 1.4.0.55
                    
#r "nuget: ModelingEvolution.Drawing, 1.4.0.55"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ModelingEvolution.Drawing@1.4.0.55
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ModelingEvolution.Drawing&version=1.4.0.55
                    
Install as a Cake Addin
#tool nuget:?package=ModelingEvolution.Drawing&version=1.4.0.55
                    
Install as a Cake Tool

ModelingEvolution.Drawing

NuGet NuGet Downloads CI License

A high-performance .NET generic math library for 2D geometry, shapes, intersections, and drawing primitives. Built on INumber<T> constraints from .NET generic math, all types work with float, double, decimal, or any compatible numeric type -- with zero boxing and full type safety.

Table of Contents

Why This Library

  • Generic math -- write geometry code once; use it with float, double, or any IFloatingPointIeee754<T> type.
  • No boxing -- all core types are struct or readonly record struct, avoiding heap allocations.
  • Rich interface hierarchy -- IShape<T, TSelf> unifies area, perimeter, centroid, bounding box, containment, rotation, and scaling across all shape types.
  • Comprehensive intersections -- 21 intersection combinations (line, segment, circle, triangle, rectangle, polygon) with zero-alloc FirstOf variants.
  • Polygon booleans -- union, intersection, difference, and clustering via Clipper2.
  • Exact Bezier clipping -- Path<T>.Intersect(Rectangle<T>) computes analytically exact sub-curves, not polygon approximations.
  • Serialization built in -- Protobuf (protobuf-net) attributes on all core types, plus custom JSON converters and SVG exporters.

Installation

dotnet add package ModelingEvolution.Drawing

Or via the NuGet Package Manager:

Install-Package ModelingEvolution.Drawing

Quick Start

using ModelingEvolution.Drawing;

// Points and vectors (using double precision)
var a = new Point<double>(1.0, 2.0);
var b = new Point<double>(4.0, 6.0);

double distance = a.DistanceTo(b);          // 5.0
Point<double> mid = a.Lerp(b, 0.5);         // {X=2.5, Y=4}

// Shapes with area and perimeter
var circle = new Circle<double>(Point<double>.Zero, 10.0);
double area = circle.Area();                 // ~314.159
double perimeter = circle.Perimeter();       // ~62.832
bool inside = circle.Contains(new Point<double>(3.0, 4.0)); // true

// Intersections
var line = Line<double>.From(new Point<double>(0, 0), new Point<double>(10, 10));
Segment<double>? chord = circle.Intersect(line); // the chord through the circle

// Polygon boolean operations
var poly1 = new Polygon<double>(
    new Point<double>(0, 0), new Point<double>(10, 0),
    new Point<double>(10, 10), new Point<double>(0, 10));
var poly2 = new Polygon<double>(
    new Point<double>(5, 5), new Point<double>(15, 5),
    new Point<double>(15, 15), new Point<double>(5, 15));

Polygon<double> merged = poly1 | poly2;     // union
Polygon<double> overlap = poly1 & poly2;    // intersection

API Reference

Core Types

Type Description
Point<T> 2D Cartesian point. Supports arithmetic with vectors and sizes, distance, lerp, reflect, rotate, clamp, midpoint, matrix transform, tuple conversion, and parsing.
Vector<T> 2D vector with magnitude, direction, dot/cross product, normalize, projection, reflect, rotate, perpendicular (CW/CCW), angle between, lerp, and matrix transform.
Size<T> Width/height pair with arithmetic operators, component-wise multiply/divide, and parsing.
Matrix<T> 3x3 affine transformation matrix. Supports translate, rotate, scale, skew, invert, append/prepend, and point/vector transforms.
Rectangle<T> Axis-aligned rectangle with contains, intersect, inflate, offset, bounds, tiling, diagonal, distance-to-point, and LTRB construction.

Shapes

All shapes implement IShape<T, TSelf>, providing Area(), Perimeter(), Centroid(), BoundingBox(), Contains(Point<T>), Rotate(Degree<T>, Point<T>), and Scale(T).

Type Description
Circle<T> Center + radius. Intersects with lines, segments, circles, and rectangles. Tangent point detection. PointAt(Radian<T>) for parameterized access.
Triangle<T> Three vertices. Incircle, circumcircle, orthocenter, angles, edges. Classification: IsRight(), IsAcute(), IsObtuse(), IsEquilateral(), IsIsosceles(), IsScalene(). Similarity and congruence tests.
Polygon<T> Immutable polygon backed by ReadOnlyMemory<Point<T>>. Shoelace area, ray-casting containment, convex hull (Graham scan), IsConvex(), edges, simplify. Boolean operations: union, intersect, subtract, cluster. Operators: \| (union), & (intersect), - (subtract).
Rectangle<T> Implements IArea<T>, IPerimeter<T>, ICentroid<T>, IBoundingBox<T>, IScalable<T, Rectangle<T>>. Rotation returns Polygon<T>.

Lines and Segments

Type Description
Line<T> Infinite line (supports vertical lines). From two points, point + direction, or equation. Intersect with lines, segments, circles, triangles, rectangles, polygons. Distance-to-point, angle-between, parallel/perpendicular tests, project point, reflect point.
Segment<T> Bounded line segment (start + end). Length, midpoint, direction, lerp, split, reverse. Intersect with segments, lines, circles, rectangles. Distance-to-point, project point, parallel test. Liang-Barsky rectangle clipping.

Curves and Paths

Type Description
BezierCurve<T> Cubic Bezier curve (4 control points). Evaluate(t), Split(t), SubCurve(t0, t1) via De Casteljau. Extremum points, linear equation intersection, rectangle edge crossings.
Path<T> Immutable sequence of BezierCurve<T> segments. Create from points (FromPoints) with smoothing, or from SVG path data (Parse). Length(), PointAt(t) with arc-length parameterization. Exact Intersect(Rectangle<T>) that splits Bezier curves at crossing points. Transform, rotate, reverse, append. Close() and ToPolygon() for rasterization.
PolygonalCurve<T> Mutable sequence of connected points that generates smooth Bezier segments via GetSmoothSegment(i).

Angles and Coordinates

Type Description
Degree<T> Angle in degrees. Implicit conversion from numeric values and from Radian<T>. Arithmetic, normalize to (-180, 180], abs.
Radian<T> Angle in radians. Implicit conversion from Degree<T>. Arithmetic, normalize to (-pi, pi], abs. Static Sin/Cos helpers.
PolarPoint<T> Polar coordinates (radius, angle). Implicit conversions to/from Point<T>, explicit to Vector2 and Vector<T>.
CylindricalPoint<T> Cylindrical coordinates (radius, angle, height). Implicit from PolarPoint<T>, to Vector3.

Equations

Type Description
LinearEquation<T> y = Ax + B. Create from points, angle, or direction. Compute, zero point, intersect, perpendicular, translate, mirror.
QuadraticEquation<T> ax^2 + bx + c = 0. Zero points using the discriminant.
CubicEquation<T> ax^3 + bx^2 + cx + d = 0. Root finding via Newton-Raphson.
CircleEquation<T> (x-cx)^2 + (y-cy)^2 = r^2. Intersect with LinearEquation<T>.

Colors

Type Description
Color ARGB color as a 32-bit uint. Parse from hex (#RRGGBB, #AARRGGBB), rgba(r,g,b,a), or HSV strings. GetHue(), GetSaturation(), GetLightness(), GetBrightness(), MakeTransparent(float). Implicit from string or tuple. JSON and Protobuf serialization.
HsvColor HSV (hue, saturation, value) color space with optional alpha. Implicit conversions to/from Color. Parse from hsv()/hsva() strings, JSON arrays, or hex.

Interfaces

// Composable geometry traits
public interface IArea<T>         { T Area(); }
public interface IPerimeter<T>    { T Perimeter(); }
public interface ICentroid<T>     { Point<T> Centroid(); }
public interface IBoundingBox<T>  { Rectangle<T> BoundingBox(); }
public interface IRotatable<T, TSelf> { TSelf Rotate(Degree<T> angle, Point<T> origin = default); }
public interface IScalable<T, TSelf>  { TSelf Scale(T factor); }

// Unified shape interface -- combines all of the above plus point containment
public interface IShape<T, TSelf> : IArea<T>, IPerimeter<T>, ICentroid<T>,
    IBoundingBox<T>, IRotatable<T, TSelf>, IScalable<T, TSelf>
{
    bool Contains(Point<T> point);
}

Implementing types: Circle<T>, Triangle<T>, Polygon<T>, and Rectangle<T> (partial -- no IRotatable since rotation produces a Polygon<T>).

Key Features

Generic Math

All geometry types are parameterized by T with constraints like IFloatingPointIeee754<T>, IMinMaxValue<T>, etc. This means the same code works with any numeric type:

// Single precision
var pf = new Point<float>(1f, 2f);
var cf = new Circle<float>(pf, 5f);

// Double precision
var pd = new Point<double>(1.0, 2.0);
var cd = new Circle<double>(pd, 5.0);

// Both compute area with their respective precision
float areaF = cf.Area();
double areaD = cd.Area();

IShape Interface Hierarchy

Write generic algorithms over any shape:

void PrintShapeInfo<T, TShape>(TShape shape)
    where T : INumber<T>, ITrigonometricFunctions<T>, IRootFunctions<T>,
              IFloatingPoint<T>, ISignedNumber<T>, IFloatingPointIeee754<T>, IMinMaxValue<T>
    where TShape : IShape<T, TShape>
{
    Console.WriteLine($"Area:      {shape.Area()}");
    Console.WriteLine($"Perimeter: {shape.Perimeter()}");
    Console.WriteLine($"Centroid:  {shape.Centroid()}");
    Console.WriteLine($"Bounds:    {shape.BoundingBox()}");
}

Intersections

The Intersections static class provides 21 combination overloads covering every pair of: Line, Segment, Circle, Triangle, Rectangle, and Polygon. Each combination also has a zero-allocation FirstOf variant.

// Line x Line
Point<double>? p = Intersections.Of(line1, line2);

// Line x Circle -> chord (Segment) or null
Segment<double>? chord = Intersections.Of(line, circle);

// Tangent detection
Point<double>? tangent = Intersections.TangentPoint(line, circle);

// Circle x Circle -> radical chord or tangent point
Segment<double>? radicalChord = Intersections.Of(circle1, circle2);

// Segment x Segment
Point<double>? hit = Intersections.Of(seg1, seg2);

// Zero-alloc first-hit variants
Segment<double>? first = Intersections.FirstOf(line, polygon);

Instance methods are also available on each shape for convenience:

Segment<double>? chord = circle.Intersect(line);
Point<double>? hit = segment.Intersect(otherSegment);
Segment<double>? clipped = line.Intersect(rectangle);

Polygon Boolean Operations

Powered by Clipper2:

var a = new Polygon<double>( /* ... */ );
var b = new Polygon<double>( /* ... */ );

// Operators
Polygon<double> union      = a | b;
Polygon<double> intersect  = a & b;
Polygon<double> difference = a - b;

// Methods returning multiple result polygons
List<Polygon<double>> unions = a.Union(b, removeHoles: true);
List<Polygon<double>> diffs  = a.Subtract(b);

// Batch operations
List<Polygon<double>> merged = Polygon<double>.Union(polygons);

// Clustering overlapping polygons
IEnumerable<Polygon<double>> clusters = Polygon<double>.Cluster(polygons);

Bezier Path Clipping

Path<T>.Intersect(Rectangle<T>) performs analytically exact Bezier-rectangle clipping. It finds the precise parameter values where each cubic Bezier segment crosses the rectangle edges, then uses SubCurve(t0, t1) via De Casteljau subdivision to return exact sub-paths -- not polygon approximations.

var path = Path<double>.FromPoints(points);
IEnumerable<Path<double>> clipped = path.Intersect(viewport);

foreach (var subPath in clipped)
{
    // Each sub-path contains only the Bezier segments inside the rectangle,
    // with curves split precisely at the rectangle boundaries.
}

Serialization

Protobuf -- all core types carry [ProtoContract] / [ProtoMember] attributes for use with protobuf-net:

var point = new Point<float>(1f, 2f);
byte[] bytes = Serializer.Serialize(point);

JSON -- custom System.Text.Json converters for Point<T>, Polygon<T>, Path<T>, Color, and HsvColor:

var polygon = new Polygon<double>( /* ... */ );
string json = JsonSerializer.Serialize(polygon);

String parsing -- IParsable<T> implementations on Point<T>, Rectangle<T>, Size<T>, Path<T>, Color, and HsvColor:

var point = Point<double>.Parse("1.5, 2.5");
var rect  = Rectangle<double>.Parse("0 0 100 200");
var color = Color.Parse("#FF8800");
var path  = Path<double>.Parse("M 0 0 C 1 2, 3 4, 5 6", null);

SVG Export

Polygon<T> and Path<T> support SVG export via the [SvgExporter] attribute and the SvgExporter infrastructure:

// Path<T>.ToString() produces SVG path data
var path = Path<double>.FromPoints(points);
string svgData = path.ToString(); // "M 0 0 C 1.5 2.3, 3.1 4.2, 5 6 ..."

Code Examples

Points: Distance, Lerp, Rotate

var a = new Point<double>(0, 0);
var b = new Point<double>(3, 4);

double dist = a.DistanceTo(b);               // 5.0
Point<double> quarter = a.Lerp(b, 0.25);     // {X=0.75, Y=1}
Point<double> reflected = a.Reflect(b);       // {X=6, Y=8}

// Rotate 90 degrees around the origin
Degree<double> angle = 90.0;
Point<double> rotated = b.Rotate(angle);      // {X=-4, Y=3}

// Rotate around a custom center
var center = new Point<double>(1, 1);
Point<double> rotated2 = b.Rotate(angle, center);

// Clamp to a rectangle
var bounds = new Rectangle<double>(0, 0, 10, 10);
Point<double> clamped = new Point<double>(15, -3).Clamp(bounds); // {X=10, Y=0}

Shapes: Area and Perimeter

// Circle
var circle = new Circle<double>(new Point<double>(5, 5), 3.0);
double cArea = circle.Area();        // ~28.274
double cPerim = circle.Perimeter();  // ~18.850

// Triangle
var triangle = new Triangle<double>(
    new Point<double>(0, 0),
    new Point<double>(4, 0),
    new Point<double>(0, 3));
double tArea = triangle.Area();      // 6.0
double tPerim = triangle.Perimeter(); // 12.0

// Polygon (shoelace formula)
var poly = new Polygon<double>(
    new Point<double>(0, 0), new Point<double>(4, 0),
    new Point<double>(4, 3), new Point<double>(0, 3));
double pArea = poly.Area();          // 12.0

Line-Circle Intersection

var circle = new Circle<double>(new Point<double>(0, 0), 5.0);
var line = Line<double>.From(new Point<double>(-10, 0), new Point<double>(10, 0));

// Secant -- returns the chord as a segment
Segment<double>? chord = circle.Intersect(line);
// chord.Value.Start ~ {X=-5, Y=0}, chord.Value.End ~ {X=5, Y=0}

// Tangent detection
var tangentLine = Line<double>.Horizontal(5.0);
Point<double>? tangent = circle.TangentPoint(tangentLine);
// tangent.Value ~ {X=0, Y=5}

Segment-Rectangle Clipping

var viewport = new Rectangle<double>(0, 0, 100, 100);
var segment = new Segment<double>(
    new Point<double>(-20, 50),
    new Point<double>(150, 50));

// Liang-Barsky clipping
Segment<double>? clipped = segment.Intersect(viewport);
// clipped.Value: Start={X=0, Y=50}, End={X=100, Y=50}

Path-Rectangle Clipping

var points = new[]
{
    new Point<double>(10, 50),
    new Point<double>(50, 10),
    new Point<double>(90, 50),
    new Point<double>(130, 90)
};
var path = Path<double>.FromPoints(points);
var viewport = new Rectangle<double>(20, 20, 60, 60); // 20,20 to 80,80

// Exact Bezier clipping -- returns sub-paths with curves split at boundaries
foreach (var subPath in path.Intersect(viewport))
{
    Console.WriteLine($"Sub-path with {subPath.Count} Bezier segments");
}

Triangle Properties

var t = new Triangle<double>(
    new Point<double>(0, 0),
    new Point<double>(4, 0),
    new Point<double>(2, 3));

// Derived circles
Circle<double> incircle = t.Incircle();       // largest circle inside
Circle<double> circumcircle = t.Circumcircle(); // passes through all vertices

// Center points
Point<double> centroid = t.Centroid();
Point<double> orthocenter = t.Orthocenter;

// Classification
bool right = t.IsRight();
bool equilateral = t.IsEquilateral();
bool isosceles = t.IsIsosceles();

// Similarity and congruence
var t2 = new Triangle<double>(
    new Point<double>(0, 0),
    new Point<double>(8, 0),
    new Point<double>(4, 6));
bool similar = t.IsSimilarTo(t2);     // true (same shape, scaled 2x)
bool congruent = t.IsCongruentTo(t2); // false (different size)

// Interior angles
var (atA, atB, atC) = t.Angles;

Polygon Operations

// Convex hull
var cloud = new Polygon<double>(
    new Point<double>(0, 0), new Point<double>(1, 3),
    new Point<double>(2, 1), new Point<double>(4, 4),
    new Point<double>(3, 0), new Point<double>(5, 2));
Polygon<double> hull = cloud.ConvexHull();

// Point containment (ray casting)
bool inside = hull.Contains(new Point<double>(2, 2));

// Boolean operations
var a = new Polygon<double>( /* ... */ );
var b = new Polygon<double>( /* ... */ );
List<Polygon<double>> union = a.Union(b, removeHoles: true);
List<Polygon<double>> diff = a.Subtract(b);
List<Polygon<double>> inter = a.Intersect(b);

// Simplify (remove collinear/close points)
Polygon<double> simplified = hull.Simplify(epsilon: 0.1);

// Convexity test
bool convex = hull.IsConvex();

// Clustering overlapping polygons
var clusters = Polygon<double>.Cluster(polygons);

Matrix Transformations

// Build a transformation
var matrix = Matrix<double>.Identity;
matrix.Translate(10.0, 20.0);
matrix.Rotate(Degree<double>.Create(45.0));
matrix.Scale(2.0, 2.0);

// Transform points and vectors
Point<double> transformed = matrix.Transform(new Point<double>(1, 0));
Vector<double> rotatedVec = matrix.Transform(new Vector<double>(1, 0));

// Compose matrices
var combined = matrix1 * matrix2;

// Invert
matrix.Invert();
Point<double> original = matrix.Transform(transformed);

// Transform a path
var path = Path<double>.FromPoints(points);
Path<double> transformedPath = path.Transform(matrix);

Requirements

  • .NET 9.0 or later (uses generic math interfaces from .NET 7+)
  • Dependencies: Clipper2 1.4.0, protobuf-net 3.2.45

License

See LICENSE for details.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (9)

Showing the top 5 NuGet packages that depend on ModelingEvolution.Drawing:

Package Downloads
RocketWelder.SDK

High-performance video streaming client library for RocketWelder services with zero-copy shared memory support

BlazorBlaze

High-performance 2D canvas engine for Blazor using SkiaSharp. Features include scene graph, hit detection, camera zoom/pan, extensible controls, and reactive data binding.

ModelingEvolution.Controls.Blazor.ResizableControl

Resizable

ModelingEvolution.Controls.Blazor.SvgCanvasControl

SvgCanvasControl

ModelingEvolution.HdrSplitControl

HDR Split Canvas Controls for Blazor

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.4.0.55 0 2/6/2026
1.3.0.54 0 2/6/2026
1.2.1.53 0 2/6/2026
1.2.0.52 0 2/6/2026
1.1.5.51 110 1/18/2026
1.1.4.50 85 1/18/2026
1.1.3.49 85 1/18/2026
1.1.2.48 87 1/18/2026
1.1.1.47 157 1/12/2026
1.1.0.46 89 1/12/2026
1.0.63.45 806 11/24/2025
1.0.62.44 649 9/28/2025
1.0.61.43 132 9/26/2025
1.0.60.42 147 9/26/2025
1.0.59.41 160 9/26/2025
1.0.58.36 189 9/5/2025
1.0.53.35 331 2/26/2025
1.0.48.34 176 2/3/2025
1.0.47.33 143 1/28/2025
1.0.46.33 135 1/28/2025
1.0.45.33 152 1/27/2025
1.0.44.33 143 1/27/2025
1.0.43.33 141 1/27/2025
1.0.42.32 140 1/27/2025
1.0.41.31 154 1/27/2025
1.0.40.31 144 1/27/2025
1.0.39.29 153 1/27/2025
1.0.38.28 160 1/26/2025
1.0.37.28 157 1/26/2025
1.0.36.28 147 1/22/2025
1.0.35.26 219 12/2/2024
1.0.34.26 158 11/29/2024
1.0.32.26 157 11/4/2024
1.0.31.26 158 11/4/2024
1.0.30.26 155 11/3/2024
1.0.29.26 169 10/30/2024
1.0.28.25 144 10/30/2024
1.0.27.24 193 9/30/2024
1.0.26.23 166 9/19/2024
1.0.25.22 453 5/7/2024
1.0.23.22 156 5/7/2024
1.0.22.22 162 5/7/2024
1.0.21.21 148 5/7/2024
1.0.20.21 173 4/17/2024
1.0.19.18 187 2/26/2024
1.0.18.18 188 2/26/2024
1.0.17.17 170 2/26/2024
1.0.16.17 174 2/26/2024
1.0.15.15 172 2/15/2024
1.0.14.14 179 2/14/2024
1.0.13.13 173 2/12/2024
1.0.10.13 185 2/7/2024
1.0.6.12 174 2/6/2024
1.0.5.9 167 2/5/2024
1.0.0 169 2/5/2024
0.0.3.7 158 2/5/2024