feat (revit): receive Region as native FilledRegion (#696)
* regions with failed viewId * render stuff in the first found suitable view * use native or fallback conversion depending on the view * better comments * implement conditional conversion * remove comment * comment * unload Root Host converter * fix highlighting the model * inject PlanView converter * specify views in which receive is supported * throw unsupported views in advance * remove redundant check * ViewManager added; View check is moved to the beginning of receive operation (to throw once and not for every object) * simplify and remove unused --------- Co-authored-by: Claire Kuang <kuang.claire@gmail.com>
This commit is contained in:
+1
@@ -61,6 +61,7 @@ public static class ServiceRegistration
|
||||
serviceCollection.AddScoped<ITransactionManager, TransactionManager>();
|
||||
serviceCollection.AddScoped<RevitGroupBaker>();
|
||||
serviceCollection.AddScoped<RevitMaterialBaker>();
|
||||
serviceCollection.AddScoped<RevitViewManager>();
|
||||
serviceCollection.AddSingleton<RevitUtils>();
|
||||
serviceCollection.AddSingleton<IFailuresPreprocessor, HideWarningsFailuresPreprocessor>();
|
||||
serviceCollection.AddSingleton(DefaultTraversal.CreateTraversalFunc());
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
using Autodesk.Revit.DB;
|
||||
|
||||
namespace Speckle.Connectors.Revit.HostApp;
|
||||
|
||||
/// <summary>
|
||||
/// Handles Revit Views per Send/Receive, e.g. determines whether the View is supported for specific operation.
|
||||
/// </summary>
|
||||
public class RevitViewManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Determine if the View is supported for Receive operation. Currently only 3d view or horizontal 2d views are supported.
|
||||
/// Views like Section, Elevation, ViewSheet etc. are not supported
|
||||
/// </summary>
|
||||
public bool IsSupportedReceiveView(View activeView)
|
||||
{
|
||||
switch (activeView.ViewType)
|
||||
{
|
||||
case ViewType.ThreeD:
|
||||
case ViewType.FloorPlan:
|
||||
case ViewType.AreaPlan:
|
||||
case ViewType.CeilingPlan:
|
||||
return true;
|
||||
case ViewType.Detail:
|
||||
return IsHorizontalView(activeView);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsHorizontalView(View activeView) => Math.Abs(activeView.ViewDirection.Z - 1) < 0.00001;
|
||||
}
|
||||
+18
@@ -33,6 +33,7 @@ public sealed class RevitHostObjectBuilder(
|
||||
RevitGroupBaker groupManager,
|
||||
RevitMaterialBaker materialBaker,
|
||||
RootObjectUnpacker rootObjectUnpacker,
|
||||
RevitViewManager viewManager,
|
||||
ILogger<RevitHostObjectBuilder> logger,
|
||||
IThreadContext threadContext,
|
||||
RevitToHostCacheSingleton revitToHostCacheSingleton,
|
||||
@@ -61,6 +62,13 @@ public sealed class RevitHostObjectBuilder(
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
// ignore Receive in any other views (e.g. Section, Elevation, ViewSheet etc.)
|
||||
View activeView = converterSettings.Current.Document.ActiveView;
|
||||
if (!viewManager.IsSupportedReceiveView(activeView))
|
||||
{
|
||||
throw new ConversionException($"Receive in '{activeView.ViewType}' View is not supported");
|
||||
}
|
||||
|
||||
var baseGroupName = $"Project {projectName}: Model {modelName}"; // TODO: unify this across connectors!
|
||||
|
||||
onOperationProgressed.Report(new("Converting", null));
|
||||
@@ -201,6 +209,16 @@ public sealed class RevitHostObjectBuilder(
|
||||
new(Status.SUCCESS, localToGlobalMap.AtomicObject, directShapes.UniqueId, "Direct Shape")
|
||||
);
|
||||
}
|
||||
else if (result is List<string> elementsIds)
|
||||
{
|
||||
// This is the case when conversion returns not a GeometryObject, but Documentation elements (Annotations, Details etc.)
|
||||
// If Regions were a part of DataObject, it can return more than 1 native shape
|
||||
foreach (var elementId in elementsIds)
|
||||
{
|
||||
conversionResults.Add(new(Status.SUCCESS, localToGlobalMap.AtomicObject, elementId, "Filled Region"));
|
||||
bakedObjectIds.Add(elementId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ConversionException($"Failed to cast {result.GetType()} to direct shape definition wrapper.");
|
||||
|
||||
+1
@@ -25,6 +25,7 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\LinkedModelHandler.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RevitMaterialBaker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\SupportedCategoriesUtils.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RevitViewManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\HideWarningsFailuresPreprocessor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\IdStorageSchema.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\IStorageSchema.cs" />
|
||||
|
||||
@@ -14,19 +14,31 @@ public class RevitRootToHostConverter : IRootToHostConverter
|
||||
{
|
||||
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
|
||||
private readonly ITypedConverter<Base, List<DB.GeometryObject>> _baseToGeometryConverter;
|
||||
private readonly ITypedConverter<Base, List<string>> _planViewToGeometryConverter;
|
||||
|
||||
public RevitRootToHostConverter(
|
||||
ITypedConverter<Base, List<string>> planViewToGeometryConverter,
|
||||
ITypedConverter<Base, List<DB.GeometryObject>> baseToGeometryConverter,
|
||||
IConverterSettingsStore<RevitConversionSettings> converterSettings
|
||||
)
|
||||
{
|
||||
_planViewToGeometryConverter = planViewToGeometryConverter;
|
||||
_baseToGeometryConverter = baseToGeometryConverter;
|
||||
_converterSettings = converterSettings;
|
||||
}
|
||||
|
||||
public object Convert(Base target)
|
||||
{
|
||||
List<GeometryObject> geometryObjects = _baseToGeometryConverter.Convert(target);
|
||||
// If ActiveView is a 2d view, use PlanView converter (will ignore DirectShapes)
|
||||
// Unsupported views already filtered out in HostObjectBuilder
|
||||
View activeView = _converterSettings.Current.Document.ActiveView;
|
||||
if (activeView.ViewType != ViewType.ThreeD)
|
||||
{
|
||||
return _planViewToGeometryConverter.Convert(target);
|
||||
}
|
||||
|
||||
// Use default behavior and covert everything to DirectShapes
|
||||
List<DB.GeometryObject> geometryObjects = _baseToGeometryConverter.Convert(target);
|
||||
|
||||
if (geometryObjects.Count == 0)
|
||||
{
|
||||
|
||||
+2
@@ -31,6 +31,7 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Settings\ReferencePointType.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Settings\RevitConversionSettingsFactory.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\BaseToHostGeometryObjectConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\PlanViewToHostGeometryObjectConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\Geometry\ArcConverterToHost.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\Geometry\CircleConverterToHost.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\Geometry\CurveConverterToHost.cs" />
|
||||
@@ -42,6 +43,7 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\Geometry\PlaneConverterToHost.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\Geometry\PointConverterToHost.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\Geometry\PolylineConverterToHost.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\Geometry\RegionConverterToHost.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\Geometry\TransformConverterToHost.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\LocalToGlobalToDirectShapeConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\RenderMaterialToHostConverter.cs" />
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
using Autodesk.Revit.DB;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Converters.RevitShared.Settings;
|
||||
using Speckle.Objects;
|
||||
|
||||
namespace Speckle.Converters.RevitShared.ToHost.TopLevel;
|
||||
|
||||
public class RegionConverterToHost : ITypedConverter<SOG.Region, string>
|
||||
{
|
||||
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
|
||||
private readonly ITypedConverter<ICurve, DB.CurveArray> _curveConverter;
|
||||
|
||||
public RegionConverterToHost(
|
||||
IConverterSettingsStore<RevitConversionSettings> converterSettings,
|
||||
ITypedConverter<ICurve, DB.CurveArray> curveConverter
|
||||
)
|
||||
{
|
||||
_converterSettings = converterSettings;
|
||||
_curveConverter = curveConverter;
|
||||
}
|
||||
|
||||
public string Convert(SOG.Region target)
|
||||
{
|
||||
List<DB.GeometryObject> resultList = new();
|
||||
List<CurveLoop> profileLoops = new();
|
||||
|
||||
// convert boundary loop and add to profileLoops list
|
||||
CurveLoop boundaryLoop = new();
|
||||
List<DB.Curve> outerLoop = _curveConverter.Convert(target.boundary).Cast<DB.Curve>().ToList();
|
||||
outerLoop.ForEach(x => boundaryLoop.Append(x));
|
||||
profileLoops.Add(boundaryLoop);
|
||||
|
||||
// convert inner loops and add to profileLoops list
|
||||
List<List<DB.Curve>> innerLoops = target
|
||||
.innerLoops.Select(x => _curveConverter.Convert(x).Cast<DB.Curve>().ToList())
|
||||
.ToList();
|
||||
foreach (var innerLoop in innerLoops)
|
||||
{
|
||||
CurveLoop voidLoop = new();
|
||||
innerLoop.ForEach(x => voidLoop.Append(x));
|
||||
profileLoops.Add(voidLoop);
|
||||
}
|
||||
|
||||
// get FilledRegionType from the document to create a new FilledRegion element
|
||||
using var filledRegionCollector = new FilteredElementCollector(_converterSettings.Current.Document);
|
||||
Element filledRegionElementType = filledRegionCollector.OfClass(typeof(DB.FilledRegionType)).FirstElement();
|
||||
|
||||
// follow the pattern of the native CAD import: try to draw native FilledRegion in the Active view,
|
||||
// or draw a linked CAD document, if imported into unsupported View (in our case: don't catch the error, so the converter will default to fallback)
|
||||
View activeView = _converterSettings.Current.Document.ActiveView;
|
||||
|
||||
// Autodesk.Revit.Exceptions.ArgumentException will be thrown if ActiveView invalid
|
||||
using FilledRegion filledRegion = FilledRegion.Create(
|
||||
_converterSettings.Current.Document,
|
||||
filledRegionElementType.Id,
|
||||
activeView.Id,
|
||||
profileLoops
|
||||
);
|
||||
|
||||
return filledRegion.UniqueId;
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
using System.Collections;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Objects.Data;
|
||||
using Speckle.Sdk.Common.Exceptions;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Extensions;
|
||||
|
||||
namespace Speckle.Converters.RevitShared.ToSpeckle;
|
||||
|
||||
public class PlanViewToHostGeometryObjectConverter : ITypedConverter<Base, List<string>>
|
||||
{
|
||||
private readonly ITypedConverter<SOG.Region, string> _regionToFilledRegionConverter;
|
||||
|
||||
public PlanViewToHostGeometryObjectConverter(ITypedConverter<SOG.Region, string> regionToFilledRegionConverter)
|
||||
{
|
||||
_regionToFilledRegionConverter = regionToFilledRegionConverter;
|
||||
}
|
||||
|
||||
public List<string> Convert(Base target)
|
||||
{
|
||||
switch (target)
|
||||
{
|
||||
case SOG.Region region:
|
||||
return new List<string>() { _regionToFilledRegionConverter.Convert(region) };
|
||||
|
||||
case DataObject dataObj:
|
||||
List<string> results = new();
|
||||
|
||||
var displayValue = target.TryGetDisplayValue<Base>();
|
||||
if ((displayValue is IList && !displayValue.Any()) || displayValue is null)
|
||||
{
|
||||
throw new ValidationException($"No display value found for {target.speckle_type}");
|
||||
}
|
||||
dataObj.displayValue.ForEach(x => results.AddRange(Convert(x)));
|
||||
|
||||
if (results.Count == 0)
|
||||
{
|
||||
throw new ConversionException($"No objects could be converted for {target.speckle_type}.");
|
||||
}
|
||||
|
||||
return results;
|
||||
|
||||
default:
|
||||
throw new ConversionException($"Objects of type {target.speckle_type} cannot be converted in 2d view.");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user