diff --git a/src/Speckle.Objects/Geometry/Region.cs b/src/Speckle.Objects/Geometry/Region.cs new file mode 100644 index 00000000..fcc044dd --- /dev/null +++ b/src/Speckle.Objects/Geometry/Region.cs @@ -0,0 +1,97 @@ +using Speckle.Objects.Other; +using Speckle.Sdk.Common; +using Speckle.Sdk.Models; + +namespace Speckle.Objects.Geometry; + +/// +/// Flat polygon, defined by an outer boundary and inner loops. +/// +[SpeckleType("Objects.Geometry.Region")] +public class Region : Base, IHasArea, IHasBoundingBox, ITransformable, IDisplayValue> +{ + /// + /// Boundary of a region. + /// Should be a planar, closed, non-self-intersecting ICurve. + /// + public required ICurve boundary { get; set; } + + /// + /// Loops (voids) in the region. + /// Each loop should be planar, closed, non-self-intersecting ICurve, located inside the boundary. + /// The loops should not intersect or touch each other. + /// + public required List innerLoops { get; set; } = new(); + + /// + /// The units of object's coordinates. + /// This should be one of + /// + public required string units { get; set; } + + /// + /// Indication whether the region is just a geometry (false) or has a hatch pattern (true). + /// It's a distinction for receiving in apps that support both Region and Hatch (aka region with hatch pattern) + /// + public required bool hasHatchPattern { get; set; } + + /// + public double area { get; set; } + + /// + public Box? bbox { get; set; } + + /// + [DetachProperty] + public List displayValue { get; set; } = new(); + + /// + public bool TransformTo(Transform transform, out ITransformable transformed) + { + // assign self to the returned object, in case transformation fails + transformed = this; + + // transform boundary + if (boundary is ITransformable boundaryTransformable) + { + boundaryTransformable.TransformTo(transform, out ITransformable transformedBoundaryResult); + var transformedBoundary = (ICurve)transformedBoundaryResult; + + // transform inner loops + var transformedLoops = new List(); + foreach (var loop in innerLoops) + { + if (loop is ITransformable loopTransformable) + { + loopTransformable.TransformTo(transform, out ITransformable transformedLoop); + transformedLoops.Add((ICurve)transformedLoop); + } + else + { + return false; + } + } + + // transform display meshes + var transformedMeshes = new List(); + foreach (var mesh in displayValue) + { + mesh.TransformTo(transform, out ITransformable transformedMesh); + transformedMeshes.Add((Mesh)transformedMesh); + } + + // if boundary and loops transformations succeeded + transformed = new Region + { + boundary = transformedBoundary, + innerLoops = transformedLoops, + hasHatchPattern = hasHatchPattern, + displayValue = transformedMeshes, + units = units, + }; + return true; + } + + return false; + } +}