feat (autocad): region and hatches conversions (#681)
* clean send (except closed circular arcs) * split functions * include inner loops for complex regions * reorient only polycurves with arcs * regions to host * merge conflict * substract regions on receive * optimize * add checks * fix icurve, receive hatches * hatch receive * reduce dataObject conversions * finally hatches are recorded in a database * regrouped * simplify * rename * send hatch (only the first) * remove hatches for now * comment * fixed icurve converter and reference in DataObject converter * reformat * hatch to speckle * hatch to host * hatch receive works * set solid pattern on receive * send properly and throw if complex hacth * calculate mesh area from region * prevent Autocad crash by catching exceptions in the middle of transaction * sending both polylines and curves for hatches * boolean operation for sending hatches * turned 2d Hatch curves into 3d - now Brep creation doesn't fail! * circles handled * construct both polyline and vertices in parallel * handling splines on receive * comments * don't reverse proper segments * basic comment fixes * open block table for write more concise * use top-level transaction --------- Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
This commit is contained in:
+6
-1
@@ -32,6 +32,10 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Geometry\CurveToHostConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Geometry\EllipseToHostConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Geometry\PolycurveToHostConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Geometry\RegionToHostConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\RegionHatchToHostRawConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\RegionToHostRawConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\ICurveToHostRawConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\ArcToHostRawConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\CurveToHostRawConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\IntervalToHostRawConverter.cs" />
|
||||
@@ -44,6 +48,7 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\PointToHostRawConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Geometry\PointToHostConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Geometry\ArcToSpeckleConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Geometry\HatchToSpeckleConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Geometry\RegionToSpeckleConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Geometry\SurfaceToSpeckleConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Geometry\Solid3dToSpeckleConverter.cs" />
|
||||
@@ -77,4 +82,4 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Raw\DBSplineToSpeckleRawConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Raw\VectorToSpeckleRawConverter.cs" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
+14
-43
@@ -1,5 +1,6 @@
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Objects;
|
||||
using Speckle.Objects.Data;
|
||||
using Speckle.Sdk.Common.Exceptions;
|
||||
using Speckle.Sdk.Models;
|
||||
@@ -9,46 +10,31 @@ namespace Speckle.Converters.AutocadShared.ToHost.Geometry;
|
||||
[NameAndRankValue(typeof(DataObject), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)]
|
||||
public class DataObjectConverter : IToHostTopLevelConverter, ITypedConverter<DataObject, List<(ADB.Entity a, Base b)>>
|
||||
{
|
||||
private readonly ITypedConverter<SOG.Arc, ADB.Arc> _arcConverter;
|
||||
private readonly ITypedConverter<ICurve, List<(ADB.Entity, Base)>> _curveConverter;
|
||||
private readonly ITypedConverter<SOG.BrepX, List<(ADB.Entity a, Base b)>> _brepXConverter;
|
||||
private readonly ITypedConverter<SOG.Circle, ADB.Circle> _circleConverter;
|
||||
private readonly ITypedConverter<SOG.Curve, ADB.Curve> _curveConverter;
|
||||
private readonly ITypedConverter<SOG.Ellipse, ADB.Ellipse> _ellipseConverter;
|
||||
private readonly ITypedConverter<SOG.ExtrusionX, List<(ADB.Entity a, Base b)>> _extrusionXConverter;
|
||||
private readonly ITypedConverter<SOG.Line, ADB.Line> _lineConverter;
|
||||
private readonly ITypedConverter<SOG.Mesh, ADB.PolyFaceMesh> _meshConverter;
|
||||
private readonly ITypedConverter<SOG.Point, ADB.DBPoint> _pointConverter;
|
||||
private readonly ITypedConverter<SOG.Polycurve, List<(ADB.Entity a, Base b)>> _polycurveConverter;
|
||||
private readonly ITypedConverter<SOG.Polyline, ADB.Polyline3d> _polylineConverter;
|
||||
private readonly ITypedConverter<SOG.SubDX, List<(ADB.Entity a, Base b)>> _subDXConverter;
|
||||
private readonly ITypedConverter<SOG.Region, ADB.Entity> _regionConverter;
|
||||
|
||||
public DataObjectConverter(
|
||||
ITypedConverter<SOG.Arc, ADB.Arc> arcConverter,
|
||||
ITypedConverter<ICurve, List<(ADB.Entity, Base)>> curveConverter,
|
||||
ITypedConverter<SOG.BrepX, List<(ADB.Entity a, Base b)>> brepXConverter,
|
||||
ITypedConverter<SOG.Circle, ADB.Circle> circleConverter,
|
||||
ITypedConverter<SOG.Curve, ADB.Curve> curveConverter,
|
||||
ITypedConverter<SOG.Ellipse, ADB.Ellipse> ellipseConverter,
|
||||
ITypedConverter<SOG.ExtrusionX, List<(ADB.Entity a, Base b)>> extrusionXConverter,
|
||||
ITypedConverter<SOG.Line, ADB.Line> lineConverter,
|
||||
ITypedConverter<SOG.Mesh, ADB.PolyFaceMesh> meshConverter,
|
||||
ITypedConverter<SOG.Point, ADB.DBPoint> pointConverter,
|
||||
ITypedConverter<SOG.Polycurve, List<(ADB.Entity, Base)>> polycurveConverter,
|
||||
ITypedConverter<SOG.Polyline, ADB.Polyline3d> polylineConverter,
|
||||
ITypedConverter<SOG.SubDX, List<(ADB.Entity a, Base b)>> subDXConverter
|
||||
ITypedConverter<SOG.SubDX, List<(ADB.Entity a, Base b)>> subDXConverter,
|
||||
ITypedConverter<SOG.Region, ADB.Entity> regionConverter
|
||||
)
|
||||
{
|
||||
_arcConverter = arcConverter;
|
||||
_brepXConverter = brepXConverter;
|
||||
_circleConverter = circleConverter;
|
||||
_curveConverter = curveConverter;
|
||||
_ellipseConverter = ellipseConverter;
|
||||
_brepXConverter = brepXConverter;
|
||||
_extrusionXConverter = extrusionXConverter;
|
||||
_lineConverter = lineConverter;
|
||||
_meshConverter = meshConverter;
|
||||
_pointConverter = pointConverter;
|
||||
_polycurveConverter = polycurveConverter;
|
||||
_polylineConverter = polylineConverter;
|
||||
_subDXConverter = subDXConverter;
|
||||
_regionConverter = regionConverter;
|
||||
}
|
||||
|
||||
public object Convert(Base target) => Convert((DataObject)target);
|
||||
@@ -67,54 +53,39 @@ public class DataObjectConverter : IToHostTopLevelConverter, ITypedConverter<Dat
|
||||
{
|
||||
switch (displayObject)
|
||||
{
|
||||
case SOG.Arc arc:
|
||||
yield return (_arcConverter.Convert(arc), arc);
|
||||
break;
|
||||
case SOG.BrepX brepX:
|
||||
foreach (var i in _brepXConverter.Convert(brepX))
|
||||
{
|
||||
yield return i;
|
||||
}
|
||||
break;
|
||||
case SOG.Circle circle:
|
||||
yield return (_circleConverter.Convert(circle), circle);
|
||||
break;
|
||||
case SOG.Curve curve:
|
||||
yield return (_curveConverter.Convert(curve), curve);
|
||||
break;
|
||||
case SOG.Ellipse ellipse:
|
||||
yield return (_ellipseConverter.Convert(ellipse), ellipse);
|
||||
break;
|
||||
case SOG.ExtrusionX extrusionX:
|
||||
foreach (var i in _extrusionXConverter.Convert(extrusionX))
|
||||
{
|
||||
yield return i;
|
||||
}
|
||||
break;
|
||||
case SOG.Line line:
|
||||
yield return (_lineConverter.Convert(line), line);
|
||||
break;
|
||||
case SOG.Mesh mesh:
|
||||
yield return (_meshConverter.Convert(mesh), mesh);
|
||||
break;
|
||||
case SOG.Point point:
|
||||
yield return (_pointConverter.Convert(point), point);
|
||||
break;
|
||||
case SOG.Polycurve polycurve:
|
||||
foreach (var i in _polycurveConverter.Convert(polycurve))
|
||||
case ICurve curve:
|
||||
foreach (var result in _curveConverter.Convert(curve))
|
||||
{
|
||||
yield return i;
|
||||
yield return result;
|
||||
}
|
||||
break;
|
||||
case SOG.Polyline polyline:
|
||||
yield return (_polylineConverter.Convert(polyline), polyline);
|
||||
break;
|
||||
case SOG.SubDX subDX:
|
||||
foreach (var i in _subDXConverter.Convert(subDX))
|
||||
{
|
||||
yield return i;
|
||||
}
|
||||
break;
|
||||
case SOG.Region region:
|
||||
yield return (_regionConverter.Convert(region), region);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ConversionException($"Found unsupported geometry: {displayObject.GetType()}");
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Converters.Autocad.ToHost.Geometry;
|
||||
|
||||
[NameAndRankValue(typeof(SOG.Region), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)]
|
||||
public class RegionToHostConverter : IToHostTopLevelConverter, ITypedConverter<SOG.Region, ADB.Entity>
|
||||
{
|
||||
private readonly ITypedConverter<SOG.Region, ADB.Region> _regionConverter;
|
||||
private readonly ITypedConverter<SOG.Region, ADB.Hatch> _hatchConverter;
|
||||
|
||||
public RegionToHostConverter(
|
||||
ITypedConverter<SOG.Region, ADB.Region> regionConverter,
|
||||
ITypedConverter<SOG.Region, ADB.Hatch> hatchConverter
|
||||
)
|
||||
{
|
||||
_regionConverter = regionConverter;
|
||||
_hatchConverter = hatchConverter;
|
||||
}
|
||||
|
||||
public object Convert(Base target) => Convert((SOG.Region)target);
|
||||
|
||||
public ADB.Entity Convert(SOG.Region target)
|
||||
{
|
||||
// Generalizing return type as Entity, because it can be a simple Region, or a Hatch
|
||||
if (target.hasHatchPattern)
|
||||
{
|
||||
return _hatchConverter.Convert(target);
|
||||
}
|
||||
return _regionConverter.Convert(target);
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Objects;
|
||||
using Speckle.Sdk.Common.Exceptions;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Converters.AutocadShared.ToHost.Raw;
|
||||
|
||||
public class ICurveToHostRawConverter : ITypedConverter<ICurve, List<(ADB.Entity, Base)>>
|
||||
{
|
||||
private readonly ITypedConverter<SOG.Line, ADB.Line> _lineConverter;
|
||||
private readonly ITypedConverter<SOG.Arc, ADB.Arc> _arcConverter;
|
||||
private readonly ITypedConverter<SOG.Ellipse, ADB.Ellipse> _ellipseConverter;
|
||||
private readonly ITypedConverter<SOG.Circle, ADB.Circle> _circleConverter;
|
||||
private readonly ITypedConverter<SOG.Polyline, ADB.Polyline3d> _polylineConverter;
|
||||
private readonly ITypedConverter<SOG.Polycurve, List<(ADB.Entity, Base)>> _polycurveConverter;
|
||||
private readonly ITypedConverter<SOG.Curve, ADB.Curve> _curveConverter;
|
||||
|
||||
public ICurveToHostRawConverter(
|
||||
ITypedConverter<SOG.Line, ADB.Line> lineConverter,
|
||||
ITypedConverter<SOG.Arc, ADB.Arc> arcConverter,
|
||||
ITypedConverter<SOG.Ellipse, ADB.Ellipse> ellipseConverter,
|
||||
ITypedConverter<SOG.Circle, ADB.Circle> circleConverter,
|
||||
ITypedConverter<SOG.Polyline, ADB.Polyline3d> polylineConverter,
|
||||
ITypedConverter<SOG.Polycurve, List<(ADB.Entity, Base)>> polycurveConverter,
|
||||
ITypedConverter<SOG.Curve, ADB.Curve> curveConverter
|
||||
)
|
||||
{
|
||||
_lineConverter = lineConverter;
|
||||
_arcConverter = arcConverter;
|
||||
_ellipseConverter = ellipseConverter;
|
||||
_circleConverter = circleConverter;
|
||||
_polylineConverter = polylineConverter;
|
||||
_polycurveConverter = polycurveConverter;
|
||||
_curveConverter = curveConverter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a given ICurve object to a list of ADB.Curve.
|
||||
/// </summary>
|
||||
/// <param name="target">The ICurve object to convert.</param>
|
||||
/// <returns>The converted list of ADB.Curve.</returns>
|
||||
/// <exception cref="NotSupportedException">Thrown when the conversion is not supported for the given type of curve.</exception>
|
||||
/// <remarks>⚠️ This conversion does NOT perform scaling.</remarks>
|
||||
public List<(ADB.Entity, Base)> Convert(ICurve target) =>
|
||||
target switch
|
||||
{
|
||||
SOG.Line line => new() { (_lineConverter.Convert(line), line) },
|
||||
SOG.Arc arc => new() { (_arcConverter.Convert(arc), arc) },
|
||||
SOG.Circle circle => new() { (_circleConverter.Convert(circle), circle) },
|
||||
SOG.Ellipse ellipse => new() { (_ellipseConverter.Convert(ellipse), ellipse) },
|
||||
SOG.Polyline polyline => new() { (_polylineConverter.Convert(polyline), polyline) },
|
||||
SOG.Curve curve => new() { (_curveConverter.Convert(curve), curve) },
|
||||
SOG.Polycurve polycurve => _polycurveConverter.Convert(polycurve),
|
||||
_ => throw new ValidationException($"Unable to convert curves of type {target.GetType().Name}")
|
||||
};
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Objects;
|
||||
using Speckle.Sdk.Common.Exceptions;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Converters.Autocad.ToHost.Raw;
|
||||
|
||||
public class RegionHatchToHostRawConverter : ITypedConverter<SOG.Region, ADB.Hatch>
|
||||
{
|
||||
private readonly ITypedConverter<ICurve, List<(ADB.Entity, Base)>> _curveConverter;
|
||||
private readonly IConverterSettingsStore<AutocadConversionSettings> _settingsStore;
|
||||
|
||||
public RegionHatchToHostRawConverter(
|
||||
ITypedConverter<ICurve, List<(ADB.Entity, Base)>> curveConverter,
|
||||
IConverterSettingsStore<AutocadConversionSettings> settingsStore
|
||||
)
|
||||
{
|
||||
_curveConverter = curveConverter;
|
||||
_settingsStore = settingsStore;
|
||||
}
|
||||
|
||||
public ADB.Hatch Convert(SOG.Region target)
|
||||
{
|
||||
// Access a top-level transaction
|
||||
ADB.Transaction tr = _settingsStore.Current.Document.TransactionManager.TopTransaction;
|
||||
var btr = (ADB.BlockTableRecord)
|
||||
tr.GetObject(_settingsStore.Current.Document.Database.CurrentSpaceId, ADB.OpenMode.ForWrite);
|
||||
|
||||
// initialize Hatch, append to blockTableRecord
|
||||
ADB.Hatch acHatch = new();
|
||||
btr.AppendEntity(acHatch);
|
||||
tr.AddNewlyCreatedDBObject(acHatch, true);
|
||||
|
||||
// Set essential properties of the hatch object
|
||||
acHatch.SetDatabaseDefaults();
|
||||
acHatch.SetHatchPattern(ADB.HatchPatternType.PreDefined, "SOLID");
|
||||
|
||||
// Associative property must be set after the hatch object is
|
||||
// appended to the block table record and before AppendLoop
|
||||
acHatch.Associative = true;
|
||||
|
||||
// convert and assign boundary loop
|
||||
ConvertAndAssignHatchLoop(btr, tr, acHatch, target.boundary, ADB.HatchLoopTypes.External);
|
||||
foreach (var loop in target.innerLoops)
|
||||
{
|
||||
ConvertAndAssignHatchLoop(btr, tr, acHatch, loop, ADB.HatchLoopTypes.Outermost);
|
||||
}
|
||||
|
||||
return acHatch;
|
||||
}
|
||||
|
||||
private void ConvertAndAssignHatchLoop(
|
||||
ADB.BlockTableRecord acBlkTblRec,
|
||||
ADB.Transaction acTrans,
|
||||
ADB.Hatch hatch,
|
||||
ICurve curve,
|
||||
ADB.HatchLoopTypes loopType
|
||||
)
|
||||
{
|
||||
// convert loop, add to ObjectIdCollection
|
||||
var convertedCurve = _curveConverter.Convert(curve);
|
||||
CheckForNonPlanarLoops(convertedCurve);
|
||||
var dbCurve = (ADB.Curve)convertedCurve[0].Item1;
|
||||
|
||||
// If Spline, turn into segmented polyline - this is how AutoCAD imports Hatches with Curve boundaries from Rhino
|
||||
if (dbCurve is ADB.Spline spline)
|
||||
{
|
||||
if (spline.NurbsData.Degree == 1)
|
||||
{
|
||||
// for simple polylines ".ToPolylineWithPrecision" distorts the shape, so just applying a list of vertices
|
||||
dbCurve = new ADB.Polyline3d(ADB.Poly3dType.SimplePoly, spline.NurbsData.GetControlPoints(), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
dbCurve = spline.ToPolylineWithPrecision(10, false, false);
|
||||
}
|
||||
}
|
||||
using ADB.ObjectIdCollection tempDBObjColl = CreateTempObjectIdCollection(acBlkTblRec, acTrans, dbCurve);
|
||||
|
||||
// append loop: possible Autodesk.AutoCAD.Runtime.Exception: eInvalidInput
|
||||
hatch.AppendLoop(loopType, tempDBObjColl);
|
||||
hatch.EvaluateHatch(true);
|
||||
dbCurve.Erase();
|
||||
}
|
||||
|
||||
private ADB.ObjectIdCollection CreateTempObjectIdCollection(
|
||||
ADB.BlockTableRecord acBlkTblRec,
|
||||
ADB.Transaction acTrans,
|
||||
ADB.Entity loopEntity
|
||||
)
|
||||
{
|
||||
// Add the new curve object to the block table record and the transaction
|
||||
acBlkTblRec.AppendEntity(loopEntity);
|
||||
acTrans.AddNewlyCreatedDBObject(loopEntity, true);
|
||||
|
||||
// Adds the entity to an object id array
|
||||
ADB.ObjectIdCollection tempDBObjColl = new();
|
||||
tempDBObjColl.Add(loopEntity.ObjectId);
|
||||
|
||||
return tempDBObjColl;
|
||||
}
|
||||
|
||||
private void CheckForNonPlanarLoops(List<(ADB.Entity, Base)> convertedResult)
|
||||
{
|
||||
if (convertedResult.Count != 1)
|
||||
{
|
||||
// this will only be the case if it was a non-planar Polycurve: throw error
|
||||
throw new ConversionException($"Non-planar Polycurve cannot be used as a Region loop: {convertedResult}");
|
||||
}
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Objects;
|
||||
using Speckle.Sdk.Common.Exceptions;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Converters.Autocad.ToHost.Raw;
|
||||
|
||||
public class RegionToHostRawConverter : ITypedConverter<SOG.Region, ADB.Region>
|
||||
{
|
||||
private readonly ITypedConverter<ICurve, List<(ADB.Entity, Base)>> _curveConverter;
|
||||
|
||||
public RegionToHostRawConverter(ITypedConverter<ICurve, List<(ADB.Entity, Base)>> curveConverter)
|
||||
{
|
||||
_curveConverter = curveConverter;
|
||||
}
|
||||
|
||||
public ADB.Region Convert(SOG.Region target)
|
||||
{
|
||||
// Notes from docs: The curveSegments must contain only Line, Arc, Ellipse, Circle, Spline, Polyline3d, or Polyline2d objects.
|
||||
// The objects in curveSegments must be opened for read and not for write. If the objects are opened, calling this function will crash AutoCAD.
|
||||
|
||||
// Converted boundary
|
||||
List<(ADB.Entity, Base)> convertedBoundary = _curveConverter.Convert(target.boundary);
|
||||
ADB.Curve nativeBoundary = ValidateCurve(convertedBoundary);
|
||||
|
||||
// Converted loops
|
||||
var nativeLoops = new List<ADB.Curve>();
|
||||
foreach (var loop in target.innerLoops)
|
||||
{
|
||||
List<(ADB.Entity, Base)> convertedLoop = _curveConverter.Convert(loop);
|
||||
nativeLoops.Add(ValidateCurve(convertedLoop));
|
||||
}
|
||||
|
||||
// Add boundary to the ADB.DBObjectCollection
|
||||
// Calculate the outer region, method should return an array with 1 region
|
||||
// https://help.autodesk.com/view/OARX/2025/ENU/?guid=GUID-684E602E-3555-4370-BCDC-1CE594676C43
|
||||
ADB.DBObjectCollection boundaryDBObjColl = new();
|
||||
boundaryDBObjColl.Add(nativeBoundary);
|
||||
using (ADB.DBObjectCollection outerRegionColl = ADB.Region.CreateFromCurves(boundaryDBObjColl))
|
||||
{
|
||||
if (outerRegionColl.Count != 1)
|
||||
{
|
||||
throw new ConversionException(
|
||||
$"Region conversion failed for {target}: unexpected number of shapes generated ({outerRegionColl.Count}). Make sure that input loops are planar, closed, non self-intersecting curves."
|
||||
);
|
||||
}
|
||||
if (outerRegionColl[0] is ADB.Region adbRegion)
|
||||
{
|
||||
// Create and subtract the inner loops' regions, iterate through each
|
||||
foreach (var nativeLoop in nativeLoops)
|
||||
{
|
||||
// Same as above: Add loop segments to the ADB.DBObjectCollection
|
||||
// Calculate the inner region, method should return an array with 1 region
|
||||
ADB.DBObjectCollection loopDBObjColl = new();
|
||||
loopDBObjColl.Add(nativeLoop);
|
||||
using (ADB.DBObjectCollection innerRegionColl = ADB.Region.CreateFromCurves(loopDBObjColl))
|
||||
{
|
||||
if (innerRegionColl.Count != 1)
|
||||
{
|
||||
throw new ConversionException(
|
||||
$"Region conversion failed for {target}: unexpected number of shapes generated ({innerRegionColl.Count}). Make sure that input loops are planar, closed, non self-intersecting curves."
|
||||
);
|
||||
}
|
||||
if (innerRegionColl[0] is ADB.Region adbInnerRegion)
|
||||
{
|
||||
// substract region from Boundary region
|
||||
adbRegion.BooleanOperation(ADB.BooleanOperationType.BoolSubtract, adbInnerRegion);
|
||||
adbInnerRegion.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return adbRegion;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ConversionException($"Region conversion failed: {target}");
|
||||
}
|
||||
|
||||
private ADB.Curve ValidateCurve(List<(ADB.Entity, Base)> convertedResult)
|
||||
{
|
||||
if (convertedResult.Count != 1)
|
||||
{
|
||||
// this will only be the case if it was a non-planar Polycurve: throw error
|
||||
throw new ConversionException($"Non-planar Polycurve cannot be used as a Region loop: {convertedResult}");
|
||||
}
|
||||
return (ADB.Curve)convertedResult[0].Item1;
|
||||
}
|
||||
}
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Sdk.Common.Exceptions;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Converters.Autocad.Geometry;
|
||||
|
||||
[NameAndRankValue(typeof(ADB.Hatch), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)]
|
||||
public class HatchToSpeckleConverter : IToSpeckleTopLevelConverter, ITypedConverter<ADB.Hatch, SOG.Region>
|
||||
{
|
||||
private readonly ITypedConverter<ADB.Region, SOG.Region> _regionConverter;
|
||||
|
||||
public HatchToSpeckleConverter(ITypedConverter<ADB.Region, SOG.Region> regionConverter)
|
||||
{
|
||||
_regionConverter = regionConverter;
|
||||
}
|
||||
|
||||
public Base Convert(object target) => Convert((ADB.Hatch)target);
|
||||
|
||||
public SOG.Region Convert(ADB.Hatch target)
|
||||
{
|
||||
ADB.Region? regionToConvert = null;
|
||||
|
||||
for (int i = 0; i < target.NumberOfLoops; i++)
|
||||
{
|
||||
// Create 3d polyline from the HatchLoop
|
||||
ADB.HatchLoop loop = target.GetLoopAt(i);
|
||||
ADB.Curve polyline = PolylineFromLoop(loop);
|
||||
ADB.DBObjectCollection objCollection = new();
|
||||
objCollection.Add(polyline);
|
||||
|
||||
// Convert polyline into an individual Region
|
||||
using (ADB.DBObjectCollection regionCollection = ADB.Region.CreateFromCurves(objCollection))
|
||||
{
|
||||
if (regionCollection.Count != 1)
|
||||
{
|
||||
throw new ConversionException(
|
||||
$"Hatch conversion failed {target}: unexpected number of regions generated from 1 hatch loop"
|
||||
);
|
||||
}
|
||||
ADB.Region loopRegion = (ADB.Region)regionCollection[0];
|
||||
|
||||
// Assign first loop as the main Region, other Regions will be subtracted from it
|
||||
if (i == 0)
|
||||
{
|
||||
regionToConvert = loopRegion;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (regionToConvert == null)
|
||||
{
|
||||
throw new ConversionException($"Hatch conversion failed: {target}");
|
||||
}
|
||||
// subtract region from Boundary region
|
||||
double areaBefore = regionToConvert.Area;
|
||||
regionToConvert.BooleanOperation(ADB.BooleanOperationType.BoolSubtract, loopRegion);
|
||||
|
||||
// check if the region did not change after subtraction: means the loop was a separate hatch part
|
||||
if (Math.Abs(areaBefore - regionToConvert.Area) < 0.00001)
|
||||
{
|
||||
throw new ConversionException($"Composite hatches are not supported: {target}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (regionToConvert == null)
|
||||
{
|
||||
throw new ConversionException($"Hatch conversion failed: {target}");
|
||||
}
|
||||
|
||||
// convert and store Regions
|
||||
SOG.Region convertedRegion = _regionConverter.Convert(regionToConvert);
|
||||
convertedRegion.hasHatchPattern = true;
|
||||
|
||||
return convertedRegion;
|
||||
}
|
||||
|
||||
private ADB.Curve PolylineFromLoop(ADB.HatchLoop loop)
|
||||
{
|
||||
if (loop.IsPolyline)
|
||||
{
|
||||
// disposable object, wrapping into "using"
|
||||
using (AG.Point3dCollection vertices = new())
|
||||
{
|
||||
// collect vertices and construct a polyline simultaneously, it will be clear what to use after iterating
|
||||
ADB.Polyline polyline = new() { Closed = true };
|
||||
|
||||
int count = 0;
|
||||
foreach (ADB.BulgeVertex bVertex in loop.Polyline)
|
||||
{
|
||||
// don't add the end point that's the same as the start point
|
||||
AG.Point3d newPt = new(bVertex.Vertex.X, bVertex.Vertex.Y, 0);
|
||||
if (count == 0 || vertices[0].DistanceTo(newPt) > 0.00001)
|
||||
{
|
||||
vertices.Add(newPt);
|
||||
polyline.AddVertexAt(count, bVertex.Vertex, bVertex.Bulge, 0, 0);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
// if only 2 points, that's a circle
|
||||
if (vertices.Count == 2)
|
||||
{
|
||||
AG.Point3d centerPt =
|
||||
new(
|
||||
vertices[0].X + (vertices[1].X - vertices[0].X) / 2,
|
||||
vertices[0].Y + (vertices[1].Y - vertices[0].Y) / 2,
|
||||
0
|
||||
);
|
||||
return new ADB.Circle(centerPt, AG.Vector3d.ZAxis, vertices[0].DistanceTo(vertices[1]) / 2);
|
||||
}
|
||||
return polyline;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ConversionException("Hatch loop conversion failed.");
|
||||
}
|
||||
}
|
||||
+120
-4
@@ -1,24 +1,40 @@
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Objects;
|
||||
using Speckle.Sdk.Common.Exceptions;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Converters.Autocad.Geometry;
|
||||
|
||||
[NameAndRankValue(typeof(ADB.Region), NameAndRankValueAttribute.SPECKLE_DEFAULT_RANK)]
|
||||
public class RegionToSpeckleConverter : IToSpeckleTopLevelConverter, ITypedConverter<ADB.Region, SOG.Mesh>
|
||||
public class RegionToSpeckleConverter : IToSpeckleTopLevelConverter, ITypedConverter<ADB.Region, SOG.Region>
|
||||
{
|
||||
private readonly ITypedConverter<ABR.Brep, SOG.Mesh> _brepConverter;
|
||||
private readonly ITypedConverter<AG.LineSegment3d, SOG.Line> _lineConverter;
|
||||
private readonly ITypedConverter<AG.CircularArc3d, SOG.Arc> _arcConverter;
|
||||
private readonly ITypedConverter<ADB.Circle, SOG.Circle> _circleConverter;
|
||||
private readonly IConverterSettingsStore<AutocadConversionSettings> _settingsStore;
|
||||
|
||||
public RegionToSpeckleConverter(ITypedConverter<ABR.Brep, SOG.Mesh> brepConverter)
|
||||
public RegionToSpeckleConverter(
|
||||
ITypedConverter<ABR.Brep, SOG.Mesh> brepConverter,
|
||||
ITypedConverter<AG.LineSegment3d, SOG.Line> lineConverter,
|
||||
ITypedConverter<AG.CircularArc3d, SOG.Arc> arcConverter,
|
||||
ITypedConverter<ADB.Circle, SOG.Circle> circleConverter,
|
||||
IConverterSettingsStore<AutocadConversionSettings> settingsStore
|
||||
)
|
||||
{
|
||||
_brepConverter = brepConverter;
|
||||
_lineConverter = lineConverter;
|
||||
_arcConverter = arcConverter;
|
||||
_circleConverter = circleConverter;
|
||||
_settingsStore = settingsStore;
|
||||
}
|
||||
|
||||
public Base Convert(object target) => Convert((ADB.Region)target);
|
||||
|
||||
public SOG.Mesh Convert(ADB.Region target)
|
||||
public SOG.Region Convert(ADB.Region target)
|
||||
{
|
||||
// generate Mesh for displayValue
|
||||
using ABR.Brep brep = new(target);
|
||||
if (brep.IsNull)
|
||||
{
|
||||
@@ -28,6 +44,106 @@ public class RegionToSpeckleConverter : IToSpeckleTopLevelConverter, ITypedConve
|
||||
SOG.Mesh mesh = _brepConverter.Convert(brep);
|
||||
mesh.area = target.Area;
|
||||
|
||||
return mesh;
|
||||
// get all brep loops: can consist of LineSegment3d or CircularArc3d edges
|
||||
var brepLoops = brep
|
||||
.Complexes.SelectMany(complex => complex.Shells)
|
||||
.SelectMany(shell => shell.Faces)
|
||||
.SelectMany(face => face.Loops);
|
||||
|
||||
// Get and convert boundary and inner loops
|
||||
var boundary = GetConvertedLoops(brepLoops, true)[0];
|
||||
var innerLoops = GetConvertedLoops(brepLoops, false);
|
||||
|
||||
return new SOG.Region()
|
||||
{
|
||||
boundary = boundary,
|
||||
innerLoops = innerLoops,
|
||||
hasHatchPattern = false,
|
||||
displayValue = [mesh],
|
||||
units = _settingsStore.Current.SpeckleUnits
|
||||
};
|
||||
}
|
||||
|
||||
private List<ICurve> GetConvertedLoops(IEnumerable<ABR.BoundaryLoop> brepLoops, bool getOuterLoop)
|
||||
{
|
||||
var loops = new List<ICurve>();
|
||||
foreach (var loop in brepLoops)
|
||||
{
|
||||
bool outer = loop.LoopType == ABR.LoopType.LoopExterior;
|
||||
|
||||
// continue only if the loop type is as requester (outer or inner)
|
||||
if ((outer && getOuterLoop) || (!outer && !getOuterLoop))
|
||||
{
|
||||
// create segment collection for the current loop
|
||||
var segments = new List<AG.Curve3d>();
|
||||
foreach (var edge in loop.Edges)
|
||||
{
|
||||
var curve = edge.Curve;
|
||||
if (curve is AG.ExternalCurve3d xCurve && xCurve.IsNativeCurve)
|
||||
{
|
||||
segments.Add(xCurve.NativeCurve);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ConversionException("Unsupported curve type for Region conversion");
|
||||
}
|
||||
}
|
||||
// reverse segment collection with arcs in case end-start points of subsequent segments don't match
|
||||
if (
|
||||
segments.Any(x => x is AG.CircularArc3d)
|
||||
&& segments.Count > 1
|
||||
&& Math.Abs(segments[0].EndPoint.DistanceTo(segments[1].StartPoint)) > 0.00001
|
||||
)
|
||||
{
|
||||
segments.Reverse();
|
||||
}
|
||||
|
||||
// convert segments to Speckle Polycurve or Circle
|
||||
var convertedLoop = ConvertSegmentsToICurve(segments);
|
||||
loops.Add(convertedLoop);
|
||||
}
|
||||
}
|
||||
|
||||
return loops;
|
||||
}
|
||||
|
||||
private ICurve ConvertSegmentsToICurve(List<AG.Curve3d> segments)
|
||||
{
|
||||
ICurve convertedLoop;
|
||||
|
||||
// Handle edge case: if the segment is a closed Arc, then use Circle conversion to create a valid shape.
|
||||
// Also, closed arcs cause errors when receiving in other host apps, like Rhino.
|
||||
if (segments.Count == 1 && segments[0] is AG.CircularArc3d arc && arc.StartAngle + arc.EndAngle == 0)
|
||||
{
|
||||
convertedLoop = _circleConverter.Convert(
|
||||
new ADB.Circle(arc.GetPlane().PointOnPlane, arc.GetPlane().Normal, arc.Radius)
|
||||
);
|
||||
}
|
||||
// otherwise, just construct a Polycurve from subsequent segments
|
||||
else
|
||||
{
|
||||
// Maybe we need to convert to AutoCAD Polycurve
|
||||
convertedLoop = new SOG.Polycurve()
|
||||
{
|
||||
segments = segments.Select(x => ConvertSegment(x)).ToList(),
|
||||
closed = true,
|
||||
units = _settingsStore.Current.SpeckleUnits
|
||||
};
|
||||
}
|
||||
|
||||
return convertedLoop;
|
||||
}
|
||||
|
||||
private ICurve ConvertSegment(AG.Curve3d curve)
|
||||
{
|
||||
switch (curve)
|
||||
{
|
||||
case AG.LineSegment3d line:
|
||||
return _lineConverter.Convert(line);
|
||||
case AG.CircularArc3d arc:
|
||||
return _arcConverter.Convert(arc);
|
||||
}
|
||||
|
||||
throw new ConversionException($"Unsupported curve type for Region conversion: {curve}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user