GeometryHelper 9.0.2

dotnet add package GeometryHelper --version 9.0.2
                    
NuGet\Install-Package GeometryHelper -Version 9.0.2
                    
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="GeometryHelper" Version="9.0.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="GeometryHelper" Version="9.0.2" />
                    
Directory.Packages.props
<PackageReference Include="GeometryHelper" />
                    
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 GeometryHelper --version 9.0.2
                    
#r "nuget: GeometryHelper, 9.0.2"
                    
#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 GeometryHelper@9.0.2
                    
#: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=GeometryHelper&version=9.0.2
                    
Install as a Cake Addin
#tool nuget:?package=GeometryHelper&version=9.0.2
                    
Install as a Cake Tool

GeometryHelper

NuGet Version License: MIT

Geometry for engineering drawings and models, in two dimensions and in three, with the label placement algorithms that run on the 2D half. Written in C# and targeting netstandard2.0, so it loads into the .NET Framework hosts that Tekla Structures and AutoCAD provide as well as into modern .NET.

Every comparison that floating point error can affect takes a Tolerance, because coordinates that come out of a BIM model are never exact.

Installation

dotnet add package GeometryHelper

Quick start

using GeometryHelper;            // Tolerance, Angle, OffsetOptions
using GeometryHelper.Enums;      // LineEnd, OffsetJoin, PointLocation
using GeometryHelper.Geometry;   // every Geo type, 2D and 3D
using GeometryHelper.Core;       // every operation

// In the plane: grow a slab, then cut an opening out of it.
var slab = new GeoPolygon2(new GeoPoint2(0, 0), new GeoPoint2(4000, 0),
                           new GeoPoint2(4000, 2000), new GeoPoint2(0, 2000));
var duct = new GeoPolygon2(new GeoPoint2(1000, 500), new GeoPoint2(1400, 500),
                           new GeoPoint2(1400, 900), new GeoPoint2(1000, 900));

GeoPolygon2 grown = slab.Offset(50.0)[0];                 // 4100 x 2100
GeoFace2 pierced = Boolean2.Subtract(grown, duct)[0];     // one face, one hole

// In space: a beam trimmed where it runs into a wall.
var beam = new GeoLine3(new GeoPoint3(0, 0, 0), new GeoPoint3(3000, 0, 0));
var wall = new GeoPlane3(new GeoPoint3(2500, 0, 0), GeoVector3.XAxis);
beam.TryTrimTo(wall, LineEnd.End, out GeoLine3 cut);      // (0,0,0) -> (2500,0,0)

What is inside

Namespace Holds
GeometryHelper Tolerance, Angle, OffsetOptions, GeometryHelperLog
GeometryHelper.Enums PointLocation, PlaneSide, LineSide, LineEnd, LineExtension, OffsetJoin
GeometryHelper.Geometry 30 immutable shapes: GeoPoint2 … GeoFace2 in the plane, GeoPoint3 … GeoSolid3 in space, arcs and the chains that carry them, and a transformation and a local coordinate system for each dimension
GeometryHelper.Core 28 operation classes, each dimension mirroring the other: Boolean2/Boolean3, Offset2/Offset3, Distance2/Distance3, … plus Arc2, Corner2 for chamfering and rounding, and PlanarMap, which carries flat shapes between the two
GeometryHelper.Spatial GeoBvh2 and GeoBvh3, the bounding volume hierarchies for large chains and meshes
GeometryHelper.Extension turning raw point lists into geometry
GeometryHelper.Arranging label placement: Arranger, ArrangeItem, ArrangeResult, ArrangeOptions, five algorithms
GeometryHelper.Packing boxes onto sheets of paper: SheetPacker, Sheet, PackOptions, PackResult

Every operation is reachable both ways: the static form names the larger shape first, and the instance form sits on whichever of the two reads better where you are calling from.

Because both dimensions are one assembly, a flat shape in space can be laid out in its own plane, worked on with the whole 2D half of the library, and put back:

GeoCoordinateSystem3 frame = plate.GetFrame();
GeoFace2 flat = plate.ProjectToFace2(frame);
GeoFace3 back = flat.Subtract(openings)[0].ToFace3(frame);

Guides

The whole of it, searchable, with every type and member: https://nguyenthanguth.github.io/GeometryHelper/.

Guide Covers
Shared types Tolerance, Angle, the enumerations, OffsetOptions, GeometryHelperLog
Geometry in the plane points to polygons and faces; extending, trimming, offsetting, combining regions
Geometry in space points to solids; splitting, boolean bodies, meshes, local frames
Label placement five algorithms behind one entry point
Packing boxes onto sheets drawing views onto A0 to A4 sheets or sheets of a size of their own, a new sheet when one is full

Coverage

dotnet test tests/GeometryHelper.UnitTest --collect:"XPlat Code Coverage"

writes a Cobertura file under TestResults. Only this library is counted.

Build and Test

dotnet build src/GeometryHelper/GeometryHelper.csproj
dotnet test  tests/GeometryHelper.UnitTest/GeometryHelper.UnitTest.csproj

Warnings are errors in CI, and every public member is documented, so a missing XML comment or a stale cref fails the build rather than landing quietly. Every example printed in the guides is also a test.

Licence

MIT. Clipper2, which this package references and which resolves regions in the plane, is under the Boost Software License 1.0.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (5)

Showing the top 5 NuGet packages that depend on GeometryHelper:

Package Downloads
GeometryHelper.IfcConvert

Converts geometry between IFC (via xBIM) and GeometryHelper: points, vectors, matrices, faces, and solids.

GeometryHelper.CadConvert

Converts geometry between AutoCAD and GeometryHelper: points, vectors, lines, polylines, polygons, circles, and bounding boxes.

GeometryHelper.TeklaConvert.2026

Converts geometry between Tekla Structures 2026 and GeometryHelper: points, vectors, segments, planes, coordinate systems, bounding boxes, transformation matrices, and the faces and loops of a Tekla solid. Also reads the objects of IFC reference models into solids placed where Tekla shows them, through GeometryHelper.IfcConvert. Built for Tekla Structures 2026; install the package whose year matches your Tekla.

GeometryHelper.TeklaConvert.2025

Converts geometry between Tekla Structures 2025 and GeometryHelper: points, vectors, segments, planes, coordinate systems, bounding boxes, transformation matrices, and the faces and loops of a Tekla solid. Also reads the objects of IFC reference models into solids placed where Tekla shows them, through GeometryHelper.IfcConvert. Built for Tekla Structures 2025; install the package whose year matches your Tekla.

GeometryHelper.TeklaConvert.2020

Converts geometry between Tekla Structures 2020 and GeometryHelper: points, vectors, segments, planes, coordinate systems, bounding boxes, transformation matrices, and the faces and loops of a Tekla solid. Also reads the objects of IFC reference models into solids placed where Tekla shows them, through GeometryHelper.IfcConvert. Built for Tekla Structures 2020; install the package whose year matches your Tekla.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
9.0.2 0 10/1/2026
9.0.1 0 10/1/2026
9.0.0 0 10/1/2026
8.0.0 20 9/30/2026
7.0.0 127 9/29/2026
6.3.0 163 9/29/2026
6.2.0 168 9/29/2026
6.1.0 210 9/28/2026
6.0.0 219 9/28/2026
5.1.0 247 9/21/2026
5.0.0 245 9/21/2026

NEW IN 9.0.2

     FIXED: surface meshes of faces with holes, GeoFace3.TriangulateSurface and all built on it: GeoSolid3.Triangulate
     and TriangulateSurface, GetMassProperties, the clash check, GeoBvh3, distances, rays, collisions, the OBJ export.
     - A face ear clipping gave up on came back as the fan of its boundary, laid across every hole. It gave up on holes
       in a row a hair out of line, as the pits of slabs from Tekla Structures stand, and on holes a rib of 2 mm or
       less apart; both are clipped now. Such slabs had their centroid put up to 14.5 mm off.
     - A face whose holes touch each other or its boundary could be meshed across a hole and taken as right. Such a
       face is cut into strips at its corners now, its material read as the booleans read it.
     - No face is meshed as a fan any more. Of 5 750 random faces with holes, 9.0.1 meshed 4 766 wrong; none now.
     Volume, Centroid, SurfaceArea and the booleans do not mesh faces and were right. Details in CHANGELOG.md.

     NEW IN 9.0.1

     FIXED: booleans of bodies lying within a few hundredths of a millimetre of each other, as slabs drawn side by
     side in a Tekla Structures model do.
     - TrySubtract no longer takes material from a body the other only touches, nor gives a body back larger than it
       was: a difference that finds nothing of the one body within the other hands the first back as it came.
     - A sliver thinner than twice the point tolerance is material, placed by the middle of its thickness, where it
       was taken for empty; a closed piece that thin is a piece of its own when a body is split into its pieces
       (Boolean3.SplitShells, and the booleans).
     - A narrow cap of a cut keeps the side it faces; the two pieces of a face cut along an edge within the tolerance
       of the plane meet along one line; a wedge too thin to cut no longer throws the whole cut away.
     Of 864 pairs of concrete slabs whose boxes meet, 240 broke V(A) - V(A - B) = V(A ^ B) by more than 1 mm3 and 8
     do now; 850 that only touch come back as they were. Details in CHANGELOG.md.

     NEW IN 9.0.0

     Nothing in GeometryHelper changed: it is released at 9.0.0 with GeometryHelper.TeklaConvert, which removes
     FaceConvert.TryReadFace and LoopConvert.TryReadLoop.

     NEW IN 8.0.0

     BREAKING: the default tolerance is set for a model in millimetres.
     - Tolerance.DefaultEqualPoint, DefaultEqualVector and DefaultEqualPlanar are 1E-2, all three 1E-4 before:
       points a hundredth apart are one point, a vector shorter than a hundredth has no direction, and a face may
       stand a hundredth out of flat. Offsets, booleans and force-directed label placement merge what lies a
       hundredth apart. Geometry in metres wants a tolerance of its own:
       new Tolerance(1E-5, 1E-5, Tolerance.DefaultEqualAngleRad, 1E-5).
     - TransformBy on GeoPolygon3, GeoFace3 and GeoSolid3 carries polygons over as they are under a move that
       keeps every length; a stretch, shear or projection is still checked.
     - NEW: GeometryHelper.Packing: SheetPacker.Pack lays boxes onto A0 to A4 or custom sheets.
     - NEW: GeoTransform3.ToCoordinateSystem and GeoTransform2.ToCoordinateSystem.
     - NEW: Tolerance.Default, the four defaults together; new Tolerance() has every threshold at 0.
     - NEW: GeoFace3.FromLoops reads a face from loops of corners out of flat, as triangles on its own corners, so
       a body a modeller gives with faces out of flat stays closed.
     - FIXED, solids: Locate on thin faces; prepared solids at shallow crossings; booleans of bent bars, of bodies
       far from the origin and of faces lying back to back no longer throw or come out open where another way of
       cutting closes them, and the booleans that try return false with a warning in the log instead of throwing;
       IsClosed with a short edge beside a long one; the surface triangulation of a face with two holes bridged to
       one corner; a polygon of three corners at a planar tolerance of nought; SignedDistanceTo on the surface.
       Details in CHANGELOG.md.

     NEW IN 7.0.0

     BREAKING: code that reads ArrangeItem.Offset, or treats OffsetTop / OffsetBottom as nullable, has to change.
     - ArrangeItem.Offset only sets: it writes OffsetTop and OffsetBottom both. The sides are plain doubles, 50
       each unless set. Set after a side, Offset overwrites it; code that sets Offset first, or only, places every
       label where 6.3.0 did.
     - NEW: ArrangeItem.Side (ArrangeSide.Both / Top / Bottom) keeps a label to one side of its leader; Top is the
       side facing up in the drawing, the left of a vertical leader. With no free place there, the label is left
       on its first place on that side and reported not placed.
     - FIXED, label placement: a label with a gap of its own per side no longer goes to the wider side; rows are
       tried nearest first; a vertical leader is judged by the tolerance's angle; the second pass keeps clear of
       the regions of every item; MaxBacktrackSteps counts steps back only, and bounded backtracking and
       constraint satisfaction search in a loop; MaximumCandidates caps every candidate; gaps refuse NaN and
       infinity; a gap of double.MaxValue no longer makes Arranger.Run throw. Labels with the same gap on both
       sides are placed as in 6.2.0 and 6.3.0. Details in CHANGELOG.md.

     NEW IN 6.3.0

     Nothing breaks: code built against 6.2.0 runs unchanged.
     - ArrangeItem.OffsetTop and OffsetBottom: a gap of its own for each side of a label's leader, the side that
       faces up in the drawing and the side that faces down, whichever way the leader runs. A side left unset
       takes Offset, and with neither set every candidate and every result is what it was.

     NEW IN 6.2.0

     Nothing in GeometryHelper itself changed: it is released at 6.2.0 with GeometryHelper.IfcConvert and
     GeometryHelper.TeklaConvert, which read a whole IFC model on every core at once.

     NEW IN 6.1.0

     Nothing breaks: code built against 6.0.0 runs unchanged.
     - ClashResult.Depth: how deep two parts run into each other, the least thickness of what they share.
       ClashOptions takes minimumDepth and minimumVolume; a shallower or smaller overlap is reported as Touch.
     - ClashBar: reinforcement checked by its centre line and radius, Clash3.Find(bars, parts, ...), with no
       body built and no boolean run; each clash says its Depth and LengthInside.

     NEW IN 6.0.0

     A summary. The full notes, with the reason and the measurement behind each item, and those of every
     earlier version: https://github.com/nguyenthanguth/GeometryHelper/blob/main/CHANGELOG.md

     BREAKING. Three changes, and no [Obsolete] forwarders left behind.
     - RENAMED. GetClosestOnBoundary(other) is GetShortestLineTo(other) on GeoLine2, GeoCircle2,
       GeoRectangle2, GeoPolygon2, GeoPolyline2 and GeoLine3, and Projection2.GetClosestSegment and
       Projection3.GetClosestSegment are GetShortestLineTo. On GeoPolygonArc2 and GeoPolylineArc2 the old
       method is GetClosestEdge, returning the GeoEdge2 it always meant.
     - MEASURED DIFFERENTLY. Whether an arc reaches a point of its own circle is judged as a distance, no
       longer within a whole degree of angle: DistanceTo no longer finds an arc nearer than its own points
       allow, and GetIntersections no longer finds crossings off the end of an arc.
     - RESHAPED. Label placement (GeometryHelper.Arranging): Arrange is ArrangeItem (Box, Leader, Offset),
       and Arrange.Run is Arranger.Run, which returns an ArrangeResult (Translation, Placed) per item
       instead of writing into the items. ArrangeOptions.Default is a new instance each time it is read.

     NEW
     - Nearest things from both sides: GetClosestEdge on the straight shapes, exact GetShortestLineTo on
       the curved ones, and GeoSolid3 measured against every shape in space.
     - SignedDistanceTo on the eight closed shapes: negative inside, nought on the boundary, positive
       outside, as Locate answers.
     - Arcs in space: GeoEdge3, GeoPolylineArc3 (a reinforcing bar), GeoPolygonArc3, Corner3.Fillet and
       arcs through PlanarMap; in GeometryHelper.TeklaConvert, ReinforcementConvert and
       RebarGeometryConvert. Crossings, collisions and cutting are exact; a curved chain is measured in
       space through ToPolyline3(chordTolerance).
     - Whichever of two shapes is in hand can be asked about the other: some fifty-five operations Core
       worked out and no type offered are now on the types, both ways round, GeoFace2 and GeoEdge2
       included.
     - The families that change geometry: regions in the plane cut by a line or a chain; arcs, chains,
       loops and edges extended and trimmed; fillets and chamfers for the straight shapes and in space;
       booleans on GeoPolygon3, GeoFace3 and GeoObb3; GeoPolylineArc3.OffsetInPlane; GeoObb3.TryExpand;
       GeoTriangle3.ToPolygon3 and ToFace3.
     - Chains in space answer CollidesWith, GetIntersections and TryIntersectWith; GeoFace2 and
       GeoRectangle2 gained TryIntersectWith; GeoLine3, GeoPolyline3, GeoRay3 and GeoEdge2 gained Locate.
     - Solid against solid: TryCutOpenings, TriangulateSurface, Intersect (one body per shared region),
       SplitShells and TryGetContact.
     - GeoSolid3.Prepare() and GeoPreparedSolid3, for a body asked many questions; Clash3.Find with
       ClashOptions, ClashResult and ClashKind.
     - Making bodies: Extrude, Cylinder, Pipe, Sweep and Revolve.
     - GetMassProperties, Section, ConvexHull2 and ConvexHull3, GeoRectangle2.Fit and GeoObb3.Fit.
     - IsValid on every value type, MakeValid for a polygon crossing itself, and Tolerance.Use scopes.
     - GeometryHelper.Export: ObjWriter, SvgWriter and Wkt.
     - StartPoint, EndPoint, MidPoint and TrySplitAtDistance across the four chains.

     FIXED
     - A body's openings are read as material everywhere: fifteen GeoSolid3 queries read the faces
       running across a hole, so a pin through a bolt hole was a clash.
     - Intersecting a bent bar took tens of seconds and could be wrong or throw; eight bent bars against a
       beam went from 57 s to 12 ms.
     - The solid booleans were wrong on about one pair in 250 where a body passed a few thousandths from a
       corner of the other, and could throw; they could also leave a sheet of no thickness inside their
       result, on which SplitShells threw.
     - Ear clipping meshed across the gaps of an I section's end and across the holes of a face carrying
       a row of points along an edge.
     - Two shapes lay in one plane when turned half a degree apart, and crossings under a degree were
       taken as parallel.
     - An arc or an edge was measured to a GeoCircle2's rim instead of its disc; two arcs are weighed all
       four ways round; a concave GeoPolygon3 is measured by triangles inside it, not by its fan.
     - NaN and infinity are refused wherever a negative size was.
     - IsClosed accepts watertight bodies whose edges are split, or shared by four faces.
     - GeometryHelperLog.Writer is called one message at a time.

     FASTER
     - The clash check: a steel frame of 789 parts with 400 bolt holes, prepared and checked, 9.7 s to
       2.0 s (7.3 s to 0.65 s under the server garbage collector); 80 crossing bars 3.0 s to 0.04 s; 2000
       boxes 39 ms to 3 ms. Every pair and kind of clash found is the same.

     EARLIER. 5.1.0 rounds each corner by its own radius; 5.0.0 made four packages one and brought arcs,
     chamfers, fillets and GeoTransform2. Both in full in the same file.