Compare commits
53 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 27a7d72de3 | |||
| 678f113d05 | |||
| 92da66bbbb | |||
| 79a5228899 | |||
| 4d9411de42 | |||
| 3780747992 | |||
| 4514b1b831 | |||
| 2bbbbf6204 | |||
| e1b5dea3f7 | |||
| 2d2c274030 | |||
| 81dd72a281 | |||
| b82349478c | |||
| 7d0690f7a0 | |||
| 62a0cb895d | |||
| f28ce73d33 | |||
| 15425c5328 | |||
| 7c645e3c51 | |||
| 795d068175 | |||
| 90c2bd2873 | |||
| bd7a3c7c43 | |||
| ea976309bc | |||
| 1b5787274a | |||
| 7e595deabc | |||
| 66091b2b73 | |||
| 4f8d8d4f07 | |||
| 4fba12f966 | |||
| 348975c33d | |||
| cd6888868e | |||
| f2d4e64005 | |||
| a92b88f6d3 | |||
| abfdbdeffa | |||
| efe66e7e98 | |||
| c3fa1bb0dc | |||
| e487981e5b | |||
| 9a6dda629b | |||
| 46e7d6e432 | |||
| b9f4845fa7 | |||
| 36863efc5a | |||
| a0ce883a3f | |||
| bc0fe17d08 | |||
| 2e52409db6 | |||
| f434cde7b3 | |||
| 3e596cac29 | |||
| 876d5c1bfe | |||
| 3424de9130 | |||
| 279e900105 | |||
| ac7398be49 | |||
| f1f17eea3d | |||
| 642607acad | |||
| 7f3b23e71e | |||
| 9b0a6c3202 | |||
| de662e4a2b | |||
| 2cb7211734 |
@@ -7,7 +7,7 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
file_version: ${{ steps.set-version.outputs.file_version }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -83,7 +83,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
@@ -3,48 +3,45 @@ using Autodesk.Revit.DB.ExtensibleStorage;
|
||||
using Autodesk.Revit.UI;
|
||||
using Autodesk.Revit.UI.Events;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Models;
|
||||
using Speckle.Connectors.DUI.Utils;
|
||||
using Speckle.Connectors.Revit.Plugin;
|
||||
using Speckle.Converters.RevitShared.Helpers;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.SQLite;
|
||||
|
||||
namespace Speckle.Connectors.Revit.HostApp;
|
||||
|
||||
// POC: should be interfaced out
|
||||
internal sealed class RevitDocumentStore : DocumentModelStore
|
||||
{
|
||||
// POC: move to somewhere central?
|
||||
private static readonly Guid s_revitDocumentStoreId = new("D35B3695-EDC9-4E15-B62A-D3FC2CB83FA3");
|
||||
|
||||
private readonly ILogger<RevitDocumentStore> _logger;
|
||||
private readonly IAppIdleManager _idleManager;
|
||||
private readonly RevitContext _revitContext;
|
||||
private readonly DocumentModelStorageSchema _documentModelStorageSchema;
|
||||
private readonly IdStorageSchema _idStorageSchema;
|
||||
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
|
||||
private readonly IThreadContext _threadContext;
|
||||
private readonly ISqLiteJsonCacheManager _jsonCacheManager;
|
||||
|
||||
public RevitDocumentStore(
|
||||
ILogger<DocumentModelStore> logger,
|
||||
IAppIdleManager idleManager,
|
||||
RevitContext revitContext,
|
||||
IJsonSerializer jsonSerializer,
|
||||
DocumentModelStorageSchema documentModelStorageSchema,
|
||||
IdStorageSchema idStorageSchema,
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler,
|
||||
IThreadContext threadContext,
|
||||
IRevitTask revitTask
|
||||
IRevitTask revitTask,
|
||||
ISqLiteJsonCacheManagerFactory jsonCacheManagerFactory,
|
||||
ILogger<RevitDocumentStore> logger
|
||||
)
|
||||
: base(logger, jsonSerializer)
|
||||
{
|
||||
_jsonCacheManager = jsonCacheManagerFactory.CreateForUser("ConnectorsFileData");
|
||||
_idleManager = idleManager;
|
||||
_revitContext = revitContext;
|
||||
_documentModelStorageSchema = documentModelStorageSchema;
|
||||
_idStorageSchema = idStorageSchema;
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
_threadContext = threadContext;
|
||||
_logger = logger;
|
||||
|
||||
UIApplication uiApplication = _revitContext.UIApplication.NotNull();
|
||||
|
||||
@@ -101,80 +98,36 @@ internal sealed class RevitDocumentStore : DocumentModelStore
|
||||
return;
|
||||
}
|
||||
|
||||
_threadContext
|
||||
.RunOnMain(() =>
|
||||
{
|
||||
//if not the same active document then don't save the current cards to a bad document!
|
||||
if (!EnsureActiveDocumentIsSame(document))
|
||||
{
|
||||
return;
|
||||
}
|
||||
using Transaction t = new(document, "Speckle Write State");
|
||||
t.Start();
|
||||
using DataStorage ds = GetSettingsDataStorage(document) ?? DataStorage.Create(document);
|
||||
|
||||
using Entity stateEntity = new(_documentModelStorageSchema.GetSchema());
|
||||
string serializedModels = Serialize();
|
||||
stateEntity.Set("contents", serializedModels);
|
||||
|
||||
using Entity idEntity = new(_idStorageSchema.GetSchema());
|
||||
idEntity.Set("Id", s_revitDocumentStoreId);
|
||||
|
||||
ds.SetEntity(idEntity);
|
||||
ds.SetEntity(stateEntity);
|
||||
t.Commit();
|
||||
})
|
||||
.FireAndForget();
|
||||
}
|
||||
|
||||
private bool EnsureActiveDocumentIsSame(Document document)
|
||||
{
|
||||
var localDoc = _revitContext.UIApplication?.ActiveUIDocument?.Document;
|
||||
if (localDoc == null)
|
||||
try
|
||||
{
|
||||
return false;
|
||||
var key = document.ProjectInformation.UniqueId.NotNull();
|
||||
_jsonCacheManager.UpdateObject(key, modelCardState);
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
var key = document.ProjectInformation.UniqueId.NotNull();
|
||||
_logger.LogError(ex, "Failed to save model card state for document {DocumentId}", key);
|
||||
}
|
||||
|
||||
return localDoc.Equals(document);
|
||||
}
|
||||
|
||||
protected override void LoadState()
|
||||
{
|
||||
var stateEntity = GetSpeckleEntity(_revitContext.UIApplication?.ActiveUIDocument?.Document);
|
||||
var document = _revitContext.UIApplication?.ActiveUIDocument?.Document;
|
||||
// POC: this can happen? A: Not really, imho (dim) (Adam seyz yes it can if loading also triggers a save)
|
||||
if (document == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var stateEntity = GetSpeckleEntity(document);
|
||||
if (stateEntity == null || !stateEntity.IsValid())
|
||||
{
|
||||
ClearAndSave();
|
||||
return;
|
||||
}
|
||||
|
||||
string modelsString = stateEntity.Get<string>("contents");
|
||||
LoadFromString(modelsString);
|
||||
}
|
||||
|
||||
private DataStorage? GetSettingsDataStorage(Document doc)
|
||||
{
|
||||
using FilteredElementCollector collector = new(doc);
|
||||
FilteredElementCollector dataStorages = collector.OfClass(typeof(DataStorage));
|
||||
|
||||
foreach (Element element in dataStorages)
|
||||
{
|
||||
DataStorage dataStorage = (DataStorage)element;
|
||||
Entity settingIdEntity = dataStorage.GetEntity(_idStorageSchema.GetSchema());
|
||||
if (!settingIdEntity.IsValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Guid id = settingIdEntity.Get<Guid>("Id");
|
||||
if (!id.Equals(s_revitDocumentStoreId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return dataStorage;
|
||||
}
|
||||
|
||||
return null;
|
||||
var key = document.ProjectInformation.UniqueId.NotNull();
|
||||
var state = _jsonCacheManager.GetObject(key);
|
||||
LoadFromString(state);
|
||||
}
|
||||
|
||||
private Entity? GetSpeckleEntity(Document? doc)
|
||||
|
||||
+52
-2
@@ -2,7 +2,9 @@ using Autodesk.Revit.DB;
|
||||
using Speckle.Connectors.DUI.Exceptions;
|
||||
using Speckle.Connectors.DUI.Models.Card.SendFilter;
|
||||
using Speckle.Connectors.DUI.Utils;
|
||||
using Speckle.Converters.RevitShared.Extensions;
|
||||
using Speckle.Converters.RevitShared.Helpers;
|
||||
using Speckle.Sdk;
|
||||
|
||||
namespace Speckle.Connectors.RevitShared.Operations.Send.Filters;
|
||||
|
||||
@@ -75,8 +77,8 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
|
||||
//this used to throw an exception, but we don't want to fail loudly if the view is not found
|
||||
return [];
|
||||
}
|
||||
using var viewCollector = new FilteredElementCollector(_doc, view.Id);
|
||||
var elementsInView = viewCollector.ToElements();
|
||||
|
||||
IEnumerable<Element> elementsInView = GetFilteredElementsForView(view);
|
||||
|
||||
// NOTE: FilteredElementCollector() includes sweeps and reveals from a wall family's definition and includes them as additional objects
|
||||
// on this return. displayValue for Wall already includes these, therefore we end up with duplicate elements on wall sweeps
|
||||
@@ -125,4 +127,52 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
|
||||
_revitContext = revitContext;
|
||||
_doc = _revitContext.UIApplication?.ActiveUIDocument.Document;
|
||||
}
|
||||
|
||||
// NOTE: Element collector returns parts and source elements even when Parts Visibility is set as "Show Parts" only.
|
||||
// Below function collects list of ids to exclude from final list.
|
||||
private HashSet<ElementId> GetSourceElementIdsToExclude(IEnumerable<Element> elements)
|
||||
{
|
||||
var elementsToExclude = new HashSet<ElementId>();
|
||||
|
||||
foreach (var element in elements)
|
||||
{
|
||||
// check if element is a part
|
||||
if (element.Category?.GetBuiltInCategory() == BuiltInCategory.OST_Parts && element is Part part)
|
||||
{
|
||||
try
|
||||
{
|
||||
// get source element ids from the part
|
||||
var sourceIds = part.GetSourceElementIds();
|
||||
if (sourceIds != null)
|
||||
{
|
||||
foreach (var sourceId in sourceIds)
|
||||
{
|
||||
elementsToExclude.Add(sourceId.HostElementId);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) when (!e.IsFatal())
|
||||
{
|
||||
// silently continue processing other Parts if one fails
|
||||
// this follows the pattern used elsewhere in the codebase
|
||||
}
|
||||
}
|
||||
}
|
||||
return elementsToExclude;
|
||||
}
|
||||
|
||||
private IEnumerable<Element> GetFilteredElementsForView(View view)
|
||||
{
|
||||
using var viewCollector = new FilteredElementCollector(_doc, view.Id);
|
||||
var allElements = viewCollector.ToElements();
|
||||
|
||||
// parts filtering when view is set to show Parts only (and overwrites allElements)
|
||||
if (view.PartsVisibility == PartsVisibility.ShowPartsOnly)
|
||||
{
|
||||
var idsToExclude = GetSourceElementIdsToExclude(allElements);
|
||||
return allElements.Where(e => !idsToExclude.Contains(e.Id));
|
||||
}
|
||||
|
||||
return allElements;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,7 +343,6 @@
|
||||
"speckle.converters.rhino8": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"RhinoCommon": "[8.9.24194.18121, )",
|
||||
"Speckle.Converters.Common": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
|
||||
+282
-178
@@ -37,91 +37,173 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
|
||||
|
||||
protected override void SolveInstance(IGH_DataAccess da)
|
||||
{
|
||||
object data = new();
|
||||
da.GetData(0, ref data);
|
||||
|
||||
List<OutputParamWrapper> outputParams = new();
|
||||
|
||||
switch (data)
|
||||
// on first iteration, discover all fields from all objects to create stable output structure
|
||||
if (da.Iteration == 0)
|
||||
{
|
||||
case SpeckleCollectionWrapperGoo collectionGoo when collectionGoo.Value != null:
|
||||
// get children elements from the wrapper to override the elements prop while parsing
|
||||
List<IGH_Goo> children = collectionGoo.Value.Elements.Select(o => ((SpeckleWrapper)o).CreateGoo()).ToList();
|
||||
outputParams = ParseSpeckleWrapper(collectionGoo.Value, children);
|
||||
break;
|
||||
case SpeckleDataObjectWrapperGoo dataObjectGoo when dataObjectGoo.Value != null:
|
||||
// get geometries from the wrapper to override the displayvalue prop while parsing
|
||||
List<IGH_Goo> display = dataObjectGoo.Value.Geometries.Select(o => o.CreateGoo()).ToList();
|
||||
outputParams = ParseSpeckleWrapper(dataObjectGoo.Value, null, display);
|
||||
break;
|
||||
case SpeckleGeometryWrapperGoo objectGoo when objectGoo.Value != null:
|
||||
outputParams = ParseSpeckleWrapper(objectGoo.Value);
|
||||
break;
|
||||
case SpeckleBlockInstanceWrapperGoo blockInstanceGoo when blockInstanceGoo.Value != null:
|
||||
outputParams = ParseSpeckleWrapper(blockInstanceGoo.Value);
|
||||
break;
|
||||
case SpeckleBlockDefinitionWrapperGoo blockDef:
|
||||
outputParams = ParseSpeckleWrapper(blockDef.Value);
|
||||
break;
|
||||
case SpeckleMaterialWrapperGoo materialGoo when materialGoo.Value != null:
|
||||
outputParams = ParseSpeckleWrapper(materialGoo.Value);
|
||||
break;
|
||||
HashSet<string> allFields = DiscoverAllFieldsFromInput();
|
||||
|
||||
case SpecklePropertyGroupGoo propGoo:
|
||||
Name = $"properties ({propGoo.Value.Count})";
|
||||
outputParams = new();
|
||||
foreach (var key in propGoo.Value.Keys)
|
||||
if (allFields.Count > 0)
|
||||
{
|
||||
var requiredOutputs = CreateOutputParamsFromFieldNames(allFields);
|
||||
|
||||
if (OutputMismatch(requiredOutputs))
|
||||
{
|
||||
ISpecklePropertyGoo value = propGoo.Value[key];
|
||||
object? outputValue = value is SpecklePropertyGoo prop
|
||||
? prop.Value
|
||||
: value is SpecklePropertyGroupGoo propGroup
|
||||
? propGroup
|
||||
: value;
|
||||
|
||||
OutputParamWrapper output =
|
||||
outputValue is IList
|
||||
? CreateOutputParamByKeyValue(key, outputValue, GH_ParamAccess.list)
|
||||
: CreateOutputParamByKeyValue(key, outputValue, GH_ParamAccess.item);
|
||||
outputParams.Add(output);
|
||||
OnPingDocument()?.ScheduleSolution(5, _ => CreateOutputs(requiredOutputs));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Type cannot be deconstructed: {data.GetType().Name}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// process current object normally
|
||||
object data = new();
|
||||
if (!da.GetData(0, ref data))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var outputParams = DeconstructObject(data);
|
||||
if (outputParams == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// set component name based on the current object
|
||||
NickName = Name;
|
||||
|
||||
if (da.Iteration == 0 && OutputMismatch(outputParams))
|
||||
// set output data - fill missing fields with nulls for objects that don't have all fields
|
||||
SetOutputData(da, outputParams);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Discovers all unique field names from all input objects by looking at volatile data directly.
|
||||
/// </summary>
|
||||
private HashSet<string> DiscoverAllFieldsFromInput()
|
||||
{
|
||||
HashSet<string> allFields = new();
|
||||
|
||||
foreach (var item in Params.Input[0].VolatileData.AllData(true))
|
||||
{
|
||||
OnPingDocument()
|
||||
.ScheduleSolution(
|
||||
5,
|
||||
_ =>
|
||||
{
|
||||
CreateOutputs(outputParams);
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < outputParams.Count; i++)
|
||||
var objectOutputs = DeconstructObject(item);
|
||||
if (objectOutputs != null)
|
||||
{
|
||||
var outParam = Params.Output[i];
|
||||
var outParamWrapper = outputParams[i];
|
||||
switch (outParam.Access)
|
||||
foreach (var output in objectOutputs)
|
||||
{
|
||||
case GH_ParamAccess.item:
|
||||
da.SetData(i, outParamWrapper.Value);
|
||||
break;
|
||||
case GH_ParamAccess.list:
|
||||
da.SetDataList(i, outParamWrapper.Value as IList);
|
||||
break;
|
||||
allFields.Add(output.Param.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allFields;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates output parameter wrappers from a set of field names, all with item access.
|
||||
/// </summary>
|
||||
private List<OutputParamWrapper> CreateOutputParamsFromFieldNames(HashSet<string> fieldNames) =>
|
||||
fieldNames
|
||||
.OrderBy(name => name)
|
||||
.Select(fieldName => CreateOutputParamByKeyValue(fieldName, null, GH_ParamAccess.item))
|
||||
.ToList();
|
||||
|
||||
/// <summary>
|
||||
/// Deconstructs a single object into its constituent fields/properties.
|
||||
/// </summary>
|
||||
private List<OutputParamWrapper>? DeconstructObject(object data) =>
|
||||
data switch
|
||||
{
|
||||
// get children elements from wrapper to override elements prop while parsing
|
||||
SpeckleCollectionWrapperGoo collectionGoo when collectionGoo.Value != null
|
||||
=> ParseSpeckleWrapper(
|
||||
collectionGoo.Value,
|
||||
collectionGoo.Value.Elements.Select(o => ((SpeckleWrapper)o).CreateGoo()).ToList()
|
||||
),
|
||||
|
||||
// get geometries from wrapper to override displayValue prop while parsing
|
||||
SpeckleDataObjectWrapperGoo dataObjectGoo when dataObjectGoo.Value != null
|
||||
=> ParseSpeckleWrapper(
|
||||
dataObjectGoo.Value,
|
||||
null,
|
||||
dataObjectGoo.Value.Geometries.Select(o => o.CreateGoo()).ToList()
|
||||
),
|
||||
|
||||
SpeckleGeometryWrapperGoo objectGoo when objectGoo.Value != null => ParseSpeckleWrapper(objectGoo.Value),
|
||||
|
||||
SpeckleBlockInstanceWrapperGoo blockInstanceGoo when blockInstanceGoo.Value != null
|
||||
=> ParseSpeckleWrapper(blockInstanceGoo.Value),
|
||||
|
||||
SpeckleBlockDefinitionWrapperGoo blockDef when blockDef.Value != null => ParseSpeckleWrapper(blockDef.Value),
|
||||
|
||||
SpeckleMaterialWrapperGoo materialGoo when materialGoo.Value != null => ParseSpeckleWrapper(materialGoo.Value),
|
||||
|
||||
SpecklePropertyGroupGoo propGoo when propGoo.Value != null => ParsePropertyGroup(propGoo),
|
||||
|
||||
_ => HandleUnsupportedType(data)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Handles SpecklePropertyGroupGoo objects by extracting their key-value pairs.
|
||||
/// </summary>
|
||||
private List<OutputParamWrapper> ParsePropertyGroup(SpecklePropertyGroupGoo propGoo)
|
||||
{
|
||||
Name = $"properties ({propGoo.Value.Count})";
|
||||
List<OutputParamWrapper> objectOutputs = new();
|
||||
|
||||
foreach (var key in propGoo.Value.Keys)
|
||||
{
|
||||
ISpecklePropertyGoo value = propGoo.Value[key];
|
||||
object? outputValue = value switch
|
||||
{
|
||||
SpecklePropertyGoo prop => prop.Value,
|
||||
SpecklePropertyGroupGoo propGroup => propGroup,
|
||||
_ => value
|
||||
};
|
||||
objectOutputs.Add(CreateOutputParamByKeyValue(key, outputValue, GH_ParamAccess.item));
|
||||
}
|
||||
|
||||
return objectOutputs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles unsupported object types by logging an error and returning null.
|
||||
/// </summary>
|
||||
private List<OutputParamWrapper>? HandleUnsupportedType(object data)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Type cannot be deconstructed: {data.GetType().Name}");
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets output data for the current iteration, filling missing fields with null values.
|
||||
/// Uses a lookup dictionary for efficient field matching.
|
||||
/// </summary>
|
||||
private void SetOutputData(IGH_DataAccess da, List<OutputParamWrapper> currentOutputs)
|
||||
{
|
||||
if (Params.Output.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// create a lookup for current outputs by field name
|
||||
var outputLookup = currentOutputs.ToDictionary(o => o.Param.Name, o => o.Value);
|
||||
|
||||
// set data for each output parameter
|
||||
for (int i = 0; i < Params.Output.Count; i++)
|
||||
{
|
||||
var outputParam = Params.Output[i];
|
||||
|
||||
// set the value if it exists, otherwise set null
|
||||
object? value = outputLookup.TryGetValue(outputParam.Name, out var fieldValue) ? fieldValue : null;
|
||||
|
||||
switch (outputParam.Access)
|
||||
{
|
||||
case GH_ParamAccess.item:
|
||||
da.SetData(i, value);
|
||||
break;
|
||||
case GH_ParamAccess.list:
|
||||
da.SetDataList(i, value as IList ?? new List<object?>());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<OutputParamWrapper> ParseSpeckleWrapper(
|
||||
@@ -146,125 +228,146 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
|
||||
return result;
|
||||
}
|
||||
|
||||
// cycle through base props
|
||||
// process each property of the Base object
|
||||
foreach (var prop in @base.GetMembers(DynamicBaseMemberType.Instance | DynamicBaseMemberType.Dynamic))
|
||||
{
|
||||
// Convert and add to corresponding output structure
|
||||
var value = prop.Value;
|
||||
switch (value)
|
||||
// skip internal dynamic property keys
|
||||
if (prop.Key == nameof(Base.DynamicPropertyKeys))
|
||||
{
|
||||
case null:
|
||||
result.Add(CreateOutputParamByKeyValue(prop.Key, null, GH_ParamAccess.item));
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
case IList list:
|
||||
List<object> nativeObjects = new();
|
||||
|
||||
// override list value if base is a collection and this is the elements prop, since this is empty if coming from a collectionwrapper
|
||||
if (@base is Collection && prop.Key == "elements" && elements != null)
|
||||
{
|
||||
list = elements;
|
||||
}
|
||||
|
||||
// override list value if base is a dataobject and this is the displayvalue prop, since this is empty if coming from a dataobject wrapper
|
||||
if (@base is Speckle.Objects.Data.DataObject && prop.Key == "displayValue" && displayValue != null)
|
||||
{
|
||||
list = displayValue;
|
||||
}
|
||||
|
||||
foreach (var x in list)
|
||||
{
|
||||
switch (x)
|
||||
{
|
||||
case SpeckleWrapper wrapper:
|
||||
nativeObjects.Add(wrapper.CreateGoo());
|
||||
break;
|
||||
|
||||
case Base xBase:
|
||||
nativeObjects.AddRange(ConvertOrCreateWrapper(xBase));
|
||||
break;
|
||||
|
||||
default:
|
||||
nativeObjects.Add(x);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result.Add(CreateOutputParamByKeyValue(prop.Key, nativeObjects, GH_ParamAccess.list));
|
||||
break;
|
||||
|
||||
case Dictionary<string, object?> dict: // this should be treated a properties dict
|
||||
SpecklePropertyGroupGoo propertyGoo = new();
|
||||
propertyGoo.CastFrom(dict);
|
||||
result.Add(CreateOutputParamByKeyValue(prop.Key, propertyGoo, GH_ParamAccess.item));
|
||||
break;
|
||||
|
||||
case SpeckleWrapper wrapper:
|
||||
result.Add(CreateOutputParamByKeyValue(prop.Key, wrapper.CreateGoo(), GH_ParamAccess.item));
|
||||
break;
|
||||
|
||||
case Base baseValue:
|
||||
result.Add(CreateOutputParamByKeyValue(prop.Key, ConvertOrCreateWrapper(baseValue), GH_ParamAccess.list));
|
||||
break;
|
||||
|
||||
default:
|
||||
// we don't want to output dynamic property keys
|
||||
if (prop.Key == nameof(Base.DynamicPropertyKeys))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
result.Add(CreateOutputParamByKeyValue(prop.Key, prop.Value, GH_ParamAccess.item));
|
||||
break;
|
||||
var outputParam = CreateOutputParamForProperty(prop, @base, elements, displayValue);
|
||||
if (outputParam != null)
|
||||
{
|
||||
result.Add(outputParam);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an output parameter for a single property, handling different value types appropriately.
|
||||
/// </summary>
|
||||
private OutputParamWrapper CreateOutputParamForProperty(
|
||||
KeyValuePair<string, object?> prop,
|
||||
Base @base,
|
||||
List<IGH_Goo>? elements,
|
||||
List<IGH_Goo>? displayValue
|
||||
) =>
|
||||
prop.Value switch
|
||||
{
|
||||
null => CreateOutputParamByKeyValue(prop.Key, null, GH_ParamAccess.item),
|
||||
IList list => CreateListOutputParam(prop.Key, list, @base, elements, displayValue),
|
||||
Dictionary<string, object?> dict => CreateDictionaryOutputParam(prop.Key, dict),
|
||||
SpeckleWrapper wrapper => CreateOutputParamByKeyValue(prop.Key, wrapper.CreateGoo(), GH_ParamAccess.item),
|
||||
Base baseValue => CreateOutputParamByKeyValue(prop.Key, ConvertOrCreateWrapper(baseValue), GH_ParamAccess.list),
|
||||
_ => CreateOutputParamByKeyValue(prop.Key, prop.Value, GH_ParamAccess.item)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Creates an output parameter for list properties, with special handling for collection elements and display values.
|
||||
/// </summary>
|
||||
private OutputParamWrapper CreateListOutputParam(
|
||||
string key,
|
||||
IList list,
|
||||
Base @base,
|
||||
List<IGH_Goo>? elements,
|
||||
List<IGH_Goo>? displayValue
|
||||
)
|
||||
{
|
||||
// override list value for special cases
|
||||
IList actualList = key switch
|
||||
{
|
||||
"elements" when @base is Collection && elements != null => elements,
|
||||
"displayValue" when @base is Speckle.Objects.Data.DataObject && displayValue != null => displayValue,
|
||||
_ => list
|
||||
};
|
||||
|
||||
List<object> nativeObjects = new();
|
||||
foreach (var item in actualList)
|
||||
{
|
||||
switch (item)
|
||||
{
|
||||
case SpeckleWrapper wrapper:
|
||||
nativeObjects.Add(wrapper.CreateGoo());
|
||||
break;
|
||||
case Base baseItem:
|
||||
nativeObjects.AddRange(ConvertOrCreateWrapper(baseItem));
|
||||
break;
|
||||
default:
|
||||
nativeObjects.Add(item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return CreateOutputParamByKeyValue(key, nativeObjects, GH_ParamAccess.list);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an output parameter for dictionary properties, converting them to SpecklePropertyGroupGoo.
|
||||
/// </summary>
|
||||
private OutputParamWrapper CreateDictionaryOutputParam(string key, Dictionary<string, object?> dict)
|
||||
{
|
||||
SpecklePropertyGroupGoo propertyGoo = new();
|
||||
propertyGoo.CastFrom(dict);
|
||||
return CreateOutputParamByKeyValue(key, propertyGoo, GH_ParamAccess.item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Speckle Base object to host geometry or creates a wrapper if conversion fails.
|
||||
/// Returns a list of SpeckleGeometryWrapperGoo objects.
|
||||
/// </summary>
|
||||
private List<SpeckleGeometryWrapperGoo> ConvertOrCreateWrapper(Base @base)
|
||||
{
|
||||
try
|
||||
{
|
||||
// convert the base and create a wrapper for each result
|
||||
// attempt conversion to host geometry
|
||||
List<(object, Base)> convertedBase = SpeckleConversionContext.Current.ConvertToHost(@base);
|
||||
List<SpeckleGeometryWrapperGoo> convertedWrappers = new();
|
||||
foreach ((object o, Base b) in convertedBase)
|
||||
{
|
||||
GeometryBase? g = o as GeometryBase;
|
||||
SpeckleGeometryWrapper convertedWrapper =
|
||||
new()
|
||||
{
|
||||
Base = b,
|
||||
GeometryBase = g,
|
||||
Name = b["name"] as string ?? "",
|
||||
Color = null,
|
||||
Material = null
|
||||
};
|
||||
|
||||
convertedWrappers.Add(new(convertedWrapper));
|
||||
}
|
||||
|
||||
return convertedWrappers;
|
||||
return convertedBase.Select(CreateGeometryWrapper).ToList();
|
||||
}
|
||||
catch (ConversionException)
|
||||
{
|
||||
// some classes, like RawEncoding, have no direct conversion or fallback value.
|
||||
// when this is the case, wrap it to allow users to further expand the object.
|
||||
SpeckleGeometryWrapper convertedWrapper =
|
||||
new()
|
||||
{
|
||||
Base = @base,
|
||||
GeometryBase = null,
|
||||
Name = @base[Constants.NAME_PROP] as string ?? "",
|
||||
Color = null,
|
||||
Material = null
|
||||
};
|
||||
|
||||
return new() { new SpeckleGeometryWrapperGoo(convertedWrapper) };
|
||||
// fallback: create wrapper without conversion for objects that can't be converted
|
||||
return new List<SpeckleGeometryWrapperGoo> { CreateFallbackWrapper(@base) };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a SpeckleGeometryWrapperGoo from a converted geometry and base object pair.
|
||||
/// </summary>
|
||||
private SpeckleGeometryWrapperGoo CreateGeometryWrapper((object geometry, Base @base) converted)
|
||||
{
|
||||
SpeckleGeometryWrapper wrapper =
|
||||
new()
|
||||
{
|
||||
Base = converted.@base,
|
||||
GeometryBase = converted.geometry as GeometryBase,
|
||||
Name = converted.@base["name"] as string ?? "",
|
||||
Color = null,
|
||||
Material = null
|
||||
};
|
||||
return new SpeckleGeometryWrapperGoo(wrapper);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a fallback wrapper for Base objects that cannot be converted to host geometry.
|
||||
/// </summary>
|
||||
private SpeckleGeometryWrapperGoo CreateFallbackWrapper(Base @base)
|
||||
{
|
||||
SpeckleGeometryWrapper wrapper =
|
||||
new()
|
||||
{
|
||||
Base = @base,
|
||||
GeometryBase = null,
|
||||
Name = @base[Constants.NAME_PROP] as string ?? "",
|
||||
Color = null,
|
||||
Material = null
|
||||
};
|
||||
return new SpeckleGeometryWrapperGoo(wrapper);
|
||||
}
|
||||
|
||||
private OutputParamWrapper CreateOutputParamByKeyValue(string key, object? value, GH_ParamAccess access)
|
||||
{
|
||||
Param_GenericObject param =
|
||||
@@ -297,19 +400,17 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
|
||||
return myParam;
|
||||
}
|
||||
|
||||
public bool DestroyParameter(GH_ParameterSide side, int index)
|
||||
{
|
||||
return side == GH_ParameterSide.Output;
|
||||
}
|
||||
public bool DestroyParameter(GH_ParameterSide side, int index) => side == GH_ParameterSide.Output;
|
||||
|
||||
private void CreateOutputs(List<OutputParamWrapper> outputParams)
|
||||
{
|
||||
// TODO: better, nicer handling of creation/removal
|
||||
// remove all existing output parameters
|
||||
while (Params.Output.Count > 0)
|
||||
{
|
||||
Params.UnregisterOutputParameter(Params.Output[^1]);
|
||||
}
|
||||
|
||||
// add new output parameters
|
||||
foreach (var newParam in outputParams)
|
||||
{
|
||||
var param = new Param_GenericObject
|
||||
@@ -322,11 +423,15 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
|
||||
Params.RegisterOutputParam(param);
|
||||
}
|
||||
|
||||
// notify Grasshopper of parameter changes
|
||||
Params.OnParametersChanged();
|
||||
VariableParameterMaintenance();
|
||||
ExpireSolution(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the current output parameter structure differs from the required structure.
|
||||
/// </summary>
|
||||
private bool OutputMismatch(List<OutputParamWrapper> outputParams)
|
||||
{
|
||||
if (Params.Output.Count != outputParams.Count)
|
||||
@@ -334,10 +439,10 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
|
||||
return true;
|
||||
}
|
||||
|
||||
var count = 0;
|
||||
foreach (var newParam in outputParams)
|
||||
for (int i = 0; i < outputParams.Count; i++)
|
||||
{
|
||||
var oldParam = Params.Output[count];
|
||||
var newParam = outputParams[i];
|
||||
var oldParam = Params.Output[i];
|
||||
if (
|
||||
oldParam.NickName != newParam.Param.NickName
|
||||
|| oldParam.Name != newParam.Param.Name
|
||||
@@ -346,7 +451,6 @@ public class DeconstructSpeckleParam : GH_Component, IGH_VariableParameterCompon
|
||||
{
|
||||
return true;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
+39
-9
@@ -22,21 +22,51 @@ public partial class SpecklePropertyGroupGoo : GH_Goo<Dictionary<string, ISpeckl
|
||||
return CastFromModelObject(modelObject.UserText);
|
||||
|
||||
case ModelUserText userText:
|
||||
Dictionary<string, ISpecklePropertyGoo> dictionary = new();
|
||||
foreach (KeyValuePair<string, string> entry in userText)
|
||||
{
|
||||
SpecklePropertyGoo value = new() { Value = entry.Value };
|
||||
dictionary.Add(entry.Key, value);
|
||||
}
|
||||
|
||||
Value = dictionary;
|
||||
return true;
|
||||
var processedDictionary = ConvertToNested(userText.ToDictionary(o => o.Key, o => (object)o.Value));
|
||||
return CastFrom(processedDictionary);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Property keys may already be concatenated with the `.` char, eg if baked from grasshopper.
|
||||
public Dictionary<string, object> ConvertToNested(Dictionary<string, object> flatDict)
|
||||
{
|
||||
var nestedDict = new Dictionary<string, object>();
|
||||
|
||||
foreach (string keyPath in flatDict.Keys)
|
||||
{
|
||||
var keys = keyPath.Split('.');
|
||||
var current = nestedDict;
|
||||
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
{
|
||||
var key = keys[i];
|
||||
|
||||
if (i == keys.Length - 1)
|
||||
{
|
||||
current[key] = flatDict[keyPath];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!current.TryGetValue(key, out var next))
|
||||
{
|
||||
var newDict = new Dictionary<string, object>();
|
||||
current[key] = newDict;
|
||||
current = newDict;
|
||||
}
|
||||
else
|
||||
{
|
||||
current = (Dictionary<string, object>)next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nestedDict;
|
||||
}
|
||||
|
||||
private bool CastToModelObject<T>(ref T target)
|
||||
{
|
||||
var type = typeof(T);
|
||||
|
||||
+11
-6
@@ -35,19 +35,20 @@ public partial class SpeckleGeometryWrapperGoo : GH_Goo<SpeckleGeometryWrapper>,
|
||||
|
||||
private bool HandleModelObject(ModelObject modelObject)
|
||||
{
|
||||
modelObject.CastTo<GeometryBase>(out GeometryBase? geometryBase);
|
||||
if (geometryBase is null)
|
||||
modelObject.CastTo<IGH_GeometricGoo>(out IGH_GeometricGoo? geometryGoo);
|
||||
if (geometryGoo is null)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not retrieve geometry from model object.");
|
||||
}
|
||||
|
||||
GeometryBase geometryBase = geometryGoo.ToGeometryBase();
|
||||
Base converted = SpeckleConversionContext.Current.ConvertToSpeckle(geometryBase);
|
||||
|
||||
// get layer, props, color, and mat
|
||||
SpeckleCollectionWrapper? collection = GetLayerCollectionFromModelObject(modelObject);
|
||||
SpecklePropertyGroupGoo? props = GetPropsFromModelObjectAndAssignToBase(modelObject, converted);
|
||||
Color? color = GetColorFromModelObject(modelObject);
|
||||
SpeckleMaterialWrapper? material = GetMaterialFromModelObject(modelObject);
|
||||
Color? color = GetColorFromModelObject(modelObject, material);
|
||||
|
||||
// get the definition if this is an instance
|
||||
SpeckleBlockDefinitionWrapper? definition = GetBlockDefinition(geometryBase);
|
||||
@@ -189,6 +190,7 @@ public partial class SpeckleGeometryWrapperGoo : GH_Goo<SpeckleGeometryWrapper>,
|
||||
Transform = instance.Xform,
|
||||
Definition = definition, // May be null in pure Grasshopper workflows
|
||||
Parent = parent,
|
||||
Path = parent?.Path ?? new(),
|
||||
Name = name,
|
||||
Color = color,
|
||||
Material = mat,
|
||||
@@ -200,6 +202,7 @@ public partial class SpeckleGeometryWrapperGoo : GH_Goo<SpeckleGeometryWrapper>,
|
||||
GeometryBase = geometryBase,
|
||||
Base = @base,
|
||||
Parent = parent,
|
||||
Path = parent?.Path ?? new(),
|
||||
Name = name,
|
||||
Color = color,
|
||||
Material = mat,
|
||||
@@ -268,7 +271,7 @@ public partial class SpeckleGeometryWrapperGoo : GH_Goo<SpeckleGeometryWrapper>,
|
||||
return null;
|
||||
}
|
||||
|
||||
private Color? GetColorFromModelObject(ModelObject modelObject)
|
||||
private Color? GetColorFromModelObject(ModelObject modelObject, SpeckleMaterialWrapper? material)
|
||||
{
|
||||
// we need to retrieve the actual color by the color source (otherwise will return default color for anything other than by object)
|
||||
int? argb = null;
|
||||
@@ -281,8 +284,10 @@ public partial class SpeckleGeometryWrapperGoo : GH_Goo<SpeckleGeometryWrapper>,
|
||||
argb = modelObject.Display.Color?.Color.ToArgb();
|
||||
break;
|
||||
case ObjectColorSource.ColorFromMaterial:
|
||||
Rhino.Render.RenderMaterial? mat = GetRenderMaterial(modelObject);
|
||||
argb = mat?.ToMaterial(Rhino.Render.RenderTexture.TextureGeneration.Skip)?.DiffuseColor.ToArgb();
|
||||
if (material is not null)
|
||||
{
|
||||
argb = material.Material.diffuse;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -347,7 +347,6 @@
|
||||
"speckle.converters.rhino8": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"RhinoCommon": "[8.9.24194.18121, )",
|
||||
"Speckle.Converters.Common": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
+42
@@ -0,0 +1,42 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<!--This csproj is NOT designed to be run as a rhino plugin-->
|
||||
<!--This csproj is NOT the rhino importer-->
|
||||
<!--This csproj is the connector code used by the Speckle.Importers.Rhino project-->
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<Configurations>Debug;Release;Local</Configurations>
|
||||
<RhinoVersion>8</RhinoVersion>
|
||||
<DefineConstants>$(DefineConstants);RHINO8;RHINO7_OR_GREATER;RHINO8_OR_GREATER</DefineConstants>
|
||||
<TargetExt>.dll</TargetExt>
|
||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||
<UseWpf>true</UseWpf>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<GenerateResourceUsePreserializedResources>true</GenerateResourceUsePreserializedResources>
|
||||
|
||||
<!-- .NET Core uses this to move native dependencies into a root for runtime selection and usage for non-windows development https://learn.microsoft.com/en-us/dotnet/core/project-sdk/msbuild-props#enablewindowstargeting -->
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <!--This is needed for managed dependencies-->
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <!--This is needed for the rest-->
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.21.25188.17001"/>
|
||||
<PackageReference Include="RhinoWindows" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.21.25188.17001"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\Converters\Rhino\Speckle.Converters.Rhino8\Speckle.Converters.Rhino8.csproj" />
|
||||
<ProjectReference Include="..\..\..\Sdk\Speckle.Connectors.Common\Speckle.Connectors.Common.csproj" />
|
||||
<ProjectReference Include="..\..\..\DUI3\Speckle.Connectors.DUI.WebView\Speckle.Connectors.DUI.WebView.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="EmbeddedResources\**\*" />
|
||||
<EmbeddedResource Include="Resources\**\*.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\Speckle.Connectors.RhinoShared\Speckle.Connectors.RhinoShared.projitems" Label="Shared" />
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\Toolbars\Speckle.Connectors.Rhino.rui" Link="$(AssemblyName).rui" CopyToOutputDirectory="PreserveNewest"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -235,9 +235,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.1-rc.1, )",
|
||||
"Speckle.Sdk": "[3.5.1-rc.1, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.1-rc.1, )"
|
||||
"Speckle.Objects": "[3.5.1, )",
|
||||
"Speckle.Sdk": "[3.5.1, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -261,7 +261,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.1-rc.1, )"
|
||||
"Speckle.Objects": "[3.5.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.rhino8": {
|
||||
@@ -311,18 +311,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.1-rc.1, )",
|
||||
"resolved": "3.5.1-rc.1",
|
||||
"contentHash": "kkHlYdOdTuky7BvyU3fRayS5P/3E3EDQ6yBbwWqQqjGN3c/fozqI+20Xm87KJrkMb/9XpMfQxMfZX1n4UN80sA==",
|
||||
"requested": "[3.5.1, )",
|
||||
"resolved": "3.5.1",
|
||||
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.1-rc.1"
|
||||
"Speckle.Sdk": "3.5.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.1-rc.1, )",
|
||||
"resolved": "3.5.1-rc.1",
|
||||
"contentHash": "ZyNQI1IyWSAQ4Xc72v0WOeXon9BwtpIiw6wCIT7GrRjymSe5jXW/qMpG3YpBMccGPljCDZJFDmd87fKnWNRt3w==",
|
||||
"requested": "[3.5.1, )",
|
||||
"resolved": "3.5.1",
|
||||
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
@@ -330,14 +330,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.1-rc.1"
|
||||
"Speckle.Sdk.Dependencies": "3.5.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.1-rc.1, )",
|
||||
"resolved": "3.5.1-rc.1",
|
||||
"contentHash": "zDaocTPBFl2M5NkMsE9VqTLgbY7wy/MrE21JMBqOOcxHVtwDNOlSY9npBWDy8aAUEl6eJEKPoQr2B3Zd/RmJ9g=="
|
||||
"requested": "[3.5.1, )",
|
||||
"resolved": "3.5.1",
|
||||
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
|
||||
}
|
||||
},
|
||||
"net8.0-windows7.0/win-x64": {
|
||||
|
||||
+18
@@ -78,6 +78,24 @@ public class LocalToGlobalToDirectShapeConverter
|
||||
.DirectShapeLibrary.GetDirectShapeLibrary(_converterSettings.Current.Document)
|
||||
.FindDefinition(target.atomicObject.applicationId ?? target.atomicObject.id.NotNull());
|
||||
result.SetShape(def);
|
||||
|
||||
// add snapping references for meshes and curves
|
||||
foreach (var shape in def)
|
||||
{
|
||||
switch (shape)
|
||||
{
|
||||
case DB.Mesh m:
|
||||
foreach (var v in m.Vertices)
|
||||
{
|
||||
result.AddReferencePoint(v);
|
||||
}
|
||||
break;
|
||||
case DB.Curve c:
|
||||
result.AddReferenceCurve(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result; // note fast exit here
|
||||
}
|
||||
|
||||
|
||||
+4
-5
@@ -22,15 +22,14 @@ public class ClassPropertiesExtractor
|
||||
{
|
||||
Dictionary<string, object?> elementPropertiesDict = ExtractElementProperties(element);
|
||||
|
||||
// add type specific props not included in parameters.
|
||||
// so far, no extra props are needed
|
||||
/*
|
||||
// type specific properties
|
||||
switch (element)
|
||||
{
|
||||
default:
|
||||
// area scheme for area elements
|
||||
case DB.Area area:
|
||||
elementPropertiesDict.Add("areaScheme", area.AreaScheme?.Name);
|
||||
break;
|
||||
}
|
||||
*/
|
||||
|
||||
return elementPropertiesDict;
|
||||
}
|
||||
|
||||
+152
-42
@@ -44,9 +44,27 @@ public class MaterialQuantitiesToSpeckleLite : ITypedConverter<DB.Element, Dicti
|
||||
public Dictionary<string, object> Convert(DB.Element target)
|
||||
{
|
||||
Dictionary<string, object> quantities = new();
|
||||
if (target.Category?.HasMaterialQuantities ?? false) //category can be null
|
||||
switch (target)
|
||||
{
|
||||
foreach (DB.ElementId? matId in target.GetMaterialIds(false))
|
||||
case DBA.Railing railing:
|
||||
// railings can have subelements including top rails, hand rails, and balusters.
|
||||
// they also do *not* have any materials associated with their category.
|
||||
List<DB.ElementId> railingElementIds = [railing.GetTypeId(), railing.TopRail, .. railing.GetHandRails()];
|
||||
ProcessMaterialsByElementTypes(railingElementIds, quantities);
|
||||
break;
|
||||
default:
|
||||
ProcessMaterialsByCategory(target, quantities);
|
||||
break;
|
||||
}
|
||||
|
||||
return quantities;
|
||||
}
|
||||
|
||||
private void ProcessMaterialsByCategory(DB.Element element, Dictionary<string, object> quantities)
|
||||
{
|
||||
if (element.Category?.HasMaterialQuantities ?? false) //category can be null
|
||||
{
|
||||
foreach (DB.ElementId? matId in element.GetMaterialIds(false))
|
||||
{
|
||||
if (matId is null)
|
||||
{
|
||||
@@ -56,11 +74,18 @@ public class MaterialQuantitiesToSpeckleLite : ITypedConverter<DB.Element, Dicti
|
||||
var materialQuantity = new Dictionary<string, object>();
|
||||
var unitSettings = _converterSettings.Current.Document.GetUnits();
|
||||
|
||||
// add material props
|
||||
if (TryAddMaterialPropertiesToQuantitiesDict(matId, materialQuantity, out string matName))
|
||||
{
|
||||
quantities[matName] = materialQuantity;
|
||||
}
|
||||
|
||||
// add area and volume props
|
||||
var areaUnitType = unitSettings.GetFormatOptions(DB.SpecTypeId.Area).GetUnitTypeId();
|
||||
AddMaterialProperty(
|
||||
materialQuantity,
|
||||
"area",
|
||||
_scalingService.Scale(target.GetMaterialArea(matId, false), areaUnitType),
|
||||
_scalingService.Scale(element.GetMaterialArea(matId, false), areaUnitType),
|
||||
areaUnitType
|
||||
);
|
||||
|
||||
@@ -68,57 +93,142 @@ public class MaterialQuantitiesToSpeckleLite : ITypedConverter<DB.Element, Dicti
|
||||
AddMaterialProperty(
|
||||
materialQuantity,
|
||||
"volume",
|
||||
_scalingService.Scale(target.GetMaterialVolume(matId), volumeUnitType),
|
||||
_scalingService.Scale(element.GetMaterialVolume(matId), volumeUnitType),
|
||||
volumeUnitType
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_converterSettings.Current.Document.GetElement(matId) is DB.Material material)
|
||||
private void ProcessMaterialsByElementTypes(List<DB.ElementId> elementIds, Dictionary<string, object> quantities)
|
||||
{
|
||||
Dictionary<DB.ElementId, double> matLengths = new(); // stores mat id to total length found for mat
|
||||
|
||||
foreach (DB.ElementId elementId in elementIds)
|
||||
{
|
||||
if (
|
||||
_converterSettings.Current.Document.GetElement(elementId) is DB.Element element
|
||||
&& _converterSettings.Current.Document.GetElement(element.GetTypeId()) is DB.ElementType elementType
|
||||
)
|
||||
{
|
||||
DB.ElementId elementMatId = DB.ElementId.InvalidElementId;
|
||||
|
||||
foreach (DB.Parameter param in elementType.Parameters)
|
||||
{
|
||||
materialQuantity["materialName"] = material.Name;
|
||||
materialQuantity["materialCategory"] = material.MaterialCategory;
|
||||
materialQuantity["materialClass"] = material.MaterialClass;
|
||||
|
||||
// get StructuralAssetId (or try to)
|
||||
DB.ElementId structuralAssetId = material.StructuralAssetId;
|
||||
if (structuralAssetId != DB.ElementId.InvalidElementId)
|
||||
DB.Definition def = param.Definition;
|
||||
if (param.StorageType == DB.StorageType.ElementId && def.GetDataType() == DB.SpecTypeId.Reference.Material)
|
||||
{
|
||||
StructuralAssetProperties structuralAssetProperties = _structuralAssetExtractor.TryGetProperties(
|
||||
structuralAssetId
|
||||
);
|
||||
elementMatId = param.AsElementId();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
materialQuantity["structuralAsset"] = structuralAssetProperties.Name;
|
||||
AddMaterialProperty(
|
||||
materialQuantity,
|
||||
"density",
|
||||
structuralAssetProperties.Density,
|
||||
structuralAssetProperties.DensityUnitId
|
||||
);
|
||||
|
||||
// more reliable way of determining material type (wood/concrete/type) as it uses Revit enum
|
||||
// materialClass, materialCategory etc. are user string inputs
|
||||
materialQuantity["materialType"] = structuralAssetProperties.MaterialType;
|
||||
|
||||
// Only add compressive strength for concrete materials (used by F+E for Automate)
|
||||
if (
|
||||
structuralAssetProperties.MaterialType == "Concrete"
|
||||
&& structuralAssetProperties.CompressiveStrength.HasValue
|
||||
)
|
||||
if (elementMatId != DB.ElementId.InvalidElementId)
|
||||
{
|
||||
// try get the length from the element
|
||||
foreach (DB.Parameter eParam in element.Parameters)
|
||||
{
|
||||
DB.Definition eParamDef = eParam.Definition;
|
||||
var forgeTypeId = eParamDef.GetDataType();
|
||||
if (forgeTypeId == DB.SpecTypeId.Length)
|
||||
{
|
||||
AddMaterialProperty(
|
||||
materialQuantity,
|
||||
"compressiveStrength",
|
||||
structuralAssetProperties.CompressiveStrength.Value,
|
||||
structuralAssetProperties.CompressiveStrengthUnitId!
|
||||
);
|
||||
double length = eParam.AsDouble();
|
||||
if (matLengths.TryGetValue(elementMatId, out double _))
|
||||
{
|
||||
matLengths[elementMatId] += length;
|
||||
}
|
||||
else
|
||||
{
|
||||
matLengths.Add(elementMatId, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
quantities[material.Name] = materialQuantity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return quantities;
|
||||
foreach (var entry in matLengths)
|
||||
{
|
||||
var materialQuantity = new Dictionary<string, object>();
|
||||
var unitSettings = _converterSettings.Current.Document.GetUnits();
|
||||
|
||||
// add material props
|
||||
if (TryAddMaterialPropertiesToQuantitiesDict(entry.Key, materialQuantity, out string matName))
|
||||
{
|
||||
quantities[matName] = materialQuantity;
|
||||
|
||||
// add length prop
|
||||
var lengthUnitType = unitSettings.GetFormatOptions(DB.SpecTypeId.Length).GetUnitTypeId();
|
||||
AddMaterialProperty(
|
||||
materialQuantity,
|
||||
"length",
|
||||
_scalingService.Scale(entry.Value, lengthUnitType),
|
||||
lengthUnitType
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the material properties (like name, category, and class) to the material quantity dictionary
|
||||
/// </summary>
|
||||
/// <param name="matId">the material id</param>
|
||||
/// <param name="materialQuantity"></param>
|
||||
/// <param name="matName"></param>
|
||||
/// <returns>true if material is found, false if not</returns>
|
||||
private bool TryAddMaterialPropertiesToQuantitiesDict(
|
||||
DB.ElementId matId,
|
||||
Dictionary<string, object> materialQuantity,
|
||||
out string matName
|
||||
)
|
||||
{
|
||||
matName = "";
|
||||
if (_converterSettings.Current.Document.GetElement(matId) is DB.Material material)
|
||||
{
|
||||
materialQuantity["materialName"] = material.Name;
|
||||
materialQuantity["materialCategory"] = material.MaterialCategory;
|
||||
materialQuantity["materialClass"] = material.MaterialClass;
|
||||
|
||||
// get StructuralAssetId (or try to)
|
||||
DB.ElementId structuralAssetId = material.StructuralAssetId;
|
||||
if (structuralAssetId != DB.ElementId.InvalidElementId)
|
||||
{
|
||||
StructuralAssetProperties structuralAssetProperties = _structuralAssetExtractor.TryGetProperties(
|
||||
structuralAssetId
|
||||
);
|
||||
|
||||
materialQuantity["structuralAsset"] = structuralAssetProperties.Name;
|
||||
AddMaterialProperty(
|
||||
materialQuantity,
|
||||
"density",
|
||||
structuralAssetProperties.Density,
|
||||
structuralAssetProperties.DensityUnitId
|
||||
);
|
||||
|
||||
// more reliable way of determining material type (wood/concrete/type) as it uses Revit enum
|
||||
// materialClass, materialCategory etc. are user string inputs
|
||||
materialQuantity["materialType"] = structuralAssetProperties.MaterialType;
|
||||
|
||||
// Only add compressive strength for concrete materials (used by F+E for Automate)
|
||||
if (
|
||||
structuralAssetProperties.MaterialType == "Concrete"
|
||||
&& structuralAssetProperties.CompressiveStrength.HasValue
|
||||
)
|
||||
{
|
||||
AddMaterialProperty(
|
||||
materialQuantity,
|
||||
"compressiveStrength",
|
||||
structuralAssetProperties.CompressiveStrength.Value,
|
||||
structuralAssetProperties.CompressiveStrengthUnitId!
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
matName = material.Name;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -129,7 +239,7 @@ public class MaterialQuantitiesToSpeckleLite : ITypedConverter<DB.Element, Dicti
|
||||
/// <param name="value">The numeric value of the property</param>
|
||||
/// <param name="unitId">The Forge type ID representing the units of the property</param>
|
||||
/// <remarks>
|
||||
/// Saves code when used repeatedbly. Etabs implements an extension method to dicts (see utils folder). May be worth exploring.
|
||||
/// Saves code when used repeatedly. Etabs implements an extension method to dicts (see utils folder). May be worth exploring.
|
||||
/// </remarks>
|
||||
private void AddMaterialProperty(
|
||||
Dictionary<string, object> materialQuantity,
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<TargetFrameworks>net48;net8.0</TargetFrameworks>
|
||||
<Configurations>Debug;Release;Local</Configurations>
|
||||
<DefineConstants>$(DefineConstants);RHINO8;RHINO7_OR_GREATER;RHINO8_OR_GREATER</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RhinoCommon" VersionOverride="8.9.24194.18121" IncludeAssets="compile;build"/>
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net48'">
|
||||
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' != 'net48'">
|
||||
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.21.25188.17001"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\Sdk\Speckle.Converters.Common\Speckle.Converters.Common.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
@@ -317,6 +317,288 @@
|
||||
"resolved": "3.5.1",
|
||||
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
|
||||
}
|
||||
},
|
||||
"net8.0": {
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.3, )",
|
||||
"resolved": "1.0.3",
|
||||
"contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3"
|
||||
}
|
||||
},
|
||||
"Microsoft.SourceLink.GitHub": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.Build.Tasks.Git": "8.0.0",
|
||||
"Microsoft.SourceLink.Common": "8.0.0"
|
||||
}
|
||||
},
|
||||
"PolySharp": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.14.1, )",
|
||||
"resolved": "1.14.1",
|
||||
"contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ=="
|
||||
},
|
||||
"RhinoCommon": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.21.25188.17001, )",
|
||||
"resolved": "8.21.25188.17001",
|
||||
"contentHash": "Wo6JXheyDBvilyIwDY6xZCQJC4170jzAlTSlMgh8yokUY+vYyCl4KJVXZofIOynNt/xx5wLqb2On5gZZekXR6w==",
|
||||
"dependencies": {
|
||||
"System.Drawing.Common": "7.0.0"
|
||||
}
|
||||
},
|
||||
"Speckle.InterfaceGenerator": {
|
||||
"type": "Direct",
|
||||
"requested": "[0.9.6, )",
|
||||
"resolved": "0.9.6",
|
||||
"contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w=="
|
||||
},
|
||||
"GraphQL.Client": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client.Abstractions": "6.0.0",
|
||||
"GraphQL.Client.Abstractions.Websocket": "6.0.0",
|
||||
"System.Reactive": "5.0.0"
|
||||
}
|
||||
},
|
||||
"GraphQL.Client.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Primitives": "6.0.0"
|
||||
}
|
||||
},
|
||||
"GraphQL.Client.Abstractions.Websocket": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client.Abstractions": "6.0.0"
|
||||
}
|
||||
},
|
||||
"GraphQL.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA=="
|
||||
},
|
||||
"Microsoft.Build.Tasks.Git": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
|
||||
},
|
||||
"Microsoft.Data.Sqlite": {
|
||||
"type": "Transitive",
|
||||
"resolved": "7.0.5",
|
||||
"contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==",
|
||||
"dependencies": {
|
||||
"Microsoft.Data.Sqlite.Core": "7.0.5",
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": "2.1.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.Data.Sqlite.Core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "7.0.5",
|
||||
"contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==",
|
||||
"dependencies": {
|
||||
"SQLitePCLRaw.core": "2.1.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Primitives": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Binder": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Options": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Primitives": "2.2.0",
|
||||
"System.ComponentModel.Annotations": "4.5.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==",
|
||||
"dependencies": {
|
||||
"System.Memory": "4.5.1",
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net461": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.3",
|
||||
"contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA=="
|
||||
},
|
||||
"Microsoft.SourceLink.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
|
||||
},
|
||||
"Microsoft.Win32.SystemEvents": {
|
||||
"type": "Transitive",
|
||||
"resolved": "7.0.0",
|
||||
"contentHash": "2nXPrhdAyAzir0gLl8Yy8S5Mnm/uBSQQA7jEsILOS1MTyS7DbmV1NgViMtvV1sfCD1ebITpNwb1NIinKeJgUVQ=="
|
||||
},
|
||||
"Speckle.Newtonsoft.Json": {
|
||||
"type": "Transitive",
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
"contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==",
|
||||
"dependencies": {
|
||||
"SQLitePCLRaw.lib.e_sqlite3": "2.1.4",
|
||||
"SQLitePCLRaw.provider.e_sqlite3": "2.1.4"
|
||||
}
|
||||
},
|
||||
"SQLitePCLRaw.core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
"contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==",
|
||||
"dependencies": {
|
||||
"System.Memory": "4.5.3"
|
||||
}
|
||||
},
|
||||
"SQLitePCLRaw.lib.e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
"contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg=="
|
||||
},
|
||||
"SQLitePCLRaw.provider.e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
"contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==",
|
||||
"dependencies": {
|
||||
"SQLitePCLRaw.core": "2.1.4"
|
||||
}
|
||||
},
|
||||
"System.ComponentModel.Annotations": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.0",
|
||||
"contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg=="
|
||||
},
|
||||
"System.Drawing.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "7.0.0",
|
||||
"contentHash": "KIX+oBU38pxkKPxvLcLfIkOV5Ien8ReN78wro7OF5/erwcmortzeFx+iBswlh2Vz6gVne0khocQudGwaO1Ey6A==",
|
||||
"dependencies": {
|
||||
"Microsoft.Win32.SystemEvents": "7.0.0"
|
||||
}
|
||||
},
|
||||
"System.Memory": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.3",
|
||||
"contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA=="
|
||||
},
|
||||
"System.Reactive": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ=="
|
||||
},
|
||||
"System.Runtime.CompilerServices.Unsafe": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.1",
|
||||
"contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw=="
|
||||
},
|
||||
"speckle.converters.common": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.1, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Binder": "2.2.0",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Options": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A=="
|
||||
},
|
||||
"Speckle.DoubleNumerics": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[4.1.0, )",
|
||||
"resolved": "4.1.0",
|
||||
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.1, )",
|
||||
"resolved": "3.5.1",
|
||||
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.1, )",
|
||||
"resolved": "3.5.1",
|
||||
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.1, )",
|
||||
"resolved": "3.5.1",
|
||||
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
namespace Speckle.Converters.Rhino.Extensions;
|
||||
|
||||
public static class GeometryBaseExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Getting translation vector from origin to the Geometry bbox Center (if geometry is far from origin and translation needed)
|
||||
/// This is needed for some objects, because of Rhino using single precision numbers for Mesh vertices: https://wiki.mcneel.com/rhino/farfromorigin
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// Vector from origin to Geometry bbox center (if translation needed), otherwise zero-length vector.
|
||||
/// </returns>
|
||||
public static bool IsFarFromOrigin(this RG.GeometryBase geometry, out RG.Vector3d vectorToGeometry)
|
||||
{
|
||||
var geometryBbox = geometry.GetBoundingBox(false); // 'false' for 'accurate' parameter to accelerate bbox calculation
|
||||
if (geometryBbox.Min.DistanceTo(RG.Point3d.Origin) > 1e5 || geometryBbox.Max.DistanceTo(RG.Point3d.Origin) > 1e5)
|
||||
{
|
||||
vectorToGeometry = new RG.Vector3d(geometryBbox.Center);
|
||||
return true;
|
||||
}
|
||||
|
||||
vectorToGeometry = new RG.Vector3d();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,8 @@
|
||||
using Rhino;
|
||||
using Rhino;
|
||||
|
||||
namespace Speckle.Converters.Rhino;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the settings used for Rhino and Grasshopper conversions.
|
||||
/// </summary>
|
||||
public record RhinoConversionSettings(
|
||||
RhinoDoc Document,
|
||||
string SpeckleUnits,
|
||||
bool ModelFarFromOrigin,
|
||||
bool AddVisualizationProperties
|
||||
);
|
||||
public record RhinoConversionSettings(RhinoDoc Document, string SpeckleUnits, bool AddVisualizationProperties);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Rhino;
|
||||
using Rhino;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.InterfaceGenerator;
|
||||
|
||||
@@ -13,24 +13,5 @@ public class RhinoConversionSettingsFactory(
|
||||
public RhinoConversionSettings Current => settingsStore.Current;
|
||||
|
||||
public RhinoConversionSettings Create(RhinoDoc document, bool addVisualizationProperties) =>
|
||||
new(
|
||||
document,
|
||||
unitsConverter.ConvertOrThrow(RhinoDoc.ActiveDoc.ModelUnitSystem),
|
||||
ModelFarFromOrigin(),
|
||||
addVisualizationProperties
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Quick check whether any of the objects in the scene might be located too far from origin and cause precision issues during meshing.
|
||||
/// It prevents 'normal' Rhino models (not too far from origin) from unnecessary Bbox calculations on every object on Send.
|
||||
/// </summary>
|
||||
private bool ModelFarFromOrigin()
|
||||
{
|
||||
RG.BoundingBox bbox = RhinoDoc.ActiveDoc.Objects.BoundingBox;
|
||||
if (bbox.Min.DistanceTo(RG.Point3d.Origin) > 1e5 || bbox.Max.DistanceTo(RG.Point3d.Origin) > 1e5)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
new(document, unitsConverter.ConvertOrThrow(RhinoDoc.ActiveDoc.ModelUnitSystem), addVisualizationProperties);
|
||||
}
|
||||
|
||||
+14
-61
@@ -1,14 +1,13 @@
|
||||
using Rhino;
|
||||
using Rhino.DocObjects;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Converters.Rhino.Extensions;
|
||||
using Speckle.DoubleNumerics;
|
||||
using Speckle.Sdk.Common.Exceptions;
|
||||
|
||||
namespace Speckle.Converters.Rhino.ToSpeckle.Meshing;
|
||||
|
||||
public static class DisplayMeshExtractor
|
||||
{
|
||||
public static RG.Mesh GetDisplayMesh(RhinoObject obj)
|
||||
public static RG.Mesh GetDisplayMesh(RhinoObject obj, RhinoDoc doc)
|
||||
{
|
||||
// note: unsure this is nice, we get bigger meshes - we should to benchmark (conversion time vs size tradeoffs)
|
||||
var joinedMesh = new RG.Mesh();
|
||||
@@ -23,15 +22,15 @@ public static class DisplayMeshExtractor
|
||||
switch (obj)
|
||||
{
|
||||
case BrepObject brep:
|
||||
joinedMesh.Append(GetGeometryDisplayMesh(brep.BrepGeometry));
|
||||
joinedMesh.Append(GetGeometryDisplayMesh(brep.BrepGeometry, doc));
|
||||
break;
|
||||
case ExtrusionObject extrusion:
|
||||
joinedMesh.Append(GetGeometryDisplayMesh(extrusion.ExtrusionGeometry.ToBrep()));
|
||||
joinedMesh.Append(GetGeometryDisplayMesh(extrusion.ExtrusionGeometry.ToBrep(), doc));
|
||||
break;
|
||||
case SubDObject subDObject:
|
||||
if (subDObject.Geometry is RG.SubD subdGeometry)
|
||||
{
|
||||
joinedMesh.Append(GetGeometryDisplayMesh(subdGeometry));
|
||||
joinedMesh.Append(GetGeometryDisplayMesh(subdGeometry, doc));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -49,18 +48,16 @@ public static class DisplayMeshExtractor
|
||||
/// <summary>
|
||||
/// Extracting Rhino Mesh from Rhino GeometryBase using specified MeshingParameters settings, e.g. minimumEdgeLength.
|
||||
/// </summary>
|
||||
public static RG.Mesh GetGeometryDisplayMesh(RG.GeometryBase geometry, bool highPrecision = false)
|
||||
public static RG.Mesh GetGeometryDisplayMesh(RG.GeometryBase geometry, RhinoDoc doc)
|
||||
{
|
||||
double minEdgeLength = highPrecision ? GetAccurateMinEdgeLegth(geometry) : 0.05;
|
||||
|
||||
// declare "renderMeshes" as a separate var, because it needs to be checked for null after each Mesh.Create method
|
||||
RG.Mesh[] renderMeshes;
|
||||
var joinedMesh = new RG.Mesh();
|
||||
|
||||
RG.MeshingParameters meshParams = RG.MeshingParameters.DocumentCurrentSetting(doc);
|
||||
switch (geometry)
|
||||
{
|
||||
case RG.Brep brep:
|
||||
renderMeshes = RG.Mesh.CreateFromBrep(brep, new(0.05, minEdgeLength));
|
||||
renderMeshes = RG.Mesh.CreateFromBrep(brep, meshParams);
|
||||
break;
|
||||
case RG.SubD subd:
|
||||
#pragma warning disable CA2000
|
||||
@@ -69,7 +66,7 @@ public static class DisplayMeshExtractor
|
||||
renderMeshes = [subdMesh];
|
||||
break;
|
||||
case RG.Extrusion extrusion:
|
||||
renderMeshes = RG.Mesh.CreateFromBrep(extrusion.ToBrep(), new(0.05, minEdgeLength));
|
||||
renderMeshes = RG.Mesh.CreateFromBrep(extrusion.ToBrep(), meshParams);
|
||||
break;
|
||||
default:
|
||||
throw new ConversionException($"Unsupported object for display mesh generation {geometry.GetType().FullName}");
|
||||
@@ -93,61 +90,17 @@ public static class DisplayMeshExtractor
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculating optimal meshing parameter 'minimumEdgeLength' for the given geometry.
|
||||
/// </summary>
|
||||
private static double GetAccurateMinEdgeLegth(RG.GeometryBase geometry)
|
||||
{
|
||||
// adjust meshing parameters if Brep edges are too close to the document tolerance
|
||||
double minEdgeLength = 0.05;
|
||||
if (geometry is RG.Brep brep && brep.Edges.Any(x => x.GetLength() < minEdgeLength))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return minEdgeLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracting Rhino Mesh and converting to Speckle with the most suitable settings (e.g. moving to origin first, if needed)
|
||||
/// This is needed because of Rhino using single precision numbers for Mesh vertices: https://wiki.mcneel.com/rhino/farfromorigin
|
||||
/// Extracting Rhino Mesh and converting to Speckle with the most suitable settings
|
||||
/// </summary>
|
||||
/// <returns>List of converted Speckle meshes</returns>
|
||||
public static List<SOG.Mesh> GetSpeckleMeshes(
|
||||
RG.GeometryBase geometry,
|
||||
bool modelFarFromOrigin,
|
||||
string units,
|
||||
ITypedConverter<RG.Mesh, SOG.Mesh> meshConverter
|
||||
ITypedConverter<RG.Mesh, SOG.Mesh> meshConverter,
|
||||
RhinoDoc doc
|
||||
)
|
||||
{
|
||||
RG.GeometryBase geometryToMesh = geometry;
|
||||
RG.Vector3d? vector = null;
|
||||
|
||||
// 1.1. If needed, move geometry to origin
|
||||
if (modelFarFromOrigin && geometry.IsFarFromOrigin(out RG.Vector3d vectorToGeometry))
|
||||
{
|
||||
geometryToMesh = geometry.Duplicate();
|
||||
geometryToMesh.Transform(RG.Transform.Translation(-vectorToGeometry));
|
||||
vector = vectorToGeometry;
|
||||
}
|
||||
// 1.2. Extract Rhino Mesh
|
||||
RG.Mesh movedDisplayMesh = GetGeometryDisplayMesh(geometryToMesh, true);
|
||||
|
||||
// 2. Convert extracted Mesh to Speckle. We don't move geometry back yet, because 'far from origin' geometry is causing Speckle conversion issues too
|
||||
List<SOG.Mesh> displayValue = new() { meshConverter.Convert(movedDisplayMesh) };
|
||||
|
||||
// 3. Move Speckle geometry back from origin, if translation was applied
|
||||
MoveSpeckleMeshes(displayValue, vector, units);
|
||||
|
||||
RG.Mesh displayMesh = GetGeometryDisplayMesh(geometry, doc);
|
||||
List<SOG.Mesh> displayValue = new() { meshConverter.Convert(displayMesh) };
|
||||
return displayValue;
|
||||
}
|
||||
|
||||
public static void MoveSpeckleMeshes(List<SOG.Mesh> displayValue, RG.Vector3d? vectorToGeometry, string units)
|
||||
{
|
||||
if (vectorToGeometry is RG.Vector3d vector)
|
||||
{
|
||||
Matrix4x4 matrix = new(1, 0, 0, vector.X, 0, 1, 0, vector.Y, 0, 0, 1, vector.Z, 0, 0, 0, 1);
|
||||
SO.Transform transform = new() { matrix = matrix, units = units };
|
||||
displayValue.ForEach(x => x.Transform(transform));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -30,9 +30,8 @@ public class BrepToSpeckleConverter : ITypedConverter<RG.Brep, SOG.BrepX>
|
||||
|
||||
List<SOG.Mesh> displayValue = DisplayMeshExtractor.GetSpeckleMeshes(
|
||||
target,
|
||||
_settingsStore.Current.ModelFarFromOrigin,
|
||||
_settingsStore.Current.SpeckleUnits,
|
||||
_meshConverter
|
||||
_meshConverter,
|
||||
_settingsStore.Current.Document
|
||||
);
|
||||
|
||||
var bx = new SOG.BrepX()
|
||||
|
||||
+2
-3
@@ -30,9 +30,8 @@ public class ExtrusionToSpeckleConverter : ITypedConverter<RG.Extrusion, SOG.Ext
|
||||
|
||||
List<SOG.Mesh> displayValue = DisplayMeshExtractor.GetSpeckleMeshes(
|
||||
target,
|
||||
_settingsStore.Current.ModelFarFromOrigin,
|
||||
_settingsStore.Current.SpeckleUnits,
|
||||
_meshConverter
|
||||
_meshConverter,
|
||||
_settingsStore.Current.Document
|
||||
);
|
||||
|
||||
var bx = new SOG.ExtrusionX()
|
||||
|
||||
+2
-3
@@ -41,9 +41,8 @@ public class HatchToSpeckleConverter : ITypedConverter<RG.Hatch, SOG.Region>
|
||||
|
||||
List<SOG.Mesh> displayValue = DisplayMeshExtractor.GetSpeckleMeshes(
|
||||
brep,
|
||||
_settingsStore.Current.ModelFarFromOrigin,
|
||||
_settingsStore.Current.SpeckleUnits,
|
||||
_meshConverter
|
||||
_meshConverter,
|
||||
_settingsStore.Current.Document
|
||||
);
|
||||
|
||||
return new SOG.Region
|
||||
|
||||
+16
-23
@@ -1,7 +1,5 @@
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Common.Objects;
|
||||
using Speckle.Converters.Rhino.Extensions;
|
||||
using Speckle.Converters.Rhino.ToSpeckle.Meshing;
|
||||
using Speckle.Sdk.Common.Exceptions;
|
||||
|
||||
namespace Speckle.Converters.Rhino.ToSpeckle.Raw;
|
||||
@@ -34,39 +32,34 @@ public class MeshToSpeckleConverter : ITypedConverter<RG.Mesh, SOG.Mesh>
|
||||
throw new ValidationException("Cannot convert a mesh with 0 vertices/faces");
|
||||
}
|
||||
|
||||
// Extracting Rhino Mesh and converting to Speckle with the most suitable settings (e.g. moving to origin first, if needed)
|
||||
// This is needed because of Rhino using single precision numbers for Mesh vertices: https://wiki.mcneel.com/rhino/farfromorigin
|
||||
RG.Mesh meshToConvert = target;
|
||||
RG.Vector3d? vector = null;
|
||||
|
||||
// 1. If needed, move geometry to origin
|
||||
if (_settingsStore.Current.ModelFarFromOrigin && target.IsFarFromOrigin(out RG.Vector3d vectorToGeometry))
|
||||
{
|
||||
meshToConvert = (RG.Mesh)target.Duplicate();
|
||||
meshToConvert.Transform(RG.Transform.Translation(-vectorToGeometry));
|
||||
vector = vectorToGeometry;
|
||||
}
|
||||
// 2. Convert extracted Mesh to Speckle. We don't move geometry back yet, because 'far from origin' geometry is causing Speckle conversion issues too
|
||||
SOG.Mesh convertedMesh = ConvertMesh(meshToConvert);
|
||||
|
||||
// 3. Move Speckle geometry back from origin, if translation was applied
|
||||
DisplayMeshExtractor.MoveSpeckleMeshes([convertedMesh], vector, _settingsStore.Current.SpeckleUnits);
|
||||
SOG.Mesh convertedMesh = ConvertMesh(target);
|
||||
|
||||
return convertedMesh;
|
||||
}
|
||||
|
||||
private SOG.Mesh ConvertMesh(RG.Mesh target)
|
||||
// Rhino common is casting mesh vertex coords from doubles to float: by default the api returns `Vertices` as float instead of double precision
|
||||
// https://github.com/mcneel/rhino3dm/blob/71c63a8c1c87782a13a1b76c825e4b792b36fd09/src/dotnet/opennurbs/opennurbs_mesh.cs#L6990-L7000
|
||||
// We need to use double precision or else meshes far from origin will come out distorted: do *not* access `Vertices` directly - use `ToPoint3dArray`
|
||||
private double[] ConvertDoublePrecisionVertices(RG.Mesh target)
|
||||
{
|
||||
var vertexCoordinates = new double[target.Vertices.Count * 3];
|
||||
RG.Point3d[] vertices = target.Vertices.ToPoint3dArray();
|
||||
var x = 0;
|
||||
for (int i = 0; i < target.Vertices.Count; i++)
|
||||
for (int i = 0; i < vertices.Length; i++)
|
||||
{
|
||||
var v = target.Vertices[i];
|
||||
var v = vertices[i];
|
||||
vertexCoordinates[x++] = v.X;
|
||||
vertexCoordinates[x++] = v.Y;
|
||||
vertexCoordinates[x++] = v.Z;
|
||||
}
|
||||
|
||||
return vertexCoordinates;
|
||||
}
|
||||
|
||||
private SOG.Mesh ConvertMesh(RG.Mesh target)
|
||||
{
|
||||
var vertexCoordinates = ConvertDoublePrecisionVertices(target);
|
||||
|
||||
List<int> faces = new();
|
||||
|
||||
foreach (RG.MeshNgon polygon in target.GetNgonAndFacesEnumerable())
|
||||
@@ -81,7 +74,7 @@ public class MeshToSpeckleConverter : ITypedConverter<RG.Mesh, SOG.Mesh>
|
||||
}
|
||||
|
||||
var colors = new int[target.VertexColors.Count];
|
||||
x = 0;
|
||||
int x = 0;
|
||||
foreach (var c in target.VertexColors)
|
||||
{
|
||||
colors[x++] = c.ToArgb();
|
||||
|
||||
+2
-3
@@ -30,9 +30,8 @@ public class SubDToSpeckleConverter : ITypedConverter<RG.SubD, SOG.SubDX>
|
||||
|
||||
List<SOG.Mesh> displayValue = DisplayMeshExtractor.GetSpeckleMeshes(
|
||||
target,
|
||||
_settingsStore.Current.ModelFarFromOrigin,
|
||||
_settingsStore.Current.SpeckleUnits,
|
||||
_meshConverter
|
||||
_meshConverter,
|
||||
_settingsStore.Current.Document
|
||||
);
|
||||
|
||||
var bx = new SOG.SubDX()
|
||||
|
||||
@@ -115,6 +115,30 @@ public class ConfigBinding : IBinding
|
||||
}
|
||||
}
|
||||
|
||||
public GlobalConfig? GetGlobalConfig()
|
||||
{
|
||||
var rawConfig = _jsonCacheManager.GetObject("global");
|
||||
if (rawConfig is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var config = _serializer.Deserialize<GlobalConfig>(rawConfig);
|
||||
if (config is null)
|
||||
{
|
||||
throw new SerializationException("Failed to deserialize global config");
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
catch (SerializationException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public AccountsConfig? GetAccountsConfig()
|
||||
{
|
||||
var rawConfig = _jsonCacheManager.GetObject("accounts");
|
||||
@@ -182,6 +206,11 @@ public class ConnectorConfig
|
||||
public bool DarkTheme { get; set; } = true;
|
||||
}
|
||||
|
||||
public class GlobalConfig
|
||||
{
|
||||
public bool IsUpdateNotificationDisabled { get; set; }
|
||||
}
|
||||
|
||||
public class AccountsConfig
|
||||
{
|
||||
public string? UserSelectedAccountId { get; set; }
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<PackageVersion Include="altcover" Version="8.9.3" />
|
||||
<PackageVersion Include="Bullseye" Version="5.0.0" />
|
||||
<PackageVersion Include="CefSharp.Wpf" Version="92.0.260" />
|
||||
<PackageVersion Include="Dapper" Version="2.1.66" />
|
||||
<PackageVersion Include="Esri.ArcGISPro.Extensions30" Version="3.2.0.49743" />
|
||||
<PackageVersion Include="FluentAssertions" Version="6.12.1" />
|
||||
<PackageVersion Include="Glob" Version="1.1.9" />
|
||||
@@ -17,6 +18,7 @@
|
||||
<PackageVersion Include="Moq" Version="4.20.70" />
|
||||
<PackageVersion Include="Microsoft.Build" Version="17.11.4" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
|
||||
<PackageVersion Include="Npgsql" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="2.2.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="2.2.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
|
||||
@@ -26,6 +28,7 @@
|
||||
<PackageVersion Include="NUnit3TestAdapter" version="4.6.0" />
|
||||
<PackageVersion Include="Revit.Async" Version="2.1.1" />
|
||||
<PackageVersion Include="RhinoCommon" Version="8.9.24194.18121" />
|
||||
<PackageVersion Include="Rhino.Inside" Version="8.0.7-beta" />
|
||||
<PackageVersion Include="Grasshopper" Version="8.9.24194.18121" />
|
||||
<PackageVersion Include="RhinoWindows" Version="8.9.24194.18121" />
|
||||
<PackageVersion Include="Semver" Version="3.0.0" />
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace Speckle.Importers.JobProcessor.Domain;
|
||||
|
||||
/// <summary>
|
||||
/// DB model for the fileimport job.
|
||||
/// </summary>
|
||||
internal sealed class FileimportJob
|
||||
{
|
||||
public required string Id { get; init; }
|
||||
public required string JobType { get; init; }
|
||||
public required FileimportPayload Payload { get; init; }
|
||||
public required JobStatus Status { get; init; }
|
||||
public required int Attempt { get; init; }
|
||||
public required int MaxAttempt { get; init; }
|
||||
public required DateTime CreatedAt { get; init; }
|
||||
public required DateTime UpdatedAt { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace Speckle.Importers.JobProcessor.Domain;
|
||||
|
||||
/// <summary>
|
||||
/// Payload for the fileimport job
|
||||
/// </summary>
|
||||
internal sealed class FileimportPayload
|
||||
{
|
||||
public required string JobId { get; init; }
|
||||
public required string Token { get; init; }
|
||||
public required string BlobId { get; init; }
|
||||
public required string JobType { get; init; }
|
||||
public required string ModelId { get; init; }
|
||||
public required string FileName { get; init; }
|
||||
public required string FileType { get; init; }
|
||||
public required string ProjectId { get; init; }
|
||||
public required Uri ServerUrl { get; init; }
|
||||
public required int PayloadVersion { get; init; }
|
||||
public required int TimeOutSeconds { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Speckle.Importers.JobProcessor.Domain;
|
||||
|
||||
/// <summary>
|
||||
/// Status enumeration for the job.
|
||||
/// </summary>
|
||||
internal enum JobStatus
|
||||
{
|
||||
QUEUED,
|
||||
PROCESSING,
|
||||
SUCCEEDED,
|
||||
FAILED,
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace Speckle.Importers.JobProcessor.JobHandlers;
|
||||
|
||||
public sealed class MaxAttemptsExceededException : Exception
|
||||
{
|
||||
public MaxAttemptsExceededException() { }
|
||||
|
||||
public MaxAttemptsExceededException(string? message)
|
||||
: base(message) { }
|
||||
|
||||
public MaxAttemptsExceededException(string? message, Exception? innerException)
|
||||
: base(message, innerException) { }
|
||||
}
|
||||
|
||||
public sealed class JobTimeoutException : Exception
|
||||
{
|
||||
public JobTimeoutException() { }
|
||||
|
||||
public JobTimeoutException(string? message)
|
||||
: base(message) { }
|
||||
|
||||
public JobTimeoutException(string? message, Exception? innerException)
|
||||
: base(message, innerException) { }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Speckle.Importers.JobProcessor.Domain;
|
||||
using Speckle.Sdk.Api;
|
||||
using Version = Speckle.Sdk.Api.GraphQL.Models.Version;
|
||||
|
||||
namespace Speckle.Importers.JobProcessor.JobHandlers;
|
||||
|
||||
internal interface IJobHandler
|
||||
{
|
||||
public Task<Version> ProcessJob(FileimportJob job, IClient client, CancellationToken cancellationToken);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Importers.JobProcessor.Domain;
|
||||
using Speckle.Importers.Rhino;
|
||||
using Speckle.Sdk.Api;
|
||||
using Version = Speckle.Sdk.Api.GraphQL.Models.Version;
|
||||
|
||||
namespace Speckle.Importers.JobProcessor.JobHandlers;
|
||||
|
||||
internal sealed class RhinoJobHandler(ILogger<RhinoJobHandler> logger) : IJobHandler
|
||||
{
|
||||
public async Task<Version> ProcessJob(FileimportJob job, IClient client, CancellationToken cancellationToken)
|
||||
{
|
||||
var directory = Directory.CreateTempSubdirectory("speckle-file-import");
|
||||
try
|
||||
{
|
||||
string targetFilePath = $"{directory.FullName}/{job.Payload.JobId}.{job.Payload.FileType}";
|
||||
await client.FileImport.DownloadFile(
|
||||
job.Payload.ProjectId,
|
||||
job.Payload.BlobId,
|
||||
targetFilePath,
|
||||
null,
|
||||
cancellationToken
|
||||
);
|
||||
|
||||
return await Importer.Import(
|
||||
targetFilePath,
|
||||
job.Payload.ProjectId,
|
||||
job.Payload.ModelId,
|
||||
client.Account,
|
||||
cancellationToken
|
||||
);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
await Cleanup(directory.FullName);
|
||||
}
|
||||
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)
|
||||
{
|
||||
logger.LogError(ex, "Failed to cleanup file");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task Cleanup(string path)
|
||||
{
|
||||
//Some weird cases where *something* is keeping a lock on the file, this *may* fix things...
|
||||
await Task.Delay(100);
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
Directory.Delete(path, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connectors.Common.Extensions;
|
||||
using Speckle.Connectors.Logging;
|
||||
using Speckle.Importers.JobProcessor.Domain;
|
||||
using Speckle.Importers.JobProcessor.JobHandlers;
|
||||
using Speckle.Importers.JobProcessor.JobQueue;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL.Inputs;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Speckle.Sdk.Logging;
|
||||
using Version = Speckle.Sdk.Api.GraphQL.Models.Version;
|
||||
|
||||
namespace Speckle.Importers.JobProcessor;
|
||||
|
||||
internal sealed class JobProcessorInstance(
|
||||
Repository repository,
|
||||
ILogger<JobProcessorInstance> logger,
|
||||
IJobHandler jobHandler,
|
||||
IAccountFactory accountFactory,
|
||||
IClientFactory clientFactory,
|
||||
ISdkActivityFactory activityFactory
|
||||
)
|
||||
{
|
||||
private static readonly TimeSpan s_idleTimeout = TimeSpan.FromSeconds(1);
|
||||
|
||||
public async Task StartProcessing(CancellationToken cancellationToken = default)
|
||||
{
|
||||
await using var connection = await repository.SetupConnection(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
logger.LogInformation("Listening for jobs...");
|
||||
|
||||
while (true)
|
||||
{
|
||||
FileimportJob? job = await repository.GetNextJob(connection, cancellationToken);
|
||||
if (job == null)
|
||||
{
|
||||
logger.LogDebug("No job found, sleeping for {timeout}", s_idleTimeout);
|
||||
await Task.Delay(s_idleTimeout, cancellationToken);
|
||||
continue;
|
||||
}
|
||||
logger.LogInformation("Starting {jobId}", job.Id);
|
||||
|
||||
using var activity = activityFactory.Start();
|
||||
ActivityScope.SetTag("jobId", job.Id);
|
||||
ActivityScope.SetTag("jobType", job.Payload.JobType);
|
||||
ActivityScope.SetTag("job.attempt", job.Attempt.ToString());
|
||||
ActivityScope.SetTag("serverUrl", job.Payload.ServerUrl.ToString());
|
||||
ActivityScope.SetTag("projectId", job.Payload.ProjectId);
|
||||
ActivityScope.SetTag("modelId", job.Payload.ModelId);
|
||||
ActivityScope.SetTag("blobId", job.Payload.BlobId);
|
||||
|
||||
try
|
||||
{
|
||||
JobStatus jobStatus = await AttemptJob(job, cancellationToken);
|
||||
await repository.SetJobStatus(connection, job.Id, jobStatus, cancellationToken);
|
||||
activity?.SetStatus(SdkActivityStatusCode.Ok);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
activity?.RecordException(ex);
|
||||
activity?.SetStatus(SdkActivityStatusCode.Error);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task ReportSuccess(
|
||||
FileimportJob job,
|
||||
Version version,
|
||||
IClient client,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var input = new FileImportSuccessInput
|
||||
{
|
||||
projectId = job.Payload.ProjectId,
|
||||
jobId = job.Payload.BlobId,
|
||||
warnings = [],
|
||||
result = new FileImportResult(0, 0, 0, "Rhino Importer", versionId: version.id)
|
||||
};
|
||||
await client.FileImport.FinishFileImportJob(input, cancellationToken);
|
||||
}
|
||||
|
||||
private static async Task ReportFailed(
|
||||
FileimportJob job,
|
||||
IClient client,
|
||||
Exception ex,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var input = new FileImportErrorInput()
|
||||
{
|
||||
projectId = job.Payload.ProjectId,
|
||||
jobId = job.Payload.BlobId,
|
||||
warnings = [],
|
||||
reason = string.IsNullOrEmpty(ex.Message) ? ex.GetType().ToString() : ex.Message,
|
||||
result = new FileImportResult(0, 0, 0, "Rhino Importer", versionId: null)
|
||||
};
|
||||
await client.FileImport.FinishFileImportJob(input, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task<IClient> SetupClient(FileimportJob job, CancellationToken cancellationToken)
|
||||
{
|
||||
var account = await accountFactory.CreateAccount(
|
||||
job.Payload.ServerUrl,
|
||||
job.Payload.Token,
|
||||
cancellationToken: cancellationToken
|
||||
);
|
||||
|
||||
return clientFactory.Create(account);
|
||||
}
|
||||
|
||||
[SuppressMessage("Design", "CA1031:Do not catch general exception types")]
|
||||
private async Task<JobStatus> AttemptJob(FileimportJob job, CancellationToken cancellationToken)
|
||||
{
|
||||
using var activity = activityFactory.Start();
|
||||
|
||||
IClient? speckleClient = null;
|
||||
try
|
||||
{
|
||||
speckleClient = await SetupClient(job, cancellationToken);
|
||||
UserActivityScope.AddUserScope(speckleClient.Account);
|
||||
|
||||
if (job.Attempt > job.MaxAttempt)
|
||||
{
|
||||
//something went wrong, it should have been marked as failed
|
||||
throw new MaxAttemptsExceededException("Unhandled error silently failed the job multiple times");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Version version = await ExecuteJobWithTimeout(job, speckleClient, cancellationToken);
|
||||
await ReportSuccess(job, version, speckleClient, cancellationToken);
|
||||
logger.LogInformation("Job {jobId} has succeeded creating {versionId}", job.Id, version.id);
|
||||
|
||||
activity?.SetStatus(SdkActivityStatusCode.Ok);
|
||||
return JobStatus.SUCCEEDED;
|
||||
}
|
||||
catch (JobTimeoutException ex)
|
||||
{
|
||||
logger.LogInformation(ex, "Executing job timed out");
|
||||
|
||||
if (job.Attempt >= job.MaxAttempt)
|
||||
{
|
||||
throw new MaxAttemptsExceededException("The final attempt to process the job failed", ex);
|
||||
}
|
||||
|
||||
activity?.RecordException(ex);
|
||||
activity?.SetStatus(SdkActivityStatusCode.Error);
|
||||
return JobStatus.QUEUED;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "Attempt {attempt} to process {jobId} failed", job.Attempt, job.Id);
|
||||
|
||||
if (speckleClient is not null)
|
||||
{
|
||||
await ReportFailed(job, speckleClient, ex, cancellationToken);
|
||||
}
|
||||
|
||||
activity?.RecordException(ex);
|
||||
activity?.SetStatus(SdkActivityStatusCode.Error);
|
||||
return JobStatus.FAILED;
|
||||
}
|
||||
finally
|
||||
{
|
||||
speckleClient?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="job"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns><see cref="Version"/> if attempt was successful, <see langword="null"/> if job timedout, but can be re-attempted without exceeding <see cref="FileimportJob.MaxAttempt"/></returns>
|
||||
/// <exception cref="OperationCanceledException">Timeout was reached AND MaxAttempt was reached</exception>
|
||||
private async Task<Version> ExecuteJobWithTimeout(
|
||||
FileimportJob job,
|
||||
IClient client,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
using CancellationTokenSource timeout = new();
|
||||
timeout.CancelAfter(TimeSpan.FromSeconds(job.Payload.TimeOutSeconds));
|
||||
using CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(
|
||||
timeout.Token,
|
||||
cancellationToken
|
||||
);
|
||||
try
|
||||
{
|
||||
return await jobHandler.ProcessJob(job, client, linkedSource.Token);
|
||||
}
|
||||
catch (OperationCanceledException ex) when (timeout.IsCancellationRequested)
|
||||
{
|
||||
throw new JobTimeoutException(
|
||||
$"Job was cancelled due to reaching the {job.Payload.TimeOutSeconds} second timeout",
|
||||
ex
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Data;
|
||||
using System.Text.Json;
|
||||
using Dapper;
|
||||
|
||||
namespace Speckle.Importers.JobProcessor.JobQueue;
|
||||
|
||||
internal sealed class JsonHandler<T> : SqlMapper.TypeHandler<T>
|
||||
{
|
||||
private readonly JsonSerializerOptions _options = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, };
|
||||
|
||||
public override void SetValue(IDbDataParameter parameter, T? value)
|
||||
{
|
||||
parameter.Value = JsonSerializer.Serialize(value);
|
||||
}
|
||||
|
||||
public override T? Parse(object value)
|
||||
{
|
||||
if (value is string json)
|
||||
{
|
||||
return JsonSerializer.Deserialize<T>(json, _options);
|
||||
}
|
||||
|
||||
throw new DataException($"Cannot convert {value.GetType()} to {typeof(T)}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
using System.Data;
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Npgsql;
|
||||
using Speckle.Importers.JobProcessor.Domain;
|
||||
|
||||
namespace Speckle.Importers.JobProcessor.JobQueue;
|
||||
|
||||
internal sealed class Repository(ILogger<Repository> logger)
|
||||
{
|
||||
public async Task<NpgsqlConnection> SetupConnection(CancellationToken cancellationToken)
|
||||
{
|
||||
string fileImportQueuePostgresUrl =
|
||||
Environment.GetEnvironmentVariable("FILEIMPORT_QUEUE_POSTGRES_URL")
|
||||
?? throw new ArgumentException("Expected FILEIMPORT_QUEUE_POSTGRES_URL environment variable to be set");
|
||||
|
||||
string connectionString = ParseConnectionString(new(fileImportQueuePostgresUrl));
|
||||
var connection = new NpgsqlConnection(connectionString);
|
||||
await connection.OpenAsync(cancellationToken).ConfigureAwait(false);
|
||||
return connection;
|
||||
}
|
||||
|
||||
private static string ParseConnectionString(Uri connectionUrl)
|
||||
{
|
||||
if (connectionUrl.Scheme is not "postgres" and not "postgresql")
|
||||
{
|
||||
throw new ArgumentException("Invalid URI scheme. Expected 'postgres' or 'postgresql'.", nameof(connectionUrl));
|
||||
}
|
||||
var userInfo = connectionUrl.UserInfo.Split(':');
|
||||
if (userInfo.Length != 2)
|
||||
{
|
||||
throw new ArgumentException("Invalid URI format: missing username or password.");
|
||||
}
|
||||
|
||||
var builder = new NpgsqlConnectionStringBuilder
|
||||
{
|
||||
Host = connectionUrl.Host,
|
||||
Port = connectionUrl.Port > 0 ? connectionUrl.Port : 5432, // Default to 5432 if not specified
|
||||
Username = userInfo[0],
|
||||
Password = userInfo[1],
|
||||
Database = connectionUrl.AbsolutePath.TrimStart('/')
|
||||
};
|
||||
return builder.ConnectionString;
|
||||
}
|
||||
|
||||
public async Task<FileimportJob?> GetNextJob(IDbConnection connection, CancellationToken cancellationToken)
|
||||
{
|
||||
//lang=postgresql
|
||||
const string COMMAND_TEXT = """
|
||||
WITH next_job AS (
|
||||
UPDATE background_jobs
|
||||
SET
|
||||
"attempt" = "attempt" + 1,
|
||||
"status" = @Status1,
|
||||
"updatedAt" = NOW()
|
||||
WHERE id = (
|
||||
SELECT id FROM background_jobs
|
||||
WHERE ( --queued job
|
||||
(payload ->> 'fileType') = ANY(@FileTypes)
|
||||
AND status = @Status2
|
||||
)
|
||||
OR ( --timed job left on processing state
|
||||
(payload ->> 'fileType') = ANY(@FileTypes)
|
||||
AND status = @Status1
|
||||
AND "updatedAt" < NOW() - ("timeoutMs" * interval '1 millisecond')
|
||||
)
|
||||
ORDER BY "createdAt"
|
||||
FOR UPDATE SKIP LOCKED
|
||||
LIMIT 1
|
||||
)
|
||||
RETURNING *
|
||||
)
|
||||
SELECT * FROM next_job;
|
||||
""";
|
||||
|
||||
var command = new CommandDefinition(
|
||||
commandText: COMMAND_TEXT,
|
||||
parameters: new
|
||||
{
|
||||
Status1 = nameof(JobStatus.PROCESSING).ToLowerInvariant(),
|
||||
Status2 = nameof(JobStatus.QUEUED).ToLowerInvariant(),
|
||||
FileTypes = SupportedFileTypes.FileTypes,
|
||||
},
|
||||
cancellationToken: cancellationToken
|
||||
);
|
||||
|
||||
return await connection.QueryFirstOrDefaultAsync<FileimportJob?>(command);
|
||||
}
|
||||
|
||||
public async Task SetJobStatus(
|
||||
IDbConnection connection,
|
||||
string jobId,
|
||||
JobStatus jobStatus,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
logger.LogInformation("Updating job: {jobId}'s status to {jobStatus}", jobId, jobStatus);
|
||||
|
||||
//lang=postgresql
|
||||
const string COMMAND_TEXT = """
|
||||
UPDATE background_jobs
|
||||
SET status = @status, "updatedAt" = NOW()
|
||||
WHERE id = @jobId
|
||||
""";
|
||||
|
||||
var command = new CommandDefinition(
|
||||
commandText: COMMAND_TEXT,
|
||||
parameters: new { status = jobStatus.ToString().ToLowerInvariant(), jobId, },
|
||||
cancellationToken: cancellationToken
|
||||
);
|
||||
|
||||
await connection.ExecuteAsync(command);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// See https://aka.ms/new-console-template for more information
|
||||
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Rhino.Runtime.InProcess;
|
||||
using RhinoInside;
|
||||
using Speckle.Importers.JobProcessor.Domain;
|
||||
using Speckle.Importers.JobProcessor.JobHandlers;
|
||||
using Speckle.Importers.JobProcessor.JobQueue;
|
||||
|
||||
namespace Speckle.Importers.JobProcessor;
|
||||
|
||||
public static class Program
|
||||
{
|
||||
static Program()
|
||||
{
|
||||
Resolver.Initialize();
|
||||
}
|
||||
|
||||
[STAThread]
|
||||
public static async Task Main()
|
||||
{
|
||||
ILogger? logger = null;
|
||||
try
|
||||
{
|
||||
// Dapper doesn't understand how to handle JSON deserialization, so we need to tell it what types can be deserialzied
|
||||
SqlMapper.AddTypeHandler(new JsonHandler<FileimportPayload>());
|
||||
|
||||
// DI setup
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddJobProcessor();
|
||||
|
||||
serviceCollection.AddTransient<IJobHandler, RhinoJobHandler>();
|
||||
|
||||
var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
|
||||
var processor = serviceProvider.GetRequiredService<JobProcessorInstance>();
|
||||
logger = serviceProvider.GetRequiredService<ILogger<object>>();
|
||||
|
||||
TaskScheduler.UnobservedTaskException += (sender, args) =>
|
||||
logger.LogCritical(args.Exception, "Unobserved Task Exception");
|
||||
|
||||
using (new RhinoCore(["/netcore-8"], WindowStyle.NoWindow))
|
||||
{
|
||||
//What ever thread RhinoCore is created on it will grab as soon as it's available, and it will hog it forever.
|
||||
//Right now, we're giving it the main STA thread (not 100% if it needs STA or if it could work on any thread)
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
logger.LogInformation("Job processor has started!");
|
||||
try
|
||||
{
|
||||
await processor.StartProcessing();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogCritical(ex, "Unhandled exception in Main");
|
||||
throw;
|
||||
}
|
||||
})
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (logger is not null)
|
||||
{
|
||||
logger.LogCritical(ex, "Unhandled exception reached entry point");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Unhandled exception reached entry point: {ex}");
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"Local Docker DB": {
|
||||
"commandName": "Project",
|
||||
"environmentVariables": {
|
||||
"FILEIMPORT_QUEUE_POSTGRES_URL": "postgresql://speckle:speckle@127.0.0.1:5432/speckle"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Connectors.Common;
|
||||
using Speckle.Importers.JobProcessor.JobQueue;
|
||||
using Speckle.Sdk;
|
||||
|
||||
namespace Speckle.Importers.JobProcessor;
|
||||
|
||||
internal static class ServiceRegistration
|
||||
{
|
||||
private static readonly Application s_application = new(".NET File Import Job Processor", "jobprocessor");
|
||||
|
||||
public static IServiceCollection AddJobProcessor(this IServiceCollection serviceCollection)
|
||||
{
|
||||
serviceCollection.AddLoggingConfig();
|
||||
serviceCollection.AddTransient<JobProcessorInstance>();
|
||||
serviceCollection.AddTransient<Repository>();
|
||||
return serviceCollection;
|
||||
}
|
||||
|
||||
private static IServiceCollection AddLoggingConfig(this IServiceCollection serviceCollection)
|
||||
{
|
||||
serviceCollection.Initialize(s_application, HostAppVersion.v3);
|
||||
|
||||
return serviceCollection;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<OutputType>Exe</OutputType>
|
||||
<Configurations>Debug;Release;Local</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Npgsql" />
|
||||
<PackageReference Include="Dapper" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" />
|
||||
<PackageReference Include="Serilog.Formatting.Compact" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" />
|
||||
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.21.25188.17001"/>
|
||||
<PackageReference Include="RhinoWindows" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.21.25188.17001"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Speckle.Importers.Rhino\Speckle.Importers.Rhino.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,65 @@
|
||||
namespace Speckle.Importers.JobProcessor;
|
||||
|
||||
public static class SupportedFileTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// This is the full list of file extensions that this job processor will look for jobs with that extension
|
||||
/// This also happens to be the full list of file types that Rhino 8 supports.,
|
||||
/// </summary>
|
||||
public static readonly string[] FileTypes =
|
||||
[
|
||||
"3dm", // Rhino 3D Model (except ver 1.x save)
|
||||
"3dmbak", // Rhino 3D Model Backup
|
||||
"rws", // Rhino Worksession
|
||||
"3mf", // 3MF
|
||||
"3ds", // 3D Studio
|
||||
"amf", // AMF
|
||||
"ai", // Adobe Illustrator
|
||||
"dwg", // AutoCAD Drawing
|
||||
"dxf", // AutoCAD Drawing Exchange
|
||||
"x", // DirectX
|
||||
"e57", // E57
|
||||
"dst", // Embroidery
|
||||
"exp",
|
||||
"dst", // Encapsulated PostScript
|
||||
"exp",
|
||||
"off", // Geomview OFF
|
||||
"gf", // GHS Geometry
|
||||
"gft",
|
||||
"gltf", // GL Transmission Format
|
||||
"glb",
|
||||
"gts", // GTS (GNU Triangulated Surface)
|
||||
"igs", // IGES
|
||||
"iges",
|
||||
"lwo", // Lightwave
|
||||
"dgn", // Microstation
|
||||
"fbx", // MotionBuilder
|
||||
"scn", // NextEngine Scan
|
||||
"obj", // OBJ (Wavefront)
|
||||
"pdf", // PDF
|
||||
"ply", // PLY
|
||||
"asc", // Points
|
||||
"csv",
|
||||
"xyz",
|
||||
"pts",
|
||||
"cgo_ascii", // Points
|
||||
"cgo_asci",
|
||||
"txt", // Points
|
||||
"raw", // Raw Triangles
|
||||
"m", // Recon M
|
||||
"svg", // Scalable Vector Graphics
|
||||
"skp", // SketchUp
|
||||
"slc", // Slice
|
||||
"sldprt", // SOLIDWORKS
|
||||
"sldasm",
|
||||
"stp", // STEP
|
||||
"step",
|
||||
"stl", // STL (Stereolithography)
|
||||
"vda", // VDA
|
||||
"wrl", // VRML/Open Inventor
|
||||
"vrml",
|
||||
"iv",
|
||||
"gdf", // WAMIT
|
||||
"zpr", // Zcorp (3D Systems)
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,382 @@
|
||||
{
|
||||
"version": 2,
|
||||
"dependencies": {
|
||||
"net8.0-windows7.0": {
|
||||
"Dapper": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.1.66, )",
|
||||
"resolved": "2.1.66",
|
||||
"contentHash": "/q77jUgDOS+bzkmk3Vy9SiWMaetTw+NOoPAV0xPBsGVAyljd5S6P+4RUW7R3ZUGGr9lDRyPKgAMj2UAOwvqZYw=="
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.3, )",
|
||||
"resolved": "1.0.3",
|
||||
"contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3"
|
||||
}
|
||||
},
|
||||
"Microsoft.SourceLink.GitHub": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.Build.Tasks.Git": "8.0.0",
|
||||
"Microsoft.SourceLink.Common": "8.0.0"
|
||||
}
|
||||
},
|
||||
"Npgsql": {
|
||||
"type": "Direct",
|
||||
"requested": "[9.0.3, )",
|
||||
"resolved": "9.0.3",
|
||||
"contentHash": "tPvY61CxOAWxNsKLEBg+oR646X4Bc8UmyQ/tJszL/7mEmIXQnnBhVJZrZEEUv0Bstu0mEsHZD5At3EO8zQRAYw==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.2"
|
||||
}
|
||||
},
|
||||
"PolySharp": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.14.1, )",
|
||||
"resolved": "1.14.1",
|
||||
"contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ=="
|
||||
},
|
||||
"RhinoCommon": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.21.25188.17001, )",
|
||||
"resolved": "8.21.25188.17001",
|
||||
"contentHash": "Wo6JXheyDBvilyIwDY6xZCQJC4170jzAlTSlMgh8yokUY+vYyCl4KJVXZofIOynNt/xx5wLqb2On5gZZekXR6w==",
|
||||
"dependencies": {
|
||||
"System.Drawing.Common": "7.0.0"
|
||||
}
|
||||
},
|
||||
"RhinoWindows": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.21.25188.17001, )",
|
||||
"resolved": "8.21.25188.17001",
|
||||
"contentHash": "9zqCorcLRBeiW/j1RTwUS4E7bnZetAdA9WDdtd/AQccjOpxdtw76wdN+ciyQ6qslseWkwZ9qSBeh7QaM800Ntw==",
|
||||
"dependencies": {
|
||||
"RhinoCommon": "[8.21.25188.17001]"
|
||||
}
|
||||
},
|
||||
"Serilog.Extensions.Logging": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "YEAMWu1UnWgf1c1KP85l1SgXGfiVo0Rz6x08pCiPOIBt2Qe18tcZLvdBUuV5o1QHvrs8FAry9wTIhgBRtjIlEg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging": "8.0.0",
|
||||
"Serilog": "3.1.1"
|
||||
}
|
||||
},
|
||||
"Serilog.Formatting.Compact": {
|
||||
"type": "Direct",
|
||||
"requested": "[3.0.0, )",
|
||||
"resolved": "3.0.0",
|
||||
"contentHash": "wQsv14w9cqlfB5FX2MZpNsTawckN4a8dryuNGbebB/3Nh1pXnROHZov3swtu3Nj5oNG7Ba+xdu7Et/ulAUPanQ==",
|
||||
"dependencies": {
|
||||
"Serilog": "4.0.0"
|
||||
}
|
||||
},
|
||||
"Serilog.Sinks.Console": {
|
||||
"type": "Direct",
|
||||
"requested": "[6.0.0, )",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "fQGWqVMClCP2yEyTXPIinSr5c+CBGUvBybPxjAGcf7ctDhadFhrQw03Mv8rJ07/wR5PDfFjewf2LimvXCDzpbA==",
|
||||
"dependencies": {
|
||||
"Serilog": "4.0.0"
|
||||
}
|
||||
},
|
||||
"Speckle.InterfaceGenerator": {
|
||||
"type": "Direct",
|
||||
"requested": "[0.9.6, )",
|
||||
"resolved": "0.9.6",
|
||||
"contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w=="
|
||||
},
|
||||
"GraphQL.Client": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "8yPNBbuVBpTptivyAlak4GZvbwbUcjeQTL4vN1HKHRuOykZ4r7l5fcLS6vpyPyLn0x8FsL31xbOIKyxbmR9rbA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client.Abstractions": "6.0.0",
|
||||
"GraphQL.Client.Abstractions.Websocket": "6.0.0",
|
||||
"System.Reactive": "5.0.0"
|
||||
}
|
||||
},
|
||||
"GraphQL.Client.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "h7uzWFORHZ+CCjwr/ThAyXMr0DPpzEANDa4Uo54wqCQ+j7qUKwqYTgOrb1W40sqbvNaZm9v/X7It31SUw0maHA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Primitives": "6.0.0"
|
||||
}
|
||||
},
|
||||
"GraphQL.Client.Abstractions.Websocket": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "Nr9bPf8gIOvLuXpqEpqr9z9jslYFJOvd0feHth3/kPqeR3uMbjF5pjiwh4jxyMcxHdr8Pb6QiXkV3hsSyt0v7A==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client.Abstractions": "6.0.0"
|
||||
}
|
||||
},
|
||||
"GraphQL.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA=="
|
||||
},
|
||||
"Microsoft.Build.Tasks.Git": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
|
||||
},
|
||||
"Microsoft.Data.Sqlite": {
|
||||
"type": "Transitive",
|
||||
"resolved": "7.0.5",
|
||||
"contentHash": "KGxbPeWsQMnmQy43DSBxAFtHz3l2JX8EWBSGUCvT3CuZ8KsuzbkqMIJMDOxWtG8eZSoCDI04aiVQjWuuV8HmSw==",
|
||||
"dependencies": {
|
||||
"Microsoft.Data.Sqlite.Core": "7.0.5",
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": "2.1.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.Data.Sqlite.Core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "7.0.5",
|
||||
"contentHash": "FTerRmQPqHrCrnoUzhBu+E+1DNGwyrAMLqHkAqOOOu5pGfyMOj8qQUBxI/gDtWtG11p49UxSfWmBzRNlwZqfUg==",
|
||||
"dependencies": {
|
||||
"SQLitePCLRaw.core": "2.1.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.2",
|
||||
"contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
|
||||
},
|
||||
"Microsoft.Extensions.Options": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
|
||||
"Microsoft.Extensions.Primitives": "8.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g=="
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net461": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.3",
|
||||
"contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA=="
|
||||
},
|
||||
"Microsoft.SourceLink.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
|
||||
},
|
||||
"Microsoft.Win32.SystemEvents": {
|
||||
"type": "Transitive",
|
||||
"resolved": "7.0.0",
|
||||
"contentHash": "2nXPrhdAyAzir0gLl8Yy8S5Mnm/uBSQQA7jEsILOS1MTyS7DbmV1NgViMtvV1sfCD1ebITpNwb1NIinKeJgUVQ=="
|
||||
},
|
||||
"Speckle.Newtonsoft.Json": {
|
||||
"type": "Transitive",
|
||||
"resolved": "13.0.2",
|
||||
"contentHash": "g1BejUZwax5PRfL6xHgLEK23sqHWOgOj9hE7RvfRRlN00AGt8GnPYt8HedSK7UB3HiRW8zCA9Pn0iiYxCK24BA=="
|
||||
},
|
||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
"contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==",
|
||||
"dependencies": {
|
||||
"SQLitePCLRaw.lib.e_sqlite3": "2.1.4",
|
||||
"SQLitePCLRaw.provider.e_sqlite3": "2.1.4"
|
||||
}
|
||||
},
|
||||
"SQLitePCLRaw.core": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
"contentHash": "inBjvSHo9UDKneGNzfUfDjK08JzlcIhn1+SP5Y3m6cgXpCxXKCJDy6Mka7LpgSV+UZmKSnC8rTwB0SQ0xKu5pA==",
|
||||
"dependencies": {
|
||||
"System.Memory": "4.5.3"
|
||||
}
|
||||
},
|
||||
"SQLitePCLRaw.lib.e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
"contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg=="
|
||||
},
|
||||
"SQLitePCLRaw.provider.e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
"contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==",
|
||||
"dependencies": {
|
||||
"SQLitePCLRaw.core": "2.1.4"
|
||||
}
|
||||
},
|
||||
"System.Drawing.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "7.0.0",
|
||||
"contentHash": "KIX+oBU38pxkKPxvLcLfIkOV5Ien8ReN78wro7OF5/erwcmortzeFx+iBswlh2Vz6gVne0khocQudGwaO1Ey6A==",
|
||||
"dependencies": {
|
||||
"Microsoft.Win32.SystemEvents": "7.0.0"
|
||||
}
|
||||
},
|
||||
"System.Memory": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.3",
|
||||
"contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA=="
|
||||
},
|
||||
"System.Reactive": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ=="
|
||||
},
|
||||
"speckle.connectors.common": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.5.1, )",
|
||||
"Speckle.Sdk": "[3.5.1, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.5.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui.webview": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Web.WebView2": "[1.0.1938.49, )",
|
||||
"Speckle.Connectors.DUI": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.logging": {
|
||||
"type": "Project"
|
||||
},
|
||||
"speckle.connectors.rhinoimporter": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Connectors.DUI.WebView": "[1.0.0, )",
|
||||
"Speckle.Converters.Rhino8": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.common": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.5.1, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.rhino8": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Speckle.Converters.Common": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.importers.rhino": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Rhino.Inside": "[8.0.7-beta, )",
|
||||
"Speckle.Connectors.RhinoImporter": "[1.0.0, )",
|
||||
"Speckle.Converters.Rhino8": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "8.0.0",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.0",
|
||||
"Microsoft.Extensions.Options": "8.0.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
"resolved": "8.0.2",
|
||||
"contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
|
||||
}
|
||||
},
|
||||
"Microsoft.Web.WebView2": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[1.0.1938.49, )",
|
||||
"resolved": "1.0.1938.49",
|
||||
"contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw=="
|
||||
},
|
||||
"Rhino.Inside": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.7-beta, )",
|
||||
"resolved": "8.0.7-beta",
|
||||
"contentHash": "PNZdWysS2zI9vaxJ8HQig3iZA3Xg1yG1hnq09VPStQpceoj94iJ6xk0Ubddaw5oswkYYUIJRmQ8xr+bLTROs/g==",
|
||||
"dependencies": {
|
||||
"Grasshopper": "8.0.23304.9001",
|
||||
"RhinoCommon": "8.0.23304.9001"
|
||||
}
|
||||
},
|
||||
"Serilog": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[4.0.1, )",
|
||||
"resolved": "4.0.0",
|
||||
"contentHash": "2jDkUrSh5EofOp7Lx5Zgy0EB+7hXjjxE2ktTb1WVQmU00lDACR2TdROGKU0K1pDTBSJBN1PqgYpgOZF8mL7NJw=="
|
||||
},
|
||||
"Speckle.DoubleNumerics": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[4.1.0, )",
|
||||
"resolved": "4.1.0",
|
||||
"contentHash": "20DtS+FsDRsOD9+AU3TwNFZ0qrKo5f6f7B5ZR9wStsIHHHC9k7DpjbCvuNtmnSjx54MD+TJC7wV2f5iyGVPj1A=="
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.1, )",
|
||||
"resolved": "3.5.1",
|
||||
"contentHash": "pBFTwdc49aQgE6Sho/9uYoqRRnkRyqEp9Sg+xBKWJ2i+XdKts91n//GgolUT2i9Xh46MJiZXgezWQx3ne6kr7w==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.5.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.1, )",
|
||||
"resolved": "3.5.1",
|
||||
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.5.1"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.5.1, )",
|
||||
"resolved": "3.5.1",
|
||||
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Importers.Rhino.Internal;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Version = Speckle.Sdk.Api.GraphQL.Models.Version;
|
||||
|
||||
namespace Speckle.Importers.Rhino;
|
||||
|
||||
/// <summary>
|
||||
/// Entry point for the rhino import.
|
||||
/// Is a wrapper around an internal DI container.
|
||||
/// It's very important that the state of services doesn't bleed between job,
|
||||
/// So every import creates a new container for its processing
|
||||
/// I don't trust the current services to not hold on to caches or state that could influence the next run
|
||||
/// </summary>
|
||||
public static class Importer
|
||||
{
|
||||
public static async Task<Version> Import(
|
||||
string filePath,
|
||||
string projectId,
|
||||
string modelId,
|
||||
Account account,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddRhinoImporter();
|
||||
using var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
var instance = serviceProvider.GetRequiredService<ImporterInstance>();
|
||||
return await instance.RunRhinoImport(filePath, projectId, modelId, account, cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using Speckle.Sdk.SQLite;
|
||||
|
||||
namespace Speckle.Importers.Rhino.Internal;
|
||||
|
||||
/// <summary>
|
||||
/// Dummy implementation of <see cref="ISqLiteJsonCacheManager"/> to avoid
|
||||
/// </summary>
|
||||
public sealed class DummySqliteJsonCacheManager : ISqLiteJsonCacheManager
|
||||
{
|
||||
public void Dispose() { }
|
||||
|
||||
public IReadOnlyCollection<(string Id, string Json)> GetAllObjects() => [];
|
||||
|
||||
public void DeleteObject(string id) { }
|
||||
|
||||
public string? GetObject(string id) => null;
|
||||
|
||||
public void SaveObject(string id, string json) { }
|
||||
|
||||
public void UpdateObject(string id, string json) { }
|
||||
|
||||
public void SaveObjects(IEnumerable<(string id, string json)> items) { }
|
||||
|
||||
public bool HasObject(string objectId) => false;
|
||||
}
|
||||
|
||||
public sealed class DummySqliteJsonCacheManagerFactory : ISqLiteJsonCacheManagerFactory
|
||||
{
|
||||
private static readonly ISqLiteJsonCacheManager s_instance = new DummySqliteJsonCacheManager();
|
||||
|
||||
public ISqLiteJsonCacheManager CreateForUser(string scope) => s_instance;
|
||||
|
||||
public ISqLiteJsonCacheManager CreateFromStream(string streamId) => s_instance;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Rhino;
|
||||
using Rhino.Collections;
|
||||
|
||||
namespace Speckle.Importers.Rhino.Internal.FileTypeConfig;
|
||||
|
||||
public sealed class DefaultConfig : IFileTypeConfig
|
||||
{
|
||||
public ArchivableDictionary? ImportOptions => null;
|
||||
|
||||
public void PreProcessDocument(RhinoDoc doc) { }
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Rhino;
|
||||
using Rhino.Collections;
|
||||
|
||||
namespace Speckle.Importers.Rhino.Internal.FileTypeConfig;
|
||||
|
||||
/// <summary>
|
||||
/// Represents configuration for a specific file type (e.g. <c>.skp</c>) to customise the import behaviour
|
||||
/// </summary>
|
||||
internal interface IFileTypeConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Options to pass to the <see cref="RhinoDoc.Import(string, ArchivableDictionary?)"/> command
|
||||
/// </summary>
|
||||
public ArchivableDictionary? ImportOptions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Run any operations on objects in the rhino document to clean up the export before converting to speckle
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Ran on the document after importing, but before any Speckle conversion
|
||||
/// </remarks>
|
||||
/// <param name="doc"></param>
|
||||
public void PreProcessDocument(RhinoDoc doc);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using Rhino;
|
||||
using Rhino.Collections;
|
||||
using Rhino.DocObjects;
|
||||
using Rhino.Geometry;
|
||||
|
||||
namespace Speckle.Importers.Rhino.Internal.FileTypeConfig;
|
||||
|
||||
public sealed class SketchupConfig : IFileTypeConfig
|
||||
{
|
||||
public ArchivableDictionary? ImportOptions => null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up step to strip imported meshes of their NGon data, leaving only the triangle/quad data behind.
|
||||
/// This works around a bug in the sketchup importer creating invalid ngons.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Without this cleanup step, skp imports send incorrect meshes to speckle
|
||||
/// I believe there is a bug in Rhino's skp file importing logic
|
||||
/// The <see cref="MeshNgon.BoundaryVertexIndexList()"/> function documents that it will return ccw faces,
|
||||
/// and this holds true for native modeled rhino meshes, but not meshes from sketchup imports.
|
||||
/// Since Speckle's conversions rely on this function returning how it's documented, the resulting speckle geometry
|
||||
/// would be invalid without this step
|
||||
/// </remarks>
|
||||
/// <param name="doc"></param>
|
||||
public void PreProcessDocument(RhinoDoc doc)
|
||||
{
|
||||
// Process regular meshes in the document
|
||||
foreach (var obj in doc.Objects.GetObjectList(ObjectType.Mesh))
|
||||
{
|
||||
if (obj.Geometry is not Mesh mesh)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mesh.Ngons.Count <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
mesh.Ngons.Clear();
|
||||
_ = doc.Objects.Replace(obj.Id, mesh);
|
||||
}
|
||||
|
||||
//TODO: same for meshes inside blocks
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using Rhino;
|
||||
using Speckle.Importers.Rhino.Internal.FileTypeConfig;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Version = Speckle.Sdk.Api.GraphQL.Models.Version;
|
||||
|
||||
namespace Speckle.Importers.Rhino.Internal;
|
||||
|
||||
internal sealed class ImporterInstance(Sender sender)
|
||||
{
|
||||
public async Task<Version> RunRhinoImport(
|
||||
string filePath,
|
||||
string projectId,
|
||||
string modelId,
|
||||
Account account,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
using RhinoDoc open = RhinoDoc.CreateHeadless(null);
|
||||
try
|
||||
{
|
||||
var config = GetConfig(Path.GetExtension(filePath));
|
||||
|
||||
RhinoDoc.ActiveDoc = open;
|
||||
if (!open.Import(filePath, config.ImportOptions))
|
||||
{
|
||||
throw new SpeckleException("Rhino could not import this file");
|
||||
}
|
||||
|
||||
config.PreProcessDocument(open);
|
||||
|
||||
var version = await sender.Send(projectId, modelId, account, cancellationToken);
|
||||
return version;
|
||||
}
|
||||
finally
|
||||
{
|
||||
//Being a bit extra defensive to ensure we're cleaning up the old doc
|
||||
RhinoDoc.ActiveDoc?.Dispose();
|
||||
RhinoDoc.ActiveDoc = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static IFileTypeConfig GetConfig(string extension) =>
|
||||
extension.ToLowerInvariant() switch
|
||||
{
|
||||
".skp" => new SketchupConfig(),
|
||||
_ => new DefaultConfig(),
|
||||
};
|
||||
}
|
||||
+5
-2
@@ -1,8 +1,11 @@
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
|
||||
namespace Speckle.Importers.Rhino;
|
||||
namespace Speckle.Importers.Rhino.Internal;
|
||||
|
||||
public class ImporterThreadContext : ThreadContext
|
||||
/// <summary>
|
||||
/// A custom implementation of the ThreadContext to behave correctly with windowless rhino
|
||||
/// </summary>
|
||||
internal sealed class ImporterThreadContext : ThreadContext
|
||||
{
|
||||
protected override Task<T> WorkerToMainAsync<T>(Func<Task<T>> action)
|
||||
{
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
|
||||
namespace Speckle.Importers.Rhino;
|
||||
namespace Speckle.Importers.Rhino.Internal;
|
||||
|
||||
public class Progress(ILogger<Progress> logger) : IProgress<CardProgress>
|
||||
internal sealed class Progress(ILogger<Progress> logger) : IProgress<CardProgress>
|
||||
{
|
||||
private readonly TimeSpan _debounce = TimeSpan.FromMilliseconds(1000);
|
||||
private DateTime _lastTime = DateTime.UtcNow;
|
||||
@@ -0,0 +1,64 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Rhino;
|
||||
using Rhino.DocObjects;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Rhino;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Speckle.Sdk.Logging;
|
||||
using Version = Speckle.Sdk.Api.GraphQL.Models.Version;
|
||||
|
||||
namespace Speckle.Importers.Rhino.Internal;
|
||||
|
||||
internal sealed class Sender(
|
||||
ISdkActivityFactory activityFactory,
|
||||
IServiceProvider serviceProvider,
|
||||
IRhinoConversionSettingsFactory rhinoConversionSettingsFactory,
|
||||
Progress progress,
|
||||
ILogger<Sender> logger
|
||||
)
|
||||
{
|
||||
public async Task<Version> Send(
|
||||
string projectId,
|
||||
string modelId,
|
||||
Account account,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
// NOTE: introduction of AddVisualizationProperties setting not accounted for, hence hardcoded as true (i.e. "as before")
|
||||
using var activity = activityFactory.Start();
|
||||
using var scope = serviceProvider.CreateScope();
|
||||
scope
|
||||
.ServiceProvider.GetRequiredService<IConverterSettingsStore<RhinoConversionSettings>>()
|
||||
.Initialize(rhinoConversionSettingsFactory.Create(RhinoDoc.ActiveDoc, true));
|
||||
|
||||
List<RhinoObject> rhinoObjects = RhinoDoc
|
||||
.ActiveDoc.Objects.GetObjectList(ObjectType.AnyObject)
|
||||
.Where(obj => obj != null)
|
||||
.ToList();
|
||||
|
||||
if (rhinoObjects.Count == 0)
|
||||
{
|
||||
throw new SpeckleException("There are no objects found in the file");
|
||||
}
|
||||
|
||||
var operation = scope.ServiceProvider.GetRequiredService<SendOperation<RhinoObject>>();
|
||||
var buildResults = await operation.Build(rhinoObjects, projectId, progress, cancellationToken);
|
||||
var (results, version) = await operation.Send(
|
||||
buildResults.RootObject,
|
||||
projectId,
|
||||
modelId,
|
||||
"Rhino File Importer",
|
||||
null,
|
||||
account,
|
||||
progress,
|
||||
cancellationToken
|
||||
);
|
||||
|
||||
logger.LogInformation($"Root: {results.RootId}");
|
||||
|
||||
return version;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Connectors.Common;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.Rhino.DependencyInjection;
|
||||
using Speckle.Converters.Rhino;
|
||||
using Speckle.Sdk.SQLite;
|
||||
|
||||
namespace Speckle.Importers.Rhino.Internal;
|
||||
|
||||
internal static class ServiceRegistration
|
||||
{
|
||||
public static IServiceCollection AddRhinoImporter(this IServiceCollection services)
|
||||
{
|
||||
services.Initialize(HostApplications.RhinoImporter, HostAppVersion.v8);
|
||||
|
||||
services.AddRhino(false);
|
||||
services.AddRhinoConverters();
|
||||
services.AddTransient<Progress>();
|
||||
services.AddTransient<Sender>();
|
||||
services.AddTransient<ImporterInstance>();
|
||||
|
||||
// override default thread context
|
||||
services.AddSingleton<IThreadContext>(new ImporterThreadContext());
|
||||
|
||||
// override sqlite cache, since we don't want to persist to disk any object data
|
||||
services.AddTransient<ISqLiteJsonCacheManagerFactory, DummySqliteJsonCacheManagerFactory>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
using System.CommandLine;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Rhino;
|
||||
using Rhino.Runtime.InProcess;
|
||||
using Serilog;
|
||||
using Serilog.Formatting.Compact;
|
||||
using Speckle.Connectors.Common;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.Rhino.DependencyInjection;
|
||||
using Speckle.Converters.Rhino;
|
||||
using Speckle.Newtonsoft.Json;
|
||||
using Speckle.Sdk.Logging;
|
||||
|
||||
namespace Speckle.Importers.Rhino;
|
||||
|
||||
public static class Program
|
||||
{
|
||||
static Program()
|
||||
{
|
||||
Resolver.Initialize();
|
||||
}
|
||||
|
||||
[STAThread]
|
||||
public static async Task<int> Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
RootCommand rootCommand = new();
|
||||
|
||||
Argument<string> pathArg = new(name: "Source File Path", description: "Path to file to load and parse");
|
||||
Argument<string> resultsPathArg =
|
||||
new(name: "Results File Path", description: "Path to file to write results information (like version id)");
|
||||
Argument<string> projectIdArg = new(name: "Project Id", description: "The project id to publish to");
|
||||
Argument<string> modelIdArg = new(name: "Model Id", description: "The model id to publish to");
|
||||
Argument<string> serverUrlArg = new(name: "Server Url", description: "The url of the server to publish to.");
|
||||
Argument<string> tokenArg = new(name: "Token", description: "The speckle token to use for publish");
|
||||
|
||||
rootCommand.AddArgument(pathArg);
|
||||
rootCommand.AddArgument(resultsPathArg);
|
||||
rootCommand.AddArgument(projectIdArg);
|
||||
rootCommand.AddArgument(modelIdArg);
|
||||
rootCommand.AddArgument(serverUrlArg);
|
||||
rootCommand.AddArgument(tokenArg);
|
||||
|
||||
rootCommand.SetHandler(Handle, pathArg, resultsPathArg, projectIdArg, modelIdArg, serverUrlArg, tokenArg);
|
||||
|
||||
await rootCommand.InvokeAsync(args).ConfigureAwait(false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#pragma warning disable CA1031
|
||||
catch (Exception e)
|
||||
#pragma warning restore CA1031
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable CA1506
|
||||
private static async Task Handle(
|
||||
string filePath,
|
||||
string resultsPath,
|
||||
string projectId,
|
||||
string modelId,
|
||||
#pragma warning restore CA1506
|
||||
string serverUrl,
|
||||
string token
|
||||
)
|
||||
{
|
||||
// Create file with account info here
|
||||
var accountsDir = SpecklePathProvider.AccountsFolderPath;
|
||||
if (!Directory.Exists(accountsDir))
|
||||
{
|
||||
Directory.CreateDirectory(accountsDir);
|
||||
}
|
||||
|
||||
using (new RhinoCore([], WindowStyle.NoWindow))
|
||||
{
|
||||
//doc is often null so dispose the active doc too
|
||||
using var doc = RhinoDoc.Open(filePath, out _);
|
||||
using var __ = RhinoDoc.ActiveDoc;
|
||||
var services = new ServiceCollection();
|
||||
services.Initialize(HostApplications.RhinoImporter, HostAppVersion.v8);
|
||||
services.AddRhino(false);
|
||||
services.AddRhinoConverters();
|
||||
// override default
|
||||
services.AddSingleton<IThreadContext>(new ImporterThreadContext());
|
||||
services.AddTransient<Progress>();
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.Enrich.FromLogContext()
|
||||
.WriteTo.Console(new RenderedCompactJsonFormatter())
|
||||
.CreateLogger();
|
||||
services.AddLogging(loggingBuilder =>
|
||||
{
|
||||
loggingBuilder.ClearProviders();
|
||||
loggingBuilder.AddSerilog(dispose: true);
|
||||
});
|
||||
|
||||
// but the Rhino connector has `.rhp` as it is extension.
|
||||
var container = services.BuildServiceProvider();
|
||||
try
|
||||
{
|
||||
var sender = ActivatorUtilities.CreateInstance<Sender>(container);
|
||||
var version = await sender.Send(projectId, modelId, new Uri(serverUrl), token);
|
||||
|
||||
var result =
|
||||
version == null
|
||||
? new RhinoImportResult() { Success = false, ErrorMessage = "Failed to create version!" }
|
||||
: new RhinoImportResult() { Success = true, CommitId = version.id };
|
||||
|
||||
File.WriteAllText(resultsPath, JsonConvert.SerializeObject(result));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
container.GetRequiredService<ILogger<Sender>>().LogError(ex, "Fatal error for import");
|
||||
var results = new RhinoImportResult() { Success = false, ErrorMessage = ex.Message, };
|
||||
File.WriteAllText(resultsPath, JsonConvert.SerializeObject(results));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
using System.Reflection;
|
||||
using Speckle.Sdk.Common;
|
||||
|
||||
namespace Speckle.Importers.Rhino;
|
||||
|
||||
//from https://github.com/mcneel/rhino.inside/blob/master/LICENSE
|
||||
// https://github.com/mcneel/rhino.inside/commit/19b4d571ab3b02f5b29f1e0cf9112845e3323697
|
||||
// https://github.com/mcneel/rhino.inside/blob/master/DotNet/RhinoInside/Resolver.cs
|
||||
//
|
||||
public static class Resolver
|
||||
{
|
||||
/// <summary>
|
||||
/// Set up an assembly resolver to load RhinoCommon and other Rhino
|
||||
/// assemblies from where Rhino is installed
|
||||
/// </summary>
|
||||
public static void Initialize()
|
||||
{
|
||||
if (System.IntPtr.Size != 8)
|
||||
{
|
||||
throw new InvalidOperationException("Only 64 bit applications can use RhinoInside");
|
||||
}
|
||||
|
||||
AppDomain.CurrentDomain.AssemblyResolve += ResolveForRhinoAssemblies;
|
||||
}
|
||||
|
||||
private static string? s_rhinoSystemDirectory;
|
||||
|
||||
/// <summary>
|
||||
/// Directory used by assembly resolver to attempt load core Rhino assemblies. If not manually set,
|
||||
/// this will be determined by inspecting the registry
|
||||
/// </summary>
|
||||
public static string? RhinoSystemDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(s_rhinoSystemDirectory))
|
||||
{
|
||||
s_rhinoSystemDirectory = FindRhinoSystemDirectory();
|
||||
}
|
||||
|
||||
return s_rhinoSystemDirectory;
|
||||
}
|
||||
set => s_rhinoSystemDirectory = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not to use the newest installation of Rhino on the system. By default the resolver will only use an
|
||||
/// installation with a matching major version.
|
||||
/// </summary>
|
||||
public static bool UseLatest { get; set; }
|
||||
|
||||
private static Assembly? ResolveForRhinoAssemblies(object? sender, ResolveEventArgs args)
|
||||
{
|
||||
string? assemblyName = new AssemblyName(args.Name).Name;
|
||||
if (RhinoSystemDirectory is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string path = System.IO.Path.Combine(RhinoSystemDirectory, assemblyName + ".dll");
|
||||
if (assemblyName != null && assemblyName.StartsWith("Speckle.Connectors.Rhino"))
|
||||
{
|
||||
path = Path.Combine(
|
||||
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location).NotNull(),
|
||||
assemblyName + ".rhp"
|
||||
);
|
||||
}
|
||||
|
||||
if (System.IO.File.Exists(path))
|
||||
{
|
||||
return Assembly.LoadFrom(path);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string? FindRhinoSystemDirectory()
|
||||
{
|
||||
var major = 8;
|
||||
string baseName = @"SOFTWARE\McNeel\Rhinoceros";
|
||||
using var baseKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(baseName);
|
||||
if (baseKey == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
string[] children = baseKey.GetSubKeyNames();
|
||||
Array.Sort(children);
|
||||
string versionName = "";
|
||||
for (int i = children.Length - 1; i >= 0; i--)
|
||||
{
|
||||
// 20 Jan 2020 S. Baer (https://github.com/mcneel/rhino.inside/issues/248)
|
||||
// A generic double.TryParse is failing when run under certain locales.
|
||||
if (
|
||||
double.TryParse(
|
||||
children[i],
|
||||
System.Globalization.NumberStyles.Any,
|
||||
System.Globalization.CultureInfo.InvariantCulture,
|
||||
out double d
|
||||
)
|
||||
)
|
||||
{
|
||||
versionName = children[i];
|
||||
|
||||
if (!UseLatest && (int)Math.Floor(d) != major)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
using var installKey = baseKey.OpenSubKey($"{versionName}\\Install");
|
||||
if (installKey is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
string? corePath = (string?)installKey.GetValue("CoreDllPath");
|
||||
if (System.IO.File.Exists(corePath))
|
||||
{
|
||||
return System.IO.Path.GetDirectoryName(corePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using Speckle.Newtonsoft.Json;
|
||||
|
||||
namespace Speckle.Importers.Rhino;
|
||||
|
||||
public class RhinoImportResult
|
||||
{
|
||||
[JsonProperty("success")]
|
||||
public bool Success { get; set; }
|
||||
|
||||
[JsonProperty("commitId")]
|
||||
public string CommitId { get; set; }
|
||||
|
||||
[JsonProperty("errorMessage")]
|
||||
public string ErrorMessage { get; set; }
|
||||
}
|
||||
@@ -1,70 +1 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Rhino;
|
||||
using Rhino.DocObjects;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Rhino;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Speckle.Sdk.Logging;
|
||||
using Version = Speckle.Sdk.Api.GraphQL.Models.Version;
|
||||
|
||||
namespace Speckle.Importers.Rhino;
|
||||
|
||||
public class Sender(
|
||||
ISdkActivityFactory activityFactory,
|
||||
IServiceProvider serviceProvider,
|
||||
IRhinoConversionSettingsFactory rhinoConversionSettingsFactory,
|
||||
IAccountFactory accountFactory,
|
||||
Progress progress,
|
||||
ILogger<Sender> logger
|
||||
)
|
||||
{
|
||||
public async Task<Version?> Send(string projectId, string modelId, Uri serverUrl, string token)
|
||||
{
|
||||
// NOTE: introduction of AddVisualizationProperties setting not accounted for, hence hardcoded as true (i.e. "as before")
|
||||
using var activity = activityFactory.Start();
|
||||
using var scope = serviceProvider.CreateScope();
|
||||
scope
|
||||
.ServiceProvider.GetRequiredService<IConverterSettingsStore<RhinoConversionSettings>>()
|
||||
.Initialize(rhinoConversionSettingsFactory.Create(RhinoDoc.ActiveDoc, true));
|
||||
try
|
||||
{
|
||||
List<RhinoObject> rhinoObjects = RhinoDoc
|
||||
.ActiveDoc.Objects.GetObjectList(ObjectType.AnyObject)
|
||||
.Where(obj => obj != null)
|
||||
.ToList();
|
||||
|
||||
if (rhinoObjects.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var account = await accountFactory.CreateAccount(serverUrl, token);
|
||||
var operation = scope.ServiceProvider.GetRequiredService<SendOperation<RhinoObject>>();
|
||||
var buildResults = await operation.Build(rhinoObjects, projectId, progress, CancellationToken.None);
|
||||
var (results, version) = await operation.Send(
|
||||
buildResults.RootObject,
|
||||
projectId,
|
||||
modelId,
|
||||
token,
|
||||
null,
|
||||
account,
|
||||
progress,
|
||||
CancellationToken.None
|
||||
);
|
||||
|
||||
logger.LogInformation($"Root: {results.RootId}");
|
||||
|
||||
return version;
|
||||
}
|
||||
#pragma warning disable CA1031
|
||||
catch (Exception ex)
|
||||
#pragma warning restore CA1031
|
||||
{
|
||||
logger.LogError(ex, "Error while sending");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<Configurations>Debug;Release;Local</Configurations>
|
||||
<OutputType>Exe</OutputType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<Configurations>Debug;Release;Local</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" />
|
||||
<PackageReference Include="RhinoWindows" IncludeAssets="compile; build" PrivateAssets="all" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" />
|
||||
<PackageReference Include="Serilog.Formatting.Compact" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" />
|
||||
<PackageReference Include="System.CommandLine" />
|
||||
<PackageReference Include="RhinoCommon" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.21.25188.17001"/>
|
||||
<PackageReference Include="Grasshopper" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.21.25188.17001"/>
|
||||
<PackageReference Include="RhinoWindows" IncludeAssets="compile; build" PrivateAssets="all" VersionOverride="8.21.25188.17001"/>
|
||||
<PackageReference Include="Rhino.Inside" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\Connectors\Rhino\Speckle.Connectors.Rhino8\Speckle.Connectors.Rhino8.csproj" />
|
||||
<ProjectReference Include="..\..\..\Connectors\Rhino\Speckle.Connectors.RhinoImporter\Speckle.Connectors.RhinoImporter.csproj" />
|
||||
<ProjectReference Include="..\..\..\Converters\Rhino\Speckle.Converters.Rhino8\Speckle.Converters.Rhino8.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
{
|
||||
"version": 2,
|
||||
"dependencies": {
|
||||
".NETFramework,Version=v4.8": {
|
||||
"net8.0-windows7.0": {
|
||||
"Grasshopper": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.21.25188.17001, )",
|
||||
"resolved": "8.21.25188.17001",
|
||||
"contentHash": "qpt9XQWuKLFfzn2cnYf5akfHRwYuffthK5AXsSY1UhjD0eG6bYSTtmAkNVH+Wdg130MZkGqJmPd012Om1kJPBA==",
|
||||
"dependencies": {
|
||||
"RhinoCommon": "[8.21.25188.17001]"
|
||||
}
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.3, )",
|
||||
"resolved": "1.0.3",
|
||||
"contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
|
||||
"dependencies": {
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3"
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3"
|
||||
}
|
||||
},
|
||||
"Microsoft.SourceLink.GitHub": {
|
||||
@@ -27,47 +36,32 @@
|
||||
"resolved": "1.14.1",
|
||||
"contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ=="
|
||||
},
|
||||
"Rhino.Inside": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.7-beta, )",
|
||||
"resolved": "8.0.7-beta",
|
||||
"contentHash": "PNZdWysS2zI9vaxJ8HQig3iZA3Xg1yG1hnq09VPStQpceoj94iJ6xk0Ubddaw5oswkYYUIJRmQ8xr+bLTROs/g==",
|
||||
"dependencies": {
|
||||
"Grasshopper": "8.0.23304.9001",
|
||||
"RhinoCommon": "8.0.23304.9001"
|
||||
}
|
||||
},
|
||||
"RhinoCommon": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.9.24194.18121, )",
|
||||
"resolved": "8.9.24194.18121",
|
||||
"contentHash": "XRMnm38sBFeMT5AAtRTJdSaql/YNtT02AGi8TEVP1VZ4fkm8VJ1q2nNioWN3tW/+H8Tdi4nV+DuhB/5uE41MCg=="
|
||||
"requested": "[8.21.25188.17001, )",
|
||||
"resolved": "8.21.25188.17001",
|
||||
"contentHash": "Wo6JXheyDBvilyIwDY6xZCQJC4170jzAlTSlMgh8yokUY+vYyCl4KJVXZofIOynNt/xx5wLqb2On5gZZekXR6w==",
|
||||
"dependencies": {
|
||||
"System.Drawing.Common": "7.0.0"
|
||||
}
|
||||
},
|
||||
"RhinoWindows": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.9.24194.18121, )",
|
||||
"resolved": "8.9.24194.18121",
|
||||
"contentHash": "E+MMgC1ITYiPjcHTd+N2vLlg6pNDnRpNL1yGalfPp1eqHJiXkzsGpM1D8oz865D6ybnJAHOOeEVaKqUzFUSUag==",
|
||||
"requested": "[8.21.25188.17001, )",
|
||||
"resolved": "8.21.25188.17001",
|
||||
"contentHash": "9zqCorcLRBeiW/j1RTwUS4E7bnZetAdA9WDdtd/AQccjOpxdtw76wdN+ciyQ6qslseWkwZ9qSBeh7QaM800Ntw==",
|
||||
"dependencies": {
|
||||
"RhinoCommon": "[8.9.24194.18121]"
|
||||
}
|
||||
},
|
||||
"Serilog.Extensions.Logging": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "YEAMWu1UnWgf1c1KP85l1SgXGfiVo0Rz6x08pCiPOIBt2Qe18tcZLvdBUuV5o1QHvrs8FAry9wTIhgBRtjIlEg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging": "8.0.0",
|
||||
"Serilog": "3.1.1"
|
||||
}
|
||||
},
|
||||
"Serilog.Formatting.Compact": {
|
||||
"type": "Direct",
|
||||
"requested": "[3.0.0, )",
|
||||
"resolved": "3.0.0",
|
||||
"contentHash": "wQsv14w9cqlfB5FX2MZpNsTawckN4a8dryuNGbebB/3Nh1pXnROHZov3swtu3Nj5oNG7Ba+xdu7Et/ulAUPanQ==",
|
||||
"dependencies": {
|
||||
"Serilog": "4.0.0"
|
||||
}
|
||||
},
|
||||
"Serilog.Sinks.Console": {
|
||||
"type": "Direct",
|
||||
"requested": "[6.0.0, )",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "fQGWqVMClCP2yEyTXPIinSr5c+CBGUvBybPxjAGcf7ctDhadFhrQw03Mv8rJ07/wR5PDfFjewf2LimvXCDzpbA==",
|
||||
"dependencies": {
|
||||
"Serilog": "4.0.0"
|
||||
"RhinoCommon": "[8.21.25188.17001]"
|
||||
}
|
||||
},
|
||||
"Speckle.InterfaceGenerator": {
|
||||
@@ -76,15 +70,6 @@
|
||||
"resolved": "0.9.6",
|
||||
"contentHash": "HKH7tYrYYlCK1ct483hgxERAdVdMtl7gUKW9ijWXxA1UsYR4Z+TrRHYmzZ9qmpu1NnTycSrp005NYM78GDKV1w=="
|
||||
},
|
||||
"System.CommandLine": {
|
||||
"type": "Direct",
|
||||
"requested": "[2.0.0-beta4.22272.1, )",
|
||||
"resolved": "2.0.0-beta4.22272.1",
|
||||
"contentHash": "1uqED/q2H0kKoLJ4+hI2iPSBSEdTuhfCYADeJrAqERmiGQ2NNacYKRNEQ+gFbU4glgVyK8rxI+ZOe1onEtr/Pg==",
|
||||
"dependencies": {
|
||||
"System.Memory": "4.5.4"
|
||||
}
|
||||
},
|
||||
"GraphQL.Client": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
@@ -92,7 +77,6 @@
|
||||
"dependencies": {
|
||||
"GraphQL.Client.Abstractions": "6.0.0",
|
||||
"GraphQL.Client.Abstractions.Websocket": "6.0.0",
|
||||
"System.Net.WebSockets.Client.Managed": "1.0.22",
|
||||
"System.Reactive": "5.0.0"
|
||||
}
|
||||
},
|
||||
@@ -117,29 +101,11 @@
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "yg72rrYDapfsIUrul7aF6wwNnTJBOFvuA9VdDTQpPa8AlAriHbufeXYLBcodKjfUdkCnaiggX1U/nEP08Zb5GA=="
|
||||
},
|
||||
"Microsoft.Bcl.AsyncInterfaces": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
|
||||
"dependencies": {
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.Bcl.HashCode": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.1.1",
|
||||
"contentHash": "MalY0Y/uM/LjXtHfX/26l2VtN4LDNZ2OE3aumNOHDLsT4fNYy2hiHXI4CXCqKpNUNm7iJ2brrc4J89UdaL56FA=="
|
||||
},
|
||||
"Microsoft.Build.Tasks.Git": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
|
||||
},
|
||||
"Microsoft.CSharp": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.7.0",
|
||||
"contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
|
||||
},
|
||||
"Microsoft.Data.Sqlite": {
|
||||
"type": "Transitive",
|
||||
"resolved": "7.0.5",
|
||||
@@ -157,44 +123,69 @@
|
||||
"SQLitePCLRaw.core": "2.1.4"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "nOP8R1mVb/6mZtm2qgAJXn/LFm/2kMjHDAg/QJLFG6CuWYJtaD3p1BwQhufBVvRzL9ceJ/xF0SQ0qsI2GkDQAA==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "65MrmXCziWaQFrI0UHkQbesrX5wTwf9XPjY5yFm/VkgJKFJ5gqvXRoXjIZcf2wLi5ZlwGz/oMYfyURVCWbM5iw==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Primitives": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Configuration.Binder": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "vJ9xvOZCnUAIHcGC3SU35r3HKmHTVIeHzo6u/qzlHAqD8m6xv92MLin4oJntTvkpKxVX3vI1GFFkIQtU3AdlsQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Configuration": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": "8.0.0",
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
}
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Options": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "UpZLNLBpIZ0GTebShui7xXYh6DmBHjWM8NxGxZbdQh/bPZ5e6YswqI+bru6BnEL5eWiOdodsXtEz3FROcgi/qg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
|
||||
"Microsoft.Extensions.Primitives": "8.0.0",
|
||||
"System.ValueTuple": "4.5.0"
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Primitives": "2.2.0",
|
||||
"System.ComponentModel.Annotations": "4.5.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==",
|
||||
"dependencies": {
|
||||
"System.Memory": "4.5.5",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
"System.Memory": "4.5.1",
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net48": {
|
||||
"Microsoft.NETFramework.ReferenceAssemblies.net461": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.3",
|
||||
"contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ=="
|
||||
"contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA=="
|
||||
},
|
||||
"Microsoft.SourceLink.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
|
||||
},
|
||||
"Microsoft.Win32.SystemEvents": {
|
||||
"type": "Transitive",
|
||||
"resolved": "7.0.0",
|
||||
"contentHash": "2nXPrhdAyAzir0gLl8Yy8S5Mnm/uBSQQA7jEsILOS1MTyS7DbmV1NgViMtvV1sfCD1ebITpNwb1NIinKeJgUVQ=="
|
||||
},
|
||||
"Speckle.Newtonsoft.Json": {
|
||||
"type": "Transitive",
|
||||
"resolved": "13.0.2",
|
||||
@@ -206,7 +197,7 @@
|
||||
"contentHash": "EWI1olKDjFEBMJu0+3wuxwziIAdWDVMYLhuZ3Qs84rrz+DHwD00RzWPZCa+bLnHCf3oJwuFZIRsHT5p236QXww==",
|
||||
"dependencies": {
|
||||
"SQLitePCLRaw.lib.e_sqlite3": "2.1.4",
|
||||
"SQLitePCLRaw.provider.dynamic_cdecl": "2.1.4"
|
||||
"SQLitePCLRaw.provider.e_sqlite3": "2.1.4"
|
||||
}
|
||||
},
|
||||
"SQLitePCLRaw.core": {
|
||||
@@ -222,113 +213,41 @@
|
||||
"resolved": "2.1.4",
|
||||
"contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg=="
|
||||
},
|
||||
"SQLitePCLRaw.provider.dynamic_cdecl": {
|
||||
"SQLitePCLRaw.provider.e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
"contentHash": "ZsaKKhgYF9B1fvcnOGKl3EycNAwd9CRWX7v0rEfuPWhQQ5Jjpvf2VEHahiLIGHio3hxi3EIKFJw9KvyowWOUAw==",
|
||||
"contentHash": "CSlb5dUp1FMIkez9Iv5EXzpeq7rHryVNqwJMWnpq87j9zWZexaEMdisDktMsnnrzKM6ahNrsTkjqNodTBPBxtQ==",
|
||||
"dependencies": {
|
||||
"SQLitePCLRaw.core": "2.1.4"
|
||||
}
|
||||
},
|
||||
"System.Buffers": {
|
||||
"System.ComponentModel.Annotations": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.1",
|
||||
"contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
|
||||
"resolved": "4.5.0",
|
||||
"contentHash": "UxYQ3FGUOtzJ7LfSdnYSFd7+oEv6M8NgUatatIN2HxNtDdlcvFAf+VIq4Of9cDMJEJC0aSRv/x898RYhB4Yppg=="
|
||||
},
|
||||
"System.Collections.Immutable": {
|
||||
"System.Drawing.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "9.0.4",
|
||||
"contentHash": "wfm2NgK22MmBe5qJjp52qzpkeDZKb4l9LbdubhZSehY1z4LS+lld6R+B+UQNb2AZRHu/QJlHxEUcRst5hIEejg==",
|
||||
"resolved": "7.0.0",
|
||||
"contentHash": "KIX+oBU38pxkKPxvLcLfIkOV5Ien8ReN78wro7OF5/erwcmortzeFx+iBswlh2Vz6gVne0khocQudGwaO1Ey6A==",
|
||||
"dependencies": {
|
||||
"System.Memory": "4.5.5",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
}
|
||||
},
|
||||
"System.Diagnostics.DiagnosticSource": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.1",
|
||||
"contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
|
||||
"dependencies": {
|
||||
"System.Memory": "4.5.5",
|
||||
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||
}
|
||||
},
|
||||
"System.Formats.Nrbf": {
|
||||
"type": "Transitive",
|
||||
"resolved": "9.0.4",
|
||||
"contentHash": "HuDjUInoaQvnX+3bRg2lX+KLD2raiDJA0v3c4hmukAHSQT8rpa6+2NKRoz+1W8Jmbu5ocvc+xPPKgHMVjeJpOg==",
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.HashCode": "1.1.1",
|
||||
"System.Reflection.Metadata": "9.0.4",
|
||||
"System.ValueTuple": "4.5.0"
|
||||
"Microsoft.Win32.SystemEvents": "7.0.0"
|
||||
}
|
||||
},
|
||||
"System.Memory": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.5",
|
||||
"contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.5.1",
|
||||
"System.Numerics.Vectors": "4.5.0",
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
|
||||
}
|
||||
},
|
||||
"System.Net.WebSockets.Client.Managed": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.22",
|
||||
"contentHash": "WqEOxPlXjuZrIjUtXNE9NxEfU/n5E35iV2PtoZdJSUC4tlrqwHnTee+wvMIM4OUaJWmwrymeqcgYrE0IkGAgLA==",
|
||||
"dependencies": {
|
||||
"System.Buffers": "4.4.0",
|
||||
"System.Numerics.Vectors": "4.4.0"
|
||||
}
|
||||
},
|
||||
"System.Numerics.Vectors": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.0",
|
||||
"contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
|
||||
"resolved": "4.5.3",
|
||||
"contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA=="
|
||||
},
|
||||
"System.Reactive": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ==",
|
||||
"dependencies": {
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
}
|
||||
},
|
||||
"System.Reflection.Metadata": {
|
||||
"type": "Transitive",
|
||||
"resolved": "9.0.4",
|
||||
"contentHash": "qeJNsMmZPc/Lieg0Md+D4F6LoLcxV3b9QsUNmBRXc2ZVOkMbAcwuO9l2jbQFv3n+fLiHJilN8v6i5aJNivjrCQ==",
|
||||
"dependencies": {
|
||||
"System.Collections.Immutable": "9.0.4",
|
||||
"System.Memory": "4.5.5"
|
||||
}
|
||||
"contentHash": "erBZjkQHWL9jpasCE/0qKAryzVBJFxGHVBAvgRN1bzM0q2s1S4oYREEEL0Vb+1kA/6BKb5FjUZMp5VXmy+gzkQ=="
|
||||
},
|
||||
"System.Runtime.CompilerServices.Unsafe": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
|
||||
},
|
||||
"System.Threading.Channels": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "CMaFr7v+57RW7uZfZkPExsPB6ljwzhjACWW1gfU35Y56rk72B/Wu+sTqxVmGSk4SFUlPc3cjeKND0zktziyjBA==",
|
||||
"dependencies": {
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
}
|
||||
},
|
||||
"System.Threading.Tasks.Extensions": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.4",
|
||||
"contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
|
||||
"dependencies": {
|
||||
"System.Runtime.CompilerServices.Unsafe": "4.5.3"
|
||||
}
|
||||
},
|
||||
"System.ValueTuple": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.5.0",
|
||||
"contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ=="
|
||||
"resolved": "4.5.1",
|
||||
"contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw=="
|
||||
},
|
||||
"speckle.connectors.common": {
|
||||
"type": "Project",
|
||||
@@ -354,23 +273,15 @@
|
||||
"Speckle.Connectors.DUI": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.grasshopper8": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"GrasshopperAsyncComponent": "[2.0.3, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Converters.Rhino8": "[1.0.0, )",
|
||||
"System.Resources.Extensions": "[9.0.4, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.logging": {
|
||||
"type": "Project"
|
||||
},
|
||||
"speckle.connectors.rhino8": {
|
||||
"speckle.connectors.rhinoimporter": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Connectors.DUI.WebView": "[1.0.0, )",
|
||||
"Speckle.Connectors.Grasshopper8": "[1.0.0, )"
|
||||
"Speckle.Converters.Rhino8": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.common": {
|
||||
@@ -383,54 +294,35 @@
|
||||
"speckle.converters.rhino8": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"RhinoCommon": "[8.9.24194.18121, )",
|
||||
"Speckle.Converters.Common": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
"GrasshopperAsyncComponent": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.0.3, )",
|
||||
"resolved": "2.0.3",
|
||||
"contentHash": "AZvHP96WhYZWftVi7J3J65LiZmXO3hGS6W4AntMMb099gkrLqeiBKC2DOYD6YM9cOyQqly3S5knbUL2yr0jc4Q==",
|
||||
"dependencies": {
|
||||
"PolySharp": "1.14.1"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "MZtBIwfDFork5vfjpJdG5g8wuJFt7d/y3LOSVVtDK/76wlbtz6cjltfKHqLx2TKVqTj5/c41t77m1+h20zqtPA==",
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": "8.0.0",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
|
||||
"System.Threading.Tasks.Extensions": "4.5.4"
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "Nxqhadc9FCmFHzU+fz3oc8sFlE6IadViYg8dfUdGzJZ2JUxnCsRghBhhOWdM4B2zSZqEc+0BjliBh/oNdRZuig==",
|
||||
"dependencies": {
|
||||
"Microsoft.Bcl.AsyncInterfaces": "8.0.0",
|
||||
"Microsoft.Extensions.DependencyInjection": "8.0.0",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.0",
|
||||
"Microsoft.Extensions.Options": "8.0.0",
|
||||
"System.Diagnostics.DiagnosticSource": "8.0.0",
|
||||
"System.ValueTuple": "4.5.0"
|
||||
"Microsoft.Extensions.Configuration.Binder": "2.2.0",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Options": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.Logging.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
|
||||
"System.Buffers": "4.5.1",
|
||||
"System.Memory": "4.5.5"
|
||||
}
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "B2WqEox8o+4KUOpL7rZPyh6qYjik8tHi2tN8Z9jZkHzED8ElYgZa/h6K+xliB435SqUcWT290Fr2aa8BtZjn8A=="
|
||||
},
|
||||
"Microsoft.Web.WebView2": {
|
||||
"type": "CentralTransitive",
|
||||
@@ -438,16 +330,6 @@
|
||||
"resolved": "1.0.1938.49",
|
||||
"contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw=="
|
||||
},
|
||||
"Serilog": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[4.0.1, )",
|
||||
"resolved": "4.0.0",
|
||||
"contentHash": "2jDkUrSh5EofOp7Lx5Zgy0EB+7hXjjxE2ktTb1WVQmU00lDACR2TdROGKU0K1pDTBSJBN1PqgYpgOZF8mL7NJw==",
|
||||
"dependencies": {
|
||||
"System.Diagnostics.DiagnosticSource": "8.0.1",
|
||||
"System.Threading.Channels": "8.0.0"
|
||||
}
|
||||
},
|
||||
"Speckle.DoubleNumerics": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[4.1.0, )",
|
||||
@@ -470,8 +352,6 @@
|
||||
"contentHash": "J//TnVqZ+RzvMoRu0x3HjVb+7rnYTRMCLQ0bN05fagl0UwyHYrc+Lwn19eyTPMnRoAQIFPXIdZjb2Yk7fyd0FA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
"Microsoft.Data.Sqlite": "7.0.5",
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0",
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
@@ -485,29 +365,6 @@
|
||||
"requested": "[3.5.1, )",
|
||||
"resolved": "3.5.1",
|
||||
"contentHash": "jNFvbO0CVzBKSGAPtN2J20aixChyqVetLSU/4TwjVERY8UJdbhbvoxYIalaBZoTSIXdQoHshNC7Ul1o6+vTCcA=="
|
||||
},
|
||||
"System.Resources.Extensions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[9.0.4, )",
|
||||
"resolved": "9.0.4",
|
||||
"contentHash": "X1oj5i1gy6O4TDU8Z48djqbDJJz4qkT+LgvD0Z2pbCsXieJNms9H5yBbvahXdEJad07Ntai5bST4J5XWoNmMsg==",
|
||||
"dependencies": {
|
||||
"System.Formats.Nrbf": "9.0.4",
|
||||
"System.Memory": "4.5.5"
|
||||
}
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8/win7-x64": {
|
||||
"SQLitePCLRaw.lib.e_sqlite3": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.1.4",
|
||||
"contentHash": "2C9Q9eX7CPLveJA0rIhf9RXAvu+7nWZu1A2MdG6SD/NOu26TakGgL1nsbc0JAspGijFOo3HoN79xrx8a368fBg=="
|
||||
},
|
||||
"Microsoft.Web.WebView2": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[1.0.1938.49, )",
|
||||
"resolved": "1.0.1938.49",
|
||||
"contentHash": "z8KnFnaTYzhA/ZnyRX0qGfS1NU5ZBJeClAH64F0fVDvdDJTvME7xl6zTJ0Jlfe1BtL3C0NH9xTy64shg2baKdw=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,6 +230,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.Grasshop
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.Rhino8", "Connectors\Rhino\Speckle.Connectors.Rhino8\Speckle.Connectors.Rhino8.csproj", "{5807B4F7-5CCB-E01D-A024-0E8BEFE0D7F0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.RhinoImporter", "Connectors\Rhino\Speckle.Connectors.RhinoImporter\Speckle.Connectors.RhinoImporter.csproj", "{90EFBF9F-FED4-BD4B-DD87-F3F184C1D073}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Converters.Rhino8", "Converters\Rhino\Speckle.Converters.Rhino8\Speckle.Converters.Rhino8.csproj", "{0D2A2F22-EAB0-802A-7ECE-AB012CE69B8D}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{515D0695-DED8-6A19-72D5-7E987736EE52}"
|
||||
@@ -281,6 +283,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Importers.Ifc", "Im
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Rhino", "Rhino", "{8F2BAB74-BA0A-005A-0FF9-9498950E3ADC}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Importers.JobProcessor", "Importers\Rhino\Speckle.Importers.JobProcessor\Speckle.Importers.JobProcessor.csproj", "{391676A9-0E05-9346-FDB9-4F5296F00509}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Importers.Rhino", "Importers\Rhino\Speckle.Importers.Rhino\Speckle.Importers.Rhino.csproj", "{A9B240B9-4F3D-DB41-CB33-BD2019BBCCF2}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sdk", "Sdk", "{FF13A09E-0E72-1904-124B-A4E979EF28D3}"
|
||||
@@ -307,11 +311,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Performance", "Sdk\
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Testing", "Sdk\Speckle.Testing\Speckle.Testing.csproj", "{02E79865-6261-CDE0-77EA-3DD2E01DACBD}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "..\speckle-sharp-sdk\src\Speckle.Objects\Speckle.Objects", "..\speckle-sharp-sdk\src\Speckle.Objects\Speckle.Objects.csproj", "{FBD58ABD-9704-C232-7CF3-D96EC5CFCBE6}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Objects", "..\speckle-sharp-sdk\src\Speckle.Objects\Speckle.Objects.csproj", "{FBD58ABD-9704-C232-7CF3-D96EC5CFCBE6}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "..\speckle-sharp-sdk\src\Speckle.Sdk\Speckle.Sdk", "..\speckle-sharp-sdk\src\Speckle.Sdk\Speckle.Sdk.csproj", "{0201A282-BEFA-8897-C535-03EBAC94870B}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Sdk", "..\speckle-sharp-sdk\src\Speckle.Sdk\Speckle.Sdk.csproj", "{0201A282-BEFA-8897-C535-03EBAC94870B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "..\speckle-sharp-sdk\src\Speckle.Sdk.Dependencies\Speckle.Sdk.Dependencies", "..\speckle-sharp-sdk\src\Speckle.Sdk.Dependencies\Speckle.Sdk.Dependencies.csproj", "{0677CA87-027F-D526-4463-3517F75B76EC}"
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Sdk.Dependencies", "..\speckle-sharp-sdk\src\Speckle.Sdk.Dependencies\Speckle.Sdk.Dependencies.csproj", "{0677CA87-027F-D526-4463-3517F75B76EC}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@@ -650,6 +654,12 @@ Global
|
||||
{5807B4F7-5CCB-E01D-A024-0E8BEFE0D7F0}.Local|Any CPU.Build.0 = Local|Any CPU
|
||||
{5807B4F7-5CCB-E01D-A024-0E8BEFE0D7F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5807B4F7-5CCB-E01D-A024-0E8BEFE0D7F0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{90EFBF9F-FED4-BD4B-DD87-F3F184C1D073}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{90EFBF9F-FED4-BD4B-DD87-F3F184C1D073}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{90EFBF9F-FED4-BD4B-DD87-F3F184C1D073}.Local|Any CPU.ActiveCfg = Local|Any CPU
|
||||
{90EFBF9F-FED4-BD4B-DD87-F3F184C1D073}.Local|Any CPU.Build.0 = Local|Any CPU
|
||||
{90EFBF9F-FED4-BD4B-DD87-F3F184C1D073}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{90EFBF9F-FED4-BD4B-DD87-F3F184C1D073}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0D2A2F22-EAB0-802A-7ECE-AB012CE69B8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0D2A2F22-EAB0-802A-7ECE-AB012CE69B8D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0D2A2F22-EAB0-802A-7ECE-AB012CE69B8D}.Local|Any CPU.ActiveCfg = Local|Any CPU
|
||||
@@ -710,6 +720,12 @@ Global
|
||||
{9F11639F-3AB6-867F-7968-FFF8BFD8C410}.Local|Any CPU.Build.0 = Local|Any CPU
|
||||
{9F11639F-3AB6-867F-7968-FFF8BFD8C410}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9F11639F-3AB6-867F-7968-FFF8BFD8C410}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{391676A9-0E05-9346-FDB9-4F5296F00509}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{391676A9-0E05-9346-FDB9-4F5296F00509}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{391676A9-0E05-9346-FDB9-4F5296F00509}.Local|Any CPU.ActiveCfg = Local|Any CPU
|
||||
{391676A9-0E05-9346-FDB9-4F5296F00509}.Local|Any CPU.Build.0 = Local|Any CPU
|
||||
{391676A9-0E05-9346-FDB9-4F5296F00509}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{391676A9-0E05-9346-FDB9-4F5296F00509}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A9B240B9-4F3D-DB41-CB33-BD2019BBCCF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A9B240B9-4F3D-DB41-CB33-BD2019BBCCF2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A9B240B9-4F3D-DB41-CB33-BD2019BBCCF2}.Local|Any CPU.ActiveCfg = Local|Any CPU
|
||||
@@ -904,6 +920,7 @@ Global
|
||||
{DB77E488-CD1B-681C-687F-AFA935E8E6A8} = {1FE9B3A2-9228-8E78-261C-2221B28F0E7B}
|
||||
{2DF824DD-0A2F-0B15-F225-B054C92BC4EA} = {DB77E488-CD1B-681C-687F-AFA935E8E6A8}
|
||||
{5807B4F7-5CCB-E01D-A024-0E8BEFE0D7F0} = {DB77E488-CD1B-681C-687F-AFA935E8E6A8}
|
||||
{90EFBF9F-FED4-BD4B-DD87-F3F184C1D073} = {DB77E488-CD1B-681C-687F-AFA935E8E6A8}
|
||||
{0D2A2F22-EAB0-802A-7ECE-AB012CE69B8D} = {DB77E488-CD1B-681C-687F-AFA935E8E6A8}
|
||||
{515D0695-DED8-6A19-72D5-7E987736EE52} = {1FE9B3A2-9228-8E78-261C-2221B28F0E7B}
|
||||
{66FDCF46-2365-F12E-9F40-92E86543B5A2} = {515D0695-DED8-6A19-72D5-7E987736EE52}
|
||||
@@ -927,6 +944,7 @@ Global
|
||||
{9DC47E2F-957F-CECC-82C0-1BAD872F8E13} = {88E31408-0177-4235-0BE8-6C9C8103E392}
|
||||
{9F11639F-3AB6-867F-7968-FFF8BFD8C410} = {88E31408-0177-4235-0BE8-6C9C8103E392}
|
||||
{8F2BAB74-BA0A-005A-0FF9-9498950E3ADC} = {0B7587C3-F447-44C0-7126-E1085C8A32F4}
|
||||
{391676A9-0E05-9346-FDB9-4F5296F00509} = {8F2BAB74-BA0A-005A-0FF9-9498950E3ADC}
|
||||
{A9B240B9-4F3D-DB41-CB33-BD2019BBCCF2} = {8F2BAB74-BA0A-005A-0FF9-9498950E3ADC}
|
||||
{9AA81E7A-C9BC-C56B-BE8F-55BC1D85EEBF} = {FF13A09E-0E72-1904-124B-A4E979EF28D3}
|
||||
{1BD0E5FD-D4B8-8018-08FE-E7DD4ABA741F} = {FF13A09E-0E72-1904-124B-A4E979EF28D3}
|
||||
|
||||
+7
-3
@@ -151,6 +151,7 @@
|
||||
<Folder Name="/Connectors/Rhino/8/">
|
||||
<Project Path="Connectors\Rhino\Speckle.Connectors.Grasshopper8\Speckle.Connectors.Grasshopper8.csproj" />
|
||||
<Project Path="Connectors\Rhino\Speckle.Connectors.Rhino8\Speckle.Connectors.Rhino8.csproj" />
|
||||
<Project Path="Connectors\Rhino\Speckle.Connectors.RhinoImporter\Speckle.Connectors.RhinoImporter.csproj" Type="C#" />
|
||||
<Project Path="Converters\Rhino\Speckle.Converters.Rhino8\Speckle.Converters.Rhino8.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Connectors/Rhino/Shared/">
|
||||
@@ -184,6 +185,9 @@
|
||||
<Project Path="Importers\Ifc\Speckle.Importers.Ifc\Speckle.Importers.Ifc.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Importers/Rhino/">
|
||||
<Project Path="Importers\Rhino\Speckle.Importers.JobProcessor\Speckle.Importers.JobProcessor.csproj" Type="C#">
|
||||
<Configuration Solution="Local|Any CPU" Project="Debug|Any CPU" />
|
||||
</Project>
|
||||
<Project Path="Importers\Rhino\Speckle.Importers.Rhino\Speckle.Importers.Rhino.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Sdk/">
|
||||
@@ -199,7 +203,7 @@
|
||||
<Project Path="Sdk\Speckle.Performance\Speckle.Performance.csproj" />
|
||||
<Project Path="Sdk\Speckle.Testing\Speckle.Testing.csproj" />
|
||||
</Folder>
|
||||
<Project Path="..\speckle-sharp-sdk\src\Speckle.Objects\Speckle.Objects.csproj" />
|
||||
<Project Path="..\speckle-sharp-sdk\src\Speckle.Sdk.Dependencies\Speckle.Sdk.Dependencies.csproj" />
|
||||
<Project Path="..\speckle-sharp-sdk\src\Speckle.Sdk\Speckle.Sdk.csproj" />
|
||||
<Project Path="../speckle-sharp-sdk/src/Speckle.Objects/Speckle.Objects.csproj" />
|
||||
<Project Path="../speckle-sharp-sdk/src/Speckle.Sdk.Dependencies/Speckle.Sdk.Dependencies.csproj" />
|
||||
<Project Path="../speckle-sharp-sdk/src/Speckle.Sdk/Speckle.Sdk.csproj" />
|
||||
</Solution>
|
||||
@@ -67,6 +67,9 @@
|
||||
<Project Path="Importers\Ifc\Speckle.Importers.Ifc\Speckle.Importers.Ifc.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Importers/Rhino/">
|
||||
<Project Path="Importers\Rhino\Speckle.Importers.JobProcessor\Speckle.Importers.JobProcessor.csproj" Type="C#">
|
||||
<Configuration Solution="Local|Any CPU" Project="Debug|Any CPU" />
|
||||
</Project>
|
||||
<Project Path="Importers\Rhino\Speckle.Importers.Rhino\Speckle.Importers.Rhino.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Sdk/">
|
||||
|
||||
@@ -67,6 +67,9 @@
|
||||
<Project Path="Importers\Ifc\Speckle.Importers.Ifc\Speckle.Importers.Ifc.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Importers/Rhino/">
|
||||
<Project Path="Importers\Rhino\Speckle.Importers.JobProcessor\Speckle.Importers.JobProcessor.csproj" Type="C#">
|
||||
<Configuration Solution="Local|Any CPU" Project="Debug|Any CPU" />
|
||||
</Project>
|
||||
<Project Path="Importers\Rhino\Speckle.Importers.Rhino\Speckle.Importers.Rhino.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Sdk/">
|
||||
|
||||
@@ -310,6 +310,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Rhino", "Rhino", "{CD0C216A
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Importers.Rhino", "Importers\Rhino\Speckle.Importers.Rhino\Speckle.Importers.Rhino.csproj", "{92F065BE-97E2-4192-B561-88A6A8CFA53F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Importers.JobProcessor", "Importers\Rhino\Speckle.Importers.JobProcessor\Speckle.Importers.JobProcessor.csproj", "{1953F875-B271-484B-B589-3D4AC1288C2B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Speckle.Connectors.RhinoImporter", "Connectors\Rhino\Speckle.Connectors.RhinoImporter\Speckle.Connectors.RhinoImporter.csproj", "{5422F2C8-1E00-4DAE-BB01-65A17BE8CD68}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -779,6 +783,18 @@ Global
|
||||
{92F065BE-97E2-4192-B561-88A6A8CFA53F}.Local|Any CPU.Build.0 = Local|Any CPU
|
||||
{92F065BE-97E2-4192-B561-88A6A8CFA53F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{92F065BE-97E2-4192-B561-88A6A8CFA53F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1953F875-B271-484B-B589-3D4AC1288C2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1953F875-B271-484B-B589-3D4AC1288C2B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1953F875-B271-484B-B589-3D4AC1288C2B}.Local|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1953F875-B271-484B-B589-3D4AC1288C2B}.Local|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1953F875-B271-484B-B589-3D4AC1288C2B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1953F875-B271-484B-B589-3D4AC1288C2B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5422F2C8-1E00-4DAE-BB01-65A17BE8CD68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5422F2C8-1E00-4DAE-BB01-65A17BE8CD68}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5422F2C8-1E00-4DAE-BB01-65A17BE8CD68}.Local|Any CPU.ActiveCfg = Local|Any CPU
|
||||
{5422F2C8-1E00-4DAE-BB01-65A17BE8CD68}.Local|Any CPU.Build.0 = Local|Any CPU
|
||||
{5422F2C8-1E00-4DAE-BB01-65A17BE8CD68}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5422F2C8-1E00-4DAE-BB01-65A17BE8CD68}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -918,6 +934,8 @@ Global
|
||||
{60445031-CCEE-4421-83C0-BB10BEF7B7F2} = {FC224610-32D3-454E-9BC1-1219FE8ACD5F}
|
||||
{CD0C216A-2A96-4423-956E-3DDFCAEA510C} = {336F0341-5C39-40F7-9377-122FED4E4549}
|
||||
{92F065BE-97E2-4192-B561-88A6A8CFA53F} = {CD0C216A-2A96-4423-956E-3DDFCAEA510C}
|
||||
{1953F875-B271-484B-B589-3D4AC1288C2B} = {CD0C216A-2A96-4423-956E-3DDFCAEA510C}
|
||||
{5422F2C8-1E00-4DAE-BB01-65A17BE8CD68} = {5929C9C7-F971-449E-BC5B-4486016BD11F}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {EE253116-7070-4E9A-BCE8-2911C251B8C8}
|
||||
@@ -950,6 +968,7 @@ Global
|
||||
Connectors\Autocad\Speckle.Connectors.Civil3dShared\Speckle.Connectors.Civil3dShared.projitems*{4459f2b1-a340-488e-a856-eb2ae9c72ad4}*SharedItemsImports = 5
|
||||
Converters\Revit\Speckle.Converters.RevitShared\Speckle.Converters.RevitShared.projitems*{4d40a101-07e6-4ff2-8934-83434932591d}*SharedItemsImports = 5
|
||||
Converters\Tekla\Speckle.Converters.TeklaShared\Speckle.Converters.TeklaShared.projitems*{52666479-5401-47d6-b7ba-d554784439ea}*SharedItemsImports = 13
|
||||
Connectors\Rhino\Speckle.Connectors.RhinoShared\Speckle.Connectors.RhinoShared.projitems*{5422f2c8-1e00-4dae-bb01-65a17be8cd68}*SharedItemsImports = 5
|
||||
Converters\Autocad\Speckle.Converters.AutocadShared\Speckle.Converters.AutocadShared.projitems*{5505b953-d434-49ce-8ebd-efd7b3c378f7}*SharedItemsImports = 5
|
||||
Converters\Navisworks\Speckle.Converters.NavisworksShared\Speckle.Converters.NavisworksShared.projitems*{56680ea7-3599-4d88-83a5-b43ba93ac046}*SharedItemsImports = 5
|
||||
Converters\Rhino\Speckle.Converters.RhinoShared\Speckle.Converters.RhinoShared.projitems*{56a909ae-6e99-4d4d-a22e-38bdc5528b8e}*SharedItemsImports = 5
|
||||
|
||||
@@ -151,6 +151,7 @@
|
||||
<Folder Name="/Connectors/Rhino/8/">
|
||||
<Project Path="Connectors\Rhino\Speckle.Connectors.Grasshopper8\Speckle.Connectors.Grasshopper8.csproj" />
|
||||
<Project Path="Connectors\Rhino\Speckle.Connectors.Rhino8\Speckle.Connectors.Rhino8.csproj" />
|
||||
<Project Path="Connectors\Rhino\Speckle.Connectors.RhinoImporter\Speckle.Connectors.RhinoImporter.csproj" Type="Classic C#" />
|
||||
<Project Path="Converters\Rhino\Speckle.Converters.Rhino8\Speckle.Converters.Rhino8.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Connectors/Rhino/Shared/">
|
||||
@@ -184,6 +185,9 @@
|
||||
<Project Path="Importers\Ifc\Speckle.Importers.Ifc\Speckle.Importers.Ifc.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Importers/Rhino/">
|
||||
<Project Path="Importers\Rhino\Speckle.Importers.JobProcessor\Speckle.Importers.JobProcessor.csproj" Type="Classic C#">
|
||||
<Configuration Solution="Local|Any CPU" Project="Debug|Any CPU" />
|
||||
</Project>
|
||||
<Project Path="Importers\Rhino\Speckle.Importers.Rhino\Speckle.Importers.Rhino.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Sdk/">
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
<Project Path="Importers\Ifc\Speckle.Importers.Ifc\Speckle.Importers.Ifc.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Importers/Rhino/">
|
||||
<Project Path="Importers\Rhino\Speckle.Importers.JobProcessor\Speckle.Importers.JobProcessor.csproj" Type="C#">
|
||||
<Configuration Solution="Local|Any CPU" Project="Debug|Any CPU" />
|
||||
</Project>
|
||||
<Project Path="Importers\Rhino\Speckle.Importers.Rhino\Speckle.Importers.Rhino.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Sdk/">
|
||||
|
||||
@@ -63,6 +63,9 @@
|
||||
<Project Path="Importers\Ifc\Speckle.Importers.Ifc\Speckle.Importers.Ifc.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Importers/Rhino/">
|
||||
<Project Path="Importers\Rhino\Speckle.Importers.JobProcessor\Speckle.Importers.JobProcessor.csproj" Type="C#">
|
||||
<Configuration Solution="Local|Any CPU" Project="Debug|Any CPU" />
|
||||
</Project>
|
||||
<Project Path="Importers\Rhino\Speckle.Importers.Rhino\Speckle.Importers.Rhino.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Sdk/">
|
||||
|
||||
@@ -58,6 +58,9 @@
|
||||
<Project Path="Importers\Ifc\Speckle.Importers.Ifc\Speckle.Importers.Ifc.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Importers/Rhino/">
|
||||
<Project Path="Importers\Rhino\Speckle.Importers.JobProcessor\Speckle.Importers.JobProcessor.csproj" Type="C#">
|
||||
<Configuration Solution="Local|Any CPU" Project="Debug|Any CPU" />
|
||||
</Project>
|
||||
<Project Path="Importers\Rhino\Speckle.Importers.Rhino\Speckle.Importers.Rhino.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Sdk/">
|
||||
@@ -73,7 +76,7 @@
|
||||
<Project Path="Sdk\Speckle.Performance\Speckle.Performance.csproj" />
|
||||
<Project Path="Sdk\Speckle.Testing\Speckle.Testing.csproj" />
|
||||
</Folder>
|
||||
<Project Path="..\speckle-sharp-sdk\src\Speckle.Objects\Speckle.Objects.csproj" />
|
||||
<Project Path="..\speckle-sharp-sdk\src\Speckle.Sdk.Dependencies\Speckle.Sdk.Dependencies.csproj" />
|
||||
<Project Path="..\speckle-sharp-sdk\src\Speckle.Sdk\Speckle.Sdk.csproj" />
|
||||
<Project Path="../speckle-sharp-sdk/src/Speckle.Objects/Speckle.Objects.csproj" />
|
||||
<Project Path="../speckle-sharp-sdk/src/Speckle.Sdk.Dependencies/Speckle.Sdk.Dependencies.csproj" />
|
||||
<Project Path="../speckle-sharp-sdk/src/Speckle.Sdk/Speckle.Sdk.csproj" />
|
||||
</Solution>
|
||||
@@ -58,6 +58,9 @@
|
||||
<Project Path="Importers\Ifc\Speckle.Importers.Ifc\Speckle.Importers.Ifc.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Importers/Rhino/">
|
||||
<Project Path="Importers\Rhino\Speckle.Importers.JobProcessor\Speckle.Importers.JobProcessor.csproj" Type="C#">
|
||||
<Configuration Solution="Local|Any CPU" Project="Debug|Any CPU" />
|
||||
</Project>
|
||||
<Project Path="Importers\Rhino\Speckle.Importers.Rhino\Speckle.Importers.Rhino.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Sdk/">
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<Solution>
|
||||
<Configurations>
|
||||
<BuildType Name="Debug" />
|
||||
<BuildType Name="Local" />
|
||||
<BuildType Name="Release" />
|
||||
</Configurations>
|
||||
<Folder Name="/Build/">
|
||||
<Project Path="Build\Build.csproj" />
|
||||
<File Path=".github\workflows\pr.yml" />
|
||||
<File Path=".github\workflows\release.yml" />
|
||||
</Folder>
|
||||
<Folder Name="/Config/">
|
||||
<File Path=".csharpierrc.yaml" />
|
||||
<File Path=".editorconfig" />
|
||||
<File Path="codecov.yml" />
|
||||
<File Path="CodeMetricsConfig.txt" />
|
||||
<File Path="Directory.Build.props" />
|
||||
<File Path="Directory.Packages.props" />
|
||||
<File Path=".config\dotnet-tools.json" />
|
||||
<File Path="global.json" />
|
||||
<File Path="README.md" />
|
||||
</Folder>
|
||||
<Folder Name="/Connectors/" />
|
||||
<Folder Name="/Connectors/Rhino/">
|
||||
<Project Path="Connectors\Rhino\Speckle.Connectors.RhinoImporter\Speckle.Connectors.RhinoImporter.csproj" Type="Classic C#" />
|
||||
<Project Path="Connectors\Rhino\Speckle.Connectors.RhinoShared\Speckle.Connectors.RhinoShared.shproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Connectors/Rhino/Converter/">
|
||||
<Project Path="Converters\Rhino\Speckle.Converters.Rhino8\Speckle.Converters.Rhino8.csproj" Type="Classic C#" />
|
||||
<Project Path="Converters\Rhino\Speckle.Converters.RhinoShared\Speckle.Converters.RhinoShared.shproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Importers/" />
|
||||
<Folder Name="/Importers/Ifc/">
|
||||
<Project Path="Importers\Ifc\Speckle.Importers.Ifc.Tester2\Speckle.Importers.Ifc.Tester2.csproj" />
|
||||
<Project Path="Importers\Ifc\Speckle.Importers.Ifc.Tester\Speckle.Importers.Ifc.Tester.csproj" />
|
||||
<Project Path="Importers\Ifc\Speckle.Importers.Ifc\Speckle.Importers.Ifc.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Importers/Rhino/">
|
||||
<Project Path="Importers\Rhino\Speckle.Importers.JobProcessor\Speckle.Importers.JobProcessor.csproj" Type="C#">
|
||||
<Configuration Solution="Local|Any CPU" Project="Debug|Any CPU" />
|
||||
</Project>
|
||||
<Project Path="Importers\Rhino\Speckle.Importers.Rhino\Speckle.Importers.Rhino.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Sdk/">
|
||||
<Project Path="DUI3\Speckle.Connectors.DUI.Tests\Speckle.Connectors.DUI.Tests.csproj" />
|
||||
<Project Path="DUI3\Speckle.Connectors.DUI.WebView\Speckle.Connectors.DUI.WebView.csproj" />
|
||||
<Project Path="DUI3\Speckle.Connectors.DUI\Speckle.Connectors.DUI.csproj" />
|
||||
<Project Path="Sdk\Speckle.Common.MeshTriangulation\Speckle.Common.MeshTriangulation.csproj" />
|
||||
<Project Path="Sdk\Speckle.Connectors.Common.Tests\Speckle.Connectors.Common.Tests.csproj" />
|
||||
<Project Path="Sdk\Speckle.Connectors.Common\Speckle.Connectors.Common.csproj" />
|
||||
<Project Path="Sdk\Speckle.Connectors.Logging\Speckle.Connectors.Logging.csproj" />
|
||||
<Project Path="Sdk\Speckle.Converters.Common.Tests\Speckle.Converters.Common.Tests.csproj" />
|
||||
<Project Path="Sdk\Speckle.Converters.Common\Speckle.Converters.Common.csproj" />
|
||||
<Project Path="Sdk\Speckle.Performance\Speckle.Performance.csproj" />
|
||||
<Project Path="Sdk\Speckle.Testing\Speckle.Testing.csproj" />
|
||||
</Folder>
|
||||
</Solution>
|
||||
@@ -32,6 +32,7 @@
|
||||
<Folder Name="/Connectors/Rhino/8/">
|
||||
<Project Path="Connectors\Rhino\Speckle.Connectors.Grasshopper8\Speckle.Connectors.Grasshopper8.csproj" />
|
||||
<Project Path="Connectors\Rhino\Speckle.Connectors.Rhino8\Speckle.Connectors.Rhino8.csproj" />
|
||||
<Project Path="Connectors\Rhino\Speckle.Connectors.RhinoImporter\Speckle.Connectors.RhinoImporter.csproj" Type="C#" />
|
||||
<Project Path="Converters\Rhino\Speckle.Converters.Rhino8\Speckle.Converters.Rhino8.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Connectors/Rhino/Shared/">
|
||||
@@ -46,6 +47,9 @@
|
||||
<Project Path="Importers\Ifc\Speckle.Importers.Ifc\Speckle.Importers.Ifc.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Importers/Rhino/">
|
||||
<Project Path="Importers\Rhino\Speckle.Importers.JobProcessor\Speckle.Importers.JobProcessor.csproj" Type="C#">
|
||||
<Configuration Solution="Local|Any CPU" Project="Debug|Any CPU" />
|
||||
</Project>
|
||||
<Project Path="Importers\Rhino\Speckle.Importers.Rhino\Speckle.Importers.Rhino.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Sdk/">
|
||||
|
||||
@@ -47,6 +47,9 @@
|
||||
<Project Path="Importers\Ifc\Speckle.Importers.Ifc\Speckle.Importers.Ifc.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Importers/Rhino/">
|
||||
<Project Path="Importers\Rhino\Speckle.Importers.JobProcessor\Speckle.Importers.JobProcessor.csproj" Type="C#">
|
||||
<Configuration Solution="Local|Any CPU" Project="Debug|Any CPU" />
|
||||
</Project>
|
||||
<Project Path="Importers\Rhino\Speckle.Importers.Rhino\Speckle.Importers.Rhino.csproj" />
|
||||
</Folder>
|
||||
<Folder Name="/Sdk/">
|
||||
|
||||
Reference in New Issue
Block a user