Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 16 additions & 11 deletions Clipper_Engine/Compute/BooleanDifference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ public static partial class Compute
/**** Public methods ****/
/***************************************************/

[Description("Perform a boolean difference operation between a main region and a list of reference regions using Clipper2.")]
[Description("Perform a boolean difference operation between a main region and a list of reference regions using Clipper2. Reference regions that do not lie on the subject plane are skipped, and the subject is kept.")]
[Input("region", "The main polyline region to subtract from.")]
[Input("refRegions", "List of reference polylines to subtract from the main region.")]
[Input("curvePlane", "Optional plane for the geometry. If null, will be fitted from the main region.")]
[Input("refRegions", "List of reference polylines to subtract from the main region. Regions off the subject plane are ignored.")]
[Input("plane", "If null, processing is in 2D (the plane is fitted from the main region). If not null, 3D geometry is processed using this plane.")]
[Input("tolerance", "Tolerance for planarity checks and numerical precision. Default is Tolerance.Distance.")]
[Output("result", "List of polylines representing the difference (main region minus reference regions).")]
public static List<Polyline> BooleanDifference(this Polyline region, List<Polyline> refRegions, Plane curvePlane = null, double tolerance = Tolerance.Distance)
public static List<Polyline> BooleanDifference(this Polyline region, List<Polyline> refRegions, Plane plane = null, double tolerance = Tolerance.Distance)
{
if (region == null || refRegions == null || region.ControlPoints.Count < 3)
return null;
Expand All @@ -52,24 +52,29 @@ public static List<Polyline> BooleanDifference(this Polyline region, List<Polyli
if (clipRegions.Count == 0)
return new List<Polyline> { region };

if (curvePlane == null)
if (plane == null)
{
curvePlane = region.FitPlane();
if (region.ControlPoints.Any(x => !x.IsInPlane(curvePlane, tolerance)))
plane = region.FitPlane();
if (plane == null || region.ControlPoints.Any(x => !x.IsInPlane(plane, tolerance)))
{
Base.Compute.RecordError("Clipper BooleanDifference method only works for coplanar polylines.");
return null;
}

// An opening on another plane must not discard the subject. Subtract only the clips that lie on this plane.
clipRegions = clipRegions.Where(x => x.ControlPoints.All(y => y != null && y.IsInPlane(plane, tolerance))).ToList();
if (clipRegions.Count == 0)
return new List<Polyline> { region };
}

if (clipRegions.Any(x => x.ControlPoints.Any(y => !y.IsInPlane(curvePlane, tolerance))))
if (!region.IsClosed(tolerance) || clipRegions.Any(x => !x.IsClosed(tolerance)))
{
Base.Compute.RecordError("Clipper BooleanDifference method only works for coplanar polylines.");
return null;
Base.Compute.RecordError("Boolean Difference works on closed regions.");
return new List<Polyline> { region };
}

// Find the orientation matrix to the global XY plane
TransformMatrix orientation = region.OrientationToGlobalXY(curvePlane, tolerance);
TransformMatrix orientation = region.OrientationToGlobalXY(plane, tolerance);
if (orientation == null)
return null;

Expand Down
28 changes: 17 additions & 11 deletions Clipper_Engine/Compute/BooleanIntersection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,32 +40,38 @@ public static partial class Compute
[Description("Perform a boolean intersection operation between two polylines using Clipper2.")]
[Input("poly1", "The first polyline for intersection.")]
[Input("poly2", "The second polyline for intersection.")]
[Input("curvePlane", "Optional plane for the geometry. If null, will be fitted from the first polyline.")]
[Input("plane", "If null, processing is in 2D (the plane is fitted from the first polyline). If not null, 3D geometry is processed using this plane.")]
[Input("tolerance", "Tolerance for planarity checks and numerical precision. Default is Tolerance.Distance.")]
[Output("result", "List of polylines representing the intersection of the two input polylines.")]
public static List<Polyline> BooleanIntersection(this Polyline poly1, Polyline poly2, Plane curvePlane = null, double tolerance = Tolerance.Distance)
public static List<Polyline> BooleanIntersection(this Polyline poly1, Polyline poly2, Plane plane = null, double tolerance = Tolerance.Distance)
{
if (poly1 == null || poly2 == null || poly1.ControlPoints.Count < 3 || poly2.ControlPoints.Count < 3)
return null;

if (curvePlane == null)
if (!poly1.IsClosed(tolerance) || !poly2.IsClosed(tolerance))
{
curvePlane = poly1.FitPlane();
if (poly1.ControlPoints.Any(x => !x.IsInPlane(curvePlane, tolerance)))
Base.Compute.RecordError("Boolean Intersection works on closed regions.");
return new List<Polyline>();
}

if (plane == null)
{
plane = poly1.FitPlane();
if (plane == null || poly1.ControlPoints.Any(x => !x.IsInPlane(plane, tolerance)))
{
Base.Compute.RecordError("Clipper BooleanIntersection method only works for coplanar polylines.");
return null;
}
}

if (poly2.ControlPoints.Any(x => !x.IsInPlane(curvePlane, tolerance)))
{
Base.Compute.RecordError("Clipper BooleanIntersection method only works for coplanar polylines.");
return null;
if (poly2.ControlPoints.Any(x => !x.IsInPlane(plane, tolerance)))
{
Base.Compute.RecordError("Clipper BooleanIntersection method only works for coplanar polylines.");
return null;
}
}

// Find the orientation matrix to the global XY plane
TransformMatrix orientation = poly1.OrientationToGlobalXY(curvePlane, tolerance);
TransformMatrix orientation = poly1.OrientationToGlobalXY(plane, tolerance);
if (orientation == null)
return null;

Expand Down
18 changes: 12 additions & 6 deletions Clipper_Engine/Compute/BooleanUnion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ public static partial class Compute

[Description("Perform a boolean union operation between multiple polylines using Clipper2.")]
[Input("polylines", "List of polylines to union together.")]
[Input("curvePlane", "Optional plane for the geometry. If null, will be fitted from the first polyline.")]
[Input("plane", "If null, processing is in 2D (the plane is fitted from the first polyline). If not null, 3D geometry is processed using this plane.")]
[Input("tolerance", "Tolerance for planarity checks and numerical precision. Default is Tolerance.Distance.")]
[Output("result", "List of polylines representing the union of all input polylines.")]
public static List<Polyline> BooleanUnion(this List<Polyline> polylines, Plane curvePlane = null, double tolerance = Tolerance.Distance)
public static List<Polyline> BooleanUnion(this List<Polyline> polylines, Plane plane = null, double tolerance = Tolerance.Distance)
{
if (polylines == null || polylines.Count == 0)
return new List<Polyline>();
Expand All @@ -55,18 +55,24 @@ public static List<Polyline> BooleanUnion(this List<Polyline> polylines, Plane c
if (validPolylines.Count == 1)
return new List<Polyline> { validPolylines[0] };

if (curvePlane == null)
if (validPolylines.Any(x => !x.IsClosed(tolerance)))
{
curvePlane = validPolylines[0].FitPlane();
if (validPolylines.Any(x => x.ControlPoints.Any(y => !y.IsInPlane(curvePlane, tolerance))))
Base.Compute.RecordError("Boolean Union works on closed regions.");
return new List<Polyline>();
}

if (plane == null)
{
plane = validPolylines[0].FitPlane();
if (plane == null || validPolylines.Any(x => x.ControlPoints.Any(y => !y.IsInPlane(plane, tolerance))))
{
Base.Compute.RecordError("Clipper BooleanUnion method only works for coplanar polylines.");
return new List<Polyline>();
}
}

// Find the orientation matrix to the global XY plane
TransformMatrix orientation = validPolylines[0].OrientationToGlobalXY(curvePlane, tolerance);
TransformMatrix orientation = validPolylines[0].OrientationToGlobalXY(plane, tolerance);
if (orientation == null)
return new List<Polyline>();

Expand Down
12 changes: 6 additions & 6 deletions Clipper_Engine/Compute/Offset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ public static partial class Compute
[Description("Offset a curve by the given distance (using Clipper http://www.angusj.com/delphi/clipper.php). Method only works for closed, planar polylines.")]
[Input("polyline", "A BHoM Polyline representing the curve to offset.")]
[Input("distance", "The distance by which to offset the curve (-Ve is inwards).")]
[Input("curvePlane", "Optional plane for the geometry. If null, will be fitted from the polyline.")]
[Input("plane", "If null, processing is in 2D (the plane is fitted from the polyline). If not null, 3D geometry is processed using this plane.")]
[Input("tolerance", "Tolerance to be used for planarity and closedness checks as well as for offset computations.")]
[Output("polylines", "Input polylines after offset.")]
public static List<Polyline> Offset(this Polyline polyline, double distance = 0, Plane curvePlane = null, double tolerance = Tolerance.Distance)
public static List<Polyline> Offset(this Polyline polyline, double distance = 0, Plane plane = null, double tolerance = Tolerance.Distance)
{
if (polyline == null)
return null;
Expand All @@ -57,18 +57,18 @@ public static List<Polyline> Offset(this Polyline polyline, double distance = 0,
return null;
}

if (curvePlane == null)
if (plane == null)
{
curvePlane = polyline.FitPlane();
if (polyline.ControlPoints.Any(x => !x.IsInPlane(curvePlane, tolerance)))
plane = polyline.FitPlane();
if (plane == null || polyline.ControlPoints.Any(x => !x.IsInPlane(plane, tolerance)))
{
Base.Compute.RecordError("Clipper Offset method only works for planar polylines.");
return null;
}
}

// Find orientation matrix to the global XY plane
TransformMatrix orientation = polyline.OrientationToGlobalXY(curvePlane, tolerance);
TransformMatrix orientation = polyline.OrientationToGlobalXY(plane, tolerance);
if (orientation == null)
return null;

Expand Down
62 changes: 62 additions & 0 deletions Clipper_Engine/Query/CullDuplicates.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* This file is part of the Buildings and Habitats object Model (BHoM)
* Copyright (c) 2015 - 2026, the respective contributors. All rights reserved.
*
* Each contributor holds copyright over their respective contributions.
* The project versioning (Git) records all such contribution source information.
*
*
* The BHoM is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* The BHoM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
*/

using BH.Engine.Geometry;
using BH.oM.Base.Attributes;
using BH.oM.Geometry;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;

namespace BH.Engine.Clipper
{
public static partial class Query
{
/***************************************************/
/**** Public methods ****/
/***************************************************/

[Description("Removes duplicate polylines. Two polylines are duplicates when their control points are equal in the same order. Either list may be shifted, so a different starting point still counts as a duplicate. If plane is null, control points are compared directly. If plane is not null, they are projected onto the plane before comparison.")]
[Input("polylines", "The collection of polylines to cull duplicates from.")]
[Input("plane", "If null, control points are compared directly. If not null, control points are projected onto this plane before comparison.")]
[Output("polylines", "The collection of polylines with duplicates removed.")]
public static List<Polyline> CullDuplicates(this IEnumerable<Polyline> polylines, Plane plane = null)
{
if (polylines == null)
return new List<Polyline>();

List<Polyline> culled = new List<Polyline>();
foreach (Polyline polyline in polylines)
{
if (polyline == null)
continue;

if (!culled.Any(x => x.IsSame(polyline, plane)))
culled.Add(polyline);
}

return culled;
}

/***************************************************/
}
}
52 changes: 52 additions & 0 deletions Clipper_Engine/Query/DoesOverlap.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* This file is part of the Buildings and Habitats object Model (BHoM)
* Copyright (c) 2015 - 2026, the respective contributors. All rights reserved.
*
* Each contributor holds copyright over their respective contributions.
* The project versioning (Git) records all such contribution source information.
*
*
* The BHoM is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* The BHoM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
*/

using BH.oM.Base.Attributes;
using BH.oM.Geometry;
using System.Collections.Generic;
using System.ComponentModel;
namespace BH.Engine.Clipper
{
public static partial class Query
{
/***************************************************/
/**** Public methods ****/
/***************************************************/

[Description("Checks if two polylines intersect on a plane without one fully containing the other.")]
[Input("poly1", "First polyline.")]
[Input("poly2", "Second polyline.")]
[Input("plane", "If null, processing is in 2D (the plane is fitted from the first polyline). If not null, 3D geometry is processed using this plane.")]
[Input("tolerance", "Distance tolerance.")]
[Output("intersects", "True if the polylines intersect.")]
public static bool DoesOverlap(this Polyline poly1, Polyline poly2, Plane plane = null, double tolerance = Tolerance.Distance)
{
if (poly1 == null || poly2 == null || poly1.ControlPoints.Count < 3 || poly2.ControlPoints.Count < 3)
return false;

List<Polyline> intersection = Compute.BooleanIntersection(poly1, poly2, plane, tolerance);
return intersection != null && intersection.Count > 0;
}

/***************************************************/
}
}
Loading
Loading