Revit receive first pass: reference geometry workflow (CNX-403) (#254)
* feat(dui3): re-enables receive binding probably the fourth time * chore(revit): drastic cleanup maybe too drastic, we will see soon * feat(revit): starts scaffolding revit root to host converter * RenderMaterialToHostConverter added back * casting to GeometryObject instead of GeometryElement * feat(dui3): fallback display values and refactors out material converter * feat(dui3): creates DS and adds point support * feat(dui3): closed nurbs fallback to display values * David/cnx 443 selection (#231) * highlight works * use status.success on receive success * question comment * wip * feat(revit): wraps receive in correct context * wip * feat(dui3): adds prototype grouping by collection and adds todos * remove invalid characters from group name * hide warnings preprocessor * exception handling and error log * added cancellation and progress reporting * chore: comments and minor cleanup * David/cnx 409 2 add rendermaterial and color manager to connector (#242) * materials wip * Make it work receiving render materials * MaterialBaker mods, cleanup * Address the PR comments * Remove invalid chars from structured material names --------- Co-authored-by: oguzhankoral <oguzhankoral@gmail.com> * Minor cleanup * Add object application id into objects of its layer render material proxy * feat(dui3): adds compatibility for objects with display values * Use LocalToGlobal logic for revit receive * Use common local to global util for arcgis too * Remove unnecessary registration * Remove using * Remove unnecessart ToList * Register LocalToGlobalConverterUtils for connectors not as common * purge materials and groups in Revit before update (#245) * purge materials and groups in Revit before update * cleaner linq * renamed _groupManager to _groupBaker * assign categories to DirectShapes after receive, updated revit invalid chars (#253) * Post conflict resolving problems * minor changes, logging, comments (#257) * minor changes, logging, comments * typo --------- Co-authored-by: David Kekesi <david@speckle.systems> Co-authored-by: kekesidavid <david.kekesi@gmail.com> Co-authored-by: oguzhankoral <oguzhankoral@gmail.com>
This commit is contained in:
committed by
GitHub
parent
2418b3e564
commit
4e48427bee
+3
-3
@@ -17,8 +17,8 @@ using Speckle.Connectors.DUI.WebView;
|
||||
using Speckle.Connectors.Utils;
|
||||
using Speckle.Connectors.Utils.Builders;
|
||||
using Speckle.Connectors.Utils.Caching;
|
||||
using Speckle.Connectors.Utils.Instances;
|
||||
using Speckle.Connectors.Utils.Operations;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Sdk.Models.GraphTraversal;
|
||||
|
||||
// POC: This is a temp reference to root object senders to tweak CI failing after having generic interfaces into common project.
|
||||
@@ -65,11 +65,11 @@ public class ArcGISConnectorModule : ISpeckleModule
|
||||
builder.AddScoped<ArcGISRootObjectBuilder>();
|
||||
builder.AddScoped<IRootObjectBuilder<MapMember>, ArcGISRootObjectBuilder>();
|
||||
|
||||
builder.AddScoped<LocalToGlobalConverterUtils>();
|
||||
|
||||
builder.AddScoped<ArcGISColorManager>();
|
||||
builder.AddScoped<MapMembersUtils>();
|
||||
|
||||
builder.AddScoped<ILocalToGlobalUnpacker, LocalToGlobalUnpacker>();
|
||||
|
||||
// register send conversion cache
|
||||
builder.AddSingleton<ISendConversionCache, SendConversionCache>();
|
||||
|
||||
|
||||
+2
-2
@@ -29,7 +29,7 @@ public class ArcGISHostObjectBuilder : IHostObjectBuilder
|
||||
private readonly IRootToHostConverter _converter;
|
||||
private readonly IFeatureClassUtils _featureClassUtils;
|
||||
private readonly ILocalToGlobalUnpacker _localToGlobalUnpacker;
|
||||
private readonly ILocalToGlobalConverterUtils _localToGlobalConverterUtils;
|
||||
private readonly LocalToGlobalConverterUtils _localToGlobalConverterUtils;
|
||||
private readonly ICrsUtils _crsUtils;
|
||||
|
||||
// POC: figure out the correct scope to only initialize on Receive
|
||||
@@ -42,7 +42,7 @@ public class ArcGISHostObjectBuilder : IHostObjectBuilder
|
||||
IConverterSettingsStore<ArcGISConversionSettings> settingsStore,
|
||||
IFeatureClassUtils featureClassUtils,
|
||||
ILocalToGlobalUnpacker localToGlobalUnpacker,
|
||||
ILocalToGlobalConverterUtils localToGlobalConverterUtils,
|
||||
LocalToGlobalConverterUtils localToGlobalConverterUtils,
|
||||
ICrsUtils crsUtils,
|
||||
GraphTraversal traverseFunction,
|
||||
ArcGISColorManager colorManager
|
||||
|
||||
+12
-1
@@ -1,4 +1,5 @@
|
||||
using Autodesk.AutoCAD.DatabaseServices;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connectors.Autocad.HostApp.Extensions;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
@@ -18,10 +19,16 @@ public class AutocadBasicConnectorBinding : IBasicConnectorBinding
|
||||
public IBridge Parent { get; }
|
||||
|
||||
private readonly DocumentModelStore _store;
|
||||
private readonly ILogger<AutocadBasicConnectorBinding> _logger;
|
||||
|
||||
public BasicConnectorBindingCommands Commands { get; }
|
||||
|
||||
public AutocadBasicConnectorBinding(DocumentModelStore store, IBridge parent, IAccountManager accountManager)
|
||||
public AutocadBasicConnectorBinding(
|
||||
DocumentModelStore store,
|
||||
IBridge parent,
|
||||
IAccountManager accountManager,
|
||||
ILogger<AutocadBasicConnectorBinding> logger
|
||||
)
|
||||
{
|
||||
_store = store;
|
||||
Parent = parent;
|
||||
@@ -31,6 +38,8 @@ public class AutocadBasicConnectorBinding : IBasicConnectorBinding
|
||||
{
|
||||
Commands.NotifyDocumentChanged();
|
||||
};
|
||||
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public string GetConnectorVersion() => typeof(AutocadBasicConnectorBinding).Assembly.GetVersion();
|
||||
@@ -84,8 +93,10 @@ public class AutocadBasicConnectorBinding : IBasicConnectorBinding
|
||||
var objectIds = Array.Empty<ObjectId>();
|
||||
|
||||
var model = _store.GetModelById(modelCardId);
|
||||
|
||||
if (model == null)
|
||||
{
|
||||
_logger.LogError("Model was null when highlighting received model");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ using AutocadColor = Autodesk.AutoCAD.Colors.Color;
|
||||
|
||||
namespace Speckle.Connectors.Autocad.HostApp;
|
||||
|
||||
public class AutocadLayerBaker : LayerPathUnpacker
|
||||
public class AutocadLayerBaker : TraversalContextUnpacker
|
||||
{
|
||||
private readonly string _layerFilterName = "Speckle";
|
||||
private readonly AutocadContext _autocadContext;
|
||||
|
||||
+35
-7
@@ -1,5 +1,6 @@
|
||||
using System.Reflection;
|
||||
using Autodesk.Revit.DB;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Revit.Async;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
@@ -22,13 +23,20 @@ internal sealed class BasicConnectorBindingRevit : IBasicConnectorBinding
|
||||
|
||||
private readonly DocumentModelStore _store;
|
||||
private readonly RevitContext _revitContext;
|
||||
private readonly ILogger<BasicConnectorBindingRevit> _logger;
|
||||
|
||||
public BasicConnectorBindingRevit(DocumentModelStore store, IBridge parent, RevitContext revitContext)
|
||||
public BasicConnectorBindingRevit(
|
||||
DocumentModelStore store,
|
||||
IBridge parent,
|
||||
RevitContext revitContext,
|
||||
ILogger<BasicConnectorBindingRevit> logger
|
||||
)
|
||||
{
|
||||
Name = "baseBinding";
|
||||
Parent = parent;
|
||||
_store = store;
|
||||
_revitContext = revitContext;
|
||||
_logger = logger;
|
||||
Commands = new BasicConnectorBindingCommands(parent);
|
||||
|
||||
// POC: event binding?
|
||||
@@ -75,17 +83,37 @@ internal sealed class BasicConnectorBindingRevit : IBasicConnectorBinding
|
||||
|
||||
public void HighlightModel(string modelCardId)
|
||||
{
|
||||
SenderModelCard model = (SenderModelCard)_store.GetModelById(modelCardId);
|
||||
var model = _store.GetModelById(modelCardId);
|
||||
|
||||
if (model is null)
|
||||
{
|
||||
_logger.LogError("Model was null when highlighting received model");
|
||||
return;
|
||||
}
|
||||
|
||||
var activeUIDoc =
|
||||
_revitContext.UIApplication?.ActiveUIDocument
|
||||
?? throw new SpeckleException("Unable to retrieve active UI document");
|
||||
|
||||
var elementIds = model
|
||||
.SendFilter.NotNull()
|
||||
.GetObjectIds()
|
||||
.Select(uid => ElementIdHelper.GetElementIdFromUniqueId(activeUIDoc.Document, uid))
|
||||
.ToList();
|
||||
var elementIds = new List<ElementId>();
|
||||
|
||||
if (model is SenderModelCard senderModelCard)
|
||||
{
|
||||
elementIds = senderModelCard
|
||||
.SendFilter.NotNull()
|
||||
.GetObjectIds()
|
||||
.Select(uid => ElementIdHelper.GetElementIdFromUniqueId(activeUIDoc.Document, uid))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
if (model is ReceiverModelCard receiverModelCard)
|
||||
{
|
||||
elementIds = receiverModelCard
|
||||
.BakedObjectIds.NotNull()
|
||||
.Select(uid => ElementIdHelper.GetElementIdFromUniqueId(activeUIDoc.Document, uid))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
if (elementIds.Count == 0)
|
||||
{
|
||||
Commands.SetModelError(modelCardId, new InvalidOperationException("No objects found to highlight."));
|
||||
|
||||
+9
-2
@@ -17,6 +17,7 @@ using Speckle.Connectors.Utils;
|
||||
using Speckle.Connectors.Utils.Builders;
|
||||
using Speckle.Connectors.Utils.Caching;
|
||||
using Speckle.Connectors.Utils.Operations;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Sdk.Models.GraphTraversal;
|
||||
|
||||
namespace Speckle.Connectors.Revit.DependencyInjection;
|
||||
@@ -46,7 +47,7 @@ public class RevitConnectorModule : ISpeckleModule
|
||||
builder.AddSingleton<IBinding, AccountBinding>();
|
||||
builder.AddSingleton<IBinding, SelectionBinding>();
|
||||
builder.AddSingleton<IBinding, RevitSendBinding>();
|
||||
// builder.AddSingleton<IBinding, RevitReceiveBinding>(); // TODO: Have it back once we comfortable enough!
|
||||
builder.AddSingleton<IBinding, RevitReceiveBinding>();
|
||||
builder.AddSingleton<IRevitIdleManager, RevitIdleManager>();
|
||||
|
||||
builder.ContainerBuilder.RegisterType<TopLevelExceptionHandlerBinding>().As<IBinding>().AsSelf().SingleInstance();
|
||||
@@ -71,8 +72,14 @@ public class RevitConnectorModule : ISpeckleModule
|
||||
// receive operation and dependencies
|
||||
builder.AddScoped<IHostObjectBuilder, RevitHostObjectBuilder>();
|
||||
builder.AddScoped<ITransactionManager, TransactionManager>();
|
||||
builder.AddScoped<RevitGroupBaker>();
|
||||
builder.AddScoped<RevitMaterialBaker>();
|
||||
builder.AddSingleton<RevitUtils>();
|
||||
builder.AddSingleton<IFailuresPreprocessor, HideWarningsFailuresPreprocessor>();
|
||||
builder.AddSingleton(DefaultTraversal.CreateTraversalFunc());
|
||||
|
||||
builder.AddScoped<LocalToGlobalConverterUtils>();
|
||||
|
||||
// operation progress manager
|
||||
builder.AddSingleton<IOperationProgressManager, OperationProgressManager>();
|
||||
}
|
||||
@@ -86,7 +93,7 @@ public class RevitConnectorModule : ISpeckleModule
|
||||
builder.AddSingleton<IBrowserScriptExecutor>(c => c.Resolve<CefSharpPanel>());
|
||||
builder.AddSingleton<IRevitPlugin, RevitCefPlugin>();
|
||||
#else
|
||||
// POC: different versons for different versions of CEF
|
||||
// different versions for different versions of CEF
|
||||
builder.AddSingleton(BindingOptions.DefaultBinder);
|
||||
|
||||
var panel = new CefSharpPanel();
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
using Autodesk.Revit.DB;
|
||||
using Speckle.Connectors.Utils.Operations.Receive;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.RevitShared.Settings;
|
||||
using Speckle.Sdk.Models.GraphTraversal;
|
||||
|
||||
namespace Speckle.Connectors.Revit.HostApp;
|
||||
|
||||
/// <summary>
|
||||
/// <para>On receive, this class will help structure atomic objects into nested revit groups based on the hierarchy that they're coming from. Expects to be a scoped dependency per receive operation.</para>
|
||||
/// <para>How to use: during atomic object conversion, on each succesful conversion call <see cref="AddToGroupMapping"/>. Afterward, at the end of the recieve operation, call <see cref="BakeGroups"/> to actually create the groups in the revit document.</para>
|
||||
/// </summary>
|
||||
public class RevitGroupBaker : TraversalContextUnpacker
|
||||
{
|
||||
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
|
||||
private readonly RevitUtils _revitUtils;
|
||||
|
||||
public RevitGroupBaker(IConverterSettingsStore<RevitConversionSettings> converterSettings, RevitUtils revitUtils)
|
||||
{
|
||||
_converterSettings = converterSettings;
|
||||
_revitUtils = revitUtils;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the object to the correct group in preparation for <see cref="BakeGroups"/> at the end of the receive operation.
|
||||
/// </summary>
|
||||
/// <param name="traversalContext"></param>
|
||||
/// <param name="revitElement"></param>
|
||||
public void AddToGroupMapping(TraversalContext traversalContext, Element revitElement)
|
||||
{
|
||||
var collectionPath = GetCollectionPath(traversalContext);
|
||||
var currentLayerName = string.Empty;
|
||||
FakeGroup? previousGroup = null;
|
||||
var currentDepth = 0;
|
||||
|
||||
foreach (var collection in collectionPath)
|
||||
{
|
||||
currentLayerName += collection.name + "-";
|
||||
if (_groupCache.TryGetValue(currentLayerName, out var g))
|
||||
{
|
||||
previousGroup = g;
|
||||
currentDepth++;
|
||||
continue;
|
||||
}
|
||||
|
||||
var group = new FakeGroup()
|
||||
{
|
||||
// POC group names should be unique
|
||||
Name = _revitUtils.RemoveInvalidChars(collection.name),
|
||||
Depth = currentDepth++,
|
||||
Parent = previousGroup!
|
||||
};
|
||||
_groupCache[currentLayerName] = group;
|
||||
previousGroup = group;
|
||||
}
|
||||
|
||||
previousGroup!.Ids.Add(revitElement.Id);
|
||||
}
|
||||
|
||||
private readonly Dictionary<string, FakeGroup> _groupCache = new();
|
||||
|
||||
/// <summary>
|
||||
/// Bakes the accumulated groups in Revit, with their objects.
|
||||
/// </summary>
|
||||
/// <param name="baseGroupName"></param>
|
||||
public void BakeGroups(string baseGroupName)
|
||||
{
|
||||
var orderedGroups = _groupCache.Values.OrderByDescending(group => group.Depth);
|
||||
Group? lastGroup = null;
|
||||
|
||||
foreach (var group in orderedGroups)
|
||||
{
|
||||
var docGroup = _converterSettings.Current.Document.Create.NewGroup(group.Ids);
|
||||
group.Parent?.Ids.Add(docGroup.Id);
|
||||
docGroup.GroupType.Name = group.Name;
|
||||
lastGroup = docGroup;
|
||||
}
|
||||
|
||||
lastGroup!.GroupType.Name = _revitUtils.RemoveInvalidChars(baseGroupName);
|
||||
}
|
||||
|
||||
public void PurgeGroups(string baseGroupName)
|
||||
{
|
||||
var validBaseGroupName = _revitUtils.RemoveInvalidChars(baseGroupName);
|
||||
var document = _converterSettings.Current.Document;
|
||||
|
||||
using (var collector = new FilteredElementCollector(document))
|
||||
{
|
||||
var groupIds = collector
|
||||
.OfClass(typeof(GroupType))
|
||||
.Where(g => g.Name == validBaseGroupName)
|
||||
.Select(g => g.Id)
|
||||
.ToList();
|
||||
|
||||
document.Delete(groupIds);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Little intermediate data structure that helps with the operations above.
|
||||
/// </summary>
|
||||
private sealed class FakeGroup
|
||||
{
|
||||
public List<ElementId> Ids { get; set; } = new();
|
||||
public int Depth { get; set; }
|
||||
public string Name { get; set; }
|
||||
public FakeGroup Parent { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
using Autodesk.Revit.DB;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connectors.Utils.Operations.Receive;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.RevitShared.Settings;
|
||||
using Speckle.Objects.Other;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
using Speckle.Sdk.Models.Extensions;
|
||||
using Speckle.Sdk.Models.GraphTraversal;
|
||||
|
||||
namespace Speckle.Connectors.Revit.HostApp;
|
||||
|
||||
/// <summary>
|
||||
/// Utility class that converts and bakes materials in Revit. Expects to be a scoped dependency per unit of work.
|
||||
/// </summary>
|
||||
public class RevitMaterialBaker
|
||||
{
|
||||
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
|
||||
private readonly ILogger<RevitMaterialBaker> _logger;
|
||||
private readonly RevitUtils _revitUtils;
|
||||
|
||||
public RevitMaterialBaker(
|
||||
ILogger<RevitMaterialBaker> logger,
|
||||
RevitUtils revitUtils,
|
||||
IConverterSettingsStore<RevitConversionSettings> converterSettings
|
||||
)
|
||||
{
|
||||
_logger = logger;
|
||||
_revitUtils = revitUtils;
|
||||
_converterSettings = converterSettings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks the every atomic object has render material or not, if not it tries to find it from its layer tree and mutates
|
||||
/// its render material proxy objects list with the traversal current. It will also map displayable objects' display values to their
|
||||
/// respective material proxy.
|
||||
/// </summary>
|
||||
public void MapLayersRenderMaterials(RootObjectUnpackerResult unpackedRoot)
|
||||
{
|
||||
if (unpackedRoot.RenderMaterialProxies is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var context in unpackedRoot.ObjectsToConvert)
|
||||
{
|
||||
if (context.Current.applicationId is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var targetRenderMaterialProxy = unpackedRoot.RenderMaterialProxies.FirstOrDefault(rmp =>
|
||||
rmp.objects.Contains(context.Current.applicationId)
|
||||
);
|
||||
|
||||
if (targetRenderMaterialProxy is null)
|
||||
{
|
||||
var layerParents = context.GetAscendants().Where(parent => parent is Layer);
|
||||
|
||||
var layer = layerParents.FirstOrDefault(layer =>
|
||||
unpackedRoot.RenderMaterialProxies.Any(rmp => rmp.objects.Contains(layer.applicationId!))
|
||||
);
|
||||
|
||||
if (layer is not null)
|
||||
{
|
||||
var layerRenderMaterialProxy = unpackedRoot.RenderMaterialProxies.First(rmp =>
|
||||
rmp.objects.Contains(layer.applicationId!)
|
||||
);
|
||||
|
||||
targetRenderMaterialProxy = layerRenderMaterialProxy;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetRenderMaterialProxy is null)
|
||||
{
|
||||
continue; // exit fast, no proxy, we can't do much more.
|
||||
}
|
||||
// We mutate the existing proxy list that comes from source application. Because we do not keep track of parent-child relationship of objects in terms of render materials.
|
||||
targetRenderMaterialProxy.objects.Add(context.Current.applicationId!);
|
||||
|
||||
// This is somewhat evil: we're unpacking here displayable elements by adding their display value to the target render material proxy.
|
||||
// If the display value items do not have an application id, we will generate one.
|
||||
var displayable = context.Current.TryGetDisplayValue();
|
||||
if (displayable != null)
|
||||
{
|
||||
foreach (var @base in displayable)
|
||||
{
|
||||
if (@base.applicationId == null)
|
||||
{
|
||||
var guid = Guid.NewGuid().ToString();
|
||||
@base.applicationId = guid;
|
||||
targetRenderMaterialProxy.objects.Add(guid);
|
||||
}
|
||||
else
|
||||
{
|
||||
targetRenderMaterialProxy.objects.Add(@base.applicationId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will bake render materials in the revit document.
|
||||
/// </summary>
|
||||
/// <param name="speckleRenderMaterialProxies"></param>
|
||||
/// <param name="baseLayerName"></param>
|
||||
/// <returns></returns>
|
||||
public Dictionary<string, ElementId> BakeMaterials(
|
||||
List<RenderMaterialProxy> speckleRenderMaterialProxies,
|
||||
string baseLayerName
|
||||
)
|
||||
{
|
||||
Dictionary<string, ElementId> objectIdAndMaterialIndexMap = new();
|
||||
foreach (var proxy in speckleRenderMaterialProxies)
|
||||
{
|
||||
var speckleRenderMaterial = proxy.value;
|
||||
|
||||
try
|
||||
{
|
||||
var diffuse = System.Drawing.Color.FromArgb(speckleRenderMaterial.diffuse);
|
||||
double transparency = 1 - speckleRenderMaterial.opacity;
|
||||
double smoothness = 1 - speckleRenderMaterial.roughness;
|
||||
string materialId = speckleRenderMaterial.applicationId ?? speckleRenderMaterial.id;
|
||||
string matName = _revitUtils.RemoveInvalidChars($"{speckleRenderMaterial.name}-({materialId})-{baseLayerName}");
|
||||
|
||||
var newMaterialId = Autodesk.Revit.DB.Material.Create(_converterSettings.Current.Document, matName);
|
||||
var revitMaterial = (Autodesk.Revit.DB.Material)_converterSettings.Current.Document.GetElement(newMaterialId);
|
||||
revitMaterial.Color = new Color(diffuse.R, diffuse.G, diffuse.B);
|
||||
|
||||
revitMaterial.Transparency = (int)(transparency * 100);
|
||||
revitMaterial.Shininess = (int)(speckleRenderMaterial.metalness * 128);
|
||||
revitMaterial.Smoothness = (int)(smoothness * 128);
|
||||
|
||||
foreach (var objectId in proxy.objects)
|
||||
{
|
||||
objectIdAndMaterialIndexMap[objectId] = revitMaterial.Id;
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
_logger.LogError(ex, "Failed to create material in Revit");
|
||||
}
|
||||
}
|
||||
|
||||
return objectIdAndMaterialIndexMap;
|
||||
}
|
||||
|
||||
public void PurgeMaterials(string baseGroupName)
|
||||
{
|
||||
var validBaseGroupName = _revitUtils.RemoveInvalidChars(baseGroupName);
|
||||
var document = _converterSettings.Current.Document;
|
||||
|
||||
using (var collector = new FilteredElementCollector(document))
|
||||
{
|
||||
var materialIds = collector
|
||||
.OfClass(typeof(Autodesk.Revit.DB.Material))
|
||||
.Where(m => m.Name.Contains(validBaseGroupName))
|
||||
.Select(m => m.Id)
|
||||
.ToList();
|
||||
|
||||
document.Delete(materialIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace Speckle.Connectors.Revit.HostApp;
|
||||
|
||||
public class RevitUtils
|
||||
{
|
||||
// see Revit Parameter Name Limitations here
|
||||
// https://www.autodesk.com/support/technical/article/caas/tsarticles/ts/3RVyShGL7OMlDJPLasuKFL.html
|
||||
private const string REVIT_INVALID_CHARS = @"\:{}[]|;<>?`~";
|
||||
|
||||
public string RemoveInvalidChars(string str)
|
||||
{
|
||||
foreach (char c in REVIT_INVALID_CHARS)
|
||||
{
|
||||
str = str.Replace(c.ToString(), string.Empty);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
using Autodesk.Revit.DB;
|
||||
|
||||
namespace Speckle.Connectors.Revit.Operations.Receive;
|
||||
|
||||
/// <summary>
|
||||
/// This class will suppress warnings on the Revit UI
|
||||
/// Currently we use it after Revit receive when we create the group hierarchy
|
||||
/// </summary>
|
||||
|
||||
public class HideWarningsFailuresPreprocessor : IFailuresPreprocessor
|
||||
{
|
||||
public FailureProcessingResult PreprocessFailures(FailuresAccessor failuresAccessor)
|
||||
{
|
||||
failuresAccessor.DeleteAllWarnings();
|
||||
return FailureProcessingResult.Continue;
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -9,5 +9,7 @@ public interface ITransactionManager : IDisposable
|
||||
void RollbackSubTransaction();
|
||||
void RollbackTransaction();
|
||||
void StartSubtransaction();
|
||||
void StartTransaction();
|
||||
|
||||
// POC improve how the error handling behaviour is selected
|
||||
void StartTransaction(bool enableFailurePreprocessor = false);
|
||||
}
|
||||
|
||||
+158
-53
@@ -1,28 +1,35 @@
|
||||
using Autodesk.Revit.DB;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Revit.Async;
|
||||
using Speckle.Connectors.Revit.HostApp;
|
||||
using Speckle.Connectors.Utils.Builders;
|
||||
using Speckle.Connectors.Utils.Conversion;
|
||||
using Speckle.Connectors.Utils.Operations;
|
||||
using Speckle.Connectors.Utils.Instances;
|
||||
using Speckle.Connectors.Utils.Operations.Receive;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.RevitShared.Helpers;
|
||||
using Speckle.Converters.RevitShared.Settings;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Logging;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
using Speckle.Sdk.Models.GraphTraversal;
|
||||
|
||||
namespace Speckle.Connectors.Revit.Operations.Receive;
|
||||
|
||||
/// <summary>
|
||||
/// Potentially consolidate all application specific IHostObjectBuilders
|
||||
/// https://spockle.atlassian.net/browse/DUI3-465
|
||||
/// </summary>
|
||||
internal sealed class RevitHostObjectBuilder : IHostObjectBuilder, IDisposable
|
||||
{
|
||||
private readonly IRootToHostConverter _converter;
|
||||
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
|
||||
private readonly GraphTraversal _traverseFunction;
|
||||
private readonly RevitMaterialCacheSingleton _revitMaterialCacheSingleton;
|
||||
private readonly ITransactionManager _transactionManager;
|
||||
private readonly ISyncToThread _syncToThread;
|
||||
private readonly ILocalToGlobalUnpacker _localToGlobalUnpacker;
|
||||
private readonly LocalToGlobalConverterUtils _localToGlobalConverterUtils;
|
||||
private readonly RevitGroupBaker _groupBaker;
|
||||
private readonly RevitMaterialBaker _materialBaker;
|
||||
private readonly ILogger<RevitHostObjectBuilder> _logger;
|
||||
|
||||
private readonly RootObjectUnpacker _rootObjectUnpacker;
|
||||
private readonly ISdkActivityFactory _activityFactory;
|
||||
|
||||
public RevitHostObjectBuilder(
|
||||
@@ -30,15 +37,27 @@ internal sealed class RevitHostObjectBuilder : IHostObjectBuilder, IDisposable
|
||||
IConverterSettingsStore<RevitConversionSettings> converterSettings,
|
||||
GraphTraversal traverseFunction,
|
||||
ITransactionManager transactionManager,
|
||||
ISyncToThread syncToThread,
|
||||
ISdkActivityFactory activityFactory
|
||||
ISdkActivityFactory activityFactory,
|
||||
ILocalToGlobalUnpacker localToGlobalUnpacker,
|
||||
LocalToGlobalConverterUtils localToGlobalConverterUtils,
|
||||
RevitGroupBaker groupManager,
|
||||
RevitMaterialBaker materialBaker,
|
||||
RootObjectUnpacker rootObjectUnpacker,
|
||||
ILogger<RevitHostObjectBuilder> logger,
|
||||
RevitMaterialCacheSingleton revitMaterialCacheSingleton
|
||||
)
|
||||
{
|
||||
_converter = converter;
|
||||
_converterSettings = converterSettings;
|
||||
_traverseFunction = traverseFunction;
|
||||
_transactionManager = transactionManager;
|
||||
_syncToThread = syncToThread;
|
||||
_localToGlobalUnpacker = localToGlobalUnpacker;
|
||||
_localToGlobalConverterUtils = localToGlobalConverterUtils;
|
||||
_groupBaker = groupManager;
|
||||
_materialBaker = materialBaker;
|
||||
_rootObjectUnpacker = rootObjectUnpacker;
|
||||
_logger = logger;
|
||||
_revitMaterialCacheSingleton = revitMaterialCacheSingleton;
|
||||
_activityFactory = activityFactory;
|
||||
}
|
||||
|
||||
@@ -49,58 +68,144 @@ internal sealed class RevitHostObjectBuilder : IHostObjectBuilder, IDisposable
|
||||
Action<string, double?>? onOperationProgressed,
|
||||
CancellationToken cancellationToken
|
||||
) =>
|
||||
_syncToThread.RunOnThread(() =>
|
||||
{
|
||||
using var activity = _activityFactory.Start("Build");
|
||||
IEnumerable<TraversalContext> objectsToConvert;
|
||||
using (var _ = _activityFactory.Start("Traverse"))
|
||||
{
|
||||
objectsToConvert = _traverseFunction.Traverse(rootObject).Where(obj => obj.Current is not Collection);
|
||||
}
|
||||
RevitTask.RunAsync(() => BuildSync(rootObject, projectName, modelName, onOperationProgressed, cancellationToken));
|
||||
|
||||
using TransactionGroup transactionGroup =
|
||||
new(_converterSettings.Current.Document, $"Received data from {projectName}");
|
||||
transactionGroup.Start();
|
||||
_transactionManager.StartTransaction();
|
||||
|
||||
var conversionResults = BakeObjects(objectsToConvert);
|
||||
|
||||
using (var _ = _activityFactory.Start("Commit"))
|
||||
{
|
||||
_transactionManager.CommitTransaction();
|
||||
transactionGroup.Assimilate();
|
||||
}
|
||||
return conversionResults;
|
||||
});
|
||||
|
||||
// POC: Potentially refactor out into an IObjectBaker.
|
||||
private HostObjectBuilderResult BakeObjects(IEnumerable<TraversalContext> objectsGraph)
|
||||
private HostObjectBuilderResult BuildSync(
|
||||
Base rootObject,
|
||||
string projectName,
|
||||
string modelName,
|
||||
Action<string, double?>? onOperationProgressed,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
using (var _ = _activityFactory.Start("BakeObjects"))
|
||||
var baseGroupName = $"Project {projectName}: Model {modelName}"; // TODO: unify this across connectors!
|
||||
|
||||
onOperationProgressed?.Invoke("Converting", null);
|
||||
using var activity = _activityFactory.Start("Build");
|
||||
|
||||
// 0 - Clean then Rock n Roll! 🎸
|
||||
using TransactionGroup preReceiveCleanTransaction = new(_converterSettings.Current.Document, "Pre-receive clean");
|
||||
preReceiveCleanTransaction.Start();
|
||||
_transactionManager.StartTransaction(true);
|
||||
|
||||
try
|
||||
{
|
||||
var conversionResults = new List<ReceiveConversionResult>();
|
||||
PreReceiveDeepClean(baseGroupName);
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
_logger.LogError(ex, "Failed to clean up before receive in Revit");
|
||||
}
|
||||
|
||||
// NOTE!!!! Add 'UniqueId' of the elements once we have receiving in place, otherwise highlight logic will fail.
|
||||
var bakedObjectIds = new List<string>();
|
||||
using (var _ = _activityFactory.Start("Commit"))
|
||||
{
|
||||
_transactionManager.CommitTransaction();
|
||||
preReceiveCleanTransaction.Assimilate();
|
||||
}
|
||||
|
||||
foreach (TraversalContext tc in objectsGraph)
|
||||
// 1 - Unpack objects and proxies from root commit object
|
||||
var unpackedRoot = _rootObjectUnpacker.Unpack(rootObject);
|
||||
var localToGlobalMaps = _localToGlobalUnpacker.Unpack(
|
||||
unpackedRoot.DefinitionProxies,
|
||||
unpackedRoot.ObjectsToConvert.ToList()
|
||||
);
|
||||
|
||||
using TransactionGroup transactionGroup =
|
||||
new(_converterSettings.Current.Document, $"Received data from {projectName}");
|
||||
transactionGroup.Start();
|
||||
_transactionManager.StartTransaction();
|
||||
|
||||
if (unpackedRoot.RenderMaterialProxies != null)
|
||||
{
|
||||
_materialBaker.MapLayersRenderMaterials(unpackedRoot);
|
||||
// NOTE: do not set _contextStack.RenderMaterialProxyCache directly, things stop working. Ogu/Dim do not know why :) not a problem as we hopefully will refactor some of these hacks out.
|
||||
var map = _materialBaker.BakeMaterials(unpackedRoot.RenderMaterialProxies, baseGroupName);
|
||||
foreach (var kvp in map)
|
||||
{
|
||||
_revitMaterialCacheSingleton.ObjectIdAndMaterialIndexMap.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
var conversionResults = BakeObjects(localToGlobalMaps, onOperationProgressed, cancellationToken);
|
||||
|
||||
using (var _ = _activityFactory.Start("Commit"))
|
||||
{
|
||||
_transactionManager.CommitTransaction();
|
||||
transactionGroup.Assimilate();
|
||||
}
|
||||
|
||||
using TransactionGroup createGroupTransaction = new(_converterSettings.Current.Document, "Creating group");
|
||||
createGroupTransaction.Start();
|
||||
_transactionManager.StartTransaction(true);
|
||||
|
||||
try
|
||||
{
|
||||
_groupBaker.BakeGroups(baseGroupName);
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
_logger.LogError(ex, "Failed to create group after receiving elements in Revit");
|
||||
}
|
||||
|
||||
using (var _ = _activityFactory.Start("Commit"))
|
||||
{
|
||||
_transactionManager.CommitTransaction();
|
||||
createGroupTransaction.Assimilate();
|
||||
}
|
||||
|
||||
_revitMaterialCacheSingleton.ObjectIdAndMaterialIndexMap.Clear(); // Massive hack!
|
||||
|
||||
return conversionResults;
|
||||
}
|
||||
|
||||
private HostObjectBuilderResult BakeObjects(
|
||||
List<LocalToGlobalMap> localToGlobalMaps,
|
||||
Action<string, double?>? onOperationProgressed,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
using var _ = _activityFactory.Start("BakeObjects");
|
||||
var conversionResults = new List<ReceiveConversionResult>();
|
||||
var bakedObjectIds = new List<string>();
|
||||
int count = 0;
|
||||
|
||||
foreach (LocalToGlobalMap localToGlobalMap in localToGlobalMaps)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
try
|
||||
{
|
||||
using var activity = _activityFactory.Start("BakeObject");
|
||||
try
|
||||
{
|
||||
var result = _converter.Convert(tc.Current);
|
||||
activity?.SetStatus(SdkActivityStatusCode.Ok);
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
conversionResults.Add(new(Status.ERROR, tc.Current, null, null, ex));
|
||||
activity?.RecordException(ex);
|
||||
activity?.SetStatus(SdkActivityStatusCode.Error);
|
||||
}
|
||||
}
|
||||
var atomicObject = _localToGlobalConverterUtils.TransformObjects(
|
||||
localToGlobalMap.AtomicObject,
|
||||
localToGlobalMap.Matrix
|
||||
);
|
||||
var result = _converter.Convert(atomicObject);
|
||||
onOperationProgressed?.Invoke("Converting", (double)++count / localToGlobalMaps.Count);
|
||||
|
||||
return new(bakedObjectIds, conversionResults);
|
||||
// Note: our current converter always returns a DS for now
|
||||
if (result is DirectShape ds)
|
||||
{
|
||||
bakedObjectIds.Add(ds.UniqueId.ToString());
|
||||
_groupBaker.AddToGroupMapping(localToGlobalMap.TraversalContext, ds);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new SpeckleConversionException($"Failed to cast {result.GetType()} to Direct Shape.");
|
||||
}
|
||||
conversionResults.Add(new(Status.SUCCESS, atomicObject, ds.UniqueId, "Direct Shape"));
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
conversionResults.Add(new(Status.ERROR, localToGlobalMap.AtomicObject, null, null, ex));
|
||||
}
|
||||
}
|
||||
return new(bakedObjectIds, conversionResults);
|
||||
}
|
||||
|
||||
private void PreReceiveDeepClean(string baseGroupName)
|
||||
{
|
||||
_groupBaker.PurgeGroups(baseGroupName);
|
||||
_materialBaker.PurgeMaterials(baseGroupName);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
+17
-8
@@ -11,11 +11,16 @@ namespace Speckle.Connectors.Revit.Operations.Receive;
|
||||
public sealed class TransactionManager : ITransactionManager
|
||||
{
|
||||
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
|
||||
private readonly IFailuresPreprocessor _errorPreprocessingService;
|
||||
private Document Document => _converterSettings.Current.Document;
|
||||
|
||||
public TransactionManager(IConverterSettingsStore<RevitConversionSettings> converterSettings)
|
||||
public TransactionManager(
|
||||
IConverterSettingsStore<RevitConversionSettings> converterSettings,
|
||||
IFailuresPreprocessor errorPreprocessingService
|
||||
)
|
||||
{
|
||||
_converterSettings = converterSettings;
|
||||
_errorPreprocessingService = errorPreprocessingService;
|
||||
}
|
||||
|
||||
// poc : these are being disposed. I'm not sure why I need to supress this warning
|
||||
@@ -24,17 +29,21 @@ public sealed class TransactionManager : ITransactionManager
|
||||
private SubTransaction? _subTransaction;
|
||||
#pragma warning restore CA2213 // Disposable fields should be disposed
|
||||
|
||||
public void StartTransaction()
|
||||
// POC find a better way to use IFailuresPreprocessor
|
||||
public void StartTransaction(bool enableFailurePreprocessor = false)
|
||||
{
|
||||
if (_transaction == null || !_transaction.IsValidObject || _transaction.GetStatus() != TransactionStatus.Started)
|
||||
{
|
||||
_transaction = new Transaction(Document, "Speckle Transaction");
|
||||
var failOpts = _transaction.GetFailureHandlingOptions();
|
||||
// POC: make sure to implement and add the failure preprocessor
|
||||
// https://spockle.atlassian.net/browse/DUI3-461
|
||||
//failOpts.SetFailuresPreprocessor(_errorPreprocessingService);
|
||||
failOpts.SetClearAfterRollback(true);
|
||||
_transaction.SetFailureHandlingOptions(failOpts);
|
||||
|
||||
if (enableFailurePreprocessor)
|
||||
{
|
||||
var failOpts = _transaction.GetFailureHandlingOptions();
|
||||
failOpts.SetFailuresPreprocessor(_errorPreprocessingService);
|
||||
failOpts.SetClearAfterRollback(true);
|
||||
_transaction.SetFailureHandlingOptions(failOpts);
|
||||
}
|
||||
|
||||
_transaction.Start();
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -22,10 +22,14 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ElementIdHelper.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\DocumentModelStorageSchema.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Elements.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RevitMaterialBaker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\HideWarningsFailuresPreprocessor.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\IdStorageSchema.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\IStorageSchema.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RevitDocumentStore.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)DependencyInjection\RevitConnectorModule.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RevitGroupBaker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RevitUtils.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\SendCollectionManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\ElementUnpacker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\ITransactionManager.cs" />
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Speckle.Connectors.Rhino.HostApp;
|
||||
/// <summary>
|
||||
/// Utility class managing layer creation. Expects to be a scoped dependency per receive operation.
|
||||
/// </summary>
|
||||
public class RhinoLayerBaker : LayerPathUnpacker
|
||||
public class RhinoLayerBaker : TraversalContextUnpacker
|
||||
{
|
||||
private readonly RhinoMaterialBaker _materialBaker;
|
||||
private readonly RhinoColorBaker _colorBaker;
|
||||
@@ -47,6 +47,7 @@ public class RhinoLayerBaker : LayerPathUnpacker
|
||||
var currentLayerName = baseLayerName;
|
||||
var currentDocument = RhinoDoc.ActiveDoc; // POC: too much effort right now to wrap around the interfaced layers
|
||||
Layer previousLayer = currentDocument.Layers.FindName(currentLayerName);
|
||||
|
||||
foreach (Collection collection in collectionPath)
|
||||
{
|
||||
currentLayerName += Layer.PathSeparator + collection.name;
|
||||
|
||||
-1
@@ -23,7 +23,6 @@ public class ArcGISConverterModule : ISpeckleModule
|
||||
builder.AddScoped<IFeatureClassUtils, FeatureClassUtils>();
|
||||
builder.AddScoped<ICrsUtils, CrsUtils>();
|
||||
builder.AddScoped<IArcGISFieldUtils, ArcGISFieldUtils>();
|
||||
builder.AddScoped<ILocalToGlobalConverterUtils, LocalToGlobalConverterUtils>();
|
||||
builder.AddScoped<ICharacterCleaner, CharacterCleaner>();
|
||||
|
||||
// single stack per conversion
|
||||
|
||||
@@ -17,6 +17,11 @@ public class RevitMaterialCacheSingleton
|
||||
/// </summary>
|
||||
public Dictionary<string, Dictionary<string, RenderMaterialProxy>> ObjectRenderMaterialProxiesMap { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// POC: The map we mutate PER RECEIVE operation, this smells a LOT! Once we have better conversion context stack that we can manage our data between connector - converter, this property must go away!
|
||||
/// </summary>
|
||||
public Dictionary<string, DB.ElementId> ObjectIdAndMaterialIndexMap { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// map (DB.Material id, RevitMaterial). This can be generated from converting render materials or material quantities.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,28 +1,112 @@
|
||||
using System.Collections;
|
||||
using Autodesk.Revit.DB;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Converters.RevitShared.Settings;
|
||||
using Speckle.Objects;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Extensions;
|
||||
|
||||
namespace Speckle.Converters.RevitShared;
|
||||
|
||||
public class RevitRootToHostConverter : IRootToHostConverter
|
||||
{
|
||||
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
|
||||
private readonly IConverterResolver<IToHostTopLevelConverter> _converterResolver;
|
||||
private readonly ITypedConverter<SOG.Point, DB.XYZ> _pointConverter;
|
||||
private readonly ITypedConverter<ICurve, DB.CurveArray> _curveConverter;
|
||||
private readonly ITypedConverter<SOG.Mesh, List<DB.GeometryObject>> _meshConverter;
|
||||
|
||||
public RevitRootToHostConverter(IConverterResolver<IToHostTopLevelConverter> converterResolver)
|
||||
public RevitRootToHostConverter(
|
||||
IConverterResolver<IToHostTopLevelConverter> converterResolver,
|
||||
ITypedConverter<SOG.Point, DB.XYZ> pointConverter,
|
||||
ITypedConverter<ICurve, DB.CurveArray> curveConverter,
|
||||
ITypedConverter<SOG.Mesh, List<DB.GeometryObject>> meshConverter,
|
||||
IConverterSettingsStore<RevitConversionSettings> converterSettings
|
||||
)
|
||||
{
|
||||
_converterResolver = converterResolver;
|
||||
_pointConverter = pointConverter;
|
||||
_curveConverter = curveConverter;
|
||||
_meshConverter = meshConverter;
|
||||
_converterSettings = converterSettings;
|
||||
}
|
||||
|
||||
public object Convert(Base target)
|
||||
{
|
||||
var objectConverter = _converterResolver.GetConversionForType(target.GetType());
|
||||
List<DB.GeometryObject> geometryObjects = new();
|
||||
|
||||
if (objectConverter == null)
|
||||
switch (target)
|
||||
{
|
||||
throw new SpeckleConversionException($"No conversion found for {target.GetType().Name}");
|
||||
case SOG.Point point:
|
||||
var xyz = _pointConverter.Convert(point);
|
||||
geometryObjects.Add(DB.Point.Create(xyz));
|
||||
break;
|
||||
case ICurve curve:
|
||||
var curves = _curveConverter.Convert(curve).Cast<DB.GeometryObject>();
|
||||
geometryObjects.AddRange(curves);
|
||||
break;
|
||||
case SOG.Mesh mesh:
|
||||
var meshes = _meshConverter.Convert(mesh).Cast<DB.GeometryObject>();
|
||||
geometryObjects.AddRange(meshes);
|
||||
break;
|
||||
default:
|
||||
geometryObjects.AddRange(FallbackToDisplayValue(target));
|
||||
break;
|
||||
}
|
||||
|
||||
return objectConverter.Convert(target)
|
||||
?? throw new SpeckleConversionException($"Conversion of object with type {target.GetType()} returned null");
|
||||
if (geometryObjects.Count == 0)
|
||||
{
|
||||
throw new SpeckleConversionException($"No supported conversion for {target.speckle_type} found.");
|
||||
}
|
||||
|
||||
var category = DB.BuiltInCategory.OST_GenericModel;
|
||||
if (target["category"] is string categoryString)
|
||||
{
|
||||
var res = Enum.TryParse($"OST_{categoryString}", out DB.BuiltInCategory cat);
|
||||
if (res)
|
||||
{
|
||||
var c = Category.GetCategory(_converterSettings.Current.Document, cat);
|
||||
if (c is not null && DirectShape.IsValidCategoryId(c.Id, _converterSettings.Current.Document))
|
||||
{
|
||||
category = cat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var ds = DB.DirectShape.CreateElement(_converterSettings.Current.Document, new DB.ElementId(category));
|
||||
ds.SetShape(geometryObjects);
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
private List<DB.GeometryObject> FallbackToDisplayValue(Base target)
|
||||
{
|
||||
var displayValue = target.TryGetDisplayValue<Base>();
|
||||
if ((displayValue is IList && !displayValue.Any()) || displayValue is null)
|
||||
{
|
||||
throw new NotSupportedException($"No display value found for {target.speckle_type}");
|
||||
}
|
||||
|
||||
List<DB.GeometryObject> geometryObjects = new();
|
||||
foreach (var baseObject in displayValue)
|
||||
{
|
||||
switch (baseObject)
|
||||
{
|
||||
case SOG.Point point:
|
||||
var xyz = _pointConverter.Convert(point);
|
||||
geometryObjects.Add(DB.Point.Create(xyz));
|
||||
break;
|
||||
case ICurve curve:
|
||||
var curves = _curveConverter.Convert(curve).Cast<DB.GeometryObject>();
|
||||
geometryObjects.AddRange(curves);
|
||||
break;
|
||||
case SOG.Mesh mesh:
|
||||
var meshes = _meshConverter.Convert(mesh).Cast<DB.GeometryObject>();
|
||||
geometryObjects.AddRange(meshes);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return geometryObjects;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-10
@@ -37,9 +37,11 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\Geometry\EllipseConverterToHost.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\Geometry\ICurveConverterToHost.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\Geometry\LineConverterToHost.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\Geometry\MeshConverterToHost.cs" />
|
||||
<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\RenderMaterialToHostConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\TopLevel\ToDirectShape\ArcToDirectShapeConverterToHost.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\TopLevel\ToDirectShape\CircleToDirectShapeConverterToHost.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\TopLevel\ToDirectShape\CurveToDirectShapeConverterToHost.cs" />
|
||||
@@ -49,16 +51,6 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\TopLevel\ToDirectShape\PolycurveConverterToHost.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\TopLevel\ToDirectShape\PolylineConverterToHost.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\Geometry\VectorConverterToHost.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\RenderMaterialToHostConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\Raw\SurfaceConverterToHost.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\TopLevel\BaseTopLevelConverterToHost.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\TopLevel\BrepToHostTopLevelConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\TopLevel\DirectShapeTopLevelConverterToHost.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\TopLevel\LevelToHostTopLevelConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\TopLevel\GridlineToHostTopLevelConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\TopLevel\MeshToHostTopLevelConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\TopLevel\ModelCurveToSpeckleTopLevelConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToHost\TopLevel\PointToHostTopLevelConverter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Raw\BeamConversionToSpeckle.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Raw\BoundarySegmentConversionToSpeckle.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ToSpeckle\Raw\BraceToSpeckleConverter.cs" />
|
||||
|
||||
+4
-42
@@ -61,23 +61,11 @@ public class ICurveConverterToHost : ITypedConverter<ICurve, DB.CurveArray>
|
||||
return _polylineConverter.Convert(spiral.displayValue);
|
||||
|
||||
case SOG.Curve nurbs:
|
||||
if (nurbs.closed) // NOTE: ensure we always nicely convert cyclical curves
|
||||
{
|
||||
return _polylineConverter.Convert(nurbs.displayValue);
|
||||
}
|
||||
var n = _curveConverter.Convert(nurbs);
|
||||
|
||||
// poc : in original converter, we were passing a bool into this method 'splitIfClosed'.
|
||||
// https://spockle.atlassian.net/browse/DUI3-462
|
||||
// I'm not entirely sure why we need to split curves, but there are several occurances
|
||||
// of the method being called and overriding the bool to be true.
|
||||
|
||||
//if (IsCurveClosed(n) && splitIfClosed)
|
||||
//{
|
||||
// var split = SplitCurveInTwoHalves(n);
|
||||
// curveArray.Append(split.Item1);
|
||||
// curveArray.Append(split.Item2);
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// curveArray.Append(n);
|
||||
//}
|
||||
curveArray.Append(n);
|
||||
return curveArray;
|
||||
|
||||
@@ -99,30 +87,4 @@ public class ICurveConverterToHost : ITypedConverter<ICurve, DB.CurveArray>
|
||||
throw new SpeckleConversionException($"The provided geometry of type {target.GetType()} is not a supported");
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCurveClosed(DB.Curve nativeCurve, double tol = 1E-6)
|
||||
{
|
||||
var endPoint = nativeCurve.GetEndPoint(0);
|
||||
var source = nativeCurve.GetEndPoint(1);
|
||||
var distanceTo = endPoint.DistanceTo(source);
|
||||
return distanceTo < tol;
|
||||
}
|
||||
|
||||
public (DB.Curve, DB.Curve) SplitCurveInTwoHalves(DB.Curve nativeCurve)
|
||||
{
|
||||
using var curveArray = new DB.CurveArray();
|
||||
// Revit does not like single curve loop edges, so we split them in two.
|
||||
var start = nativeCurve.GetEndParameter(0);
|
||||
var end = nativeCurve.GetEndParameter(1);
|
||||
var mid = start + (end - start) / 2;
|
||||
|
||||
var a = nativeCurve.Clone();
|
||||
a.MakeBound(start, mid);
|
||||
curveArray.Append(a);
|
||||
var b = nativeCurve.Clone();
|
||||
b.MakeBound(mid, end);
|
||||
curveArray.Append(b);
|
||||
|
||||
return (a, b);
|
||||
}
|
||||
}
|
||||
|
||||
+17
-16
@@ -1,30 +1,26 @@
|
||||
using Autodesk.Revit.DB;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Converters.RevitShared.ToSpeckle;
|
||||
using Speckle.Converters.RevitShared.Helpers;
|
||||
using Speckle.DoubleNumerics;
|
||||
using Speckle.Objects.Other;
|
||||
|
||||
namespace Speckle.Converters.RevitShared.ToHost.TopLevel;
|
||||
|
||||
[NameAndRankValue(nameof(SOG.Mesh), 0)]
|
||||
public class MeshToHostTopLevelConverter
|
||||
: BaseTopLevelConverterToHost<SOG.Mesh, DB.GeometryObject[]>,
|
||||
ITypedConverter<SOG.Mesh, DB.GeometryObject[]>
|
||||
public class MeshConverterToHost : ITypedConverter<SOG.Mesh, List<DB.GeometryObject>>
|
||||
{
|
||||
private readonly ITypedConverter<SOG.Point, DB.XYZ> _pointConverter;
|
||||
private readonly ITypedConverter<RenderMaterial, DB.Material> _materialConverter;
|
||||
private readonly RevitMaterialCacheSingleton _revitMaterialCacheSingleton;
|
||||
|
||||
public MeshToHostTopLevelConverter(
|
||||
public MeshConverterToHost(
|
||||
ITypedConverter<SOG.Point, XYZ> pointConverter,
|
||||
ITypedConverter<RenderMaterial, DB.Material> materialConverter
|
||||
RevitMaterialCacheSingleton revitMaterialCacheSingleton
|
||||
)
|
||||
{
|
||||
_pointConverter = pointConverter;
|
||||
_materialConverter = materialConverter;
|
||||
_revitMaterialCacheSingleton = revitMaterialCacheSingleton;
|
||||
}
|
||||
|
||||
public override GeometryObject[] Convert(SOG.Mesh mesh)
|
||||
public List<DB.GeometryObject> Convert(SOG.Mesh mesh)
|
||||
{
|
||||
TessellatedShapeBuilderTarget target = TessellatedShapeBuilderTarget.Mesh;
|
||||
TessellatedShapeBuilderFallback fallback = TessellatedShapeBuilderFallback.Salvage;
|
||||
@@ -37,14 +33,19 @@ public class MeshToHostTopLevelConverter
|
||||
};
|
||||
|
||||
var valid = tsb.AreTargetAndFallbackCompatible(target, fallback);
|
||||
//tsb.OpenConnectedFaceSet(target == TessellatedShapeBuilderTarget.Solid);
|
||||
tsb.OpenConnectedFaceSet(false);
|
||||
var vertices = ArrayToPoints(mesh.vertices, mesh.units);
|
||||
|
||||
ElementId materialId = ElementId.InvalidElementId;
|
||||
if (mesh["renderMaterial"] is RenderMaterial renderMaterial)
|
||||
|
||||
if (
|
||||
_revitMaterialCacheSingleton.ObjectIdAndMaterialIndexMap.TryGetValue(
|
||||
mesh.applicationId ?? mesh.id,
|
||||
out var mappedElementId
|
||||
)
|
||||
)
|
||||
{
|
||||
materialId = _materialConverter.Convert(renderMaterial).Id;
|
||||
materialId = mappedElementId;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
@@ -67,7 +68,7 @@ public class MeshToHostTopLevelConverter
|
||||
tsb.AddFace(face1);
|
||||
|
||||
triPoints = new List<XYZ> { points[1], points[2], points[3] };
|
||||
;
|
||||
|
||||
var face2 = new TessellatedFace(triPoints, materialId);
|
||||
tsb.AddFace(face2);
|
||||
}
|
||||
@@ -85,7 +86,7 @@ public class MeshToHostTopLevelConverter
|
||||
tsb.Build();
|
||||
var result = tsb.GetBuildResult();
|
||||
|
||||
return result.GetGeometricalObjects().ToArray();
|
||||
return result.GetGeometricalObjects().ToList();
|
||||
}
|
||||
|
||||
private static bool IsNonPlanarQuad(IList<XYZ> points)
|
||||
+55
-59
@@ -1,59 +1,55 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Autodesk.Revit.DB;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Converters.RevitShared.Settings;
|
||||
using Speckle.Objects.Other;
|
||||
|
||||
namespace Speckle.Converters.RevitShared.ToHost.Raw;
|
||||
|
||||
public class RenderMaterialToHostConverter : ITypedConverter<RenderMaterial, DB.Material>
|
||||
{
|
||||
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
|
||||
|
||||
public RenderMaterialToHostConverter(IConverterSettingsStore<RevitConversionSettings> converterSettings)
|
||||
{
|
||||
_converterSettings = converterSettings;
|
||||
}
|
||||
|
||||
public DB.Material Convert(RenderMaterial target)
|
||||
{
|
||||
string matName = RemoveProhibitedCharacters(target.name);
|
||||
|
||||
using FilteredElementCollector collector = new(_converterSettings.Current.Document);
|
||||
|
||||
// Try and find an existing material
|
||||
var existing = collector
|
||||
.OfClass(typeof(DB.Material))
|
||||
.Cast<DB.Material>()
|
||||
.FirstOrDefault(m => string.Equals(m.Name, matName, StringComparison.CurrentCultureIgnoreCase));
|
||||
|
||||
if (existing != null)
|
||||
{
|
||||
return existing;
|
||||
}
|
||||
|
||||
// Create new material
|
||||
ElementId materialId = DB.Material.Create(
|
||||
_converterSettings.Current.Document,
|
||||
matName ?? Guid.NewGuid().ToString()
|
||||
);
|
||||
DB.Material mat = (DB.Material)_converterSettings.Current.Document.GetElement(materialId);
|
||||
|
||||
var sysColor = System.Drawing.Color.FromArgb(target.diffuse);
|
||||
mat.Color = new DB.Color(sysColor.R, sysColor.G, sysColor.B);
|
||||
mat.Transparency = (int)((1d - target.opacity) * 100d);
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
private string RemoveProhibitedCharacters(string s)
|
||||
{
|
||||
if (string.IsNullOrEmpty(s))
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
return Regex.Replace(s, "[\\[\\]{}|;<>?`~]", "");
|
||||
}
|
||||
}
|
||||
// using System.Text.RegularExpressions;
|
||||
// using Autodesk.Revit.DB;
|
||||
// using Speckle.Converters.Common.Objects;
|
||||
// using Speckle.Converters.RevitShared.Helpers;
|
||||
// using Speckle.Objects.Other;
|
||||
//
|
||||
// namespace Speckle.Converters.RevitShared.ToHost.Raw;
|
||||
//
|
||||
// public class RenderMaterialToHostConverter : ITypedConverter<RenderMaterial, DB.Material>
|
||||
// {
|
||||
// private readonly IRevitConversionContextStack _contextStack;
|
||||
//
|
||||
// public RenderMaterialToHostConverter(IRevitConversionContextStack contextStack)
|
||||
// {
|
||||
// _contextStack = contextStack;
|
||||
// }
|
||||
//
|
||||
// public DB.Material Convert(RenderMaterial target)
|
||||
// {
|
||||
// string matName = RemoveProhibitedCharacters(target.name);
|
||||
//
|
||||
// using FilteredElementCollector collector = new(_contextStack.Current.Document);
|
||||
//
|
||||
// // Try and find an existing material
|
||||
// var existing = collector
|
||||
// .OfClass(typeof(DB.Material))
|
||||
// .Cast<DB.Material>()
|
||||
// .FirstOrDefault(m => string.Equals(m.Name, matName, StringComparison.CurrentCultureIgnoreCase));
|
||||
//
|
||||
// if (existing != null)
|
||||
// {
|
||||
// return existing;
|
||||
// }
|
||||
//
|
||||
// // Create new material
|
||||
// ElementId materialId = DB.Material.Create(_contextStack.Current.Document, matName ?? Guid.NewGuid().ToString());
|
||||
// DB.Material mat = (DB.Material)_contextStack.Current.Document.GetElement(materialId);
|
||||
//
|
||||
// var sysColor = System.Drawing.Color.FromArgb(target.diffuse);
|
||||
// mat.Color = new DB.Color(sysColor.R, sysColor.G, sysColor.B);
|
||||
// mat.Transparency = (int)((1d - target.opacity) * 100d);
|
||||
//
|
||||
// return mat;
|
||||
// }
|
||||
//
|
||||
// private string RemoveProhibitedCharacters(string s)
|
||||
// {
|
||||
// if (string.IsNullOrEmpty(s))
|
||||
// {
|
||||
// return s;
|
||||
// }
|
||||
//
|
||||
// return Regex.Replace(s, "[\\[\\]{}|;<>?`~]", "");
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
using Autodesk.Revit.DB;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Objects.Geometry;
|
||||
|
||||
namespace Speckle.Converters.RevitShared.ToHost.Raw;
|
||||
|
||||
public class SurfaceConverterToHost : ITypedConverter<SOG.Surface, DB.BRepBuilderSurfaceGeometry>
|
||||
{
|
||||
private readonly ITypedConverter<SOG.Point, DB.XYZ> _pointConverter;
|
||||
|
||||
public SurfaceConverterToHost(ITypedConverter<SOG.Point, XYZ> pointConverter)
|
||||
{
|
||||
_pointConverter = pointConverter;
|
||||
}
|
||||
|
||||
public BRepBuilderSurfaceGeometry Convert(SOG.Surface target)
|
||||
{
|
||||
var uvBox = new DB.BoundingBoxUV(target.knotsU[0], target.knotsV[0], target.knotsU[^1], target.knotsV[^1]);
|
||||
var surfPts = target.GetControlPoints();
|
||||
var uKnots = SurfaceKnotsToNative(target.knotsU);
|
||||
var vKnots = SurfaceKnotsToNative(target.knotsV);
|
||||
var cPts = ControlPointsToNative(surfPts);
|
||||
|
||||
BRepBuilderSurfaceGeometry result;
|
||||
if (!target.rational)
|
||||
{
|
||||
result = BRepBuilderSurfaceGeometry.CreateNURBSSurface(
|
||||
target.degreeU,
|
||||
target.degreeV,
|
||||
uKnots,
|
||||
vKnots,
|
||||
cPts,
|
||||
false,
|
||||
uvBox
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
var weights = ControlPointWeightsToNative(surfPts);
|
||||
result = BRepBuilderSurfaceGeometry.CreateNURBSSurface(
|
||||
target.degreeU,
|
||||
target.degreeV,
|
||||
uKnots,
|
||||
vKnots,
|
||||
cPts,
|
||||
weights,
|
||||
false,
|
||||
uvBox
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private double[] SurfaceKnotsToNative(List<double> list)
|
||||
{
|
||||
var count = list.Count;
|
||||
var knots = new double[count + 2];
|
||||
|
||||
int j = 0,
|
||||
k = 0;
|
||||
while (j < count)
|
||||
{
|
||||
knots[++k] = list[j++];
|
||||
}
|
||||
|
||||
knots[0] = knots[1];
|
||||
knots[count + 1] = knots[count];
|
||||
|
||||
return knots;
|
||||
}
|
||||
|
||||
public XYZ[] ControlPointsToNative(List<List<ControlPoint>> controlPoints)
|
||||
{
|
||||
var uCount = controlPoints.Count;
|
||||
var vCount = controlPoints[0].Count;
|
||||
var count = uCount * vCount;
|
||||
var points = new DB.XYZ[count];
|
||||
int p = 0;
|
||||
|
||||
controlPoints.ForEach(row =>
|
||||
row.ForEach(pt =>
|
||||
{
|
||||
var point = new SOG.Point(pt.x, pt.y, pt.z, pt.units);
|
||||
points[p++] = _pointConverter.Convert(point);
|
||||
})
|
||||
);
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
public double[] ControlPointWeightsToNative(List<List<ControlPoint>> controlPoints)
|
||||
{
|
||||
var uCount = controlPoints.Count;
|
||||
var vCount = controlPoints[0].Count;
|
||||
var count = uCount * vCount;
|
||||
var weights = new double[count];
|
||||
int p = 0;
|
||||
|
||||
controlPoints.ForEach(row => row.ForEach(pt => weights[p++] = pt.weight));
|
||||
|
||||
return weights;
|
||||
}
|
||||
}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Converters.RevitShared.ToSpeckle;
|
||||
|
||||
public abstract class BaseTopLevelConverterToHost<TSpeckle, THost> : IToHostTopLevelConverter
|
||||
where TSpeckle : Base
|
||||
where THost : notnull
|
||||
{
|
||||
public abstract THost Convert(TSpeckle target);
|
||||
|
||||
public object Convert(Base target)
|
||||
{
|
||||
var result = Convert((TSpeckle)target);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
-223
@@ -1,223 +0,0 @@
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Converters.RevitShared.ToSpeckle;
|
||||
using Speckle.Objects;
|
||||
using Speckle.Objects.Other;
|
||||
|
||||
namespace Speckle.Converters.RevitShared.ToHost.TopLevel;
|
||||
|
||||
[NameAndRankValue(nameof(SOG.Brep), 0)]
|
||||
public sealed class BrepTopLevelConverterToHost
|
||||
: BaseTopLevelConverterToHost<SOG.Brep, DB.Solid>,
|
||||
ITypedConverter<SOG.Brep, DB.Solid>
|
||||
{
|
||||
private readonly ITypedConverter<RenderMaterial, DB.Material> _materialConverter;
|
||||
private readonly ITypedConverter<SOG.Surface, DB.BRepBuilderSurfaceGeometry> _surfaceConverter;
|
||||
private readonly ITypedConverter<ICurve, DB.CurveArray> _curveConverter;
|
||||
|
||||
public BrepTopLevelConverterToHost(
|
||||
ITypedConverter<RenderMaterial, DB.Material> materialConverter,
|
||||
ITypedConverter<SOG.Surface, DB.BRepBuilderSurfaceGeometry> surfaceConverter,
|
||||
ITypedConverter<ICurve, DB.CurveArray> curveConverter
|
||||
)
|
||||
{
|
||||
_materialConverter = materialConverter;
|
||||
_surfaceConverter = surfaceConverter;
|
||||
_curveConverter = curveConverter;
|
||||
}
|
||||
|
||||
public override DB.Solid Convert(SOG.Brep target)
|
||||
{
|
||||
//Make sure face references are calculated by revit
|
||||
var bRepType = DB.BRepType.OpenShell;
|
||||
switch (target.Orientation)
|
||||
{
|
||||
case SOG.BrepOrientation.Inward:
|
||||
bRepType = DB.BRepType.Void;
|
||||
break;
|
||||
case SOG.BrepOrientation.Outward:
|
||||
bRepType = DB.BRepType.Solid;
|
||||
break;
|
||||
}
|
||||
|
||||
DB.ElementId? materialId = null;
|
||||
if (target["renderMaterial"] is RenderMaterial renderMaterial)
|
||||
{
|
||||
materialId = _materialConverter.Convert(renderMaterial).Id;
|
||||
}
|
||||
|
||||
using var builder = new DB.BRepBuilder(bRepType);
|
||||
|
||||
builder.SetAllowShortEdges();
|
||||
builder.AllowRemovalOfProblematicFaces();
|
||||
var brepEdges = new List<DB.BRepBuilderGeometryId>[target.Edges.Count];
|
||||
foreach (var face in target.Faces)
|
||||
{
|
||||
var faceId = builder.AddFace(_surfaceConverter.Convert(face.Surface), face.OrientationReversed);
|
||||
if (materialId is not null)
|
||||
{
|
||||
builder.SetFaceMaterialId(faceId, materialId);
|
||||
}
|
||||
|
||||
foreach (var loop in face.Loops)
|
||||
{
|
||||
var loopId = builder.AddLoop(faceId);
|
||||
if (face.OrientationReversed)
|
||||
{
|
||||
loop.TrimIndices.Reverse();
|
||||
}
|
||||
|
||||
foreach (var trim in loop.Trims)
|
||||
{
|
||||
if (
|
||||
trim.TrimType != SOG.BrepTrimType.Boundary
|
||||
&& trim.TrimType != SOG.BrepTrimType.Mated
|
||||
&& trim.TrimType != SOG.BrepTrimType.Seam
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (trim.Edge == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var edgeIds = brepEdges[trim.EdgeIndex];
|
||||
if (edgeIds == null)
|
||||
{
|
||||
// First time we see this edge, convert it and add
|
||||
edgeIds = brepEdges[trim.EdgeIndex] = new List<DB.BRepBuilderGeometryId>();
|
||||
var bRepBuilderGeometryIds = BrepEdgeToNative(trim.Edge).Select(edge => builder.AddEdge(edge));
|
||||
edgeIds.AddRange(bRepBuilderGeometryIds);
|
||||
}
|
||||
|
||||
var trimReversed = face.OrientationReversed ? !trim.IsReversed : trim.IsReversed;
|
||||
if (trimReversed)
|
||||
{
|
||||
for (int e = edgeIds.Count - 1; e >= 0; --e)
|
||||
{
|
||||
if (builder.IsValidEdgeId(edgeIds[e]))
|
||||
{
|
||||
builder.AddCoEdge(loopId, edgeIds[e], true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int e = 0; e < edgeIds.Count; ++e)
|
||||
{
|
||||
if (builder.IsValidEdgeId(edgeIds[e]))
|
||||
{
|
||||
builder.AddCoEdge(loopId, edgeIds[e], false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
builder.FinishLoop(loopId);
|
||||
}
|
||||
builder.FinishFace(faceId);
|
||||
}
|
||||
|
||||
var bRepBuilderOutcome = builder.Finish();
|
||||
if (bRepBuilderOutcome == DB.BRepBuilderOutcome.Failure || !builder.IsResultAvailable())
|
||||
{
|
||||
throw new SpeckleConversionException("BRepBuilder failed for unknown reason");
|
||||
}
|
||||
|
||||
var result = builder.GetResult();
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<DB.BRepBuilderEdgeGeometry> BrepEdgeToNative(SOG.BrepEdge edge)
|
||||
{
|
||||
// TODO: Trim curve with domain. Unsure if this is necessary as all our curves are converted to NURBS on Rhino output.
|
||||
var nativeCurveArray = _curveConverter.Convert(edge.Curve);
|
||||
bool isTrimmed =
|
||||
edge.Curve.domain != null
|
||||
&& edge.Domain != null
|
||||
&& (edge.Curve.domain.start != edge.Domain.start || edge.Curve.domain.end != edge.Domain.end);
|
||||
if (nativeCurveArray.Size == 1)
|
||||
{
|
||||
var nativeCurve = nativeCurveArray.get_Item(0);
|
||||
|
||||
if (edge.ProxyCurveIsReversed)
|
||||
{
|
||||
nativeCurve = nativeCurve.CreateReversed();
|
||||
}
|
||||
|
||||
if (nativeCurve == null)
|
||||
{
|
||||
return new List<DB.BRepBuilderEdgeGeometry>();
|
||||
}
|
||||
|
||||
if (isTrimmed)
|
||||
{
|
||||
nativeCurve.MakeBound(edge.Domain?.start ?? 0, edge.Domain?.end ?? 1);
|
||||
}
|
||||
|
||||
if (!nativeCurve.IsBound)
|
||||
{
|
||||
nativeCurve.MakeBound(0, nativeCurve.Period);
|
||||
}
|
||||
|
||||
if (IsCurveClosed(nativeCurve))
|
||||
{
|
||||
var (first, second) = SplitCurveInTwoHalves(nativeCurve);
|
||||
if (edge.ProxyCurveIsReversed)
|
||||
{
|
||||
first = first.CreateReversed();
|
||||
second = second.CreateReversed();
|
||||
}
|
||||
var halfEdgeA = DB.BRepBuilderEdgeGeometry.Create(first);
|
||||
var halfEdgeB = DB.BRepBuilderEdgeGeometry.Create(second);
|
||||
return edge.ProxyCurveIsReversed
|
||||
? new List<DB.BRepBuilderEdgeGeometry> { halfEdgeA, halfEdgeB }
|
||||
: new List<DB.BRepBuilderEdgeGeometry> { halfEdgeB, halfEdgeA };
|
||||
}
|
||||
|
||||
// TODO: Remove short segments if smaller than 'Revit.ShortCurveTolerance'.
|
||||
var fullEdge = DB.BRepBuilderEdgeGeometry.Create(nativeCurve);
|
||||
return new List<DB.BRepBuilderEdgeGeometry> { fullEdge };
|
||||
}
|
||||
|
||||
var iterator = edge.ProxyCurveIsReversed ? nativeCurveArray.ReverseIterator() : nativeCurveArray.ForwardIterator();
|
||||
|
||||
var result = new List<DB.BRepBuilderEdgeGeometry>();
|
||||
while (iterator.MoveNext())
|
||||
{
|
||||
var crv = (DB.Curve)iterator.Current;
|
||||
if (edge.ProxyCurveIsReversed)
|
||||
{
|
||||
crv = crv.CreateReversed();
|
||||
}
|
||||
|
||||
result.Add(DB.BRepBuilderEdgeGeometry.Create(crv));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private bool IsCurveClosed(DB.Curve nativeCurve, double tol = 1E-6)
|
||||
{
|
||||
var endPoint = nativeCurve.GetEndPoint(0);
|
||||
var source = nativeCurve.GetEndPoint(1);
|
||||
var distanceTo = endPoint.DistanceTo(source);
|
||||
return distanceTo < tol;
|
||||
}
|
||||
|
||||
private (DB.Curve, DB.Curve) SplitCurveInTwoHalves(DB.Curve nativeCurve)
|
||||
{
|
||||
// Revit does not like single curve loop edges, so we split them in two.
|
||||
var start = nativeCurve.GetEndParameter(0);
|
||||
var end = nativeCurve.GetEndParameter(1);
|
||||
var mid = start + (end - start) / 2;
|
||||
|
||||
var a = nativeCurve.Clone();
|
||||
a.MakeBound(start, mid);
|
||||
var b = nativeCurve.Clone();
|
||||
b.MakeBound(mid, end);
|
||||
|
||||
return (a, b);
|
||||
}
|
||||
}
|
||||
-75
@@ -1,75 +0,0 @@
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Converters.RevitShared.Helpers;
|
||||
using Speckle.Converters.RevitShared.Settings;
|
||||
using Speckle.Converters.RevitShared.ToSpeckle;
|
||||
using Speckle.Objects;
|
||||
using Speckle.Objects.Geometry;
|
||||
|
||||
namespace Speckle.Converters.RevitShared.ToHost.TopLevel;
|
||||
|
||||
[NameAndRankValue(nameof(SOBR.DirectShape), 0)]
|
||||
public sealed class DirectShapeTopLevelConverterToHost(
|
||||
IConverterSettingsStore<RevitConversionSettings> converterSettings,
|
||||
ITypedConverter<Brep, DB.Solid> brepConverter,
|
||||
ITypedConverter<ICurve, DB.CurveArray> curveConverter,
|
||||
ITypedConverter<SOG.Mesh, DB.GeometryObject[]> meshConverter,
|
||||
IRevitCategories revitCategories,
|
||||
ParameterValueSetter parameterValueSetter
|
||||
) : BaseTopLevelConverterToHost<SOBR.DirectShape, List<DB.GeometryObject>>
|
||||
{
|
||||
public override List<DB.GeometryObject> Convert(SOBR.DirectShape target)
|
||||
{
|
||||
var converted = new List<DB.GeometryObject>();
|
||||
|
||||
target.baseGeometries.ForEach(b =>
|
||||
{
|
||||
switch (b)
|
||||
{
|
||||
case SOG.Brep brep:
|
||||
converted.Add(brepConverter.Convert(brep));
|
||||
break;
|
||||
case SOG.Mesh mesh:
|
||||
converted.AddRange(meshConverter.Convert(mesh));
|
||||
break;
|
||||
case ICurve curve:
|
||||
converted.AddRange(curveConverter.Convert(curve).Cast<DB.Curve>());
|
||||
break;
|
||||
default:
|
||||
throw new SpeckleConversionException(
|
||||
$"Incompatible geometry type: {b.GetType()} is not supported in DirectShape conversions."
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
//from 2.16 onwards use the builtInCategory field for direct shape fallback
|
||||
DB.BuiltInCategory bic = DB.BuiltInCategory.OST_GenericModel;
|
||||
if (!Enum.TryParse(target["builtInCategory"] as string, out bic))
|
||||
{
|
||||
//pre 2.16 or coming from grasshopper, using the enum
|
||||
//TODO: move away from enum logic
|
||||
if ((int)target.category != -1)
|
||||
{
|
||||
var bicName = revitCategories.GetBuiltInFromSchemaBuilderCategory(target.category);
|
||||
#pragma warning disable IDE0002 // Simplify Member Access
|
||||
_ = DB.BuiltInCategory.TryParse(bicName, out bic);
|
||||
#pragma warning restore IDE0002 // Simplify Member Access
|
||||
}
|
||||
}
|
||||
|
||||
var cat = converterSettings.Current.Document.Settings.Categories.get_Item(bic);
|
||||
|
||||
using var revitDs = DB.DirectShape.CreateElement(converterSettings.Current.Document, cat.Id);
|
||||
if (target.applicationId != null)
|
||||
{
|
||||
revitDs.ApplicationId = target.applicationId;
|
||||
}
|
||||
|
||||
revitDs.ApplicationDataId = Guid.NewGuid().ToString();
|
||||
revitDs.SetShape(converted);
|
||||
revitDs.Name = target.name;
|
||||
parameterValueSetter.SetInstanceParameters(revitDs, target);
|
||||
|
||||
return converted;
|
||||
}
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Converters.RevitShared.Settings;
|
||||
using Speckle.Converters.RevitShared.ToSpeckle;
|
||||
using Speckle.Objects;
|
||||
|
||||
namespace Speckle.Converters.RevitShared.ToHost.TopLevel;
|
||||
|
||||
[NameAndRankValue(nameof(SOBE.GridLine), 0)]
|
||||
internal sealed class GridlineToHostTopLevelConverter : BaseTopLevelConverterToHost<SOBE.GridLine, DB.Grid>
|
||||
{
|
||||
private readonly ITypedConverter<ICurve, DB.CurveArray> _curveConverter;
|
||||
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
|
||||
|
||||
public GridlineToHostTopLevelConverter(
|
||||
ITypedConverter<ICurve, DB.CurveArray> curveConverter,
|
||||
IConverterSettingsStore<RevitConversionSettings> converterSettings
|
||||
)
|
||||
{
|
||||
_curveConverter = curveConverter;
|
||||
_converterSettings = converterSettings;
|
||||
}
|
||||
|
||||
public override DB.Grid Convert(SOBE.GridLine target)
|
||||
{
|
||||
DB.Curve curve = _curveConverter.Convert(target.baseLine).get_Item(0);
|
||||
|
||||
using DB.Grid revitGrid = curve switch
|
||||
{
|
||||
DB.Arc arc => DB.Grid.Create(_converterSettings.Current.Document, arc),
|
||||
DB.Line line => DB.Grid.Create(_converterSettings.Current.Document, line),
|
||||
_ => throw new SpeckleConversionException($"Grid line curve is of type {curve.GetType()} which is not supported")
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(target.label) && !GridNameIsTaken(target.label))
|
||||
{
|
||||
revitGrid.Name = target.label;
|
||||
}
|
||||
|
||||
return revitGrid;
|
||||
}
|
||||
|
||||
private bool GridNameIsTaken(string gridName)
|
||||
{
|
||||
using var collector = new DB.FilteredElementCollector(_converterSettings.Current.Document);
|
||||
|
||||
IEnumerable<string> gridNames = collector
|
||||
.WhereElementIsNotElementType()
|
||||
.OfClass(typeof(DB.Grid))
|
||||
.ToElements()
|
||||
.Cast<DB.Grid>()
|
||||
.Select(grid => grid.Name);
|
||||
|
||||
return gridNames.Contains(gridName);
|
||||
}
|
||||
}
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.RevitShared.Services;
|
||||
using Speckle.Converters.RevitShared.Settings;
|
||||
using Speckle.Converters.RevitShared.ToSpeckle;
|
||||
|
||||
namespace Speckle.Converters.RevitShared.ToHost.ToLevel;
|
||||
|
||||
[NameAndRankValue(nameof(SOBE.Level), 0)]
|
||||
public class LevelToHostTopLevelConverter : BaseTopLevelConverterToHost<SOBE.Level, DB.Level>
|
||||
{
|
||||
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
|
||||
private readonly ScalingServiceToHost _scalingService;
|
||||
|
||||
public LevelToHostTopLevelConverter(
|
||||
IConverterSettingsStore<RevitConversionSettings> converterSettings,
|
||||
ScalingServiceToHost scalingService
|
||||
)
|
||||
{
|
||||
_converterSettings = converterSettings;
|
||||
_scalingService = scalingService;
|
||||
}
|
||||
|
||||
public override DB.Level Convert(SOBE.Level target)
|
||||
{
|
||||
using var documentLevelCollector = new DB.FilteredElementCollector(_converterSettings.Current.Document);
|
||||
var docLevels = documentLevelCollector.OfClass(typeof(DB.Level)).ToElements().Cast<DB.Level>();
|
||||
|
||||
// POC : I'm not really understanding the linked use case for this. Do we want to bring this over?
|
||||
|
||||
//bool elevationMatch = true;
|
||||
////level by name component
|
||||
//if (target is RevitLevel speckleRevitLevel && speckleRevitLevel.referenceOnly)
|
||||
//{
|
||||
// //see: https://speckle.community/t/revit-connector-levels-and-spaces/2824/5
|
||||
// elevationMatch = false;
|
||||
// if (GetExistingLevelByName(docLevels, target.name) is DB.Level existingLevelWithSameName)
|
||||
// {
|
||||
// return existingLevelWithSameName;
|
||||
// }
|
||||
//}
|
||||
|
||||
DB.Level revitLevel;
|
||||
var targetElevation = _scalingService.ScaleToNative(target.elevation, target.units);
|
||||
|
||||
if (GetExistingLevelByElevation(docLevels, targetElevation) is DB.Level existingLevel)
|
||||
{
|
||||
revitLevel = existingLevel;
|
||||
}
|
||||
else
|
||||
{
|
||||
revitLevel = DB.Level.Create(_converterSettings.Current.Document, targetElevation);
|
||||
revitLevel.Name = target.name;
|
||||
|
||||
if (target is SOBR.RevitLevel rl && rl.createView)
|
||||
{
|
||||
using var viewPlan = CreateViewPlan(target.name, revitLevel.Id);
|
||||
}
|
||||
}
|
||||
|
||||
return revitLevel;
|
||||
}
|
||||
|
||||
private DB.Level GetExistingLevelByElevation(IEnumerable<DB.Level> docLevels, double elevation) =>
|
||||
docLevels.First(l => Math.Abs(l.Elevation - elevation) < _converterSettings.Current.Tolerance);
|
||||
|
||||
private DB.ViewPlan CreateViewPlan(string name, DB.ElementId levelId)
|
||||
{
|
||||
using var collector = new DB.FilteredElementCollector(_converterSettings.Current.Document);
|
||||
var vt = collector
|
||||
.OfClass(typeof(DB.ViewFamilyType))
|
||||
.First(el => ((DB.ViewFamilyType)el).ViewFamily == DB.ViewFamily.FloorPlan);
|
||||
|
||||
var view = DB.ViewPlan.Create(_converterSettings.Current.Document, vt.Id, levelId);
|
||||
view.Name = name;
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
-113
@@ -1,113 +0,0 @@
|
||||
using System.Collections;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Converters.RevitShared.Settings;
|
||||
using Speckle.Objects;
|
||||
using Speckle.Sdk.Common;
|
||||
|
||||
namespace Speckle.Converters.RevitShared.ToSpeckle;
|
||||
|
||||
[NameAndRankValue(nameof(SOBR.Curve.ModelCurve), 0)]
|
||||
public class ModelCurveToHostTopLevelConverter : BaseTopLevelConverterToHost<SOBR.Curve.ModelCurve, DB.ModelCurve[]>
|
||||
{
|
||||
private readonly ITypedConverter<ICurve, DB.CurveArray> _curveConverter;
|
||||
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
|
||||
|
||||
public ModelCurveToHostTopLevelConverter(
|
||||
ITypedConverter<ICurve, DB.CurveArray> curveConverter,
|
||||
IConverterSettingsStore<RevitConversionSettings> converterSettings
|
||||
)
|
||||
{
|
||||
_curveConverter = curveConverter;
|
||||
_converterSettings = converterSettings;
|
||||
}
|
||||
|
||||
public override DB.ModelCurve[] Convert(SOBR.Curve.ModelCurve target) =>
|
||||
ModelCurvesFromEnumerator(_curveConverter.Convert(target.baseCurve).GetEnumerator(), target.baseCurve).ToArray();
|
||||
|
||||
private IEnumerable<DB.ModelCurve> ModelCurvesFromEnumerator(IEnumerator curveEnum, ICurve speckleLine)
|
||||
{
|
||||
while (curveEnum.MoveNext() && curveEnum.Current != null)
|
||||
{
|
||||
var curve = (DB.Curve)curveEnum.Current;
|
||||
// Curves must be bound in order to be valid model curves
|
||||
if (!curve.IsBound)
|
||||
{
|
||||
if (speckleLine.domain.end - speckleLine.domain.start <= 0)
|
||||
{
|
||||
speckleLine.domain.start = 0;
|
||||
speckleLine.domain.end = Math.PI * 2;
|
||||
}
|
||||
curve.MakeBound(speckleLine.domain.start, speckleLine.domain.end);
|
||||
}
|
||||
|
||||
if (_converterSettings.Current.Document.IsFamilyDocument)
|
||||
{
|
||||
yield return _converterSettings.Current.Document.FamilyCreate.NewModelCurve(
|
||||
curve,
|
||||
NewSketchPlaneFromCurve(curve, _converterSettings.Current.Document)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return _converterSettings.Current.Document.Create.NewModelCurve(
|
||||
curve,
|
||||
NewSketchPlaneFromCurve(curve, _converterSettings.Current.Document)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Credits: Grevit
|
||||
/// Creates a new Sketch Plane from a Curve
|
||||
/// https://github.com/grevit-dev/Grevit/blob/3c7a5cc198e00dfa4cc1e892edba7c7afd1a3f84/Grevit.Revit/Utilities.cs#L402
|
||||
/// </summary>
|
||||
/// <param name="curve">Curve to get plane from</param>
|
||||
/// <returns>Plane of the curve</returns>
|
||||
private DB.SketchPlane NewSketchPlaneFromCurve(DB.Curve curve, DB.Document doc)
|
||||
{
|
||||
DB.XYZ startPoint = curve.GetEndPoint(0);
|
||||
DB.XYZ endPoint = curve.GetEndPoint(1);
|
||||
|
||||
// If Start end Endpoint are the same check further points.
|
||||
int i = 2;
|
||||
while (startPoint == endPoint && endPoint != null)
|
||||
{
|
||||
endPoint = curve.GetEndPoint(i);
|
||||
i++;
|
||||
}
|
||||
|
||||
// Plane to return
|
||||
DB.Plane plane;
|
||||
|
||||
// If Z Values are equal the Plane is XY
|
||||
if (startPoint.Z == endPoint.NotNull().Z)
|
||||
{
|
||||
plane = DB.Plane.CreateByNormalAndOrigin(DB.XYZ.BasisZ, startPoint);
|
||||
}
|
||||
// If X Values are equal the Plane is YZ
|
||||
else if (startPoint.X == endPoint.X)
|
||||
{
|
||||
plane = DB.Plane.CreateByNormalAndOrigin(DB.XYZ.BasisX, startPoint);
|
||||
}
|
||||
// If Y Values are equal the Plane is XZ
|
||||
else if (startPoint.Y == endPoint.Y)
|
||||
{
|
||||
plane = DB.Plane.CreateByNormalAndOrigin(DB.XYZ.BasisY, startPoint);
|
||||
}
|
||||
// Otherwise the Planes Normal Vector is not X,Y or Z.
|
||||
// We draw lines from the Origin to each Point and use the Plane this one spans up.
|
||||
else
|
||||
{
|
||||
using DB.CurveArray curves = new();
|
||||
curves.Append(curve);
|
||||
curves.Append(DB.Line.CreateBound(new DB.XYZ(0, 0, 0), startPoint));
|
||||
curves.Append(DB.Line.CreateBound(endPoint, new DB.XYZ(0, 0, 0)));
|
||||
|
||||
plane = DB.Plane.CreateByThreePoints(startPoint, new DB.XYZ(0, 0, 0), endPoint);
|
||||
}
|
||||
|
||||
return DB.SketchPlane.Create(doc, plane);
|
||||
}
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Autodesk.Revit.DB;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Converters.RevitShared.Settings;
|
||||
using Speckle.Converters.RevitShared.ToSpeckle;
|
||||
|
||||
namespace Speckle.Converters.RevitShared.ToHost.TopLevel;
|
||||
|
||||
[NameAndRankValue(nameof(SOG.Point), 0)]
|
||||
public sealed class PointToHostTopLevelConverter
|
||||
: BaseTopLevelConverterToHost<SOG.Point, DB.Solid>,
|
||||
ITypedConverter<SOG.Point, DB.Solid>
|
||||
{
|
||||
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
|
||||
private readonly ITypedConverter<SOG.Point, DB.XYZ> _pointConverter;
|
||||
|
||||
public PointToHostTopLevelConverter(
|
||||
IConverterSettingsStore<RevitConversionSettings> converterSettings,
|
||||
ITypedConverter<SOG.Point, XYZ> pointConverter
|
||||
)
|
||||
{
|
||||
_converterSettings = converterSettings;
|
||||
_pointConverter = pointConverter;
|
||||
}
|
||||
|
||||
[SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "Disposal transfter")]
|
||||
public override DB.Solid Convert(SOG.Point target)
|
||||
{
|
||||
List<Curve> profile = new();
|
||||
|
||||
XYZ center = _pointConverter.Convert(target);
|
||||
|
||||
double radius = .2;
|
||||
|
||||
XYZ profilePlus = center.Add(new XYZ(0, radius, 0));
|
||||
XYZ profileMinus = center.Subtract(new XYZ(0, radius, 0));
|
||||
|
||||
profile.Add(Line.CreateBound(profilePlus, profileMinus));
|
||||
profile.Add(Arc.Create(profileMinus, profilePlus, center.Add(new XYZ(radius, 0, 0))));
|
||||
|
||||
using SolidOptions options = new(ElementId.InvalidElementId, ElementId.InvalidElementId);
|
||||
|
||||
using Frame frame = new(center, XYZ.BasisX, XYZ.BasisZ.Multiply(-1), XYZ.BasisY);
|
||||
|
||||
if (!Frame.CanDefineRevitGeometry(frame))
|
||||
{
|
||||
throw new SpeckleConversionException("Unable to define Revit geometry");
|
||||
}
|
||||
|
||||
CurveLoop curveLoop = CurveLoop.Create(profile);
|
||||
Solid sphere = GeometryCreationUtilities.CreateRevolvedGeometry(frame, [curveLoop], 0, 2 * Math.PI, options);
|
||||
|
||||
using DirectShape ds = DirectShape.CreateElement(
|
||||
_converterSettings.Current.Document,
|
||||
new ElementId(BuiltInCategory.OST_GenericModel)
|
||||
);
|
||||
|
||||
ds.ApplicationId = target.applicationId ?? "appId";
|
||||
ds.ApplicationDataId = "Geometry object id";
|
||||
ds.SetShape([sphere]);
|
||||
|
||||
return sphere;
|
||||
}
|
||||
}
|
||||
+15
-4
@@ -8,17 +8,23 @@ namespace Speckle.Connectors.Utils.Operations.Receive;
|
||||
/// <summary>
|
||||
/// Utility class to unpack layer structure from path of collections or property tree.
|
||||
/// </summary>
|
||||
public abstract class LayerPathUnpacker
|
||||
public abstract class TraversalContextUnpacker
|
||||
{
|
||||
public List<(Collection[] path, Base current)> GetAtomicObjectsWithPath(
|
||||
IEnumerable<TraversalContext> atomicObjects
|
||||
) => atomicObjects.Select(o => (GetLayerPath(o), o.Current)).ToList();
|
||||
) => atomicObjects.Select(o => (GetCollectionPath(o), o.Current)).ToList();
|
||||
|
||||
public List<(Collection[] path, IInstanceComponent instance)> GetInstanceComponentsWithPath(
|
||||
IEnumerable<TraversalContext> instanceComponents
|
||||
) => instanceComponents.Select(o => (GetLayerPath(o), (o.Current as IInstanceComponent)!)).ToList();
|
||||
) => instanceComponents.Select(o => (GetCollectionPath(o), (o.Current as IInstanceComponent)!)).ToList();
|
||||
|
||||
public Collection[] GetLayerPath(TraversalContext context)
|
||||
/// <summary>
|
||||
/// Returns the collection path for the provided traversal context. If data is coming from a dynamic/non-dui3 connector, the collection path will be generated based on the property path. This function enforces that every collection will have a name and an application id.
|
||||
/// POC: this should be a util living somewhere else, most likely as an extension of the traversal context.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
public Collection[] GetCollectionPath(TraversalContext context)
|
||||
{
|
||||
Collection[] collectionBasedPath = context.GetAscendantOfType<Collection>().Reverse().ToArray();
|
||||
|
||||
@@ -31,6 +37,11 @@ public abstract class LayerPathUnpacker
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
foreach (var collection in collectionBasedPath)
|
||||
{
|
||||
collection.applicationId ??= Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
return collectionBasedPath;
|
||||
}
|
||||
}
|
||||
+6
-7
@@ -1,14 +1,11 @@
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.DoubleNumerics;
|
||||
using Speckle.InterfaceGenerator;
|
||||
using Speckle.DoubleNumerics;
|
||||
using Speckle.Objects;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Converters.ArcGIS3.Utils;
|
||||
namespace Speckle.Converters.Common;
|
||||
|
||||
// POC: We could pass transformation matrices to converters by default and evaluate there instead as utils.
|
||||
[GenerateAutoInterface]
|
||||
public class LocalToGlobalConverterUtils : ILocalToGlobalConverterUtils
|
||||
public class LocalToGlobalConverterUtils
|
||||
{
|
||||
private Vector3 TransformPt(Vector3 vector, Matrix4x4 matrix)
|
||||
{
|
||||
@@ -28,7 +25,9 @@ public class LocalToGlobalConverterUtils : ILocalToGlobalConverterUtils
|
||||
return atomicObject;
|
||||
}
|
||||
|
||||
List<Objects.Other.Transform> transforms = matrix.Select(x => new Objects.Other.Transform(x, "none")).ToList();
|
||||
List<Speckle.Objects.Other.Transform> transforms = matrix
|
||||
.Select(x => new Speckle.Objects.Other.Transform(x, "none"))
|
||||
.ToList();
|
||||
|
||||
if (atomicObject is ITransformable c)
|
||||
{
|
||||
Reference in New Issue
Block a user