Compare commits
106 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2101a9831b | |||
| 64d1091b79 | |||
| a411aaa3f0 | |||
| 13f3bb8ae5 | |||
| d31cb47a85 | |||
| 640cc92641 | |||
| ef9c23f7de | |||
| 04bd151da3 | |||
| edf3555289 | |||
| 378438f1bc | |||
| b485a4ff6f | |||
| 5697afc292 | |||
| 03c1d4ed32 | |||
| 07a681eda7 | |||
| b17f4b02aa | |||
| 03a780ffd5 | |||
| b1240cfbe8 | |||
| fed185fbed | |||
| 4ec45d3cd5 | |||
| 939c710bf2 | |||
| ac1345bbaf | |||
| 95a7bdb81f | |||
| b1a5824bcd | |||
| 09f9b1ee51 | |||
| 11212c946a | |||
| 2eee9561b4 | |||
| b221a69f76 | |||
| 6b0ed5c075 | |||
| 1a687fb188 | |||
| 30e050fff2 | |||
| 30ab3b108e | |||
| da31864192 | |||
| f6239d279f | |||
| 1f3ac7a5ad | |||
| 7aff696bae | |||
| 371722f28c | |||
| d25c40bcd6 | |||
| 913acc7707 | |||
| 1d83c98077 | |||
| 2c2a7929bf | |||
| 5335329719 | |||
| cc010c8cc8 | |||
| 80b136b934 | |||
| e8f61f8dbf | |||
| bf2099f8a6 | |||
| f7f31263a6 | |||
| 33e515efb6 | |||
| b8f65d4ade | |||
| 4a5c91231d | |||
| 97a8df93d9 | |||
| 96b2eb1832 | |||
| b972a2f8bd | |||
| c504848e0b | |||
| 7d88e39272 | |||
| 82adf0e20e | |||
| 66de3f978a | |||
| 0106befa7d | |||
| ef87d5838b | |||
| 295162127a | |||
| 6d336fbac4 | |||
| 6b2078fadb | |||
| bfd465449a | |||
| a9a4893bdb | |||
| 21066eebbb | |||
| 9d5ff85cff | |||
| 64befa758d | |||
| d9a0cbb4bf | |||
| 50807b1e88 | |||
| 7780071073 | |||
| be63b4ab55 | |||
| 63c5bb26cb | |||
| f7a1d98d8a | |||
| a14de5bdde | |||
| 9031950868 | |||
| a89407ae3c | |||
| 4cef9efd51 | |||
| a40e9495e5 | |||
| a3285a4f67 | |||
| afd59df48a | |||
| ae4b1b0ab5 | |||
| ed9d81d206 | |||
| a2fc846613 | |||
| 184953f5f0 | |||
| d71b36c2f7 | |||
| f152cff619 | |||
| 4aed602089 | |||
| 63c4d31467 | |||
| b807be35ff | |||
| 42c3ca1ec3 | |||
| 44105d7d75 | |||
| 2aad40bd0a | |||
| d0058d7ee5 | |||
| 38c9077831 | |||
| 904e7ece88 | |||
| 267799d916 | |||
| 2eb872b5e5 | |||
| ce9a2c8807 | |||
| 729d1a4698 | |||
| 4f74328ffc | |||
| 54370f3188 | |||
| 4a51eae628 | |||
| 34241385f9 | |||
| 7ec01ed39f | |||
| 1ff861f9db | |||
| 4c125afd7b | |||
| e561980e7f |
@@ -9,10 +9,10 @@
|
||||
],
|
||||
"rollForward": false
|
||||
},
|
||||
"gitversion.tool": {
|
||||
"version": "6.0.2",
|
||||
"dotnet-affected": {
|
||||
"version": "5.0.0",
|
||||
"commands": [
|
||||
"dotnet-gitversion"
|
||||
"dotnet-affected"
|
||||
],
|
||||
"rollForward": false
|
||||
}
|
||||
|
||||
@@ -314,6 +314,9 @@ dotnet_diagnostic.NUnit2037.severity = warning # Consider using Assert.That(coll
|
||||
dotnet_diagnostic.NUnit2038.severity = warning # Consider using Assert.That(actual, Is.InstanceOf(expected)) instead of Assert.IsInstanceOf(expected, actual)
|
||||
dotnet_diagnostic.NUnit2039.severity = warning # Consider using Assert.That(actual, Is.Not.InstanceOf(expected)) instead of Assert.IsNotInstanceOf(expected, actual)
|
||||
|
||||
# note: added to allow the copy paste from rhino inside of the ValueSet component
|
||||
dotnet_diagnostic.CA1033.severity = none
|
||||
|
||||
[*.{appxmanifest,asax,ascx,aspx,axaml,build,c,c++,cc,cginc,compute,cp,cpp,cs,cshtml,cu,cuh,cxx,dtd,fs,fsi,fsscript,fsx,fx,fxh,h,hh,hlsl,hlsli,hlslinc,hpp,hxx,inc,inl,ino,ipp,ixx,master,ml,mli,mpp,mq4,mq5,mqh,nuspec,paml,razor,resw,resx,shader,skin,tpp,usf,ush,vb,xaml,xamlx,xoml,xsd}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
name: .NET Build
|
||||
|
||||
on:
|
||||
pull_request: # Run build on every pull request that is not to main
|
||||
branches-ignore:
|
||||
- main
|
||||
pull_request
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -13,6 +11,8 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
@@ -32,6 +32,8 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
|
||||
@@ -2,12 +2,14 @@ name: .NET Build and Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main", "dev", "release/*"] # Continuous delivery on every long-lived branch
|
||||
branches: ["main", "installer-test/**"]
|
||||
tags: ["v3.*"] # Manual delivery on every 3.x tag
|
||||
|
||||
jobs:
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
SPECKLE_VERSION: "unset"
|
||||
outputs:
|
||||
version: ${{ steps.set-version.outputs.version }}
|
||||
steps:
|
||||
@@ -27,31 +29,27 @@ jobs:
|
||||
path: ~/.nuget/packages
|
||||
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
|
||||
|
||||
- name: ⚒️ Run GitVersion on Windows
|
||||
run: ./build.ps1 build-server-version
|
||||
|
||||
- name: ⚒️ Run build on Windows
|
||||
run: ./build.ps1
|
||||
run: ./build.ps1 zip
|
||||
|
||||
- name: ⬆️ Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: output-${{ env.GitVersion_FullSemVer }}
|
||||
name: output-${{ env.SPECKLE_VERSION }}
|
||||
path: output/*.*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
compression-level: 0 # no compression
|
||||
|
||||
compression-level: 0 # no compression
|
||||
- id: set-version
|
||||
name: Set version to output
|
||||
run: echo "version=${{ env.GitVersion_FullSemVer }}" >> "$Env:GITHUB_OUTPUT"
|
||||
run: echo "version=${{ env.SPECKLE_VERSION }}" >> "$Env:GITHUB_OUTPUT"
|
||||
|
||||
deploy-installers:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build-windows
|
||||
env:
|
||||
IS_TAG_BUILD: ${{ github.ref_type == 'tag' }}
|
||||
IS_RELEASE_BRANCH: ${{ startsWith(github.ref_name, 'release/') || github.ref_name == 'main'}}
|
||||
IS_PUBLIC_RELEASE: ${{ github.ref_type == 'tag' }}
|
||||
IS_TEST_INSTALLER: ${{ github.ref_type != 'tag' }}
|
||||
steps:
|
||||
- name: 🔫 Trigger Build Installers
|
||||
uses: ALEEF02/workflow-dispatch@v3.0.0
|
||||
@@ -59,7 +57,7 @@ jobs:
|
||||
workflow: Build Installers
|
||||
repo: specklesystems/connector-installers
|
||||
token: ${{ secrets.CONNECTORS_GH_TOKEN }}
|
||||
inputs: '{ "run_id": "${{ github.run_id }}", "version": "${{ needs.build-windows.outputs.version }}", "public_release": ${{ env.IS_TAG_BUILD }}, "store_artifacts": ${{ env.IS_RELEASE_BRANCH }} }'
|
||||
inputs: '{ "run_id": "${{ github.run_id }}", "version": "${{ needs.build-windows.outputs.version }}", "public_release": ${{ env.IS_PUBLIC_RELEASE }}, "store_artifacts": ${{ env.IS_TEST_INSTALLER }} }'
|
||||
ref: main
|
||||
wait-for-completion: true
|
||||
wait-for-completion-interval: 10s
|
||||
@@ -90,9 +88,6 @@ jobs:
|
||||
path: ~/.nuget/packages
|
||||
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
|
||||
|
||||
- name: ⚒️ Run GitVersion on Linux
|
||||
run: ./build.sh build-server-version
|
||||
|
||||
- name: ⚒️ Run tests on Linux
|
||||
run: ./build.sh test-only
|
||||
|
||||
@@ -106,5 +101,5 @@ jobs:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
- name: Push to nuget.org
|
||||
if: ${{ github.ref_type == 'tag' }}
|
||||
if: (github.ref_type == 'tag')
|
||||
run: dotnet nuget push output/*.nupkg --source "https://api.nuget.org/v3/index.json" --api-key ${{secrets.CONNECTORS_NUGET_TOKEN }} --skip-duplicate
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
using GlobExpressions;
|
||||
using Microsoft.Build.Construction;
|
||||
using static SimpleExec.Command;
|
||||
|
||||
namespace Build;
|
||||
|
||||
public static class Affected
|
||||
{
|
||||
public static readonly string Root = Environment.CurrentDirectory;
|
||||
public const string AFFECTED_PROJECT = "affected.proj";
|
||||
|
||||
private static IEnumerable<string> GetAffectedProjects()
|
||||
{
|
||||
var projFile = Path.Combine(Root, AFFECTED_PROJECT);
|
||||
Console.WriteLine("Affected project file: " + projFile);
|
||||
var project = ProjectRootElement.Open(projFile) ?? throw new InvalidOperationException();
|
||||
var references = project.ItemGroups.SelectMany(x => x.Items).Where(x => x.ItemType == "ProjectReference");
|
||||
|
||||
foreach (var refe in references)
|
||||
{
|
||||
var referencePath = refe.Include[(Root.Length + 1)..];
|
||||
referencePath = Path.GetDirectoryName(referencePath) ?? throw new InvalidOperationException();
|
||||
if (Path.DirectorySeparatorChar != '/')
|
||||
{
|
||||
referencePath = referencePath.Replace(Path.DirectorySeparatorChar, '/');
|
||||
}
|
||||
|
||||
yield return referencePath;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<IEnumerable<string>> GetTestProjects()
|
||||
{
|
||||
await ComputeAffected();
|
||||
var projFile = Path.Combine(Root, AFFECTED_PROJECT);
|
||||
if (File.Exists(projFile))
|
||||
{
|
||||
var references = GetAffectedProjects();
|
||||
return references.Where(x => x.Contains("Tests"));
|
||||
}
|
||||
return Glob.Files(Root, "**/*.Tests.csproj");
|
||||
}
|
||||
|
||||
public static async Task<ProjectGroup[]> GetAffectedProjectGroups()
|
||||
{
|
||||
await ComputeAffected();
|
||||
var projFile = Path.Combine(Root, AFFECTED_PROJECT);
|
||||
if (File.Exists(projFile))
|
||||
{
|
||||
var references = GetAffectedProjects().ToList();
|
||||
var groups = new List<ProjectGroup>();
|
||||
foreach (var projectGroup in Consts.ProjectGroups)
|
||||
{
|
||||
foreach (var referencePath in references)
|
||||
{
|
||||
if (projectGroup.Projects.Any(x => x.ProjectPath.Contains(referencePath)))
|
||||
{
|
||||
groups.Add(projectGroup);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var group in groups)
|
||||
{
|
||||
Console.WriteLine("Affected project group being built: " + group.HostAppSlug);
|
||||
}
|
||||
|
||||
return groups.ToArray();
|
||||
}
|
||||
|
||||
Console.WriteLine("Using all project groups: " + string.Join(',', Consts.ProjectGroups));
|
||||
return Consts.ProjectGroups;
|
||||
}
|
||||
|
||||
private static bool s_affectedComputed;
|
||||
|
||||
public static async Task ComputeAffected()
|
||||
{
|
||||
if (s_affectedComputed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var currentTag = await Versions.GetCurrentTag();
|
||||
var currentVersion = await Versions.ComputeVersion();
|
||||
var lastTag = await Versions.GetPreviousTag(currentTag);
|
||||
var lastVersion = await Versions.ComputePreviousVersion(currentTag);
|
||||
|
||||
Console.WriteLine($"Last tag: {lastTag}, Current tag: {currentTag}");
|
||||
Console.WriteLine($"Last parsed version: {lastVersion}, Current parsed version: {currentVersion}");
|
||||
|
||||
var sort = currentVersion.CompareSortOrderTo(lastVersion);
|
||||
if (sort == -1)
|
||||
{
|
||||
Console.WriteLine($"Current version {currentVersion} is less than: {lastVersion}");
|
||||
s_affectedComputed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var majorEquals = currentVersion.Major == lastVersion.Major;
|
||||
if (!majorEquals)
|
||||
{
|
||||
Console.WriteLine($"Current version {currentVersion} is not matching major version: {lastVersion}");
|
||||
s_affectedComputed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
//use tags no matter the version if major versions match
|
||||
var (currentCommit, _) = await ReadAsync("git", $"rev-list -n 1 {currentTag}");
|
||||
var (lastCommit, _) = await ReadAsync("git", $"rev-list -n 1 {lastTag}");
|
||||
await RunAsync("dotnet", $"affected -v --from {currentCommit.Trim()} --to {lastCommit.Trim()}", Root);
|
||||
|
||||
s_affectedComputed = true;
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
<PackageReference Include="Bullseye" />
|
||||
<PackageReference Include="Glob" />
|
||||
<PackageReference Include="Microsoft.Build" />
|
||||
<PackageReference Include="Semver" />
|
||||
<PackageReference Include="SimpleExec" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@ public static class Consts
|
||||
{
|
||||
public static readonly string[] Solutions = ["Speckle.Connectors.sln"];
|
||||
|
||||
public static readonly InstallerProject[] InstallerManifests =
|
||||
public static readonly ProjectGroup[] ProjectGroups =
|
||||
{
|
||||
new("arcgis", [new("Connectors/ArcGIS/Speckle.Connectors.ArcGIS3", "net6.0-windows")]),
|
||||
new(
|
||||
@@ -69,7 +69,7 @@ public static class Consts
|
||||
};
|
||||
}
|
||||
|
||||
public readonly record struct InstallerProject(string HostAppSlug, IReadOnlyList<InstallerAsset> Projects)
|
||||
public readonly record struct ProjectGroup(string HostAppSlug, IReadOnlyList<InstallerAsset> Projects)
|
||||
{
|
||||
public override string ToString() => $"{HostAppSlug}";
|
||||
}
|
||||
|
||||
+82
-78
@@ -12,13 +12,12 @@ const string TEST = "test";
|
||||
const string TEST_ONLY = "test-only";
|
||||
const string FORMAT = "format";
|
||||
const string ZIP = "zip";
|
||||
const string VERSION = "version";
|
||||
const string RESTORE_TOOLS = "restore-tools";
|
||||
const string BUILD_SERVER_VERSION = "build-server-version";
|
||||
const string CLEAN_LOCKS = "clean-locks";
|
||||
const string CHECK_SOLUTIONS = "check-solutions";
|
||||
const string DEEP_CLEAN = "deep-clean";
|
||||
const string DEEP_CLEAN_LOCAL = "deep-clean-local";
|
||||
const string DETECT_AFFECTED = "detect-affected";
|
||||
|
||||
//need to pass arguments
|
||||
/*var arguments = new List<string>();
|
||||
@@ -34,14 +33,14 @@ void Build(string solution, string configuration)
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($"Building solution '{solution}' as '{configuration}'");
|
||||
Console.WriteLine();
|
||||
Run("dotnet", $"build .\\{solution} --configuration {configuration} --no-restore");
|
||||
Run("dotnet", $"build \".\\{solution}\" --configuration {configuration} --no-restore");
|
||||
}
|
||||
void Restore(string solution)
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($"Restoring solution '{solution}'");
|
||||
Console.WriteLine();
|
||||
Run("dotnet", $"restore .\\{solution} --no-cache");
|
||||
Run("dotnet", $"restore \".\\{solution}\" --no-cache");
|
||||
}
|
||||
void DeleteFiles(string pattern)
|
||||
{
|
||||
@@ -125,17 +124,6 @@ Target(
|
||||
}
|
||||
);
|
||||
|
||||
Target(
|
||||
VERSION,
|
||||
async () =>
|
||||
{
|
||||
var (output, _) = await ReadAsync("dotnet", "minver -v w");
|
||||
output = output.Trim();
|
||||
Console.WriteLine($"Version: {output}");
|
||||
Run("echo", $"\"version={output}\" >> $GITHUB_OUTPUT");
|
||||
}
|
||||
);
|
||||
|
||||
Target(
|
||||
RESTORE_TOOLS,
|
||||
() =>
|
||||
@@ -144,6 +132,18 @@ Target(
|
||||
}
|
||||
);
|
||||
|
||||
Target(
|
||||
DETECT_AFFECTED,
|
||||
DependsOn(RESTORE_TOOLS),
|
||||
async () =>
|
||||
{
|
||||
foreach (var group in await Affected.GetAffectedProjectGroups())
|
||||
{
|
||||
Console.WriteLine("Affected project group being built: " + group.HostAppSlug);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Target(
|
||||
FORMAT,
|
||||
DependsOn(RESTORE_TOOLS),
|
||||
@@ -155,20 +155,14 @@ Target(
|
||||
|
||||
Target(
|
||||
RESTORE,
|
||||
DependsOn(FORMAT),
|
||||
DependsOn(FORMAT, DETECT_AFFECTED),
|
||||
Consts.Solutions,
|
||||
s =>
|
||||
async s =>
|
||||
{
|
||||
Run("dotnet", $"restore {s} --locked-mode");
|
||||
}
|
||||
);
|
||||
|
||||
Target(
|
||||
BUILD_SERVER_VERSION,
|
||||
DependsOn(RESTORE_TOOLS),
|
||||
() =>
|
||||
{
|
||||
Run("dotnet", "tool run dotnet-gitversion /output json /output buildserver");
|
||||
var version = await Versions.ComputeVersion();
|
||||
var fileVersion = await Versions.ComputeFileVersion();
|
||||
Console.WriteLine($"Restoring: {s} - Version: {version} & {fileVersion}");
|
||||
await RunAsync("dotnet", $"restore \"{s}\" --locked-mode");
|
||||
}
|
||||
);
|
||||
|
||||
@@ -176,14 +170,14 @@ Target(
|
||||
BUILD,
|
||||
DependsOn(RESTORE),
|
||||
Consts.Solutions,
|
||||
s =>
|
||||
async s =>
|
||||
{
|
||||
var version = Environment.GetEnvironmentVariable("GitVersion_FullSemVer") ?? "3.0.0-localBuild";
|
||||
var fileVersion = Environment.GetEnvironmentVariable("GitVersion_AssemblySemFileVer") ?? "3.0.0.0";
|
||||
Console.WriteLine($"Version: {version} & {fileVersion}");
|
||||
Run(
|
||||
var version = await Versions.ComputeVersion();
|
||||
var fileVersion = await Versions.ComputeFileVersion();
|
||||
Console.WriteLine($"Restoring: {s} - Version: {version} & {fileVersion}");
|
||||
await RunAsync(
|
||||
"dotnet",
|
||||
$"build {s} -c Release --no-restore -warnaserror -p:Version={version} -p:FileVersion={fileVersion} -v:m"
|
||||
$"build \"{s}\" -c Release --no-restore -warnaserror -p:Version={version} -p:FileVersion={fileVersion} -v:m"
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -193,23 +187,26 @@ Target(CHECK_SOLUTIONS, Solutions.CompareConnectorsToLocal);
|
||||
Target(
|
||||
TEST,
|
||||
DependsOn(BUILD, CHECK_SOLUTIONS),
|
||||
Glob.Files(".", "**/*.Tests.csproj"),
|
||||
file =>
|
||||
async () =>
|
||||
{
|
||||
Run("dotnet", $"test {file} -c Release --no-build --no-restore --verbosity=minimal");
|
||||
foreach (var s in await Affected.GetTestProjects())
|
||||
{
|
||||
await RunAsync("dotnet", $"test \"{s}\" -c Release --no-build --no-restore --verbosity=minimal");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
//all tests on purpose
|
||||
Target(
|
||||
TEST_ONLY,
|
||||
DependsOn(FORMAT),
|
||||
Glob.Files(".", "**/*.Tests.csproj"),
|
||||
file =>
|
||||
{
|
||||
Run("dotnet", $"build {file} -c Release --no-incremental");
|
||||
Run("dotnet", $"build \"{file}\" -c Release --no-incremental");
|
||||
Run(
|
||||
"dotnet",
|
||||
$"test {file} -c Release --no-build --verbosity=minimal /p:AltCover=true /p:AltCoverAttributeFilter=ExcludeFromCodeCoverage /p:AltCoverVerbosity=Warning"
|
||||
$"test \"{file}\" -c Release --no-build --verbosity=minimal /p:AltCover=true /p:AltCoverAttributeFilter=ExcludeFromCodeCoverage /p:AltCoverVerbosity=Warning"
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -218,20 +215,20 @@ Target(
|
||||
BUILD_LINUX,
|
||||
DependsOn(FORMAT),
|
||||
Glob.Files(".", "**/Speckle.Importers.Ifc.csproj"),
|
||||
file =>
|
||||
async file =>
|
||||
{
|
||||
Run("dotnet", $"restore {file} --locked-mode");
|
||||
var version = Environment.GetEnvironmentVariable("GitVersion_FullSemVer") ?? "3.0.0-localBuild";
|
||||
var fileVersion = Environment.GetEnvironmentVariable("GitVersion_AssemblySemFileVer") ?? "3.0.0.0";
|
||||
await RunAsync("dotnet", $"restore \"{file}\" --locked-mode");
|
||||
var version = await Versions.ComputeVersion();
|
||||
var fileVersion = await Versions.ComputeFileVersion();
|
||||
Console.WriteLine($"Version: {version} & {fileVersion}");
|
||||
Run(
|
||||
await RunAsync(
|
||||
"dotnet",
|
||||
$"build {file} -c Release --no-restore -warnaserror -p:Version={version} -p:FileVersion={fileVersion} -v:m"
|
||||
$"build \"{file}\" -c Release --no-restore -warnaserror -p:Version={version} -p:FileVersion={fileVersion} -v:m"
|
||||
);
|
||||
|
||||
RunAsync(
|
||||
await RunAsync(
|
||||
"dotnet",
|
||||
$"pack {file} -c Release -o output --no-build -p:Version={version} -p:FileVersion={fileVersion} -v:m"
|
||||
$"pack \"{file}\" -c Release -o output --no-build -p:Version={version} -p:FileVersion={fileVersion} -v:m"
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -239,47 +236,54 @@ Target(
|
||||
Target(
|
||||
ZIP,
|
||||
DependsOn(TEST),
|
||||
Consts.InstallerManifests,
|
||||
x =>
|
||||
async () =>
|
||||
{
|
||||
var outputDir = Path.Combine(".", "output");
|
||||
var slugDir = Path.Combine(outputDir, x.HostAppSlug);
|
||||
|
||||
Directory.CreateDirectory(outputDir);
|
||||
Directory.CreateDirectory(slugDir);
|
||||
|
||||
foreach (var asset in x.Projects)
|
||||
var version = await Versions.ComputeVersion();
|
||||
foreach (var group in await Affected.GetAffectedProjectGroups())
|
||||
{
|
||||
var fullPath = Path.Combine(".", asset.ProjectPath, "bin", "Release", asset.TargetName);
|
||||
if (!Directory.Exists(fullPath))
|
||||
Console.WriteLine($"Zipping: {group.HostAppSlug} as {version}");
|
||||
var outputDir = Path.Combine(".", "output");
|
||||
var slugDir = Path.Combine(outputDir, group.HostAppSlug);
|
||||
|
||||
Directory.CreateDirectory(outputDir);
|
||||
Directory.CreateDirectory(slugDir);
|
||||
|
||||
foreach (var asset in group.Projects)
|
||||
{
|
||||
throw new InvalidOperationException("Could not find: " + fullPath);
|
||||
var fullPath = Path.Combine(".", asset.ProjectPath, "bin", "Release", asset.TargetName);
|
||||
if (!Directory.Exists(fullPath))
|
||||
{
|
||||
throw new InvalidOperationException("Could not find: " + fullPath);
|
||||
}
|
||||
|
||||
var assetName = Path.GetFileName(asset.ProjectPath);
|
||||
var connectorDir = Path.Combine(slugDir, assetName);
|
||||
|
||||
Directory.CreateDirectory(connectorDir);
|
||||
foreach (var directory in Directory.EnumerateDirectories(fullPath, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
Directory.CreateDirectory(directory.Replace(fullPath, connectorDir));
|
||||
}
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(fullPath, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
Console.WriteLine(file);
|
||||
File.Copy(file, file.Replace(fullPath, connectorDir), true);
|
||||
}
|
||||
}
|
||||
|
||||
var assetName = Path.GetFileName(asset.ProjectPath);
|
||||
var connectorDir = Path.Combine(slugDir, assetName);
|
||||
|
||||
Directory.CreateDirectory(connectorDir);
|
||||
foreach (var directory in Directory.EnumerateDirectories(fullPath, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
Directory.CreateDirectory(directory.Replace(fullPath, connectorDir));
|
||||
}
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(fullPath, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
Console.WriteLine(file);
|
||||
File.Copy(file, file.Replace(fullPath, connectorDir), true);
|
||||
}
|
||||
var outputPath = Path.Combine(outputDir, $"{group.HostAppSlug}.zip");
|
||||
File.Delete(outputPath);
|
||||
Console.WriteLine($"Zipping: '{slugDir}' to '{outputPath}'");
|
||||
ZipFile.CreateFromDirectory(slugDir, outputPath);
|
||||
}
|
||||
|
||||
var outputPath = Path.Combine(outputDir, $"{x.HostAppSlug}.zip");
|
||||
File.Delete(outputPath);
|
||||
Console.WriteLine($"Zipping: '{slugDir}' to '{outputPath}'");
|
||||
ZipFile.CreateFromDirectory(slugDir, outputPath);
|
||||
// Directory.Delete(slugDir, true);
|
||||
string githubEnv = Environment.GetEnvironmentVariable("GITHUB_ENV") ?? "Unset";
|
||||
Console.WriteLine($"GITHUB_ENV: {githubEnv}");
|
||||
File.AppendAllText(githubEnv, $"SPECKLE_VERSION={version}{Environment.NewLine}");
|
||||
}
|
||||
);
|
||||
|
||||
Target("default", DependsOn(FORMAT, ZIP), () => Console.WriteLine("Done!"));
|
||||
Target("default", DependsOn(TEST), () => Console.WriteLine("Done!"));
|
||||
|
||||
await RunTargetsAndExitAsync(args).ConfigureAwait(true);
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
using Semver;
|
||||
using static SimpleExec.Command;
|
||||
|
||||
namespace Build;
|
||||
|
||||
public static class Versions
|
||||
{
|
||||
private static string? s_currentTag;
|
||||
private static SemVersion? s_currentVersion;
|
||||
|
||||
public static async Task<string> GetCurrentTag()
|
||||
{
|
||||
if (s_currentTag is not null)
|
||||
{
|
||||
return s_currentTag;
|
||||
}
|
||||
//finds current tag or makes one
|
||||
var (currentTag, _) = await ReadAsync("git", "describe --tags");
|
||||
currentTag = currentTag.Trim();
|
||||
s_currentTag = currentTag;
|
||||
return s_currentTag;
|
||||
}
|
||||
|
||||
public static async Task<SemVersion> ComputeVersion()
|
||||
{
|
||||
if (s_currentVersion is not null)
|
||||
{
|
||||
return s_currentVersion;
|
||||
}
|
||||
var currentTag = await GetCurrentTag();
|
||||
|
||||
if (!SemVersion.TryParse(currentTag, SemVersionStyles.AllowLowerV, out var currentVersion))
|
||||
{
|
||||
throw new InvalidOperationException($"Could not parse version: '{currentTag}'");
|
||||
}
|
||||
s_currentVersion = currentVersion;
|
||||
return s_currentVersion;
|
||||
}
|
||||
|
||||
private static string? s_currentFileVersion;
|
||||
|
||||
public static async Task<string> ComputeFileVersion()
|
||||
{
|
||||
if (s_currentFileVersion is not null)
|
||||
{
|
||||
return s_currentFileVersion;
|
||||
}
|
||||
var currentVersion = await ComputeVersion();
|
||||
s_currentFileVersion = currentVersion.WithoutPrereleaseOrMetadata() + ".0";
|
||||
return s_currentFileVersion;
|
||||
}
|
||||
|
||||
public static async Task<string> GetPreviousTag(string currentTag)
|
||||
{
|
||||
//finds a tag starting with current tag and adds no abbrevation
|
||||
var (lastTag, _) = await ReadAsync("git", $"describe --abbrev=0 --tags {currentTag}^");
|
||||
lastTag = lastTag.Trim();
|
||||
|
||||
return lastTag;
|
||||
}
|
||||
|
||||
private static SemVersion? s_previousVersion;
|
||||
|
||||
public static async Task<SemVersion> ComputePreviousVersion(string currentTag)
|
||||
{
|
||||
if (s_previousVersion is not null)
|
||||
{
|
||||
return s_previousVersion;
|
||||
}
|
||||
var lastTag = await GetPreviousTag(currentTag);
|
||||
|
||||
if (!SemVersion.TryParse(lastTag, SemVersionStyles.AllowLowerV, out var lastVersion))
|
||||
{
|
||||
throw new InvalidOperationException($"Could not parse version: '{lastTag}'");
|
||||
}
|
||||
s_previousVersion = lastVersion;
|
||||
return s_previousVersion;
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,15 @@
|
||||
"resolved": "1.14.1",
|
||||
"contentHash": "mOOmFYwad3MIOL14VCjj02LljyF1GNw1wP0YVlxtcPvqdxjGGMNdNJJxHptlry3MOd8b40Flm8RPOM8JOlN2sQ=="
|
||||
},
|
||||
"Semver": {
|
||||
"type": "Direct",
|
||||
"requested": "[3.0.0, )",
|
||||
"resolved": "3.0.0",
|
||||
"contentHash": "9jZCicsVgTebqkAujRWtC9J1A5EQVlu0TVKHcgoCuv345ve5DYf4D1MjhKEnQjdRZo6x/vdv6QQrYFs7ilGzLA==",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Primitives": "5.0.1"
|
||||
}
|
||||
},
|
||||
"SimpleExec": {
|
||||
"type": "Direct",
|
||||
"requested": "[12.0.0, )",
|
||||
@@ -75,6 +84,11 @@
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
|
||||
},
|
||||
"Microsoft.Extensions.Primitives": {
|
||||
"type": "Transitive",
|
||||
"resolved": "5.0.1",
|
||||
"contentHash": "5WPSmL4YeP7eW+Vc8XZ4DwjYWBAiSwDV9Hm63JJWcz1Ie3Xjv4KuJXzgCstj48LkLfVCYa7mLcx7y+q6yqVvtw=="
|
||||
},
|
||||
"Microsoft.NET.StringTools": {
|
||||
"type": "Transitive",
|
||||
"resolved": "17.11.4",
|
||||
|
||||
@@ -67,6 +67,8 @@ public class BasicConnectorBinding : IBasicConnectorBinding
|
||||
|
||||
public void RemoveModel(ModelCard model) => _store.RemoveModel(model);
|
||||
|
||||
public void RemoveModels(List<ModelCard> models) => _store.RemoveModels(models);
|
||||
|
||||
public async Task HighlightObjects(IReadOnlyList<string> objectIds)
|
||||
{
|
||||
await HighlightObjectsOnView(objectIds.Select(x => new ObjectID(x)).ToList());
|
||||
|
||||
@@ -132,11 +132,6 @@
|
||||
"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",
|
||||
@@ -221,14 +216,21 @@
|
||||
"resolved": "4.5.1",
|
||||
"contentHash": "Zh8t8oqolRaFa9vmOZfdQm/qKejdqz0J9kr7o2Fu0vPeoH3BL1EOXipKWwkWtLT1JPzjByrF19fGuFlNbmPpiw=="
|
||||
},
|
||||
"speckle.common.meshtriangulation": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"LibTessDotNet": "[1.1.15, )",
|
||||
"Speckle.DoubleNumerics": "[4.1.0, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.common": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )",
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -236,8 +238,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui.webview": {
|
||||
@@ -254,6 +256,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Esri.ArcGISPro.Extensions30": "[3.2.0.49743, )",
|
||||
"Speckle.Common.MeshTriangulation": "[1.0.0, )",
|
||||
"Speckle.Converters.Common": "[1.0.0, )"
|
||||
}
|
||||
},
|
||||
@@ -261,9 +264,15 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"LibTessDotNet": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[1.1.15, )",
|
||||
"resolved": "1.1.15",
|
||||
"contentHash": "KuA7N3Nv/lIeawJdQBQJR6oqWD9KETHLbWzBqapwFs+Tby+R5I4crkKujKMm5bXcSuFZ8LNtflFQVadsWCbBjg=="
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -273,6 +282,12 @@
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -299,18 +314,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "q/E+FoRIGBDskZDr7eGiYk5YYCgd17Al1PH8LWR1JdL0i9CrLdeuJ+ozeKH6uqWOrlvdYQ75OjN1UwflMxew4w==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.1.0-dev.276"
|
||||
"Speckle.Sdk": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "XCA4dM4FuNkRwt0/zP8wyJKMwZXmyPp+QBkBwXQd3edwCunTul3pE/7/m87xDpBBy4/dnpWEHlnxbK3WGQpkGw==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -320,14 +335,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.1.0-dev.276"
|
||||
"Speckle.Sdk.Dependencies": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "iPqMbAElEDsfMia8fYHEiJp98xDpipOZ7DFLGdLP54Ct8NvBpEJRSwA7+s42uqQbCoCQC3aF4uZ6NMLJnDD0Xw=="
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
|
||||
}
|
||||
},
|
||||
"net6.0-windows7.0/win-x64": {
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2022.0.2" />
|
||||
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2022.0.2" ExcludeAssets="runtime"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -130,11 +130,6 @@
|
||||
"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",
|
||||
@@ -259,9 +254,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )",
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -269,8 +264,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui.webview": {
|
||||
@@ -294,7 +289,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -306,6 +301,12 @@
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -338,18 +339,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "q/E+FoRIGBDskZDr7eGiYk5YYCgd17Al1PH8LWR1JdL0i9CrLdeuJ+ozeKH6uqWOrlvdYQ75OjN1UwflMxew4w==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.1.0-dev.276"
|
||||
"Speckle.Sdk": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "XCA4dM4FuNkRwt0/zP8wyJKMwZXmyPp+QBkBwXQd3edwCunTul3pE/7/m87xDpBBy4/dnpWEHlnxbK3WGQpkGw==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -359,14 +360,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.1.0-dev.276"
|
||||
"Speckle.Sdk.Dependencies": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "iPqMbAElEDsfMia8fYHEiJp98xDpipOZ7DFLGdLP54Ct8NvBpEJRSwA7+s42uqQbCoCQC3aF4uZ6NMLJnDD0Xw=="
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2023.0.0" />
|
||||
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2023.0.0" ExcludeAssets="runtime"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -130,11 +130,6 @@
|
||||
"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",
|
||||
@@ -259,9 +254,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )",
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -269,8 +264,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui.webview": {
|
||||
@@ -294,7 +289,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -306,6 +301,12 @@
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -338,18 +339,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "q/E+FoRIGBDskZDr7eGiYk5YYCgd17Al1PH8LWR1JdL0i9CrLdeuJ+ozeKH6uqWOrlvdYQ75OjN1UwflMxew4w==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.1.0-dev.276"
|
||||
"Speckle.Sdk": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "XCA4dM4FuNkRwt0/zP8wyJKMwZXmyPp+QBkBwXQd3edwCunTul3pE/7/m87xDpBBy4/dnpWEHlnxbK3WGQpkGw==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -359,14 +360,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.1.0-dev.276"
|
||||
"Speckle.Sdk.Dependencies": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "iPqMbAElEDsfMia8fYHEiJp98xDpipOZ7DFLGdLP54Ct8NvBpEJRSwA7+s42uqQbCoCQC3aF4uZ6NMLJnDD0Xw=="
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2024.0.0" />
|
||||
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2024.0.0" ExcludeAssets="runtime"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -130,11 +130,6 @@
|
||||
"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",
|
||||
@@ -259,9 +254,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )",
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -269,8 +264,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui.webview": {
|
||||
@@ -295,7 +290,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -307,6 +302,12 @@
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -339,18 +340,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "q/E+FoRIGBDskZDr7eGiYk5YYCgd17Al1PH8LWR1JdL0i9CrLdeuJ+ozeKH6uqWOrlvdYQ75OjN1UwflMxew4w==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.1.0-dev.276"
|
||||
"Speckle.Sdk": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "XCA4dM4FuNkRwt0/zP8wyJKMwZXmyPp+QBkBwXQd3edwCunTul3pE/7/m87xDpBBy4/dnpWEHlnxbK3WGQpkGw==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -360,14 +361,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.1.0-dev.276"
|
||||
"Speckle.Sdk.Dependencies": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "iPqMbAElEDsfMia8fYHEiJp98xDpipOZ7DFLGdLP54Ct8NvBpEJRSwA7+s42uqQbCoCQC3aF4uZ6NMLJnDD0Xw=="
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -13,8 +13,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2025.0.0" />
|
||||
<FrameworkReference Include="Microsoft.WindowsDesktop.App"/>
|
||||
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2025.0.0" ExcludeAssets="runtime"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -121,11 +121,6 @@
|
||||
"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",
|
||||
@@ -215,9 +210,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )",
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -225,8 +220,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui.webview": {
|
||||
@@ -251,7 +246,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -263,6 +258,12 @@
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -295,18 +296,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "q/E+FoRIGBDskZDr7eGiYk5YYCgd17Al1PH8LWR1JdL0i9CrLdeuJ+ozeKH6uqWOrlvdYQ75OjN1UwflMxew4w==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.1.0-dev.276"
|
||||
"Speckle.Sdk": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "XCA4dM4FuNkRwt0/zP8wyJKMwZXmyPp+QBkBwXQd3edwCunTul3pE/7/m87xDpBBy4/dnpWEHlnxbK3WGQpkGw==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
@@ -315,14 +316,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.1.0-dev.276"
|
||||
"Speckle.Sdk.Dependencies": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "iPqMbAElEDsfMia8fYHEiJp98xDpipOZ7DFLGdLP54Ct8NvBpEJRSwA7+s42uqQbCoCQC3aF4uZ6NMLJnDD0Xw=="
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
|
||||
}
|
||||
},
|
||||
"net8.0-windows7.0/win-x64": {
|
||||
|
||||
+2
@@ -79,6 +79,8 @@ public class AutocadBasicConnectorBinding : IBasicConnectorBinding
|
||||
|
||||
public void RemoveModel(ModelCard model) => _store.RemoveModel(model);
|
||||
|
||||
public void RemoveModels(List<ModelCard> models) => _store.RemoveModels(models);
|
||||
|
||||
public async Task HighlightObjects(IReadOnlyList<string> objectIds)
|
||||
{
|
||||
// POC: Will be addressed to move it into AutocadContext!
|
||||
|
||||
+6
@@ -108,6 +108,12 @@ public abstract class AutocadReceiveBaseBinding : IReceiveBinding
|
||||
{
|
||||
// reenable document activation
|
||||
Application.DocumentManager.DocumentActivationEnabled = true;
|
||||
|
||||
// regenerate doc to flush graphics, sometimes some objects (ellipses, nurbs curves) do not appear fully visible after receive.
|
||||
// Adding a regen (must be run on main thread) here, but it doesn't seem to work:
|
||||
// it's run on main thread, tried sending the "regen" string to execute, also tried regen after every object bake, but still can't fix.
|
||||
// the objects should appear visible if you manually call the "regen" command after the operation finishes, or click on a view on the view cube which also calls regen.
|
||||
Application.DocumentManager.CurrentDocument.Editor.Regen();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -83,7 +83,7 @@ public class AutocadHostObjectBuilder(
|
||||
colorBaker.ParseColors(unpackedRoot.ColorProxies, onOperationProgressed);
|
||||
}
|
||||
|
||||
// 5 - Convert atomic objects
|
||||
// 4 - Convert atomic objects
|
||||
HashSet<ReceiveConversionResult> results = new();
|
||||
HashSet<string> bakedObjectIds = new();
|
||||
Dictionary<string, IReadOnlyCollection<Entity>> applicationIdMap = new();
|
||||
@@ -116,7 +116,7 @@ public class AutocadHostObjectBuilder(
|
||||
}
|
||||
}
|
||||
|
||||
// 6 - Convert instances
|
||||
// 5 - Convert instances
|
||||
var (createdInstanceIds, consumedObjectIds, instanceConversionResults) = instanceBaker.BakeInstances(
|
||||
instanceComponentsWithPath,
|
||||
applicationIdMap,
|
||||
@@ -129,7 +129,7 @@ public class AutocadHostObjectBuilder(
|
||||
results.RemoveWhere(result => result.ResultId != null && consumedObjectIds.Contains(result.ResultId));
|
||||
results.UnionWith(instanceConversionResults);
|
||||
|
||||
// 7 - Create groups
|
||||
// 6 - Create groups
|
||||
if (unpackedRoot.GroupProxies != null)
|
||||
{
|
||||
IReadOnlyCollection<ReceiveConversionResult> groupResults = groupBaker.CreateGroups(
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@
|
||||
<StartProgram>$(ProgramW6432)\Autodesk\AutoCAD $(Civil3DVersion)\acad.exe</StartProgram>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Speckle.AutoCAD.API" />
|
||||
<PackageReference Include="Speckle.Civil3D.API" />
|
||||
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2022.0.2" ExcludeAssets="runtime"/>
|
||||
<PackageReference Include="Speckle.Civil3D.API" VersionOverride="2022.0.2" ExcludeAssets="runtime"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -139,11 +139,6 @@
|
||||
"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",
|
||||
@@ -268,9 +263,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )",
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -278,8 +273,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui.webview": {
|
||||
@@ -304,7 +299,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -316,6 +311,12 @@
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -348,18 +349,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "q/E+FoRIGBDskZDr7eGiYk5YYCgd17Al1PH8LWR1JdL0i9CrLdeuJ+ozeKH6uqWOrlvdYQ75OjN1UwflMxew4w==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.1.0-dev.276"
|
||||
"Speckle.Sdk": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "XCA4dM4FuNkRwt0/zP8wyJKMwZXmyPp+QBkBwXQd3edwCunTul3pE/7/m87xDpBBy4/dnpWEHlnxbK3WGQpkGw==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -369,14 +370,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.1.0-dev.276"
|
||||
"Speckle.Sdk.Dependencies": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "iPqMbAElEDsfMia8fYHEiJp98xDpipOZ7DFLGdLP54Ct8NvBpEJRSwA7+s42uqQbCoCQC3aF4uZ6NMLJnDD0Xw=="
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -11,8 +11,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2023.0.0" />
|
||||
<PackageReference Include="Speckle.Civil3D.API" VersionOverride="2023.0.0" />
|
||||
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2023.0.0" ExcludeAssets="runtime"/>
|
||||
<PackageReference Include="Speckle.Civil3D.API" VersionOverride="2023.0.0" ExcludeAssets="runtime"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -139,11 +139,6 @@
|
||||
"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",
|
||||
@@ -268,9 +263,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )",
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -278,8 +273,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui.webview": {
|
||||
@@ -304,7 +299,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -316,6 +311,12 @@
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -348,18 +349,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "q/E+FoRIGBDskZDr7eGiYk5YYCgd17Al1PH8LWR1JdL0i9CrLdeuJ+ozeKH6uqWOrlvdYQ75OjN1UwflMxew4w==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.1.0-dev.276"
|
||||
"Speckle.Sdk": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "XCA4dM4FuNkRwt0/zP8wyJKMwZXmyPp+QBkBwXQd3edwCunTul3pE/7/m87xDpBBy4/dnpWEHlnxbK3WGQpkGw==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -369,14 +370,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.1.0-dev.276"
|
||||
"Speckle.Sdk.Dependencies": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "iPqMbAElEDsfMia8fYHEiJp98xDpipOZ7DFLGdLP54Ct8NvBpEJRSwA7+s42uqQbCoCQC3aF4uZ6NMLJnDD0Xw=="
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -11,8 +11,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2024.0.0" />
|
||||
<PackageReference Include="Speckle.Civil3D.API" VersionOverride="2024.0.0" />
|
||||
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2024.0.0" ExcludeAssets="runtime"/>
|
||||
<PackageReference Include="Speckle.Civil3D.API" VersionOverride="2024.0.0" ExcludeAssets="runtime"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -139,11 +139,6 @@
|
||||
"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",
|
||||
@@ -268,9 +263,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )",
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -278,8 +273,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui.webview": {
|
||||
@@ -304,7 +299,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -316,6 +311,12 @@
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -348,18 +349,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "q/E+FoRIGBDskZDr7eGiYk5YYCgd17Al1PH8LWR1JdL0i9CrLdeuJ+ozeKH6uqWOrlvdYQ75OjN1UwflMxew4w==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.1.0-dev.276"
|
||||
"Speckle.Sdk": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "XCA4dM4FuNkRwt0/zP8wyJKMwZXmyPp+QBkBwXQd3edwCunTul3pE/7/m87xDpBBy4/dnpWEHlnxbK3WGQpkGw==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -369,14 +370,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.1.0-dev.276"
|
||||
"Speckle.Sdk.Dependencies": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "iPqMbAElEDsfMia8fYHEiJp98xDpipOZ7DFLGdLP54Ct8NvBpEJRSwA7+s42uqQbCoCQC3aF4uZ6NMLJnDD0Xw=="
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -13,9 +13,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2025.0.0" />
|
||||
<PackageReference Include="Speckle.Civil3d.API" VersionOverride="2025.0.0" />
|
||||
<FrameworkReference Include="Microsoft.WindowsDesktop.App" />
|
||||
<PackageReference Include="Speckle.AutoCAD.API" VersionOverride="2025.0.0" ExcludeAssets="runtime"/>
|
||||
<PackageReference Include="Speckle.Civil3d.API" VersionOverride="2025.0.0" ExcludeAssets="runtime"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -130,11 +130,6 @@
|
||||
"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",
|
||||
@@ -224,9 +219,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )",
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -234,8 +229,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui.webview": {
|
||||
@@ -261,7 +256,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection": {
|
||||
@@ -273,6 +268,12 @@
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -305,18 +306,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "q/E+FoRIGBDskZDr7eGiYk5YYCgd17Al1PH8LWR1JdL0i9CrLdeuJ+ozeKH6uqWOrlvdYQ75OjN1UwflMxew4w==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.1.0-dev.276"
|
||||
"Speckle.Sdk": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "XCA4dM4FuNkRwt0/zP8wyJKMwZXmyPp+QBkBwXQd3edwCunTul3pE/7/m87xDpBBy4/dnpWEHlnxbK3WGQpkGw==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
@@ -325,14 +326,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.1.0-dev.276"
|
||||
"Speckle.Sdk.Dependencies": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "iPqMbAElEDsfMia8fYHEiJp98xDpipOZ7DFLGdLP54Ct8NvBpEJRSwA7+s42uqQbCoCQC3aF4uZ6NMLJnDD0Xw=="
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
|
||||
}
|
||||
},
|
||||
"net8.0-windows7.0/win-x64": {
|
||||
|
||||
@@ -65,6 +65,9 @@ public class CsiSharedBasicConnectorBinding : IBasicConnectorBinding
|
||||
public void RemoveModel(ModelCard model) =>
|
||||
_topLevelExceptionHandler.CatchUnhandled(() => _threadContext.RunOnThread(() => _store.RemoveModel(model), true));
|
||||
|
||||
public void RemoveModels(List<ModelCard> models) =>
|
||||
_topLevelExceptionHandler.CatchUnhandled(() => _threadContext.RunOnThread(() => _store.RemoveModels(models), true));
|
||||
|
||||
public Task HighlightModel(string modelCardId) => Task.CompletedTask;
|
||||
|
||||
public Task HighlightObjects(IReadOnlyList<string> objectIds) => Task.CompletedTask;
|
||||
|
||||
@@ -130,11 +130,6 @@
|
||||
"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",
|
||||
@@ -259,9 +254,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )",
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -269,8 +264,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui.webview": {
|
||||
@@ -287,7 +282,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.etabs21": {
|
||||
@@ -305,6 +300,12 @@
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -337,18 +338,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "q/E+FoRIGBDskZDr7eGiYk5YYCgd17Al1PH8LWR1JdL0i9CrLdeuJ+ozeKH6uqWOrlvdYQ75OjN1UwflMxew4w==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.1.0-dev.276"
|
||||
"Speckle.Sdk": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "XCA4dM4FuNkRwt0/zP8wyJKMwZXmyPp+QBkBwXQd3edwCunTul3pE/7/m87xDpBBy4/dnpWEHlnxbK3WGQpkGw==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -358,14 +359,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.1.0-dev.276"
|
||||
"Speckle.Sdk.Dependencies": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "iPqMbAElEDsfMia8fYHEiJp98xDpipOZ7DFLGdLP54Ct8NvBpEJRSwA7+s42uqQbCoCQC3aF4uZ6NMLJnDD0Xw=="
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,11 +121,6 @@
|
||||
"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",
|
||||
@@ -215,9 +210,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )",
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -225,8 +220,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui.webview": {
|
||||
@@ -243,7 +238,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.etabs22": {
|
||||
@@ -261,6 +256,12 @@
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -293,18 +294,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "q/E+FoRIGBDskZDr7eGiYk5YYCgd17Al1PH8LWR1JdL0i9CrLdeuJ+ozeKH6uqWOrlvdYQ75OjN1UwflMxew4w==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.1.0-dev.276"
|
||||
"Speckle.Sdk": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "XCA4dM4FuNkRwt0/zP8wyJKMwZXmyPp+QBkBwXQd3edwCunTul3pE/7/m87xDpBBy4/dnpWEHlnxbK3WGQpkGw==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
@@ -313,14 +314,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.1.0-dev.276"
|
||||
"Speckle.Sdk.Dependencies": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "iPqMbAElEDsfMia8fYHEiJp98xDpipOZ7DFLGdLP54Ct8NvBpEJRSwA7+s42uqQbCoCQC3aF4uZ6NMLJnDD0Xw=="
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="WindowsFormsIntegration"/>
|
||||
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2020.0.0"/>
|
||||
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2020.0.0" ExcludeAssets="runtime"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -130,11 +130,6 @@
|
||||
"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",
|
||||
@@ -259,9 +254,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )",
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -269,8 +264,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui.webview": {
|
||||
@@ -287,7 +282,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2020": {
|
||||
@@ -307,6 +302,12 @@
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -339,18 +340,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "q/E+FoRIGBDskZDr7eGiYk5YYCgd17Al1PH8LWR1JdL0i9CrLdeuJ+ozeKH6uqWOrlvdYQ75OjN1UwflMxew4w==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.1.0-dev.276"
|
||||
"Speckle.Sdk": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "XCA4dM4FuNkRwt0/zP8wyJKMwZXmyPp+QBkBwXQd3edwCunTul3pE/7/m87xDpBBy4/dnpWEHlnxbK3WGQpkGw==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -360,14 +361,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.1.0-dev.276"
|
||||
"Speckle.Sdk.Dependencies": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "iPqMbAElEDsfMia8fYHEiJp98xDpipOZ7DFLGdLP54Ct8NvBpEJRSwA7+s42uqQbCoCQC3aF4uZ6NMLJnDD0Xw=="
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8/win-x64": {
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="WindowsFormsIntegration"/>
|
||||
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2021.0.0"/>
|
||||
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2021.0.0" ExcludeAssets="runtime"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -130,11 +130,6 @@
|
||||
"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",
|
||||
@@ -259,9 +254,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )",
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -269,8 +264,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui.webview": {
|
||||
@@ -287,7 +282,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2021": {
|
||||
@@ -307,6 +302,12 @@
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -339,18 +340,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "q/E+FoRIGBDskZDr7eGiYk5YYCgd17Al1PH8LWR1JdL0i9CrLdeuJ+ozeKH6uqWOrlvdYQ75OjN1UwflMxew4w==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.1.0-dev.276"
|
||||
"Speckle.Sdk": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "XCA4dM4FuNkRwt0/zP8wyJKMwZXmyPp+QBkBwXQd3edwCunTul3pE/7/m87xDpBBy4/dnpWEHlnxbK3WGQpkGw==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -360,14 +361,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.1.0-dev.276"
|
||||
"Speckle.Sdk.Dependencies": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "iPqMbAElEDsfMia8fYHEiJp98xDpipOZ7DFLGdLP54Ct8NvBpEJRSwA7+s42uqQbCoCQC3aF4uZ6NMLJnDD0Xw=="
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8/win-x64": {
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="WindowsFormsIntegration"/>
|
||||
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2022.0.0"/>
|
||||
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2022.0.0" ExcludeAssets="runtime"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -130,11 +130,6 @@
|
||||
"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",
|
||||
@@ -259,9 +254,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )",
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -269,8 +264,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui.webview": {
|
||||
@@ -287,7 +282,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2022": {
|
||||
@@ -307,6 +302,12 @@
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -339,18 +340,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "q/E+FoRIGBDskZDr7eGiYk5YYCgd17Al1PH8LWR1JdL0i9CrLdeuJ+ozeKH6uqWOrlvdYQ75OjN1UwflMxew4w==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.1.0-dev.276"
|
||||
"Speckle.Sdk": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "XCA4dM4FuNkRwt0/zP8wyJKMwZXmyPp+QBkBwXQd3edwCunTul3pE/7/m87xDpBBy4/dnpWEHlnxbK3WGQpkGw==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -360,14 +361,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.1.0-dev.276"
|
||||
"Speckle.Sdk.Dependencies": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "iPqMbAElEDsfMia8fYHEiJp98xDpipOZ7DFLGdLP54Ct8NvBpEJRSwA7+s42uqQbCoCQC3aF4uZ6NMLJnDD0Xw=="
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8/win-x64": {
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="WindowsFormsIntegration"/>
|
||||
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2023.0.0"/>
|
||||
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2023.0.0" ExcludeAssets="runtime"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -130,11 +130,6 @@
|
||||
"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",
|
||||
@@ -259,9 +254,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )",
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -269,8 +264,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui.webview": {
|
||||
@@ -287,7 +282,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2023": {
|
||||
@@ -307,6 +302,12 @@
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -339,18 +340,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "q/E+FoRIGBDskZDr7eGiYk5YYCgd17Al1PH8LWR1JdL0i9CrLdeuJ+ozeKH6uqWOrlvdYQ75OjN1UwflMxew4w==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.1.0-dev.276"
|
||||
"Speckle.Sdk": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "XCA4dM4FuNkRwt0/zP8wyJKMwZXmyPp+QBkBwXQd3edwCunTul3pE/7/m87xDpBBy4/dnpWEHlnxbK3WGQpkGw==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -360,14 +361,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.1.0-dev.276"
|
||||
"Speckle.Sdk.Dependencies": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "iPqMbAElEDsfMia8fYHEiJp98xDpipOZ7DFLGdLP54Ct8NvBpEJRSwA7+s42uqQbCoCQC3aF4uZ6NMLJnDD0Xw=="
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8/win-x64": {
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="WindowsFormsIntegration"/>
|
||||
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2024.0.0"/>
|
||||
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2024.0.0" ExcludeAssets="runtime"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -130,11 +130,6 @@
|
||||
"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",
|
||||
@@ -259,9 +254,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )",
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -269,8 +264,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui.webview": {
|
||||
@@ -287,7 +282,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2024": {
|
||||
@@ -307,6 +302,12 @@
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -339,18 +340,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "q/E+FoRIGBDskZDr7eGiYk5YYCgd17Al1PH8LWR1JdL0i9CrLdeuJ+ozeKH6uqWOrlvdYQ75OjN1UwflMxew4w==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.1.0-dev.276"
|
||||
"Speckle.Sdk": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "XCA4dM4FuNkRwt0/zP8wyJKMwZXmyPp+QBkBwXQd3edwCunTul3pE/7/m87xDpBBy4/dnpWEHlnxbK3WGQpkGw==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -360,14 +361,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.1.0-dev.276"
|
||||
"Speckle.Sdk.Dependencies": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "iPqMbAElEDsfMia8fYHEiJp98xDpipOZ7DFLGdLP54Ct8NvBpEJRSwA7+s42uqQbCoCQC3aF4uZ6NMLJnDD0Xw=="
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8/win-x64": {
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="WindowsFormsIntegration"/>
|
||||
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2025.0.0"/>
|
||||
<PackageReference Include="Speckle.Navisworks.API" VersionOverride="2025.0.0" ExcludeAssets="runtime"/>
|
||||
<PackageReference Include="Microsoft.Web.WebView2" VersionOverride="1.0.2045.28" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -136,11 +136,6 @@
|
||||
"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",
|
||||
@@ -265,9 +260,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )",
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -275,8 +270,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui.webview": {
|
||||
@@ -293,7 +288,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.navisworks2025": {
|
||||
@@ -313,6 +308,12 @@
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -339,18 +340,18 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "q/E+FoRIGBDskZDr7eGiYk5YYCgd17Al1PH8LWR1JdL0i9CrLdeuJ+ozeKH6uqWOrlvdYQ75OjN1UwflMxew4w==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.1.0-dev.276"
|
||||
"Speckle.Sdk": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "XCA4dM4FuNkRwt0/zP8wyJKMwZXmyPp+QBkBwXQd3edwCunTul3pE/7/m87xDpBBy4/dnpWEHlnxbK3WGQpkGw==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -360,14 +361,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.1.0-dev.276"
|
||||
"Speckle.Sdk.Dependencies": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "iPqMbAElEDsfMia8fYHEiJp98xDpipOZ7DFLGdLP54Ct8NvBpEJRSwA7+s42uqQbCoCQC3aF4uZ6NMLJnDD0Xw=="
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
|
||||
}
|
||||
},
|
||||
".NETFramework,Version=v4.8/win-x64": {
|
||||
|
||||
+2
@@ -50,6 +50,8 @@ public class NavisworksBasicConnectorBinding : IBasicConnectorBinding
|
||||
|
||||
public void RemoveModel(ModelCard model) => _store.RemoveModel(model);
|
||||
|
||||
public void RemoveModels(List<ModelCard> models) => _store.RemoveModels(models);
|
||||
|
||||
public Task HighlightModel(string modelCardId) => Task.CompletedTask;
|
||||
|
||||
public async Task HighlightObjects(IReadOnlyList<string> objectIds) =>
|
||||
|
||||
+4
-3
@@ -38,7 +38,8 @@ public class NavisworksSelectionBinding : ISelectionBinding
|
||||
{
|
||||
// Ensure there is an active document and a valid selection
|
||||
var activeDocument = NavisworksApp.ActiveDocument;
|
||||
if (activeDocument == null || activeDocument.CurrentSelection.SelectedItems.IsEmpty)
|
||||
var selection = activeDocument?.CurrentSelection?.SelectedItems ?? [];
|
||||
if (selection.Count == 0)
|
||||
{
|
||||
// Return an empty list if no valid selection exists
|
||||
return new SelectionInfo([], "No selection available");
|
||||
@@ -46,8 +47,8 @@ public class NavisworksSelectionBinding : ISelectionBinding
|
||||
|
||||
// Ensure only visible elements are processed by filtering using IsElementVisible
|
||||
var selectedObjectsIds = new HashSet<string>(
|
||||
activeDocument
|
||||
.CurrentSelection.SelectedItems.Where(_selectionService.IsVisible) // Exclude hidden elements
|
||||
selection
|
||||
.Where(_selectionService.IsVisible) // Exclude hidden elements
|
||||
.Select(_selectionService.GetModelItemPath) // Resolve to index paths
|
||||
);
|
||||
|
||||
|
||||
+39
-13
@@ -106,9 +106,21 @@ public class NavisworksSendBinding : ISendBinding
|
||||
|
||||
using var cancellationItem = _cancellationManager.GetCancellationItem(modelCardId);
|
||||
|
||||
var navisworksModelItems = GetNavisworksModelItems(modelCard);
|
||||
var progress = _operationProgressManager.CreateOperationProgressEventHandler(
|
||||
Parent,
|
||||
modelCard.ModelCardId.NotNull(),
|
||||
cancellationItem.Token
|
||||
);
|
||||
|
||||
var sendResult = await ExecuteSendOperation(scope, modelCard, navisworksModelItems, cancellationItem.Token);
|
||||
var navisworksModelItems = await GetNavisworksModelItems(modelCard, progress);
|
||||
|
||||
var sendResult = await ExecuteSendOperation(
|
||||
scope,
|
||||
modelCard,
|
||||
navisworksModelItems,
|
||||
progress,
|
||||
cancellationItem.Token
|
||||
);
|
||||
|
||||
await Commands.SetModelSendResult(modelCardId, sendResult.RootObjId, sendResult.ConversionResults);
|
||||
}
|
||||
@@ -123,12 +135,16 @@ public class NavisworksSendBinding : ISendBinding
|
||||
_logger.LogModelCardHandledError(ex);
|
||||
await Commands.SetModelError(modelCardId, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// otherwise the id of the operation persists on the cancellation manager and triggers 'Operations cancelled because of document swap!' message to UI.
|
||||
_cancellationManager.CancelOperation(modelCardId);
|
||||
}
|
||||
}
|
||||
|
||||
private SenderModelCard GetModelCard(string modelCardId) =>
|
||||
_store.GetModelById(modelCardId) is not SenderModelCard modelCard
|
||||
? throw new InvalidOperationException("No publish model card was found.")
|
||||
: modelCard;
|
||||
_store.GetModelById(modelCardId) as SenderModelCard
|
||||
?? throw new InvalidOperationException("No publish model card was found.");
|
||||
|
||||
private void InitializeConverterSettings(IServiceScope scope, SenderModelCard modelCard) =>
|
||||
scope
|
||||
@@ -143,7 +159,10 @@ public class NavisworksSendBinding : ISendBinding
|
||||
)
|
||||
);
|
||||
|
||||
private List<NAV.ModelItem> GetNavisworksModelItems(SenderModelCard modelCard)
|
||||
private async Task<List<NAV.ModelItem>> GetNavisworksModelItems(
|
||||
SenderModelCard modelCard,
|
||||
IProgress<CardProgress> onOperationProgressed
|
||||
)
|
||||
{
|
||||
var selectedPaths = modelCard.SendFilter.NotNull().RefreshObjectIds();
|
||||
var convertHiddenElementsSetting =
|
||||
@@ -156,13 +175,19 @@ public class NavisworksSendBinding : ISendBinding
|
||||
{
|
||||
throw new SpeckleSendFilterException(message);
|
||||
}
|
||||
onOperationProgressed.Report(new CardProgress("Getting selection...", null));
|
||||
await Task.CompletedTask;
|
||||
|
||||
var modelItems = selectedPaths
|
||||
.Select(_selectionService.GetModelItemFromPath)
|
||||
.SelectMany(_selectionService.GetGeometryNodes)
|
||||
.Where(_selectionService.IsVisible)
|
||||
.ToList();
|
||||
|
||||
var modelItems = new List<NAV.ModelItem>();
|
||||
double count = 0;
|
||||
foreach (var path in selectedPaths)
|
||||
{
|
||||
onOperationProgressed.Report(new CardProgress("Getting selection...", count / selectedPaths.Count));
|
||||
await Task.CompletedTask;
|
||||
var modelItem = _selectionService.GetModelItemFromPath(path);
|
||||
modelItems.AddRange(_selectionService.GetGeometryNodes(modelItem).Where(_selectionService.IsVisible));
|
||||
count++;
|
||||
}
|
||||
return modelItems.Count == 0 ? throw new SpeckleSendFilterException(message) : modelItems;
|
||||
}
|
||||
|
||||
@@ -170,6 +195,7 @@ public class NavisworksSendBinding : ISendBinding
|
||||
IServiceScope scope,
|
||||
SenderModelCard modelCard,
|
||||
List<NAV.ModelItem> navisworksModelItems,
|
||||
IProgress<CardProgress> onOperationProgressed,
|
||||
CancellationToken token
|
||||
) =>
|
||||
await scope
|
||||
@@ -177,7 +203,7 @@ public class NavisworksSendBinding : ISendBinding
|
||||
.Execute(
|
||||
navisworksModelItems,
|
||||
modelCard.GetSendInfo(_speckleApplication.ApplicationAndVersion),
|
||||
_operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCard.ModelCardId.NotNull(), token),
|
||||
onOperationProgressed,
|
||||
token
|
||||
);
|
||||
|
||||
|
||||
+20
-12
@@ -1,23 +1,31 @@
|
||||
using static Speckle.Converter.Navisworks.Helpers.ElementSelectionHelper;
|
||||
using Speckle.InterfaceGenerator;
|
||||
using static Speckle.Converter.Navisworks.Helpers.ElementSelectionHelper;
|
||||
|
||||
namespace Speckle.Connector.Navisworks.Services;
|
||||
|
||||
public interface IElementSelectionService
|
||||
{
|
||||
string GetModelItemPath(NAV.ModelItem modelItem);
|
||||
NAV.ModelItem GetModelItemFromPath(string path);
|
||||
bool IsVisible(NAV.ModelItem modelItem);
|
||||
IReadOnlyCollection<NAV.ModelItem> GetGeometryNodes(NAV.ModelItem modelItem);
|
||||
}
|
||||
|
||||
[GenerateAutoInterface]
|
||||
public class ElementSelectionService : IElementSelectionService
|
||||
{
|
||||
private readonly Dictionary<Guid, bool> _visibleCache = new();
|
||||
|
||||
public string GetModelItemPath(NAV.ModelItem modelItem) => ResolveModelItemToIndexPath(modelItem);
|
||||
|
||||
public NAV.ModelItem GetModelItemFromPath(string path) => ResolveIndexPathToModelItem(path);
|
||||
|
||||
public bool IsVisible(NAV.ModelItem modelItem) => IsElementVisible(modelItem);
|
||||
public bool IsVisible(NAV.ModelItem modelItem)
|
||||
{
|
||||
var key = modelItem.InstanceGuid;
|
||||
if (_visibleCache.TryGetValue(key, out var isVisible))
|
||||
{
|
||||
return isVisible;
|
||||
}
|
||||
//same as ElementSelectionHelper.IsElementVisible
|
||||
foreach (var item in modelItem.AncestorsAndSelf)
|
||||
{
|
||||
_visibleCache[item.InstanceGuid] = !item.IsHidden;
|
||||
}
|
||||
return _visibleCache[key];
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<NAV.ModelItem> GetGeometryNodes(NAV.ModelItem modelItem) =>
|
||||
ResolveGeometryLeafNodes(modelItem);
|
||||
public IEnumerable<NAV.ModelItem> GetGeometryNodes(NAV.ModelItem modelItem) => ResolveGeometryLeafNodes(modelItem);
|
||||
}
|
||||
|
||||
@@ -158,11 +158,6 @@
|
||||
"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",
|
||||
@@ -287,9 +282,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )",
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -297,8 +292,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.logging": {
|
||||
@@ -308,7 +303,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.revit2022": {
|
||||
@@ -327,6 +322,12 @@
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -353,11 +354,11 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "q/E+FoRIGBDskZDr7eGiYk5YYCgd17Al1PH8LWR1JdL0i9CrLdeuJ+ozeKH6uqWOrlvdYQ75OjN1UwflMxew4w==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.1.0-dev.276"
|
||||
"Speckle.Sdk": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Revit.API": {
|
||||
@@ -368,9 +369,9 @@
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "XCA4dM4FuNkRwt0/zP8wyJKMwZXmyPp+QBkBwXQd3edwCunTul3pE/7/m87xDpBBy4/dnpWEHlnxbK3WGQpkGw==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -380,14 +381,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.1.0-dev.276"
|
||||
"Speckle.Sdk.Dependencies": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "iPqMbAElEDsfMia8fYHEiJp98xDpipOZ7DFLGdLP54Ct8NvBpEJRSwA7+s42uqQbCoCQC3aF4uZ6NMLJnDD0Xw=="
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,11 +158,6 @@
|
||||
"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",
|
||||
@@ -287,9 +282,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )",
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -297,8 +292,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.logging": {
|
||||
@@ -308,7 +303,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.revit2023": {
|
||||
@@ -327,6 +322,12 @@
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -353,11 +354,11 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "q/E+FoRIGBDskZDr7eGiYk5YYCgd17Al1PH8LWR1JdL0i9CrLdeuJ+ozeKH6uqWOrlvdYQ75OjN1UwflMxew4w==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.1.0-dev.276"
|
||||
"Speckle.Sdk": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Revit.API": {
|
||||
@@ -368,9 +369,9 @@
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "XCA4dM4FuNkRwt0/zP8wyJKMwZXmyPp+QBkBwXQd3edwCunTul3pE/7/m87xDpBBy4/dnpWEHlnxbK3WGQpkGw==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -380,14 +381,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.1.0-dev.276"
|
||||
"Speckle.Sdk.Dependencies": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "iPqMbAElEDsfMia8fYHEiJp98xDpipOZ7DFLGdLP54Ct8NvBpEJRSwA7+s42uqQbCoCQC3aF4uZ6NMLJnDD0Xw=="
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,11 +158,6 @@
|
||||
"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",
|
||||
@@ -287,9 +282,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )",
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -297,8 +292,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.logging": {
|
||||
@@ -308,7 +303,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.revit2024": {
|
||||
@@ -327,6 +322,12 @@
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -353,11 +354,11 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "q/E+FoRIGBDskZDr7eGiYk5YYCgd17Al1PH8LWR1JdL0i9CrLdeuJ+ozeKH6uqWOrlvdYQ75OjN1UwflMxew4w==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.1.0-dev.276"
|
||||
"Speckle.Sdk": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Revit.API": {
|
||||
@@ -368,9 +369,9 @@
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "XCA4dM4FuNkRwt0/zP8wyJKMwZXmyPp+QBkBwXQd3edwCunTul3pE/7/m87xDpBBy4/dnpWEHlnxbK3WGQpkGw==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.Bcl.AsyncInterfaces": "5.0.0",
|
||||
@@ -380,14 +381,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.1.0-dev.276"
|
||||
"Speckle.Sdk.Dependencies": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "iPqMbAElEDsfMia8fYHEiJp98xDpipOZ7DFLGdLP54Ct8NvBpEJRSwA7+s42uqQbCoCQC3aF4uZ6NMLJnDD0Xw=="
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,11 +143,6 @@
|
||||
"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",
|
||||
@@ -237,9 +232,9 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.DependencyInjection": "[2.2.0, )",
|
||||
"Speckle.Connectors.Logging": "[1.0.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )",
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.dui": {
|
||||
@@ -247,8 +242,8 @@
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Connectors.Common": "[1.0.0, )",
|
||||
"Speckle.Sdk": "[3.1.0-dev.276, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.0-dev.276, )"
|
||||
"Speckle.Sdk": "[3.1.7, )",
|
||||
"Speckle.Sdk.Dependencies": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.connectors.logging": {
|
||||
@@ -258,7 +253,7 @@
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Microsoft.Extensions.Logging.Abstractions": "[2.2.0, )",
|
||||
"Speckle.Objects": "[3.1.0-dev.276, )"
|
||||
"Speckle.Objects": "[3.1.7, )"
|
||||
}
|
||||
},
|
||||
"speckle.converters.revit2025": {
|
||||
@@ -277,6 +272,12 @@
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0"
|
||||
}
|
||||
},
|
||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "2.2.0",
|
||||
"contentHash": "f9hstgjVmr6rmrfGSpfsVOl2irKAgr1QjrSi3FgnS7kulxband50f2brRLwySAQTADPZeTdow0mpSMcoAdadCw=="
|
||||
},
|
||||
"Microsoft.Extensions.Logging": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[2.2.0, )",
|
||||
@@ -303,11 +304,11 @@
|
||||
},
|
||||
"Speckle.Objects": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "q/E+FoRIGBDskZDr7eGiYk5YYCgd17Al1PH8LWR1JdL0i9CrLdeuJ+ozeKH6uqWOrlvdYQ75OjN1UwflMxew4w==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "Htg6IeMLTTf8fTaOKEKMPZzrseu4NAtVpiZwVtLhg7ZzdndW8WlsvEyFRShK1o3hxlPsQJOA5qfsTvf5fcz/pQ==",
|
||||
"dependencies": {
|
||||
"Speckle.Sdk": "3.1.0-dev.276"
|
||||
"Speckle.Sdk": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Revit.API": {
|
||||
@@ -318,9 +319,9 @@
|
||||
},
|
||||
"Speckle.Sdk": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "XCA4dM4FuNkRwt0/zP8wyJKMwZXmyPp+QBkBwXQd3edwCunTul3pE/7/m87xDpBBy4/dnpWEHlnxbK3WGQpkGw==",
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "oi6fz5fSsWZ+VQiZukpom/fKHRH++Vlyf8a6rlkYQPj6NAqTIV3Rgthalt7Y7wWxGNRIP4KMdGTXvrN7wqCcjA==",
|
||||
"dependencies": {
|
||||
"GraphQL.Client": "6.0.0",
|
||||
"Microsoft.CSharp": "4.7.0",
|
||||
@@ -329,14 +330,14 @@
|
||||
"Microsoft.Extensions.Logging": "2.2.0",
|
||||
"Speckle.DoubleNumerics": "4.1.0",
|
||||
"Speckle.Newtonsoft.Json": "13.0.2",
|
||||
"Speckle.Sdk.Dependencies": "3.1.0-dev.276"
|
||||
"Speckle.Sdk.Dependencies": "3.1.7"
|
||||
}
|
||||
},
|
||||
"Speckle.Sdk.Dependencies": {
|
||||
"type": "CentralTransitive",
|
||||
"requested": "[3.1.0-dev.276, )",
|
||||
"resolved": "3.1.0-dev.276",
|
||||
"contentHash": "iPqMbAElEDsfMia8fYHEiJp98xDpipOZ7DFLGdLP54Ct8NvBpEJRSwA7+s42uqQbCoCQC3aF4uZ6NMLJnDD0Xw=="
|
||||
"requested": "[3.1.7, )",
|
||||
"resolved": "3.1.7",
|
||||
"contentHash": "T7FgbPXh9zI+VkC7f9I5qchtktEhslIOo2xeCm4VKRhImrR7naTmZInQ5MXIZvRfawZlPEg6u0tWzCV1q7ov9g=="
|
||||
}
|
||||
},
|
||||
"net8.0-windows7.0/win-x64": {
|
||||
|
||||
@@ -82,6 +82,8 @@ internal sealed class BasicConnectorBindingRevit : IBasicConnectorBinding
|
||||
|
||||
public void RemoveModel(ModelCard model) => _store.RemoveModel(model);
|
||||
|
||||
public void RemoveModels(List<ModelCard> models) => _store.RemoveModels(models);
|
||||
|
||||
public async Task HighlightModel(string modelCardId)
|
||||
{
|
||||
var model = _store.GetModelById(modelCardId);
|
||||
|
||||
@@ -72,9 +72,10 @@ internal sealed class RevitReceiveBinding : IReceiveBinding
|
||||
.ServiceProvider.GetRequiredService<IConverterSettingsStore<RevitConversionSettings>>()
|
||||
.Initialize(
|
||||
_revitConversionSettingsFactory.Create(
|
||||
DetailLevelType.Coarse, //TODO figure out
|
||||
DetailLevelType.Coarse, // TODO figure out
|
||||
null,
|
||||
false
|
||||
false,
|
||||
true
|
||||
)
|
||||
);
|
||||
// Receive host objects
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Collections.Concurrent;
|
||||
using Autodesk.Revit.DB;
|
||||
using Autodesk.Revit.DB.ExtensibleStorage;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
@@ -6,6 +5,7 @@ using Microsoft.Extensions.Logging;
|
||||
using Speckle.Connectors.Common.Caching;
|
||||
using Speckle.Connectors.Common.Cancellation;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Common.Threading;
|
||||
using Speckle.Connectors.DUI.Bindings;
|
||||
using Speckle.Connectors.DUI.Bridge;
|
||||
using Speckle.Connectors.DUI.Exceptions;
|
||||
@@ -41,6 +41,8 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
private readonly IRevitConversionSettingsFactory _revitConversionSettingsFactory;
|
||||
private readonly ISpeckleApplication _speckleApplication;
|
||||
private readonly ITopLevelExceptionHandler _topLevelExceptionHandler;
|
||||
private readonly LinkedModelHandler _linkedModelHandler;
|
||||
private readonly IThreadContext _threadContext;
|
||||
|
||||
/// <summary>
|
||||
/// Used internally to aggregate the changed objects' id. Note we're using a concurrent dictionary here as the expiry check method is not thread safe, and this was causing problems. See:
|
||||
@@ -48,7 +50,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
/// As to why a concurrent dictionary, it's because it's the cheapest/easiest way to do so.
|
||||
/// https://stackoverflow.com/questions/18922985/concurrent-hashsett-in-net-framework
|
||||
/// </summary>
|
||||
private ConcurrentDictionary<ElementId, byte> ChangedObjectIds { get; set; } = new();
|
||||
private ConcurrentHashSet<ElementId> ChangedObjectIds { get; set; } = new();
|
||||
|
||||
public RevitSendBinding(
|
||||
IAppIdleManager idleManager,
|
||||
@@ -64,7 +66,9 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
ElementUnpacker elementUnpacker,
|
||||
IRevitConversionSettingsFactory revitConversionSettingsFactory,
|
||||
ISpeckleApplication speckleApplication,
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler
|
||||
ITopLevelExceptionHandler topLevelExceptionHandler,
|
||||
LinkedModelHandler linkedModelHandler,
|
||||
IThreadContext threadContext
|
||||
)
|
||||
: base("sendBinding", bridge)
|
||||
{
|
||||
@@ -81,6 +85,8 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
_revitConversionSettingsFactory = revitConversionSettingsFactory;
|
||||
_speckleApplication = speckleApplication;
|
||||
_topLevelExceptionHandler = topLevelExceptionHandler;
|
||||
_linkedModelHandler = linkedModelHandler;
|
||||
_threadContext = threadContext;
|
||||
|
||||
Commands = new SendBindingUICommands(bridge);
|
||||
// TODO expiry events
|
||||
@@ -91,8 +97,6 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
_store.DocumentChanged += (_, _) => topLevelExceptionHandler.FireAndForget(async () => await OnDocumentChanged());
|
||||
}
|
||||
|
||||
private async Task OnDocumentStoreChangedEvent(object _) => await Commands.NotifyDocumentChanged();
|
||||
|
||||
public List<ISendFilter> GetSendFilters() =>
|
||||
[
|
||||
new RevitSelectionFilter() { IsDefault = true },
|
||||
@@ -104,7 +108,8 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
[
|
||||
new DetailLevelSetting(DetailLevelType.Medium),
|
||||
new ReferencePointSetting(ReferencePointType.InternalOrigin),
|
||||
new SendParameterNullOrEmptyStringsSetting(false)
|
||||
new SendParameterNullOrEmptyStringsSetting(false),
|
||||
new LinkedModelsSetting(true)
|
||||
];
|
||||
|
||||
public void CancelSend(string modelCardId) => _cancellationManager.CancelOperation(modelCardId);
|
||||
@@ -131,23 +136,23 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
_revitConversionSettingsFactory.Create(
|
||||
_toSpeckleSettingsManager.GetDetailLevelSetting(modelCard),
|
||||
_toSpeckleSettingsManager.GetReferencePointSetting(modelCard),
|
||||
_toSpeckleSettingsManager.GetSendParameterNullOrEmptyStringsSetting(modelCard)
|
||||
_toSpeckleSettingsManager.GetSendParameterNullOrEmptyStringsSetting(modelCard),
|
||||
_toSpeckleSettingsManager.GetLinkedModelsSetting(modelCard)
|
||||
)
|
||||
);
|
||||
|
||||
List<Element> elements = await RefreshElementsOnSender(modelCard.NotNull());
|
||||
List<ElementId> elementIds = elements.Select(el => el.Id).ToList();
|
||||
var elementsByTransform = await RefreshElementsIdsOnSender(modelCard.NotNull());
|
||||
|
||||
if (elementIds.Count == 0)
|
||||
if (elementsByTransform.Count == 0)
|
||||
{
|
||||
// Handle as CARD ERROR in this function
|
||||
throw new SpeckleSendFilterException("No objects were found to convert. Please update your publish filter!");
|
||||
}
|
||||
|
||||
var sendResult = await scope
|
||||
.ServiceProvider.GetRequiredService<SendOperation<ElementId>>()
|
||||
.ServiceProvider.GetRequiredService<SendOperation<DocumentToConvert>>()
|
||||
.Execute(
|
||||
elementIds,
|
||||
elementsByTransform,
|
||||
modelCard.GetSendInfo(_speckleApplication.ApplicationAndVersion),
|
||||
_operationProgressManager.CreateOperationProgressEventHandler(Parent, modelCardId, cancellationItem.Token),
|
||||
cancellationItem.Token
|
||||
@@ -177,28 +182,76 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<List<Element>> RefreshElementsOnSender(SenderModelCard modelCard)
|
||||
private async Task<List<DocumentToConvert>> RefreshElementsIdsOnSender(SenderModelCard modelCard)
|
||||
{
|
||||
var activeUIDoc =
|
||||
_revitContext.UIApplication.NotNull().ActiveUIDocument
|
||||
?? throw new SpeckleException("Unable to retrieve active UI document");
|
||||
|
||||
if (modelCard.SendFilter is IRevitSendFilter viewFilter)
|
||||
if (modelCard.SendFilter.NotNull() is IRevitSendFilter viewFilter)
|
||||
{
|
||||
viewFilter.SetContext(_revitContext);
|
||||
}
|
||||
|
||||
var selectedObjects = modelCard.SendFilter.NotNull().RefreshObjectIds();
|
||||
var selectedObjects = await _threadContext.RunOnMainAsync(
|
||||
() => Task.FromResult(modelCard.SendFilter.NotNull().RefreshObjectIds())
|
||||
);
|
||||
|
||||
List<Element> elements = selectedObjects
|
||||
var allElements = selectedObjects
|
||||
.Select(uid => activeUIDoc.Document.GetElement(uid))
|
||||
.Where(el => el is not null)
|
||||
.ToList();
|
||||
|
||||
// split elements between main model and linked models
|
||||
var elementsOnMainModel = allElements.Where(el => el is not RevitLinkInstance).ToList();
|
||||
var linkedModels = allElements.OfType<RevitLinkInstance>().ToList();
|
||||
|
||||
// create context for main document elements
|
||||
List<DocumentToConvert> documentElementContexts = [new(null, activeUIDoc.Document, elementsOnMainModel)];
|
||||
|
||||
// get the linked models setting - this decision belongs at this level
|
||||
bool includeLinkedModels = _toSpeckleSettingsManager.GetLinkedModelsSetting(modelCard);
|
||||
|
||||
// ⚠️ process linked models - RevitSendBinding controls the flow based on settings!
|
||||
// If setting not enabled, we won't unpack (see if-else block)
|
||||
if (linkedModels.Count > 0)
|
||||
{
|
||||
var linkedDocumentContexts = new List<DocumentToConvert>();
|
||||
|
||||
foreach (var linkedModel in linkedModels)
|
||||
{
|
||||
var linkedDoc = linkedModel.GetLinkDocument();
|
||||
if (linkedDoc == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var transform = linkedModel.GetTotalTransform().Inverse;
|
||||
|
||||
// decision about whether to process elements is made here, not in the handler
|
||||
// only collects elements from linked models when the setting is enabled
|
||||
if (includeLinkedModels)
|
||||
{
|
||||
// handler is only responsible for element collection mechanics
|
||||
var linkedElements = _linkedModelHandler.GetLinkedModelElements(modelCard.SendFilter, linkedDoc);
|
||||
linkedDocumentContexts.Add(new(transform, linkedDoc, linkedElements));
|
||||
}
|
||||
// ⚠️ when disabled, still adds empty contexts to maintain warning generation in RevitRootObjectBuilder
|
||||
// this approach (to signal that warnings are needed) relies on empty element lists which smells and is a bit of an implicit mechanism
|
||||
// buuuuut, it works (for now 👀).
|
||||
else
|
||||
{
|
||||
linkedDocumentContexts.Add(new(transform, linkedDoc, new List<Element>()));
|
||||
}
|
||||
}
|
||||
documentElementContexts.AddRange(linkedDocumentContexts);
|
||||
}
|
||||
|
||||
// update ID map
|
||||
if (modelCard.SendFilter is not null && modelCard.SendFilter.IdMap is not null)
|
||||
{
|
||||
var newSelectedObjectIds = new List<string>();
|
||||
foreach (Element element in elements)
|
||||
foreach (Element element in allElements)
|
||||
{
|
||||
modelCard.SendFilter.IdMap[element.Id.ToString()] = element.UniqueId;
|
||||
newSelectedObjectIds.Add(element.UniqueId);
|
||||
@@ -212,7 +265,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
);
|
||||
}
|
||||
|
||||
return elements;
|
||||
return documentElementContexts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -241,17 +294,17 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
|
||||
foreach (ElementId elementId in addedElementIds)
|
||||
{
|
||||
ChangedObjectIds[elementId] = 1;
|
||||
ChangedObjectIds.Add(elementId);
|
||||
}
|
||||
|
||||
foreach (ElementId elementId in deletedElementIds)
|
||||
{
|
||||
ChangedObjectIds[elementId] = 1;
|
||||
ChangedObjectIds.Add(elementId);
|
||||
}
|
||||
|
||||
foreach (ElementId elementId in modifiedElementIds)
|
||||
{
|
||||
ChangedObjectIds[elementId] = 1;
|
||||
ChangedObjectIds.Add(elementId);
|
||||
}
|
||||
|
||||
if (addedElementIds.Count > 0)
|
||||
@@ -272,7 +325,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
var selectedObjects = sender.SendFilter.NotNull().SelectedObjectIds;
|
||||
objectIds.AddRange(selectedObjects);
|
||||
}
|
||||
var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objectIds.ToList());
|
||||
var unpackedObjectIds = _elementUnpacker.GetUnpackedElementIds(objectIds);
|
||||
_sendConversionCache.EvictObjects(unpackedObjectIds);
|
||||
}
|
||||
|
||||
@@ -318,7 +371,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
{
|
||||
foreach (var sender in _store.GetSenders().ToList())
|
||||
{
|
||||
await RefreshElementsOnSender(sender);
|
||||
await RefreshElementsIdsOnSender(sender);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,9 +390,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
// }
|
||||
|
||||
if (
|
||||
ChangedObjectIds.Keys.Any(e =>
|
||||
_revitContext.UIApplication.NotNull().ActiveUIDocument.Document.GetElement(e) is View
|
||||
)
|
||||
ChangedObjectIds.Any(e => _revitContext.UIApplication.NotNull().ActiveUIDocument.Document.GetElement(e) is View)
|
||||
)
|
||||
{
|
||||
await Commands.RefreshSendFilters();
|
||||
@@ -358,7 +409,7 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
}
|
||||
|
||||
var objUniqueIds = new List<string>();
|
||||
var changedIds = ChangedObjectIds.Keys.ToList();
|
||||
var changedIds = ChangedObjectIds.ToList();
|
||||
|
||||
// Handling type changes: if an element's type is changed, we need to mark as changed all objects that have that type.
|
||||
// Step 1: get any changed types
|
||||
@@ -366,10 +417,10 @@ internal sealed class RevitSendBinding : RevitBaseBinding, ISendBinding
|
||||
.Select(e => doc.GetElement(e))
|
||||
.OfType<ElementType>()
|
||||
.Select(el => el.Id)
|
||||
.ToArray();
|
||||
.ToHashSet(); // ToHashSet() for faster Contains
|
||||
|
||||
// Step 2: Find all elements of the changed types, and add them to the changed ids list.
|
||||
if (elementTypeIdsList.Length != 0)
|
||||
if (elementTypeIdsList.Count != 0)
|
||||
{
|
||||
using var collector = new FilteredElementCollector(doc);
|
||||
var collectorElements = collector
|
||||
|
||||
@@ -56,15 +56,16 @@ internal sealed class SelectionBinding : RevitBaseBinding, ISelectionBinding, ID
|
||||
}
|
||||
|
||||
var activeUIDoc = _revitContext.UIApplication.ActiveUIDocument.NotNull();
|
||||
var doc = activeUIDoc.Document;
|
||||
|
||||
// POC: this was also being called on shutdown
|
||||
// probably the bridge needs to be able to know if the plugin has been terminated
|
||||
// also on termination the OnSelectionChanged event needs unwinding
|
||||
var selectionIds = activeUIDoc
|
||||
.Selection.GetElementIds()
|
||||
.Select(eid => activeUIDoc.Document.GetElement(eid).UniqueId.ToString())
|
||||
.ToList();
|
||||
return new SelectionInfo(selectionIds, $"{selectionIds.Count} objects selected.");
|
||||
var selectionIds = activeUIDoc.Selection.GetElementIds();
|
||||
//reduce allocates by allocating what we need.
|
||||
var selectionUniqueIds = new List<string>(selectionIds.Count);
|
||||
selectionUniqueIds.AddRange(selectionIds.Select(eid => doc.GetElement(eid).UniqueId));
|
||||
return new SelectionInfo(selectionUniqueIds, $"{selectionIds.Count} objects selected.");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
+3
-2
@@ -48,12 +48,13 @@ public static class ServiceRegistration
|
||||
serviceCollection.AddSingleton<IAppIdleManager, RevitIdleManager>();
|
||||
|
||||
// send operation and dependencies
|
||||
serviceCollection.AddScoped<SendOperation<ElementId>>();
|
||||
serviceCollection.AddScoped<SendOperation<DocumentToConvert>>();
|
||||
serviceCollection.AddScoped<ElementUnpacker>();
|
||||
serviceCollection.AddScoped<SendCollectionManager>();
|
||||
serviceCollection.AddScoped<IRootObjectBuilder<ElementId>, RevitRootObjectBuilder>();
|
||||
serviceCollection.AddScoped<IRootObjectBuilder<DocumentToConvert>, RevitRootObjectBuilder>();
|
||||
serviceCollection.AddSingleton<ISendConversionCache, SendConversionCache>();
|
||||
serviceCollection.AddSingleton<ToSpeckleSettingsManager>();
|
||||
serviceCollection.AddSingleton<LinkedModelHandler>();
|
||||
|
||||
// receive operation and dependencies
|
||||
serviceCollection.AddScoped<IHostObjectBuilder, RevitHostObjectBuilder>();
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
using Autodesk.Revit.DB;
|
||||
|
||||
namespace Speckle.Connectors.Revit.HostApp;
|
||||
|
||||
public record DocumentToConvert(Transform? Transform, Document Doc, List<Element> Elements);
|
||||
@@ -1,6 +1,8 @@
|
||||
using Autodesk.Revit.DB;
|
||||
using Autodesk.Revit.DB.Architecture;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.RevitShared.Helpers;
|
||||
using Speckle.Converters.RevitShared.Settings;
|
||||
|
||||
namespace Speckle.Connectors.Revit.HostApp;
|
||||
|
||||
@@ -10,10 +12,12 @@ namespace Speckle.Connectors.Revit.HostApp;
|
||||
public class ElementUnpacker
|
||||
{
|
||||
private readonly RevitContext _revitContext;
|
||||
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
|
||||
|
||||
public ElementUnpacker(RevitContext revitContext)
|
||||
public ElementUnpacker(RevitContext revitContext, IConverterSettingsStore<RevitConversionSettings> converterSettings)
|
||||
{
|
||||
_revitContext = revitContext;
|
||||
_converterSettings = converterSettings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -21,18 +25,21 @@ public class ElementUnpacker
|
||||
/// This method will also "pack" curtain walls if necessary (ie, if mullions or panels are selected without their parent curtain wall, they are sent independently; if the parent curtain wall is selected, they will be removed out as the curtain wall will include all its children).
|
||||
/// </summary>
|
||||
/// <param name="selectionElements"></param>
|
||||
/// <param name="doc"> We use the nullable document (happiness level 5/10) for the sake of linked models - bc we use this function in 2 different places <br/>
|
||||
/// 1- RootObjectBuilder with linked model document - otherwise we cannot unpack elements from correct document.<br/>
|
||||
/// 2- Evicting the cache while introducing the settings</param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<Element> UnpackSelectionForConversion(IEnumerable<Element> selectionElements)
|
||||
public IEnumerable<Element> UnpackSelectionForConversion(IEnumerable<Element> selectionElements, Document? doc = null)
|
||||
{
|
||||
// Note: steps kept separate on purpose.
|
||||
// Step 1: unpack groups
|
||||
var atomicObjects = UnpackElements(selectionElements);
|
||||
var atomicObjects = UnpackElements(selectionElements, doc);
|
||||
|
||||
// Step 2: pack curtain wall elements, once we know the full extent of our flattened item list.
|
||||
// The behaviour we're looking for:
|
||||
// If parent wall is part of selection, does not select individual elements out. Otherwise, selects individual elements (Panels, Mullions) as atomic objects.
|
||||
// NOTE: this also conditionally "packs" stacked wall elements if their parent is present. See detailed note inside the function.
|
||||
return PackCurtainWallElementsAndStackedWalls(atomicObjects);
|
||||
return PackCurtainWallElementsAndStackedWalls(atomicObjects, doc);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -43,38 +50,52 @@ public class ElementUnpacker
|
||||
/// <remarks>
|
||||
/// This is used to invalidate object ids in the send conversion cache when the selected object id is only the parent element id
|
||||
/// </remarks>
|
||||
public IEnumerable<string> GetUnpackedElementIds(List<string> objectIds)
|
||||
public IEnumerable<string> GetUnpackedElementIds(IEnumerable<string> objectIds)
|
||||
{
|
||||
var doc = _revitContext.UIApplication?.ActiveUIDocument.Document!;
|
||||
var docElements = doc.GetElements(objectIds);
|
||||
return UnpackSelectionForConversion(docElements).Select(o => o.UniqueId).ToList();
|
||||
}
|
||||
|
||||
private List<Element> UnpackElements(IEnumerable<Element> elements)
|
||||
// We use the nullable document (happiness level 5/10) for the sake of linked models - bc we use this function in 2 different places
|
||||
// 1- RootObjectBuilder with linked model document - otherwise we cannot unpack elements from correct document.
|
||||
// 2- Evicting the cache while introducing the settings
|
||||
private List<Element> UnpackElements(IEnumerable<Element> elements, Document? doc = null)
|
||||
{
|
||||
var unpackedElements = new List<Element>(); // note: could be a hashset/map so we prevent duplicates (?)
|
||||
var doc = _revitContext.UIApplication?.ActiveUIDocument.Document!;
|
||||
if (doc == null)
|
||||
{
|
||||
doc = _revitContext.UIApplication?.ActiveUIDocument.Document!;
|
||||
}
|
||||
|
||||
foreach (var element in elements)
|
||||
{
|
||||
// UNPACK: Groups
|
||||
if (element is Group g)
|
||||
{
|
||||
// When a group is from a linked model, GetMemberIds may behave differently
|
||||
// We add null checks to handle cases where elements can't be properly resolved
|
||||
// POC: this might screw up generating hosting rel generation here, because nested families in groups get flattened out by GetMemberIds().
|
||||
var groupElements = g.GetMemberIds().Select(doc.GetElement);
|
||||
var groupElements = g.GetMemberIds().Select(doc.GetElement).Where(el => el != null);
|
||||
unpackedElements.AddRange(UnpackElements(groupElements));
|
||||
}
|
||||
else if (element is BaseArray baseArray)
|
||||
{
|
||||
var arrayElements = baseArray.GetCopiedMemberIds().Select(doc.GetElement);
|
||||
var originalElements = baseArray.GetOriginalMemberIds().Select(doc.GetElement);
|
||||
// For arrays, collect both copied and original members with null checks
|
||||
// This handles cases where some elements might not resolve in linked contexts
|
||||
var arrayElements = baseArray.GetCopiedMemberIds().Select(doc.GetElement).Where(el => el != null);
|
||||
var originalElements = baseArray.GetOriginalMemberIds().Select(doc.GetElement).Where(el => el != null);
|
||||
unpackedElements.AddRange(UnpackElements(arrayElements));
|
||||
unpackedElements.AddRange(UnpackElements(originalElements));
|
||||
}
|
||||
// UNPACK: Family instances (as they potentially have nested families inside)
|
||||
else if (element is FamilyInstance familyInstance)
|
||||
{
|
||||
var familyElements = familyInstance.GetSubComponentIds().Select(doc.GetElement).ToArray();
|
||||
var familyElements = familyInstance
|
||||
.GetSubComponentIds()
|
||||
.Select(doc.GetElement)
|
||||
.Where(el => el != null)
|
||||
.ToArray();
|
||||
|
||||
if (familyElements.Length != 0)
|
||||
{
|
||||
@@ -85,7 +106,7 @@ public class ElementUnpacker
|
||||
}
|
||||
else if (element is MultistoryStairs multistoryStairs)
|
||||
{
|
||||
var stairs = multistoryStairs.GetAllStairsIds().Select(doc.GetElement);
|
||||
var stairs = multistoryStairs.GetAllStairsIds().Select(doc.GetElement).Where(el => el != null);
|
||||
unpackedElements.AddRange(UnpackElements(stairs));
|
||||
}
|
||||
else
|
||||
@@ -95,13 +116,22 @@ public class ElementUnpacker
|
||||
}
|
||||
// Why filtering for duplicates? Well, well, well... it's related to the comment above on groups: if a group
|
||||
// contains a nested family, GetMemberIds() will return... duplicates of the exploded family components.
|
||||
return unpackedElements.GroupBy(el => el.Id).Select(g => g.First()).ToList(); // no disinctBy in here sadly.
|
||||
|
||||
// Add null check before GroupBy to prevent NullReferenceException when processing linked models with groups
|
||||
// This ensures we don't try to access .Id on any null elements that might have been added during the unpacking process
|
||||
return unpackedElements.Where(el => el != null).GroupBy(el => el.Id).Select(g => g.First()).ToList(); // no disinctBy in here sadly.
|
||||
}
|
||||
|
||||
private List<Element> PackCurtainWallElementsAndStackedWalls(List<Element> elements)
|
||||
// We use the nullable document (happiness level 5/10) for the sake of linked models - bc we use this function in 2 different places
|
||||
// 1- RootObjectBuilder with linked model document - otherwise we cannot unpack elements from correct document.
|
||||
// 2- Evicting the cache while introducing the settings
|
||||
private List<Element> PackCurtainWallElementsAndStackedWalls(List<Element> elements, Document? doc = null)
|
||||
{
|
||||
var ids = elements.Select(el => el.Id).ToArray();
|
||||
var doc = _revitContext.UIApplication?.ActiveUIDocument.Document!;
|
||||
if (doc == null)
|
||||
{
|
||||
doc = _revitContext.UIApplication?.ActiveUIDocument.Document!;
|
||||
}
|
||||
elements.RemoveAll(element =>
|
||||
(element is Mullion { Host: not null } m && ids.Contains(m.Host.Id))
|
||||
|| (element is Panel { Host: not null } p && ids.Contains(p.Host.Id))
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
using System.IO;
|
||||
using Autodesk.Revit.DB;
|
||||
using Speckle.Connectors.DUI.Models.Card.SendFilter;
|
||||
using Speckle.Connectors.RevitShared;
|
||||
using Speckle.Connectors.RevitShared.Operations.Send.Filters;
|
||||
using Speckle.Converters.RevitShared.Helpers;
|
||||
using Speckle.Sdk.Common;
|
||||
|
||||
namespace Speckle.Connectors.Revit.HostApp;
|
||||
|
||||
/// <summary>
|
||||
/// Handles unpacking elements inside linked models.
|
||||
/// This class is responsible for the mechanics of retrieving elements from linked documents
|
||||
/// based on different filter types, but not for making decisions about whether linked models
|
||||
/// should be processed (which is the responsibility of the calling code)!
|
||||
/// </summary>
|
||||
public class LinkedModelHandler
|
||||
{
|
||||
private readonly RevitContext _revitContext;
|
||||
public Dictionary<string, string> LinkedModelDisplayNames { get; } = new();
|
||||
|
||||
public LinkedModelHandler(RevitContext revitContext)
|
||||
{
|
||||
_revitContext = revitContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets elements from a linked document based on the provided send filter.
|
||||
/// This method handles the specifics of element collection but doesn't make decisions
|
||||
/// about whether the linked model should be processed - that's the caller's responsibility.
|
||||
/// </summary>
|
||||
public List<Element> GetLinkedModelElements(ISendFilter sendFilter, Document linkedDocument)
|
||||
{
|
||||
// send mode → Categories
|
||||
if (sendFilter is RevitCategoriesFilter categoryFilter && categoryFilter.SelectedCategories is not null)
|
||||
{
|
||||
var categoryIds = categoryFilter
|
||||
.SelectedCategories.Select(c => ElementIdHelper.GetElementId(c))
|
||||
.OfType<ElementId>()
|
||||
.ToList();
|
||||
|
||||
if (categoryIds.Count > 0)
|
||||
{
|
||||
return GetElementsByCategory(linkedDocument, categoryIds);
|
||||
}
|
||||
return new List<Element>();
|
||||
}
|
||||
|
||||
// send mode → Views (taken from the legacy code)
|
||||
if (sendFilter is RevitViewsFilter viewFilter && viewFilter.GetView() != null)
|
||||
{
|
||||
RevitLinkInstance linkInstance = FindLinkInstanceForDocument(
|
||||
linkedDocument.PathName,
|
||||
_revitContext.UIApplication.NotNull().ActiveUIDocument.Document
|
||||
);
|
||||
|
||||
#if REVIT2024_OR_GREATER
|
||||
// revit 2024 and 2025 we can use the three-parameter constructor to get only visible elements
|
||||
using var viewCollector = new FilteredElementCollector(
|
||||
_revitContext.UIApplication.ActiveUIDocument.Document,
|
||||
viewFilter.GetView().NotNull().Id,
|
||||
linkInstance.Id
|
||||
);
|
||||
return viewCollector.WhereElementIsNotElementType().ToElements().ToList();
|
||||
#else
|
||||
// 🚨 LIMITATION: in Revit 2023 and below, we can only check if the entire linked model is visible,
|
||||
// not individual elements within it. If the linked model is visible, all its elements will be included.
|
||||
// constructor overload pertaining to searching and filtering visible elements from a revit link only added 2024.
|
||||
if (linkInstance.IsHidden(viewFilter.GetView().NotNull()))
|
||||
{
|
||||
return new List<Element>(); // if the linked model is hidden, return no elements
|
||||
}
|
||||
// 💩 fallback to getting all elements if the linked model is visible
|
||||
return GetAllElementsForLinkedModelSelection(linkedDocument);
|
||||
#endif
|
||||
}
|
||||
|
||||
// send mode → Selection
|
||||
return GetAllElementsForLinkedModelSelection(linkedDocument);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepares display names for linked model documents based on filename
|
||||
/// </summary>
|
||||
public void PrepareLinkedModelNames(IReadOnlyList<DocumentToConvert> documentElementContexts)
|
||||
{
|
||||
LinkedModelDisplayNames.Clear();
|
||||
// Group linked models by filename
|
||||
var linkedModels = documentElementContexts
|
||||
.Where(ctx => ctx.Doc.IsLinked)
|
||||
.GroupBy(ctx => Path.GetFileNameWithoutExtension(ctx.Doc.PathName))
|
||||
.ToDictionary(g => g.Key, g => g.ToList());
|
||||
|
||||
// Create a unique key for each instance
|
||||
foreach (var group in linkedModels)
|
||||
{
|
||||
string baseName = group.Key;
|
||||
var instances = group.Value;
|
||||
|
||||
// Single instance - just use the base name
|
||||
if (instances.Count == 1)
|
||||
{
|
||||
string id = GetIdFromDocumentToConvert(instances[0]);
|
||||
LinkedModelDisplayNames[id] = baseName;
|
||||
}
|
||||
// Multiple instances - add numbering
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < instances.Count; i++)
|
||||
{
|
||||
string id = GetIdFromDocumentToConvert(instances[i]);
|
||||
LinkedModelDisplayNames[id] = $"{baseName}_{i + 1}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string GetIdFromDocumentToConvert(DocumentToConvert documentToConvert) =>
|
||||
documentToConvert.Doc.GetHashCode() + "-" + (documentToConvert.Transform?.GetHashCode() ?? 0);
|
||||
|
||||
/// <summary>
|
||||
/// Gets elements from a document that belong to the specified categories.
|
||||
/// </summary>
|
||||
private List<Element> GetElementsByCategory(Document linkedDoc, List<ElementId> categoryIds)
|
||||
{
|
||||
using var multicategoryFilter = new ElementMulticategoryFilter(categoryIds);
|
||||
using var collector = new FilteredElementCollector(linkedDoc);
|
||||
return collector
|
||||
.WhereElementIsNotElementType()
|
||||
.WhereElementIsViewIndependent()
|
||||
.WherePasses(multicategoryFilter)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
// Helper method to generate a simple hash for a transform
|
||||
// transformedElement.applicationId = ${applicationId}-t{transformHash}
|
||||
public string GetTransformHash(Transform transform)
|
||||
{
|
||||
// create a simplified representation of the transform
|
||||
string json =
|
||||
$@"{{
|
||||
""origin"": [{transform.Origin.X:F2}, {transform.Origin.Y:F2}, {transform.Origin.Z:F2}],
|
||||
""basis"": [{transform.BasisX.X:F1}, {transform.BasisY.Y:F1}, {transform.BasisZ.Z:F1}]
|
||||
}}";
|
||||
|
||||
byte[] jsonBytes = System.Text.Encoding.UTF8.GetBytes(json);
|
||||
|
||||
#pragma warning disable CA1850
|
||||
using (var sha256 = System.Security.Cryptography.SHA256.Create())
|
||||
{
|
||||
byte[] hashBytes = sha256.ComputeHash(jsonBytes);
|
||||
// keep only the first 8 characters for a short but unique hash
|
||||
return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant()[..8];
|
||||
}
|
||||
#pragma warning restore CA1850
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all elements from the linked document when using selection filters.
|
||||
/// When a linked model is selected in the main document, we include all elements
|
||||
/// from that linked model since the selection is of the entire linked instance.
|
||||
/// </summary>
|
||||
private List<Element> GetAllElementsForLinkedModelSelection(Document linkedDoc)
|
||||
{
|
||||
using var collector = new FilteredElementCollector(linkedDoc);
|
||||
return collector.WhereElementIsNotElementType().WhereElementIsViewIndependent().ToList();
|
||||
}
|
||||
|
||||
private RevitLinkInstance FindLinkInstanceForDocument(string linkedDocumentPath, Document mainDocument)
|
||||
{
|
||||
using var collector = new FilteredElementCollector(mainDocument);
|
||||
return collector
|
||||
.OfClass(typeof(RevitLinkInstance))
|
||||
.Cast<RevitLinkInstance>()
|
||||
.FirstOrDefault(link => link.GetLinkDocument()?.PathName == linkedDocumentPath)
|
||||
.NotNull();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
using System.IO;
|
||||
using Autodesk.Revit.DB;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.RevitShared.Settings;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
|
||||
namespace Speckle.Connectors.Revit.HostApp;
|
||||
@@ -13,6 +15,8 @@ public class SendCollectionManager
|
||||
private readonly IConverterSettingsStore<RevitConversionSettings> _converterSettings;
|
||||
private readonly Dictionary<string, Collection> _collectionCache = new();
|
||||
private readonly Dictionary<ElementId, (string name, Dictionary<string, object?> props)> _levelCache = new(); // stores level id and its properties
|
||||
private readonly Dictionary<string, Collection> _linkedModelCollections = new(); // cache for linked model collections
|
||||
private Collection? _mainModelCollection; // collection for main model elements
|
||||
|
||||
public SendCollectionManager(IConverterSettingsStore<RevitConversionSettings> converterSettings)
|
||||
{
|
||||
@@ -20,18 +24,72 @@ public class SendCollectionManager
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the element's host collection based on level, category and optional type. Additionally, places the host collection on the provided root object.
|
||||
/// Returns the element's host collection based on level, category and optional type if the main model only is sent.
|
||||
/// The host collection is placed on the provided root object.
|
||||
/// If linked models are being sent, nested collections are sent under the provided root object.
|
||||
/// Note, it's not nice we're mutating the root object in this function.
|
||||
/// </summary>
|
||||
/// <param name="element"></param>
|
||||
/// <param name="rootObject"></param>
|
||||
/// <returns></returns>
|
||||
public Collection GetAndCreateObjectHostCollection(Element element, Collection rootObject)
|
||||
public Collection GetAndCreateObjectHostCollection(
|
||||
Element element,
|
||||
Collection rootObject,
|
||||
bool sendWithLinkedModels,
|
||||
string? modelDisplayName = null
|
||||
)
|
||||
{
|
||||
var doc = _converterSettings.Current.Document;
|
||||
var path = new List<string>();
|
||||
|
||||
// Step 0: get the level and its properties
|
||||
// Get model path and name
|
||||
string modelPath = doc.PathName;
|
||||
string modelName = Path.GetFileNameWithoutExtension(modelPath);
|
||||
bool isLinkedModel = doc.IsLinked;
|
||||
|
||||
// Set up the correct hierarchy based on whether we have linked models or not
|
||||
Collection startingCollection;
|
||||
|
||||
if (sendWithLinkedModels) // this arg comes from RevitRootObjectBuilder and check is setting enabled and linked models present
|
||||
{
|
||||
// if we're sending linked models, create a nested structure
|
||||
// for the main model
|
||||
if (!isLinkedModel)
|
||||
{
|
||||
// create main model collection if it doesn't exist yet
|
||||
if (_mainModelCollection == null)
|
||||
{
|
||||
_mainModelCollection = new Collection(rootObject.name);
|
||||
rootObject.elements.Add(_mainModelCollection);
|
||||
}
|
||||
|
||||
startingCollection = _mainModelCollection;
|
||||
}
|
||||
// for linked models
|
||||
else
|
||||
{
|
||||
// Use display name from settings if available, otherwise use original name
|
||||
string displayName = modelDisplayName ?? modelName;
|
||||
|
||||
// Check if we already have a collection for this model display name
|
||||
if (!_linkedModelCollections.TryGetValue(displayName, out Collection? linkedModelCollection))
|
||||
{
|
||||
// First time seeing this model with this display name
|
||||
linkedModelCollection = new Collection(displayName);
|
||||
rootObject.elements.Add(linkedModelCollection);
|
||||
_linkedModelCollections[displayName] = linkedModelCollection;
|
||||
}
|
||||
|
||||
startingCollection = linkedModelCollection;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we don't have linked models, use the root directly
|
||||
startingCollection = rootObject;
|
||||
}
|
||||
|
||||
// get the level and its properties
|
||||
string levelName = "No Level";
|
||||
Dictionary<string, object?> levelProperties = new();
|
||||
if (element.LevelId != ElementId.InvalidElementId)
|
||||
@@ -43,15 +101,20 @@ public class SendCollectionManager
|
||||
}
|
||||
else
|
||||
{
|
||||
var level = (Level)doc.GetElement(element.LevelId);
|
||||
levelName = level.Name;
|
||||
levelProperties.Add("elevation", level.Elevation);
|
||||
levelProperties.Add("units", _converterSettings.Current.SpeckleUnits);
|
||||
_levelCache.Add(element.LevelId, (levelName, levelProperties));
|
||||
try
|
||||
{
|
||||
var level = (Level)doc.GetElement(element.LevelId);
|
||||
levelName = level.Name;
|
||||
levelProperties.Add("elevation", level.Elevation);
|
||||
levelProperties.Add("units", _converterSettings.Current.SpeckleUnits);
|
||||
_levelCache.Add(element.LevelId, (levelName, levelProperties));
|
||||
}
|
||||
// the exception is swallowed since if an exception occurs, we fall back to "No Level" for the element
|
||||
catch (Exception e) when (!e.IsFatal()) { }
|
||||
}
|
||||
}
|
||||
|
||||
// Step 1: create path components. Currently, this is
|
||||
// create path components. Currently, this is
|
||||
// level > category > type
|
||||
path.Add(levelName);
|
||||
path.Add(element.Category?.Name ?? "No category");
|
||||
@@ -69,19 +132,23 @@ public class SendCollectionManager
|
||||
path.Add("No type");
|
||||
}
|
||||
|
||||
string fullPathName = string.Concat(path);
|
||||
// Use the collection's name for cache keys to ensure proper separation
|
||||
string modelIdentifier = startingCollection.name;
|
||||
|
||||
// create a model-specific key for the collection cache
|
||||
string fullPathName = $"{modelIdentifier}:{string.Join(":", path)}";
|
||||
if (_collectionCache.TryGetValue(fullPathName, out Collection? value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
string flatPathName = "";
|
||||
Collection previousCollection = rootObject;
|
||||
string flatPathName = modelIdentifier;
|
||||
Collection previousCollection = startingCollection;
|
||||
|
||||
for (int i = 0; i < path.Count; i++)
|
||||
{
|
||||
var pathItem = path[i];
|
||||
flatPathName += pathItem;
|
||||
flatPathName += ":" + pathItem;
|
||||
Collection childCollection;
|
||||
if (_collectionCache.TryGetValue(flatPathName, out Collection? collection))
|
||||
{
|
||||
@@ -90,8 +157,7 @@ public class SendCollectionManager
|
||||
else
|
||||
{
|
||||
childCollection = new Collection(pathItem);
|
||||
// add props if it's the 1st path item, representing level
|
||||
// if the structure ever changes from level > category > type, this needs to be changed
|
||||
// add properties to level collection
|
||||
if (i == 0 && levelProperties.Count > 0)
|
||||
{
|
||||
childCollection["properties"] = levelProperties;
|
||||
|
||||
+12
-3
@@ -40,12 +40,17 @@ public class RevitCategoriesFilter : DiscriminatedObject, ISendFilter, IRevitSen
|
||||
/// <exception cref="SpeckleSendFilterException">Whenever no view is found.</exception>
|
||||
public List<string> RefreshObjectIds()
|
||||
{
|
||||
var objectIds = new List<string>();
|
||||
if (SelectedCategories is null)
|
||||
{
|
||||
return objectIds;
|
||||
return [];
|
||||
}
|
||||
|
||||
// ⚠️ this is ugly, BUT we need to preserve RevitLinkInstances regardless of category.
|
||||
// these get unpacked later in the RefreshElementsIdsOnSender, so if we don't do this, they'll get filtered out here
|
||||
using var linkCollector = new FilteredElementCollector(_doc);
|
||||
var linkInstanceIds = linkCollector.OfClass(typeof(RevitLinkInstance)).Select(link => link.UniqueId).ToList();
|
||||
|
||||
// get elements that match the selected categories (excluding RevitLinkInstance objects)
|
||||
var elementIds = SelectedCategories.Select(c => ElementIdHelper.GetElementId(c)).Where(e => e is not null).ToList();
|
||||
|
||||
using var categoryFilter = new ElementMulticategoryFilter(elementIds);
|
||||
@@ -55,7 +60,11 @@ public class RevitCategoriesFilter : DiscriminatedObject, ISendFilter, IRevitSen
|
||||
.WhereElementIsViewIndependent()
|
||||
.WherePasses(categoryFilter)
|
||||
.ToList();
|
||||
objectIds = elements.Select(e => e.UniqueId).ToList();
|
||||
|
||||
// combine both sets
|
||||
var objectIds = elements.Select(e => e.UniqueId).ToList();
|
||||
objectIds.AddRange(linkInstanceIds);
|
||||
|
||||
SelectedObjectIds = objectIds;
|
||||
return objectIds;
|
||||
}
|
||||
|
||||
+3
-4
@@ -53,10 +53,9 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
|
||||
/// <exception cref="SpeckleSendFilterException">Whenever no view is found.</exception>
|
||||
public List<string> RefreshObjectIds()
|
||||
{
|
||||
var objectIds = new List<string>();
|
||||
if (SelectedView is null)
|
||||
{
|
||||
return objectIds;
|
||||
return [];
|
||||
}
|
||||
|
||||
// Paşa Bilal wants it like this... (three dots = important meaning for ogu)
|
||||
@@ -75,8 +74,8 @@ public class RevitViewsFilter : DiscriminatedObject, ISendFilter, IRevitSendFilt
|
||||
throw new SpeckleSendFilterException("View not found, please update your model send filter.");
|
||||
}
|
||||
using var viewCollector = new FilteredElementCollector(_doc, view.Id);
|
||||
List<Element> elementsInView = viewCollector.ToElements().ToList();
|
||||
objectIds = elementsInView.Select(e => e.UniqueId).ToList();
|
||||
var elementsInView = viewCollector.ToElements();
|
||||
var objectIds = elementsInView.Select(e => e.UniqueId).ToList();
|
||||
SelectedObjectIds = objectIds;
|
||||
return objectIds;
|
||||
}
|
||||
|
||||
+161
-67
@@ -12,6 +12,7 @@ using Speckle.Converters.Common;
|
||||
using Speckle.Converters.RevitShared.Helpers;
|
||||
using Speckle.Converters.RevitShared.Settings;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
|
||||
@@ -25,20 +26,22 @@ public class RevitRootObjectBuilder(
|
||||
IThreadContext threadContext,
|
||||
SendCollectionManager sendCollectionManager,
|
||||
ILogger<RevitRootObjectBuilder> logger,
|
||||
RevitToSpeckleCacheSingleton revitToSpeckleCacheSingleton
|
||||
) : IRootObjectBuilder<ElementId>
|
||||
RevitToSpeckleCacheSingleton revitToSpeckleCacheSingleton,
|
||||
LinkedModelHandler linkedModelHandler
|
||||
) : IRootObjectBuilder<DocumentToConvert>
|
||||
{
|
||||
// POC: SendSelection and RevitConversionContextStack should be interfaces, former needs interfaces
|
||||
|
||||
public Task<RootObjectBuilderResult> Build(
|
||||
IReadOnlyList<ElementId> objects,
|
||||
IReadOnlyList<DocumentToConvert> documentElementContexts,
|
||||
SendInfo sendInfo,
|
||||
IProgress<CardProgress> onOperationProgressed,
|
||||
CancellationToken ct = default
|
||||
) => threadContext.RunOnMainAsync(() => Task.FromResult(BuildSync(objects, sendInfo, onOperationProgressed, ct)));
|
||||
) =>
|
||||
threadContext.RunOnMainAsync(
|
||||
() => Task.FromResult(BuildSync(documentElementContexts, sendInfo, onOperationProgressed, ct))
|
||||
);
|
||||
|
||||
private RootObjectBuilderResult BuildSync(
|
||||
IReadOnlyList<ElementId> objects,
|
||||
IReadOnlyList<DocumentToConvert> documentElementContexts,
|
||||
SendInfo sendInfo,
|
||||
IProgress<CardProgress> onOperationProgressed,
|
||||
CancellationToken cancellationToken
|
||||
@@ -51,96 +54,187 @@ public class RevitRootObjectBuilder(
|
||||
throw new SpeckleException("Family Environment documents are not supported.");
|
||||
}
|
||||
|
||||
// 0 - Init the root
|
||||
// init the root
|
||||
Collection rootObject =
|
||||
new() { name = converterSettings.Current.Document.PathName.Split('\\').Last().Split('.').First() };
|
||||
rootObject["units"] = converterSettings.Current.SpeckleUnits;
|
||||
|
||||
var revitElements = new List<Element>();
|
||||
List<SendConversionResult> results = new(revitElements.Count);
|
||||
// Convert ids to actual revit elements
|
||||
foreach (var id in objects)
|
||||
var filteredDocumentsToConvert = new List<DocumentToConvert>();
|
||||
bool sendWithLinkedModels = converterSettings.Current.SendLinkedModels;
|
||||
List<SendConversionResult> results = new();
|
||||
|
||||
// Prepare linked model display names if needed
|
||||
if (sendWithLinkedModels)
|
||||
{
|
||||
var el = converterSettings.Current.Document.GetElement(id);
|
||||
if (el == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (el.Category == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
revitElements.Add(el);
|
||||
linkedModelHandler.PrepareLinkedModelNames(documentElementContexts);
|
||||
}
|
||||
|
||||
if (revitElements.Count == 0)
|
||||
foreach (var documentElementContext in documentElementContexts)
|
||||
{
|
||||
// add appropriate warnings for linked documents
|
||||
if (documentElementContext.Doc.IsLinked && !sendWithLinkedModels)
|
||||
{
|
||||
results.Add(
|
||||
new(
|
||||
Status.WARNING,
|
||||
documentElementContext.Doc.PathName,
|
||||
typeof(RevitLinkInstance).ToString(),
|
||||
null,
|
||||
new SpeckleException("Enable linked model support from the settings to send this object")
|
||||
)
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// filter for valid elements
|
||||
// if send linked models setting is disabled List<Elements> will be empty, and we won't enter foreach loop
|
||||
var elementsInTransform = new List<Element>();
|
||||
foreach (var el in documentElementContext.Elements)
|
||||
{
|
||||
if (el == null || el.Category == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
elementsInTransform.Add(el);
|
||||
}
|
||||
|
||||
// only add contexts with elements
|
||||
if (elementsInTransform.Count > 0)
|
||||
{
|
||||
filteredDocumentsToConvert.Add(documentElementContext with { Elements = elementsInTransform });
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: check the exception!!!!
|
||||
if (filteredDocumentsToConvert.Count == 0)
|
||||
{
|
||||
throw new SpeckleSendFilterException("No objects were found. Please update your publish filter!");
|
||||
}
|
||||
|
||||
// Unpack groups (& other complex data structures)
|
||||
var atomicObjects = elementUnpacker.UnpackSelectionForConversion(revitElements).ToList();
|
||||
var atomicObjectsByDocumentAndTransform = new List<DocumentToConvert>();
|
||||
var atomicObjectCount = 0;
|
||||
foreach (var filteredDocumentToConvert in filteredDocumentsToConvert)
|
||||
{
|
||||
using (
|
||||
converterSettings.Push(currentSettings => currentSettings with { Document = filteredDocumentToConvert.Doc })
|
||||
)
|
||||
{
|
||||
var atomicObjects = elementUnpacker
|
||||
.UnpackSelectionForConversion(filteredDocumentToConvert.Elements, filteredDocumentToConvert.Doc)
|
||||
.ToList();
|
||||
atomicObjectsByDocumentAndTransform.Add(filteredDocumentToConvert with { Elements = atomicObjects });
|
||||
atomicObjectCount += atomicObjects.Count;
|
||||
}
|
||||
}
|
||||
|
||||
var countProgress = 0;
|
||||
var cacheHitCount = 0;
|
||||
var skippedObjectCount = 0;
|
||||
foreach (Element revitElement in atomicObjects)
|
||||
|
||||
foreach (var atomicObjectByDocumentAndTransform in atomicObjectsByDocumentAndTransform)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
string applicationId = revitElement.UniqueId;
|
||||
string sourceType = revitElement.GetType().Name;
|
||||
try
|
||||
string? modelDisplayName = null;
|
||||
if (atomicObjectByDocumentAndTransform.Doc.IsLinked)
|
||||
{
|
||||
if (!SupportedCategoriesUtils.IsSupportedCategory(revitElement.Category))
|
||||
string id = linkedModelHandler.GetIdFromDocumentToConvert(atomicObjectByDocumentAndTransform);
|
||||
linkedModelHandler.LinkedModelDisplayNames.TryGetValue(id, out modelDisplayName);
|
||||
}
|
||||
|
||||
// here we do magic for changing the transform and the related document according to model. first one is always the main model.
|
||||
using (
|
||||
converterSettings.Push(currentSettings =>
|
||||
currentSettings with
|
||||
{
|
||||
ReferencePointTransform = atomicObjectByDocumentAndTransform.Transform,
|
||||
Document = atomicObjectByDocumentAndTransform.Doc,
|
||||
}
|
||||
)
|
||||
)
|
||||
{
|
||||
var atomicObjects = atomicObjectByDocumentAndTransform.Elements;
|
||||
foreach (Element revitElement in atomicObjects)
|
||||
{
|
||||
var cat = revitElement.Category != null ? revitElement.Category.Name : "No category";
|
||||
results.Add(
|
||||
new(
|
||||
Status.WARNING,
|
||||
revitElement.UniqueId,
|
||||
cat,
|
||||
null,
|
||||
new SpeckleException($"Category {cat} is not supported.")
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
string applicationId = revitElement.UniqueId;
|
||||
string sourceType = revitElement.GetType().Name;
|
||||
try
|
||||
{
|
||||
if (!SupportedCategoriesUtils.IsSupportedCategory(revitElement.Category))
|
||||
{
|
||||
var cat = revitElement.Category != null ? revitElement.Category.Name : "No category";
|
||||
results.Add(
|
||||
new(
|
||||
Status.WARNING,
|
||||
revitElement.UniqueId,
|
||||
cat,
|
||||
null,
|
||||
new SpeckleException($"Category {cat} is not supported.")
|
||||
)
|
||||
);
|
||||
skippedObjectCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
Base converted;
|
||||
bool hasTransform = atomicObjectByDocumentAndTransform.Transform != null;
|
||||
|
||||
// non-transformed elements can safely rely on cache
|
||||
// TODO: Potential here to transform cached objects and NOT reconvert,
|
||||
// TODO: we wont do !hasTransform here, and re-set application id before this
|
||||
if (
|
||||
!hasTransform
|
||||
&& sendConversionCache.TryGetValue(sendInfo.ProjectId, applicationId, out ObjectReference? value)
|
||||
)
|
||||
);
|
||||
skippedObjectCount++;
|
||||
continue;
|
||||
}
|
||||
{
|
||||
converted = value;
|
||||
cacheHitCount++;
|
||||
}
|
||||
// not in cache means we convert
|
||||
else
|
||||
{
|
||||
// if it has a transform we append transform hash to the applicationId to distinguish the elements from other instances
|
||||
if (hasTransform)
|
||||
{
|
||||
string transformHash = linkedModelHandler.GetTransformHash(
|
||||
atomicObjectByDocumentAndTransform.Transform.NotNull()
|
||||
);
|
||||
applicationId = $"{applicationId}_t{transformHash}";
|
||||
}
|
||||
// normal conversions
|
||||
converted = converter.Convert(revitElement);
|
||||
converted.applicationId = applicationId;
|
||||
}
|
||||
|
||||
Base converted;
|
||||
if (sendConversionCache.TryGetValue(sendInfo.ProjectId, applicationId, out ObjectReference? value))
|
||||
{
|
||||
converted = value;
|
||||
cacheHitCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
converted = converter.Convert(revitElement);
|
||||
converted.applicationId = applicationId;
|
||||
}
|
||||
var collection = sendCollectionManager.GetAndCreateObjectHostCollection(
|
||||
revitElement,
|
||||
rootObject,
|
||||
sendWithLinkedModels,
|
||||
modelDisplayName
|
||||
);
|
||||
|
||||
var collection = sendCollectionManager.GetAndCreateObjectHostCollection(revitElement, rootObject);
|
||||
collection.elements.Add(converted);
|
||||
results.Add(new(Status.SUCCESS, applicationId, sourceType, converted));
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
logger.LogSendConversionError(ex, sourceType);
|
||||
results.Add(new(Status.ERROR, applicationId, sourceType, null, ex));
|
||||
}
|
||||
|
||||
collection.elements.Add(converted);
|
||||
results.Add(new(Status.SUCCESS, applicationId, sourceType, converted));
|
||||
onOperationProgressed.Report(new("Converting", (double)++countProgress / atomicObjectCount));
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
logger.LogSendConversionError(ex, sourceType);
|
||||
results.Add(new(Status.ERROR, applicationId, sourceType, null, ex));
|
||||
}
|
||||
|
||||
onOperationProgressed.Report(new("Converting", (double)++countProgress / atomicObjects.Count));
|
||||
}
|
||||
|
||||
if (results.All(x => x.Status == Status.ERROR) || skippedObjectCount == atomicObjects.Count)
|
||||
if (results.All(x => x.Status == Status.ERROR) || skippedObjectCount == atomicObjectCount)
|
||||
{
|
||||
throw new SpeckleException("Failed to convert all objects.");
|
||||
}
|
||||
|
||||
var idsAndSubElementIds = elementUnpacker.GetElementsAndSubelementIdsFromAtomicObjects(atomicObjects);
|
||||
var idsAndSubElementIds = elementUnpacker.GetElementsAndSubelementIdsFromAtomicObjects(
|
||||
atomicObjectsByDocumentAndTransform.SelectMany(t => t.Elements).ToList()
|
||||
);
|
||||
var renderMaterialProxies = revitToSpeckleCacheSingleton.GetRenderMaterialProxyListForObjects(idsAndSubElementIds);
|
||||
rootObject[ProxyKeys.RENDER_MATERIAL] = renderMaterialProxies;
|
||||
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using Speckle.Connectors.DUI.Settings;
|
||||
|
||||
namespace Speckle.Connectors.Revit.Operations.Send.Settings;
|
||||
|
||||
public class LinkedModelsSetting(bool value) : ICardSetting
|
||||
{
|
||||
public string? Id { get; set; } = "includeLinkedModels";
|
||||
public string? Title { get; set; } = "Include Linked Models";
|
||||
public string? Type { get; set; } = "boolean";
|
||||
public object? Value { get; set; } = value;
|
||||
public List<string>? Enum { get; set; }
|
||||
}
|
||||
+19
@@ -21,6 +21,7 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
|
||||
private readonly Dictionary<string, DetailLevelType> _detailLevelCache = new();
|
||||
private readonly Dictionary<string, Transform?> _referencePointCache = new();
|
||||
private readonly Dictionary<string, bool?> _sendNullParamsCache = new();
|
||||
private readonly Dictionary<string, bool?> _sendLinkedModelsCache = new();
|
||||
|
||||
public ToSpeckleSettingsManager(
|
||||
RevitContext revitContext,
|
||||
@@ -102,6 +103,24 @@ public class ToSpeckleSettingsManager : IToSpeckleSettingsManager
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
// NOTE: Cache invalidation currently a placeholder until we have more understanding on the sends
|
||||
// TODO: Evaluate cache invalidation for GetLinkedModelsSetting
|
||||
public bool GetLinkedModelsSetting(SenderModelCard modelCard)
|
||||
{
|
||||
var value = modelCard.Settings?.First(s => s.Id == "includeLinkedModels").Value as bool?;
|
||||
var returnValue = value != null && value.NotNull();
|
||||
|
||||
if (_sendLinkedModelsCache.TryGetValue(modelCard.ModelCardId.NotNull(), out bool? previousValue))
|
||||
{
|
||||
if (previousValue != returnValue)
|
||||
{
|
||||
EvictCacheForModelCard(modelCard);
|
||||
}
|
||||
}
|
||||
_sendLinkedModelsCache[modelCard.ModelCardId] = returnValue;
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
private void EvictCacheForModelCard(SenderModelCard modelCard)
|
||||
{
|
||||
var objectIds = modelCard.SendFilter != null ? modelCard.SendFilter.NotNull().SelectedObjectIds : [];
|
||||
|
||||
+3
@@ -20,7 +20,9 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Bindings\RevitSendBinding.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ElementIdHelper.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\DocumentModelStorageSchema.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\DocumentToConvert.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\Elements.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\LinkedModelHandler.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\RevitMaterialBaker.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)HostApp\SupportedCategoriesUtils.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Receive\HideWarningsFailuresPreprocessor.cs" />
|
||||
@@ -40,6 +42,7 @@
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\RevitSelectionFilter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Filters\RevitViewsFilter.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\RevitRootObjectBuilder.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\LinkedModelsSetting.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\SendParameterNullOrEmptyStringsSetting.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ToSpeckleSettingsManager.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Operations\Send\Settings\ReferencePointSetting.cs" />
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Connectors.Grasshopper8.Registration;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.BaseComponents;
|
||||
|
||||
public abstract class SpeckleScopedTaskCapableComponent<TInput, TOutput>(
|
||||
string name,
|
||||
string nickname,
|
||||
string description,
|
||||
string category,
|
||||
string subCategory
|
||||
) : SpeckleTaskCapableComponent<TInput, TOutput>(name, nickname, description, category, subCategory)
|
||||
{
|
||||
protected override Task<TOutput> PerformTask(TInput input, CancellationToken cancellationToken = default)
|
||||
{
|
||||
/*using*/var scope = PriorityLoader.Container.CreateScope(); // NOTE: this component does not work as intended in e.g the receive component; the scope gets disposed before the task completes.
|
||||
return PerformScopedTask(input, scope, cancellationToken);
|
||||
}
|
||||
|
||||
protected abstract Task<TOutput> PerformScopedTask(
|
||||
TInput input,
|
||||
IServiceScope scope,
|
||||
CancellationToken cancellationToken = default
|
||||
);
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Speckle.Sdk;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.BaseComponents;
|
||||
|
||||
public abstract class SpeckleTaskCapableComponent<TInput, TOutput>(
|
||||
string name,
|
||||
string nickname,
|
||||
string description,
|
||||
string category,
|
||||
string subCategory
|
||||
) : GH_TaskCapableComponent<TOutput>(name, nickname, description, category, subCategory)
|
||||
{
|
||||
protected override void SolveInstance(IGH_DataAccess da)
|
||||
{
|
||||
//TODO: We're missing activity and logging here. Will enable it for all inherited classes.
|
||||
|
||||
if (InPreSolve)
|
||||
{
|
||||
// Collect the data and create the task
|
||||
try
|
||||
{
|
||||
var input = GetInput(da);
|
||||
TaskList.Add(PerformTask(input, CancelToken));
|
||||
}
|
||||
catch (SpeckleException e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GetSolveResults(da, out TOutput result))
|
||||
{
|
||||
// INFO: This will run synchronously. Useful for Rhino.Compute runs, but can also be enabled by user.
|
||||
try
|
||||
{
|
||||
TInput input = GetInput(da);
|
||||
var syncResult = PerformTask(input).Result;
|
||||
result = syncResult;
|
||||
}
|
||||
catch (SpeckleException e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (result is not null)
|
||||
{
|
||||
SetOutput(da, result);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract TInput GetInput(IGH_DataAccess da);
|
||||
|
||||
protected abstract void SetOutput(IGH_DataAccess da, TOutput result);
|
||||
|
||||
protected abstract Task<TOutput> PerformTask(TInput input, CancellationToken cancellationToken = default);
|
||||
}
|
||||
+1860
File diff suppressed because it is too large
Load Diff
+71
@@ -0,0 +1,71 @@
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Speckle.Connectors.Grasshopper8.Components.BaseComponents;
|
||||
using Speckle.Connectors.Grasshopper8.HostApp;
|
||||
using Speckle.Connectors.Grasshopper8.Parameters;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.Collections;
|
||||
|
||||
public class CollectionPathsSelector : ValueSet<IGH_Goo>
|
||||
{
|
||||
public CollectionPathsSelector()
|
||||
: base(
|
||||
"Collection Paths Selector",
|
||||
"Paths",
|
||||
"Allows you to select a set of collection paths for filtering",
|
||||
"Speckle",
|
||||
"Collections"
|
||||
) { }
|
||||
|
||||
public override Guid ComponentGuid => new Guid("65FC4D58-2209-41B6-9B22-BE51C8B28604");
|
||||
|
||||
protected override void LoadVolatileData()
|
||||
{
|
||||
var collections = VolatileData
|
||||
.AllData(true)
|
||||
.OfType<SpeckleCollectionWrapperGoo>()
|
||||
.Select(goo => goo.Value)
|
||||
.ToList();
|
||||
if (collections.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: supporting multiple collections? maybe? not really?
|
||||
var paths = new List<string>();
|
||||
foreach (SpeckleCollectionWrapper col in collections)
|
||||
{
|
||||
paths.AddRange(GetPaths(col.Collection));
|
||||
}
|
||||
m_data.AppendRange(paths.Select(s => new GH_String(s)));
|
||||
}
|
||||
|
||||
private List<string> GetPaths(Collection c)
|
||||
{
|
||||
var currentPath = new List<string>();
|
||||
var allPaths = new HashSet<string>();
|
||||
|
||||
void GetPathsInternal(Collection col)
|
||||
{
|
||||
currentPath.Add(col.name);
|
||||
var subCols = col.elements.OfType<SpeckleCollectionWrapper>().ToList();
|
||||
|
||||
// NOTE: here we're basically outputting only paths that correspond to a collection
|
||||
// that has values inside of it.
|
||||
if (subCols.Count != col.elements.Count)
|
||||
{
|
||||
allPaths.Add(string.Join(Constants.LAYER_PATH_DELIMITER, currentPath));
|
||||
}
|
||||
|
||||
foreach (var subCol in subCols)
|
||||
{
|
||||
GetPathsInternal(subCol.Collection);
|
||||
}
|
||||
currentPath.RemoveAt(currentPath.Count - 1);
|
||||
}
|
||||
|
||||
GetPathsInternal(c);
|
||||
|
||||
return allPaths.ToList();
|
||||
}
|
||||
}
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Parameters;
|
||||
using Speckle.Connectors.Grasshopper8.HostApp;
|
||||
using Speckle.Connectors.Grasshopper8.HostApp.Extras;
|
||||
using Speckle.Connectors.Grasshopper8.Parameters;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.Collections;
|
||||
|
||||
#pragma warning disable CA1711
|
||||
public class CreateCollection : GH_Component, IGH_VariableParameterComponent
|
||||
#pragma warning restore CA1711
|
||||
{
|
||||
public override Guid ComponentGuid => new("BDCE743E-7BDB-479B-AA81-19854AB5A254");
|
||||
|
||||
protected override Bitmap Icon => BitmapBuilder.CreateCircleIconBitmap("cC");
|
||||
|
||||
private readonly DebounceDispatcher _debounceDispatcher = new();
|
||||
|
||||
public CreateCollection()
|
||||
: base("Create collection", "Create collection", "Creates a new collection", "Speckle", "Collections") { }
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
var p = CreateParameter(GH_ParameterSide.Input, 0);
|
||||
pManager.AddParameter(p);
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
pManager.AddGenericParameter("Layer", "L", "Collection that was created", GH_ParamAccess.tree);
|
||||
}
|
||||
|
||||
protected override void SolveInstance(IGH_DataAccess dataAccess)
|
||||
{
|
||||
string rootName = "Unnamed";
|
||||
Collection rootCollection = new() { name = rootName, applicationId = InstanceGuid.ToString() };
|
||||
SpeckleCollectionWrapper rootSpeckleCollectionWrapper = new(rootCollection, new() { rootName }, null);
|
||||
|
||||
foreach (var inputParam in Params.Input)
|
||||
{
|
||||
var data = inputParam.VolatileData.AllData(true).ToList();
|
||||
if (data.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var inputCollections = data.OfType<SpeckleCollectionWrapperGoo>().Empty().ToList();
|
||||
var inputNonCollections = data.Where(t => t is not SpeckleCollectionWrapperGoo).Empty().ToList();
|
||||
if (inputCollections.Count != 0 && inputNonCollections.Count != 0)
|
||||
{
|
||||
// TODO: error out! we want to disallow setting objects and collections in the same parent collection
|
||||
AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Error,
|
||||
$"Parameter {inputParam.NickName} should not contain both objects and collections."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
List<string> childPath = new() { rootName };
|
||||
childPath.Add(inputParam.NickName);
|
||||
Collection childCollection = new(inputParam.NickName) { applicationId = inputParam.InstanceGuid.ToString() };
|
||||
SpeckleCollectionWrapper childSpeckleCollectionWrapper =
|
||||
new(childCollection, childPath, null) { Topology = GrasshopperHelpers.GetParamTopology(inputParam) };
|
||||
|
||||
// if on this port we're only receiving collections, we should become "pass-through" to not create
|
||||
// needless nesting
|
||||
if (inputCollections.Count == data.Count)
|
||||
{
|
||||
var nameTest = new HashSet<string>();
|
||||
foreach (SpeckleCollectionWrapperGoo collection in inputCollections)
|
||||
{
|
||||
// update the speckle collection path
|
||||
collection.Value.Path = new ObservableCollection<string>(childPath);
|
||||
|
||||
foreach (
|
||||
string subCollectionName in collection
|
||||
.Value.Collection.elements.OfType<SpeckleCollectionWrapper>()
|
||||
.Select(v => v.Collection.name)
|
||||
)
|
||||
{
|
||||
var hasNotSeenNameBefore = nameTest.Add(subCollectionName);
|
||||
if (!hasNotSeenNameBefore)
|
||||
{
|
||||
AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Error,
|
||||
$"Duplicate collection name found: {subCollectionName} in input parameter {inputParam.NickName}. Please ensure collection names are unique per nesting level.\n See https://speckle.docs/grashopper/collections"
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
childSpeckleCollectionWrapper.Collection.elements.AddRange(collection.Value.Collection.elements);
|
||||
}
|
||||
|
||||
rootSpeckleCollectionWrapper.Collection.elements.Add(childSpeckleCollectionWrapper);
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var obj in data)
|
||||
{
|
||||
SpeckleObjectWrapperGoo wrapperGoo = new();
|
||||
if (wrapperGoo.CastFrom(obj))
|
||||
{
|
||||
wrapperGoo.Value.Path = childPath;
|
||||
wrapperGoo.Value.Parent = childSpeckleCollectionWrapper;
|
||||
childSpeckleCollectionWrapper.Collection.elements.Add(wrapperGoo.Value);
|
||||
}
|
||||
}
|
||||
|
||||
rootSpeckleCollectionWrapper.Collection.elements.Add(childSpeckleCollectionWrapper);
|
||||
}
|
||||
|
||||
dataAccess.SetData(0, new SpeckleCollectionWrapperGoo(rootSpeckleCollectionWrapper));
|
||||
}
|
||||
|
||||
public bool CanInsertParameter(GH_ParameterSide side, int index)
|
||||
{
|
||||
return side == GH_ParameterSide.Input;
|
||||
}
|
||||
|
||||
public bool CanRemoveParameter(GH_ParameterSide side, int index)
|
||||
{
|
||||
return side == GH_ParameterSide.Input;
|
||||
}
|
||||
|
||||
public IGH_Param CreateParameter(GH_ParameterSide side, int index)
|
||||
{
|
||||
var myParam = new Param_GenericObject
|
||||
{
|
||||
Name = $"Layer {Params.Input.Count + 1}",
|
||||
MutableNickName = true,
|
||||
Optional = true,
|
||||
Access = GH_ParamAccess.tree // always tree
|
||||
};
|
||||
|
||||
myParam.NickName = myParam.Name;
|
||||
myParam.Optional = true;
|
||||
return myParam;
|
||||
}
|
||||
|
||||
public bool DestroyParameter(GH_ParameterSide side, int index)
|
||||
{
|
||||
return side == GH_ParameterSide.Input;
|
||||
}
|
||||
|
||||
public void VariableParameterMaintenance()
|
||||
{
|
||||
// TODO?
|
||||
}
|
||||
|
||||
public override void AddedToDocument(GH_Document document)
|
||||
{
|
||||
base.AddedToDocument(document);
|
||||
Params.ParameterChanged += (sender, args) =>
|
||||
{
|
||||
if (args.ParameterSide == GH_ParameterSide.Output)
|
||||
{
|
||||
return;
|
||||
}
|
||||
switch (args.OriginalArguments.Type)
|
||||
{
|
||||
case GH_ObjectEventType.NickName:
|
||||
// This means the user is typing characters, debounce until it stops for 400ms before expiring the solution.
|
||||
// Prevents UI from locking too soon while writing new names for inputs.
|
||||
args.Parameter.Name = args.Parameter.NickName;
|
||||
_debounceDispatcher.Debounce(500, e => ExpireSolution(true));
|
||||
break;
|
||||
case GH_ObjectEventType.NickNameAccepted:
|
||||
args.Parameter.Name = args.Parameter.NickName;
|
||||
ExpireSolution(true);
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
using System.Collections;
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Parameters;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.Grasshopper8.HostApp;
|
||||
using Speckle.Connectors.Grasshopper8.Parameters;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.Collections;
|
||||
|
||||
#pragma warning disable CA1711
|
||||
public class ExpandCollection : GH_Component, IGH_VariableParameterComponent
|
||||
#pragma warning restore CA1711
|
||||
{
|
||||
public override Guid ComponentGuid => new("69BC8CFB-A72F-4A83-9263-F3399DDA2E5E");
|
||||
|
||||
protected override Bitmap Icon => BitmapBuilder.CreateCircleIconBitmap("eC");
|
||||
|
||||
public ExpandCollection()
|
||||
: base("Expand Collection", "expand", "Expands a new collection", "Speckle", "Collections") { }
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(
|
||||
new SpeckleCollectionParam(GH_ParamAccess.item),
|
||||
"Data",
|
||||
"D",
|
||||
"The data you want to expand",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager) { }
|
||||
|
||||
private List<SpeckleObjectWrapper> _previewObjects = new();
|
||||
|
||||
protected override void SolveInstance(IGH_DataAccess da)
|
||||
{
|
||||
SpeckleCollectionWrapperGoo res = new();
|
||||
da.GetData(0, ref res);
|
||||
var c = res.Value;
|
||||
if (c is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Name = c.Collection.name;
|
||||
NickName = c.Collection.name;
|
||||
|
||||
var objects = c
|
||||
.Collection.elements.Where(el => el is not SpeckleCollectionWrapper)
|
||||
.OfType<SpeckleObjectWrapper>()
|
||||
.Select(o => new SpeckleObjectWrapperGoo(o))
|
||||
.ToList();
|
||||
var collections = c
|
||||
.Collection.elements.Where(el => el is SpeckleCollectionWrapper)
|
||||
.OfType<SpeckleCollectionWrapper>()
|
||||
.ToList();
|
||||
|
||||
var outputParams = new List<OutputParamWrapper>();
|
||||
if (objects.Count != 0)
|
||||
{
|
||||
var param = new Param_GenericObject()
|
||||
{
|
||||
Name = "Inner objects",
|
||||
NickName = "Inner Objs",
|
||||
Description =
|
||||
"Some collections may contain a mix of objects and other collections. Here we output the atomic objects from within this collection.",
|
||||
Access = GH_ParamAccess.list
|
||||
};
|
||||
|
||||
outputParams.Add(new OutputParamWrapper(param, objects, null));
|
||||
}
|
||||
|
||||
foreach (SpeckleCollectionWrapper collection in collections)
|
||||
{
|
||||
// skip empty
|
||||
if (collection.Collection.elements.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var hasInnerCollections = collection.Collection.elements.Any(el => el is SpeckleCollectionWrapper);
|
||||
var topology = collection.Topology; // Note: this is a reminder for the future
|
||||
var nickName = collection.Collection.name;
|
||||
if (collection.Collection.name.Length > 16)
|
||||
{
|
||||
nickName = collection.Collection.name[..3];
|
||||
nickName += "..." + collection.Collection.name[^3..];
|
||||
}
|
||||
|
||||
var param = new Param_GenericObject()
|
||||
{
|
||||
Name = collection.Collection.name,
|
||||
NickName = nickName,
|
||||
Access = hasInnerCollections
|
||||
? GH_ParamAccess.item
|
||||
: topology is null
|
||||
? GH_ParamAccess.list
|
||||
: GH_ParamAccess.tree // we will directly set objects out; note access can be list or tree based on whether it will be a path based collection
|
||||
};
|
||||
if (!hasInnerCollections)
|
||||
{
|
||||
_previewObjects.AddRange(collection.Collection.elements.Cast<SpeckleObjectWrapper>());
|
||||
}
|
||||
|
||||
outputParams.Add(
|
||||
new OutputParamWrapper(
|
||||
param,
|
||||
hasInnerCollections
|
||||
? new SpeckleCollectionWrapperGoo(collection)
|
||||
: collection
|
||||
.Collection.elements.OfType<SpeckleObjectWrapper>()
|
||||
.Select(o => new SpeckleObjectWrapperGoo(o))
|
||||
.ToList(),
|
||||
topology
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (da.Iteration == 0 && OutputMismatch(outputParams))
|
||||
{
|
||||
OnPingDocument()
|
||||
.ScheduleSolution(
|
||||
5,
|
||||
_ =>
|
||||
{
|
||||
CreateOutputs(outputParams);
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
_previewObjects = new();
|
||||
|
||||
FlattenForPreview(c.Collection);
|
||||
for (int i = 0; i < outputParams.Count; i++)
|
||||
{
|
||||
var outParam = Params.Output[i];
|
||||
var outParamWrapper = outputParams[i];
|
||||
switch (outParam.Access)
|
||||
{
|
||||
case GH_ParamAccess.item:
|
||||
da.SetData(i, outParamWrapper.Values);
|
||||
break;
|
||||
case GH_ParamAccess.list:
|
||||
da.SetDataList(i, outParamWrapper.Values as IList);
|
||||
break;
|
||||
case GH_ParamAccess.tree:
|
||||
//TODO: means we need to convert the collection to a tree
|
||||
var topo = outParamWrapper.Topology.NotNull();
|
||||
var values = outParamWrapper.Values as IList;
|
||||
var tree = GrasshopperHelpers.CreateDataTreeFromTopologyAndItems(topo, values.NotNull());
|
||||
da.SetDataTree(i, tree);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private BoundingBox _clippingBox;
|
||||
public override BoundingBox ClippingBox => _clippingBox;
|
||||
|
||||
private void FlattenForPreview(Collection c)
|
||||
{
|
||||
_clippingBox = new BoundingBox();
|
||||
foreach (var element in c.elements)
|
||||
{
|
||||
if (element is Collection subCol)
|
||||
{
|
||||
FlattenForPreview(subCol);
|
||||
}
|
||||
|
||||
if (element is SpeckleObjectWrapper sg)
|
||||
{
|
||||
_previewObjects.Add(sg);
|
||||
var box = sg.GeometryBase.GetBoundingBox(false);
|
||||
_clippingBox.Union(box);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// public override void DrawViewportWires(IGH_PreviewArgs args) => base.DrawViewportWires(args);
|
||||
public override void DrawViewportMeshes(IGH_PreviewArgs args)
|
||||
{
|
||||
if (_previewObjects.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var isSelected = args.Document.SelectedObjects().Contains(this);
|
||||
foreach (var elem in _previewObjects)
|
||||
{
|
||||
elem.DrawPreview(args, isSelected);
|
||||
}
|
||||
}
|
||||
|
||||
private bool OutputMismatch(List<OutputParamWrapper> outputParams)
|
||||
{
|
||||
if (Params.Output.Count != outputParams.Count)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var count = 0;
|
||||
foreach (var newParam in outputParams)
|
||||
{
|
||||
var oldParam = Params.Output[count];
|
||||
if (
|
||||
oldParam.NickName != newParam.Param.NickName
|
||||
|| oldParam.Name != newParam.Param.Name
|
||||
|| oldParam.Access != newParam.Param.Access
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void CreateOutputs(List<OutputParamWrapper> outputParams)
|
||||
{
|
||||
// TODO: better, nicer handling of creation/removal
|
||||
while (Params.Output.Count > 0)
|
||||
{
|
||||
Params.UnregisterOutputParameter(Params.Output[^1]);
|
||||
}
|
||||
|
||||
foreach (var newParam in outputParams)
|
||||
{
|
||||
var param = new Param_GenericObject
|
||||
{
|
||||
Name = newParam.Param.Name,
|
||||
NickName = newParam.Param.NickName,
|
||||
MutableNickName = false,
|
||||
Access = newParam.Param.Access
|
||||
};
|
||||
Params.RegisterOutputParam(param);
|
||||
}
|
||||
|
||||
Params.OnParametersChanged();
|
||||
VariableParameterMaintenance();
|
||||
ExpireSolution(false);
|
||||
}
|
||||
|
||||
public void VariableParameterMaintenance() { }
|
||||
|
||||
public bool CanInsertParameter(GH_ParameterSide side, int index) => false;
|
||||
|
||||
public bool CanRemoveParameter(GH_ParameterSide side, int index) => false;
|
||||
|
||||
public IGH_Param CreateParameter(GH_ParameterSide side, int index)
|
||||
{
|
||||
var myParam = new Param_GenericObject
|
||||
{
|
||||
Name = GH_ComponentParamServer.InventUniqueNickname("ABCD", Params.Input),
|
||||
MutableNickName = true,
|
||||
Optional = true
|
||||
};
|
||||
myParam.NickName = myParam.Name;
|
||||
return myParam;
|
||||
}
|
||||
|
||||
public bool DestroyParameter(GH_ParameterSide side, int index) => side == GH_ParameterSide.Output;
|
||||
}
|
||||
|
||||
public record OutputParamWrapper(Param_GenericObject Param, object Values, string? Topology);
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Speckle.Connectors.Grasshopper8.HostApp;
|
||||
using Speckle.Connectors.Grasshopper8.Parameters;
|
||||
using Speckle.Sdk;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.Collections;
|
||||
|
||||
/// <summary>
|
||||
/// Given a collection and a path, this component will output the objects in the corresponding collection.
|
||||
/// Note: This component does not flatten the selected collection - if it contains sub collections those will not
|
||||
/// be outputted.
|
||||
///
|
||||
/// To extract those objects out, you should select that specific sub path as well.
|
||||
/// </summary>
|
||||
public class FilterObjectsByCollectionPaths : GH_Component
|
||||
{
|
||||
public override Guid ComponentGuid => new("77CAEE94-F0B9-4611-897C-71F2A22BA311");
|
||||
|
||||
public FilterObjectsByCollectionPaths()
|
||||
: base(
|
||||
"FilterObjectsByCollectionPaths",
|
||||
"ocF",
|
||||
"Filters model objects by their collection path",
|
||||
"Speckle",
|
||||
"Collections"
|
||||
) { }
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(
|
||||
new SpeckleCollectionParam(),
|
||||
"Collection",
|
||||
"C",
|
||||
"Collection to filter objects from",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
pManager.AddTextParameter("Path", "P", "Collection path to filter by", GH_ParamAccess.item);
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(
|
||||
new SpeckleObjectParam(),
|
||||
"Objects",
|
||||
"O",
|
||||
"The contents of the selected collection",
|
||||
GH_ParamAccess.tree
|
||||
);
|
||||
}
|
||||
|
||||
protected override void SolveInstance(IGH_DataAccess dataAccess)
|
||||
{
|
||||
string path = "";
|
||||
dataAccess.GetData(1, ref path);
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SpeckleCollectionWrapperGoo collectionWrapperGoo = new();
|
||||
dataAccess.GetData(0, ref collectionWrapperGoo);
|
||||
|
||||
if (collectionWrapperGoo.Value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SpeckleCollectionWrapper targetCollectionWrapper = FindCollection(collectionWrapperGoo.Value, path);
|
||||
if (string.IsNullOrEmpty(targetCollectionWrapper.Topology))
|
||||
{
|
||||
dataAccess.SetDataList(0, targetCollectionWrapper.Collection.elements);
|
||||
}
|
||||
else
|
||||
{
|
||||
var tree = GrasshopperHelpers.CreateDataTreeFromTopologyAndItems(
|
||||
targetCollectionWrapper.Topology,
|
||||
targetCollectionWrapper.Collection.elements
|
||||
);
|
||||
dataAccess.SetDataTree(0, tree);
|
||||
}
|
||||
}
|
||||
|
||||
private SpeckleCollectionWrapper FindCollection(SpeckleCollectionWrapper root, string unifiedPath)
|
||||
{
|
||||
// POC: SpeckleCollections now have a full list<string> path prop on them always. Is this easier to use?
|
||||
List<string> paths = unifiedPath.Split([Constants.LAYER_PATH_DELIMITER], StringSplitOptions.None).Skip(1).ToList();
|
||||
SpeckleCollectionWrapper currentCollectionWrapper = root;
|
||||
while (paths.Count != 0)
|
||||
{
|
||||
currentCollectionWrapper = currentCollectionWrapper
|
||||
.Collection.elements.OfType<SpeckleCollectionWrapper>()
|
||||
.First(col => col.Collection.name == paths.First());
|
||||
paths.RemoveAt(0);
|
||||
if (paths.Count == 0)
|
||||
{
|
||||
return currentCollectionWrapper;
|
||||
}
|
||||
}
|
||||
|
||||
throw new SpeckleException("Did not find collection");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace Speckle.Connectors.Grasshopper8.Components;
|
||||
|
||||
// NOTE: The number of spaces determines the order in which they display in the ribbon (nice hack)
|
||||
public static class ComponentCategories
|
||||
{
|
||||
public const string OPERATIONS = " Operations";
|
||||
public const string MODELS = " Model Management";
|
||||
public const string PARAMETERS = "Parameters";
|
||||
public const string COLLECTIONS = " Collections";
|
||||
public const string PRIMARY_RIBBON = "Speckle";
|
||||
public const string OBJECTS = " Objects";
|
||||
}
|
||||
|
||||
public enum ComponentState
|
||||
{
|
||||
Cancelled,
|
||||
Expired,
|
||||
NeedsInput,
|
||||
Receiving,
|
||||
Ready,
|
||||
Sending,
|
||||
UpToDate
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using Grasshopper.Kernel;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components;
|
||||
|
||||
public class GhContextMenuButton(
|
||||
string name,
|
||||
string nickname,
|
||||
string description,
|
||||
Func<ToolStripDropDown, bool>? populateMenuAction = null
|
||||
) : GH_DocumentObject(name, nickname, description, "Speckle", "UI")
|
||||
{
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
||||
public override void CreateAttributes() => Attributes = new GhContextMenuButtonAttributes(this);
|
||||
|
||||
public override Guid ComponentGuid => new("B01FFD91-F4EC-4332-A9AA-F917AEDAA51D");
|
||||
|
||||
public override bool AppendMenuItems(ToolStripDropDown menu) => populateMenuAction?.Invoke(menu) ?? false;
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
using Grasshopper.GUI;
|
||||
using Grasshopper.GUI.Canvas;
|
||||
using Grasshopper.Kernel;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components;
|
||||
|
||||
public class GhContextMenuButtonAttributes(GhContextMenuButton owner) : GH_Attributes<GhContextMenuButton>(owner)
|
||||
{
|
||||
protected override void Render(GH_Canvas canvas, Graphics graphics, GH_CanvasChannel channel)
|
||||
{
|
||||
base.Render(canvas, graphics, channel);
|
||||
if (channel != GH_CanvasChannel.Objects)
|
||||
{
|
||||
return; // No wires or other layers are being drawn in this component.
|
||||
}
|
||||
|
||||
using var button1 = GH_Capsule.CreateTextCapsule(
|
||||
Bounds,
|
||||
Bounds,
|
||||
Owner.Enabled ? GH_Palette.Black : GH_Palette.Grey,
|
||||
Owner.Name,
|
||||
2,
|
||||
0
|
||||
);
|
||||
button1.Render(graphics, Parent.Selected, false, false);
|
||||
}
|
||||
|
||||
public override GH_ObjectResponse RespondToMouseUp(GH_Canvas sender, GH_CanvasMouseEvent e)
|
||||
{
|
||||
if (!Owner.Enabled && e.Button == MouseButtons.Right)
|
||||
{
|
||||
// Prevents canvas from triggering the right-click behaviour, and showing the context menu.
|
||||
return GH_ObjectResponse.Handled;
|
||||
}
|
||||
|
||||
// Allowing event to bubble up to canvas will handle the event and show the context menu.
|
||||
return base.RespondToMouseUp(sender, e);
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
using Speckle.Connectors.Grasshopper8.Parameters;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.Operations.Receive;
|
||||
|
||||
internal sealed class GrasshopperCollectionRebuilder
|
||||
{
|
||||
public SpeckleCollectionWrapper RootCollectionWrapper { get; }
|
||||
|
||||
// a cache of collection path (no delimiter) to the speckle collection
|
||||
private readonly Dictionary<string, SpeckleCollectionWrapper> _cache = new();
|
||||
|
||||
public GrasshopperCollectionRebuilder(Collection baseCollection)
|
||||
{
|
||||
Collection newCollection = new() { name = baseCollection.name, applicationId = baseCollection.applicationId };
|
||||
RootCollectionWrapper = new SpeckleCollectionWrapper(newCollection, new() { baseCollection.name }, null);
|
||||
}
|
||||
|
||||
public void AppendSpeckleGrasshopperObject(
|
||||
SpeckleObjectWrapper speckleGrasshopperObjectWrapper,
|
||||
List<Collection> collectionPath
|
||||
)
|
||||
{
|
||||
var collection = GetOrCreateSpeckleCollectionFromPath(collectionPath);
|
||||
collection.Collection.elements.Add(speckleGrasshopperObjectWrapper);
|
||||
}
|
||||
|
||||
public SpeckleCollectionWrapper GetOrCreateSpeckleCollectionFromPath(List<Collection> path)
|
||||
{
|
||||
// first check if cache already has this collection
|
||||
string fullPath = string.Concat(path);
|
||||
if (_cache.TryGetValue(fullPath, out SpeckleCollectionWrapper col))
|
||||
{
|
||||
return col;
|
||||
}
|
||||
|
||||
// otherwise, iterate through the path and create speckle collections as needed
|
||||
SpeckleCollectionWrapper previousCollectionWrapper = RootCollectionWrapper;
|
||||
List<string> currentLayerPath = new();
|
||||
foreach (var collection in path)
|
||||
{
|
||||
var collectionName = collection.name;
|
||||
currentLayerPath.Add(collectionName);
|
||||
string key = string.Concat(currentLayerPath);
|
||||
|
||||
// check cache
|
||||
if (_cache.TryGetValue(key, out SpeckleCollectionWrapper currentCol))
|
||||
{
|
||||
previousCollectionWrapper = currentCol;
|
||||
continue;
|
||||
}
|
||||
|
||||
// create and cache if needed
|
||||
Collection newCollection = new() { name = collectionName };
|
||||
SpeckleCollectionWrapper newSpeckleCollectionWrapper = new(newCollection, currentLayerPath, null);
|
||||
|
||||
if (collection["topology"] is string topology)
|
||||
{
|
||||
newSpeckleCollectionWrapper.Topology = topology;
|
||||
newCollection["topology"] = topology;
|
||||
}
|
||||
|
||||
_cache[key] = newSpeckleCollectionWrapper;
|
||||
previousCollectionWrapper.Collection.elements.Add(newSpeckleCollectionWrapper);
|
||||
|
||||
previousCollectionWrapper = newSpeckleCollectionWrapper;
|
||||
}
|
||||
|
||||
return previousCollectionWrapper;
|
||||
}
|
||||
}
|
||||
+725
@@ -0,0 +1,725 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using Grasshopper.GUI;
|
||||
using Grasshopper.GUI.Canvas;
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Attributes;
|
||||
using GrasshopperAsyncComponent;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Rhino;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.Common.Instances;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Common.Operations.Receive;
|
||||
using Speckle.Connectors.Grasshopper8.HostApp;
|
||||
using Speckle.Connectors.Grasshopper8.Operations.Receive;
|
||||
using Speckle.Connectors.Grasshopper8.Parameters;
|
||||
using Speckle.Connectors.Grasshopper8.Registration;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL.Models;
|
||||
using Speckle.Sdk.Common.Exceptions;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
using Speckle.Sdk.Models.Extensions;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.Operations.Receive;
|
||||
|
||||
[Guid("1587DF34-83E5-4AFE-B42E-F7C5C37ECD68")]
|
||||
public class ReceiveAsyncComponent : GH_AsyncComponent
|
||||
{
|
||||
public ReceiveAsyncComponent()
|
||||
: base(
|
||||
"Async Receive",
|
||||
"aR",
|
||||
"Receive objects async from speckle",
|
||||
ComponentCategories.PRIMARY_RIBBON,
|
||||
ComponentCategories.OPERATIONS
|
||||
)
|
||||
{
|
||||
BaseWorker = new ReceiveComponentWorker(this);
|
||||
Attributes = new ReceiveAsyncComponentAttributes(this);
|
||||
}
|
||||
|
||||
public override Guid ComponentGuid => GetType().GUID;
|
||||
|
||||
protected override Bitmap Icon => BitmapBuilder.CreateSquareIconBitmap("aR");
|
||||
|
||||
public string InputType { get; set; }
|
||||
public bool AutoReceive { get; set; }
|
||||
public bool ReceiveOnOpen { get; set; }
|
||||
public string ReceivedVersionId { get; set; }
|
||||
public ComponentState CurrentComponentState { get; set; } = ComponentState.NeedsInput;
|
||||
public bool JustPastedIn { get; set; }
|
||||
public string LastVersionDate { get; set; }
|
||||
public string LastInfoMessage { get; set; }
|
||||
public HostApp.SpeckleUrlModelResource? UrlModelResource { get; set; }
|
||||
|
||||
// DI props
|
||||
public Client ApiClient { get; private set; }
|
||||
public GrasshopperReceiveOperation ReceiveOperation { get; private set; }
|
||||
public RootObjectUnpacker RootObjectUnpacker { get; private set; }
|
||||
public static IServiceScope? Scope { get; private set; }
|
||||
public AccountService AccountManager { get; private set; }
|
||||
public IClientFactory ClientFactory { get; private set; }
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(new SpeckleUrlModelResourceParam(GH_ParamAccess.item));
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(
|
||||
new SpeckleCollectionParam(GH_ParamAccess.item),
|
||||
"Model",
|
||||
"model",
|
||||
"The model object for the received version",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
}
|
||||
|
||||
protected override void SolveInstance(IGH_DataAccess da)
|
||||
{
|
||||
da.DisableGapLogic();
|
||||
|
||||
// Dependency Injection
|
||||
Scope = PriorityLoader.Container.CreateScope();
|
||||
ReceiveOperation = Scope.ServiceProvider.GetRequiredService<GrasshopperReceiveOperation>();
|
||||
RootObjectUnpacker = Scope.ServiceProvider.GetService<RootObjectUnpacker>();
|
||||
AccountManager = Scope.ServiceProvider.GetRequiredService<AccountService>();
|
||||
ClientFactory = Scope.ServiceProvider.GetRequiredService<IClientFactory>();
|
||||
|
||||
// We need to call this always in here to be able to react and set events :/
|
||||
ParseInput(da);
|
||||
|
||||
if (
|
||||
(
|
||||
AutoReceive
|
||||
|| CurrentComponentState == ComponentState.Ready
|
||||
|| CurrentComponentState == ComponentState.Receiving
|
||||
) && !JustPastedIn
|
||||
)
|
||||
{
|
||||
CurrentComponentState = ComponentState.Receiving;
|
||||
|
||||
// Delegate control to parent async component.
|
||||
base.SolveInstance(da);
|
||||
return;
|
||||
}
|
||||
|
||||
if (JustPastedIn)
|
||||
{
|
||||
// This ensures that we actually do a run. The worker will check and determine if it needs to pull an existing object or not.
|
||||
OnDisplayExpired(true);
|
||||
base.SolveInstance(da);
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentComponentState = ComponentState.Expired;
|
||||
Message = "Expired";
|
||||
OnDisplayExpired(true);
|
||||
}
|
||||
}
|
||||
|
||||
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
|
||||
{
|
||||
base.AppendAdditionalMenuItems(menu);
|
||||
|
||||
Menu_AppendSeparator(menu);
|
||||
if (InputType == "Model")
|
||||
{
|
||||
var autoReceiveMi = Menu_AppendItem(
|
||||
menu,
|
||||
"Load automatically",
|
||||
(s, e) =>
|
||||
{
|
||||
AutoReceive = !AutoReceive;
|
||||
RhinoApp.InvokeOnUiThread(
|
||||
(Action)
|
||||
delegate
|
||||
{
|
||||
OnDisplayExpired(true);
|
||||
}
|
||||
);
|
||||
},
|
||||
true,
|
||||
AutoReceive
|
||||
);
|
||||
autoReceiveMi.ToolTipText =
|
||||
"Toggle automatic loading. If set, any new version will be loaded instantly. This only is applicable when receiving from a model url.";
|
||||
}
|
||||
else
|
||||
{
|
||||
var autoReceiveMi = Menu_AppendItem(menu, "Automatic loading is disabled because you have specified a version.");
|
||||
autoReceiveMi.ToolTipText = "To enable automatic loading, select a model without selecting a specific version.";
|
||||
}
|
||||
|
||||
var receivOnOpenMi = Menu_AppendItem(
|
||||
menu,
|
||||
"Load when Document opened",
|
||||
(sender, args) =>
|
||||
{
|
||||
ReceiveOnOpen = !ReceiveOnOpen;
|
||||
RhinoApp.InvokeOnUiThread(
|
||||
(Action)
|
||||
delegate
|
||||
{
|
||||
OnDisplayExpired(true);
|
||||
}
|
||||
);
|
||||
},
|
||||
!AutoReceive,
|
||||
AutoReceive || ReceiveOnOpen
|
||||
);
|
||||
receivOnOpenMi.ToolTipText =
|
||||
"The node will automatically perform a load operation as soon as the document is open, or the node is copy/pasted into a new document.";
|
||||
|
||||
Menu_AppendSeparator(menu);
|
||||
|
||||
if (CurrentComponentState == ComponentState.Receiving)
|
||||
{
|
||||
Menu_AppendItem(
|
||||
menu,
|
||||
"Cancel Load",
|
||||
(s, e) =>
|
||||
{
|
||||
CurrentComponentState = ComponentState.Expired;
|
||||
RequestCancellation();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleNewCommit()
|
||||
{
|
||||
Message = "Expired";
|
||||
CurrentComponentState = ComponentState.Expired;
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Remark, $"There is a newer version available for this {InputType}");
|
||||
RhinoApp.InvokeOnUiThread(
|
||||
(Action)
|
||||
delegate
|
||||
{
|
||||
if (AutoReceive)
|
||||
{
|
||||
ExpireSolution(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnDisplayExpired(true);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public override void RemovedFromDocument(GH_Document document)
|
||||
{
|
||||
RequestCancellation();
|
||||
Scope?.Dispose();
|
||||
base.RemovedFromDocument(document);
|
||||
}
|
||||
|
||||
public override void DocumentContextChanged(GH_Document document, GH_DocumentContext context)
|
||||
{
|
||||
switch (context)
|
||||
{
|
||||
case GH_DocumentContext.Loaded:
|
||||
{
|
||||
// Will execute every time a document becomes active (from background or opening file.).
|
||||
if (UrlModelResource != null)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
// Ensure fresh instance of client.
|
||||
ResetApiClient(UrlModelResource);
|
||||
|
||||
// Get last commit from the branch
|
||||
var b = UrlModelResource.GetReceiveInfo(ApiClient);
|
||||
await b;
|
||||
|
||||
// Compare version ids. If they don't match, notify user or fetch data if in auto mode
|
||||
if (b.Result.SelectedVersionId != ReceivedVersionId)
|
||||
{
|
||||
HandleNewCommit();
|
||||
}
|
||||
|
||||
OnDisplayExpired(true);
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case GH_DocumentContext.Unloaded:
|
||||
// Will execute every time a document becomes inactive (in background or closing file.)
|
||||
// Correctly dispose of the client when changing documents to prevent subscription handlers being called in background.
|
||||
CurrentComponentState = ComponentState.Expired;
|
||||
RequestCancellation();
|
||||
ApiClient?.Dispose();
|
||||
break;
|
||||
}
|
||||
|
||||
base.DocumentContextChanged(document, context);
|
||||
}
|
||||
|
||||
private void ParseInput(IGH_DataAccess da)
|
||||
{
|
||||
HostApp.SpeckleUrlModelResource? dataInput = null;
|
||||
da.GetData(0, ref dataInput);
|
||||
if (dataInput is null)
|
||||
{
|
||||
UrlModelResource = null;
|
||||
TriggerAutoSave();
|
||||
return;
|
||||
}
|
||||
|
||||
// set the type of url input
|
||||
switch (dataInput)
|
||||
{
|
||||
case SpeckleUrlModelVersionResource:
|
||||
InputType = "Version";
|
||||
AutoReceive = false;
|
||||
LastInfoMessage = "";
|
||||
ResetApiClient(dataInput);
|
||||
return;
|
||||
case SpeckleUrlModelResource:
|
||||
InputType = "Model";
|
||||
// handled in do work
|
||||
break;
|
||||
default:
|
||||
InputType = "Invalid";
|
||||
break;
|
||||
}
|
||||
|
||||
if (UrlModelResource != null && UrlModelResource.Equals(dataInput) && !JustPastedIn)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UrlModelResource = dataInput;
|
||||
|
||||
ResetApiClient(UrlModelResource);
|
||||
}
|
||||
|
||||
private void ApiClient_OnVersionCreated(object sender, ProjectVersionsUpdatedMessage e)
|
||||
{
|
||||
HandleNewCommit();
|
||||
}
|
||||
|
||||
public void ResetApiClient(SpeckleUrlModelResource urlResource)
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO: Get any account for this server, as we don't have a mechanism yet to pass accountIds through
|
||||
Account account = AccountManager.GetAccountWithServerUrlFallback("", new Uri(urlResource.Server));
|
||||
if (account is null)
|
||||
{
|
||||
throw new SpeckleAccountManagerException($"No default account was found");
|
||||
}
|
||||
|
||||
ApiClient?.Dispose();
|
||||
ApiClient = ClientFactory.Create(account);
|
||||
ApiClient.Subscription.CreateProjectVersionsUpdatedSubscription(urlResource.ProjectId).Listeners +=
|
||||
ApiClient_OnVersionCreated;
|
||||
}
|
||||
catch (Exception e) when (!e.IsFatal())
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, e.ToFormattedString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ReceiveComponentWorker : WorkerInstance
|
||||
{
|
||||
public ReceiveComponentWorker(GH_Component p)
|
||||
: base(p) { }
|
||||
|
||||
public Base Root { get; set; }
|
||||
public SpeckleUrlModelResource? UrlModelResource { get; set; }
|
||||
public SpeckleCollectionWrapperGoo Result { get; set; }
|
||||
private List<(GH_RuntimeMessageLevel, string)> RuntimeMessages { get; } = new();
|
||||
|
||||
public override WorkerInstance Duplicate()
|
||||
{
|
||||
return new ReceiveComponentWorker(Parent);
|
||||
}
|
||||
|
||||
public override void GetData(IGH_DataAccess da, GH_ComponentParamServer p)
|
||||
{
|
||||
UrlModelResource = ((ReceiveAsyncComponent)Parent).UrlModelResource;
|
||||
}
|
||||
|
||||
public override void SetData(IGH_DataAccess da)
|
||||
{
|
||||
if (CancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (level, message) in RuntimeMessages)
|
||||
{
|
||||
Parent.AddRuntimeMessage(level, message);
|
||||
}
|
||||
|
||||
var parent = (ReceiveAsyncComponent)Parent;
|
||||
|
||||
parent.CurrentComponentState = ComponentState.UpToDate;
|
||||
|
||||
parent.JustPastedIn = false;
|
||||
|
||||
if (Result == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
da.SetData(0, Result);
|
||||
}
|
||||
|
||||
#pragma warning disable CA1506
|
||||
public override void DoWork(Action<string, double> reportProgress, Action done)
|
||||
{
|
||||
var receiveComponent = (ReceiveAsyncComponent)Parent;
|
||||
|
||||
try
|
||||
{
|
||||
if (UrlModelResource is null)
|
||||
{
|
||||
throw new InvalidOperationException("Url Resource was null");
|
||||
}
|
||||
|
||||
// Means it's a copy paste of an empty non-init component; set the record and exit fast unless ReceiveOnOpen is true.
|
||||
if (receiveComponent.JustPastedIn && !receiveComponent.AutoReceive)
|
||||
{
|
||||
receiveComponent.JustPastedIn = false;
|
||||
if (!receiveComponent.ReceiveOnOpen)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
receiveComponent.CurrentComponentState = ComponentState.Receiving;
|
||||
RhinoApp.InvokeOnUiThread(
|
||||
(Action)
|
||||
delegate
|
||||
{
|
||||
receiveComponent.OnDisplayExpired(true);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
var t = Task.Run(async () =>
|
||||
{
|
||||
// Step 1 - RECEIVE FROM SERVER
|
||||
var receiveInfo = await UrlModelResource
|
||||
.GetReceiveInfo(receiveComponent.ApiClient, CancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var progress = new Progress<CardProgress>(p =>
|
||||
{
|
||||
reportProgress(Id, p.Progress ?? 0);
|
||||
//eceiveComponent.Message = $"{p.Status}";
|
||||
});
|
||||
|
||||
if (CancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (receiveInfo == null)
|
||||
{
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
Root = await receiveComponent
|
||||
.ReceiveOperation.ReceiveCommitObject(receiveInfo, progress, CancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (CancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2 - CONVERT
|
||||
//receiveComponent.Message = $"Unpacking...";
|
||||
var localToGlobalUnpacker = new LocalToGlobalUnpacker();
|
||||
var traversalContextUnpacker = new TraversalContextUnpacker();
|
||||
var unpackedRoot = receiveComponent.RootObjectUnpacker.Unpack(Root);
|
||||
|
||||
// "flatten" block instances
|
||||
var localToGlobalMaps = localToGlobalUnpacker.Unpack(
|
||||
unpackedRoot.DefinitionProxies,
|
||||
unpackedRoot.ObjectsToConvert.ToList()
|
||||
);
|
||||
|
||||
// TODO: unpack colors and render materials
|
||||
|
||||
var collectionRebuilder = new GrasshopperCollectionRebuilder(
|
||||
(Root as Collection) ?? new Collection() { name = "unnamed" }
|
||||
);
|
||||
|
||||
int count = 0;
|
||||
int total = localToGlobalMaps.Count;
|
||||
|
||||
foreach (var map in localToGlobalMaps)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<GeometryBase> converted = Convert(map.AtomicObject);
|
||||
var path = traversalContextUnpacker.GetCollectionPath(map.TraversalContext).ToList();
|
||||
|
||||
foreach (var matrix in map.Matrix)
|
||||
{
|
||||
var mat = GrasshopperHelpers.MatrixToTransform(matrix, "meters");
|
||||
converted.ForEach(res => res.Transform(mat));
|
||||
}
|
||||
|
||||
// get the collection
|
||||
SpeckleCollectionWrapper objectCollection = collectionRebuilder.GetOrCreateSpeckleCollectionFromPath(path);
|
||||
|
||||
// get the name and properties
|
||||
SpecklePropertyGroupGoo propertyGroup = new();
|
||||
string name = "";
|
||||
if (map.AtomicObject is Speckle.Objects.Data.DataObject da)
|
||||
{
|
||||
propertyGroup.CastFrom(da.properties);
|
||||
name = da.name;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (map.AtomicObject["properties"] is Dictionary<string, object?> props)
|
||||
{
|
||||
propertyGroup.CastFrom(props);
|
||||
}
|
||||
|
||||
if (map.AtomicObject["name"] is string n)
|
||||
{
|
||||
name = n;
|
||||
}
|
||||
}
|
||||
|
||||
// create objects for every value in converted. This is where one to many is not handled very nicely.
|
||||
foreach (var geometryBase in converted)
|
||||
{
|
||||
var gh = new SpeckleObjectWrapper()
|
||||
{
|
||||
Base = map.AtomicObject,
|
||||
Path = path.Select(p => p.name).ToList(),
|
||||
Parent = objectCollection,
|
||||
GeometryBase = geometryBase,
|
||||
Properties = propertyGroup,
|
||||
Name = name
|
||||
};
|
||||
|
||||
collectionRebuilder.AppendSpeckleGrasshopperObject(gh, path);
|
||||
}
|
||||
}
|
||||
catch (ConversionException)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
//reportProgress(Id, (double)count / total);
|
||||
count++;
|
||||
}
|
||||
|
||||
Result = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
|
||||
|
||||
// DONE
|
||||
done();
|
||||
});
|
||||
t.Wait();
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
RuntimeMessages.Add((GH_RuntimeMessageLevel.Error, ex.ToFormattedString()));
|
||||
done();
|
||||
}
|
||||
}
|
||||
#pragma warning restore CA1506
|
||||
|
||||
private List<GeometryBase> Convert(Base input)
|
||||
{
|
||||
var result = ToSpeckleConversionContext.ToHostConverter.Convert(input);
|
||||
|
||||
if (result is GeometryBase geometry)
|
||||
{
|
||||
return [geometry];
|
||||
}
|
||||
if (result is List<GeometryBase> geometryList)
|
||||
{
|
||||
return geometryList;
|
||||
}
|
||||
if (result is IEnumerable<(GeometryBase, Base)> fallbackConversionResult)
|
||||
{
|
||||
// note special handling for proxying render materials OR we don't care about revit
|
||||
return fallbackConversionResult.Select(t => t.Item1).ToList();
|
||||
}
|
||||
|
||||
throw new SpeckleException("Failed to convert input to rhino");
|
||||
}
|
||||
}
|
||||
|
||||
public class ReceiveAsyncComponentAttributes : GH_ComponentAttributes
|
||||
{
|
||||
private bool _selected;
|
||||
|
||||
private class SpeckleComponentButton
|
||||
{
|
||||
public int Height { get; set; } = 26;
|
||||
public Rectangle ButtonBounds { get; set; }
|
||||
public GH_Palette Palette { get; set; }
|
||||
public string Text { get; set; }
|
||||
}
|
||||
|
||||
private List<SpeckleComponentButton> Buttons { get; set; }
|
||||
|
||||
public ReceiveAsyncComponentAttributes(GH_Component owner)
|
||||
: base(owner)
|
||||
{
|
||||
Buttons = new List<SpeckleComponentButton>()
|
||||
{
|
||||
new SpeckleComponentButton()
|
||||
{
|
||||
Text = "Select project",
|
||||
Palette = GH_Palette.Blue,
|
||||
Height = 18
|
||||
},
|
||||
new SpeckleComponentButton()
|
||||
{
|
||||
Text = "Select model",
|
||||
Palette = GH_Palette.Blue,
|
||||
Height = 18
|
||||
},
|
||||
new SpeckleComponentButton()
|
||||
{
|
||||
Text = "Select version",
|
||||
Palette = GH_Palette.Blue,
|
||||
Height = 18
|
||||
},
|
||||
new SpeckleComponentButton() { Text = "Test 2", Palette = GH_Palette.Black },
|
||||
};
|
||||
}
|
||||
|
||||
public override bool Selected
|
||||
{
|
||||
get => _selected;
|
||||
set => _selected = value;
|
||||
}
|
||||
|
||||
protected override void Layout()
|
||||
{
|
||||
base.Layout();
|
||||
|
||||
var baseRec = GH_Convert.ToRectangle(Bounds);
|
||||
|
||||
for (int i = 0; i < Buttons.Count; i++)
|
||||
{
|
||||
var button = Buttons[i];
|
||||
|
||||
var buttonBounds = i == 0 ? baseRec : Buttons[i - 1].ButtonBounds;
|
||||
buttonBounds.Y = buttonBounds.Bottom;
|
||||
buttonBounds.Height = button.Height;
|
||||
buttonBounds.Width = baseRec.Width;
|
||||
button.ButtonBounds = buttonBounds;
|
||||
baseRec.Height += button.Height;
|
||||
Bounds = baseRec;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Render(GH_Canvas canvas, Graphics graphics, GH_CanvasChannel channel)
|
||||
{
|
||||
base.Render(canvas, graphics, channel);
|
||||
|
||||
var state = ((ReceiveAsyncComponent)Owner).CurrentComponentState;
|
||||
|
||||
if (channel == GH_CanvasChannel.Objects)
|
||||
{
|
||||
// if (((ReceiveAsyncComponent)Owner).AutoReceive)
|
||||
// {
|
||||
// var autoReceiveButton = GH_Capsule.CreateTextCapsule(
|
||||
// ButtonBounds,
|
||||
// ButtonBounds,
|
||||
// GH_Palette.Blue,
|
||||
// "Auto Receive",
|
||||
// 2,
|
||||
// 0
|
||||
// );
|
||||
//
|
||||
// autoReceiveButton.Render(graphics, Selected, Owner.Locked, false);
|
||||
// autoReceiveButton.Dispose();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// var palette =
|
||||
// state == ComponentState.Expired || state == ComponentState.UpToDate || state == ComponentState.Cancelled
|
||||
// ? GH_Palette.Black
|
||||
// : GH_Palette.Transparent;
|
||||
// var text = state != ComponentState.Receiving ? "Receive" : "Receiving...";
|
||||
//
|
||||
// var button = GH_Capsule.CreateTextCapsule(
|
||||
// ButtonBounds,
|
||||
// ButtonBounds,
|
||||
// palette,
|
||||
// text,
|
||||
// 2,
|
||||
// state == ComponentState.Expired ? 10 : 0
|
||||
// );
|
||||
// button.Render(graphics, Selected, Owner.Locked, false);
|
||||
// button.Dispose();
|
||||
// }
|
||||
|
||||
foreach (var button in Buttons)
|
||||
{
|
||||
using var b = GH_Capsule.CreateTextCapsule(
|
||||
button.ButtonBounds,
|
||||
button.ButtonBounds,
|
||||
button.Palette,
|
||||
button.Text,
|
||||
2,
|
||||
0
|
||||
);
|
||||
b.Render(graphics, Selected, Owner.Locked, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override GH_ObjectResponse RespondToMouseDown(GH_Canvas sender, GH_CanvasMouseEvent e)
|
||||
{
|
||||
if (e.Button != MouseButtons.Left)
|
||||
{
|
||||
return base.RespondToMouseDown(sender, e);
|
||||
}
|
||||
|
||||
foreach (var button in Buttons)
|
||||
{
|
||||
if (((RectangleF)button.ButtonBounds).Contains(e.CanvasLocation))
|
||||
{
|
||||
Debug.WriteLine($"Button was pressed: {button.Text}");
|
||||
return GH_ObjectResponse.Handled;
|
||||
}
|
||||
}
|
||||
|
||||
return base.RespondToMouseDown(sender, e);
|
||||
|
||||
// if (!((RectangleF)ButtonBounds).Contains(e.CanvasLocation))
|
||||
// {
|
||||
// return base.RespondToMouseDown(sender, e);
|
||||
// }
|
||||
//
|
||||
// if (((ReceiveAsyncComponent)Owner).CurrentComponentState == ComponentState.Receiving)
|
||||
// {
|
||||
// return GH_ObjectResponse.Handled;
|
||||
// }
|
||||
//
|
||||
// if (((ReceiveAsyncComponent)Owner).AutoReceive)
|
||||
// {
|
||||
// ((ReceiveAsyncComponent)Owner).AutoReceive = false;
|
||||
// Owner.OnDisplayExpired(true);
|
||||
// return GH_ObjectResponse.Handled;
|
||||
// }
|
||||
//
|
||||
// // TODO: check if owner has null account/client, and call the reset thing SYNC
|
||||
// ((ReceiveAsyncComponent)Owner).CurrentComponentState = ComponentState.Ready;
|
||||
// Owner.ExpireSolution(true);
|
||||
// return GH_ObjectResponse.Handled;
|
||||
}
|
||||
}
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
using Grasshopper.Kernel;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.Common.Instances;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Common.Operations.Receive;
|
||||
using Speckle.Connectors.Grasshopper8.Components.BaseComponents;
|
||||
using Speckle.Connectors.Grasshopper8.HostApp;
|
||||
using Speckle.Connectors.Grasshopper8.Operations.Receive;
|
||||
using Speckle.Connectors.Grasshopper8.Parameters;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Common.Exceptions;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.Operations.Receive;
|
||||
|
||||
public class ReceiveComponentOutput
|
||||
{
|
||||
public SpeckleCollectionWrapperGoo RootObject { get; set; }
|
||||
}
|
||||
|
||||
public class ReceiveComponent : SpeckleScopedTaskCapableComponent<SpeckleUrlModelResource, ReceiveComponentOutput>
|
||||
{
|
||||
public ReceiveComponent()
|
||||
: base(
|
||||
"Receive from Speckle",
|
||||
"RFS",
|
||||
"Receive objects from speckle",
|
||||
ComponentCategories.PRIMARY_RIBBON,
|
||||
ComponentCategories.OPERATIONS
|
||||
) { }
|
||||
|
||||
public override Guid ComponentGuid => new("74954F59-B1B7-41FD-97DE-4C6B005F2801");
|
||||
protected override Bitmap Icon => BitmapBuilder.CreateSquareIconBitmap("R");
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(new SpeckleUrlModelResourceParam(GH_ParamAccess.item));
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(
|
||||
new SpeckleCollectionParam(GH_ParamAccess.item),
|
||||
"Model",
|
||||
"model",
|
||||
"The model object for the received version",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
}
|
||||
|
||||
protected override SpeckleUrlModelResource GetInput(IGH_DataAccess da)
|
||||
{
|
||||
SpeckleUrlModelResource? url = null;
|
||||
da.GetData(0, ref url);
|
||||
if (url is null)
|
||||
{
|
||||
throw new SpeckleException("Speckle url is null");
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
protected override void SetOutput(IGH_DataAccess da, ReceiveComponentOutput result)
|
||||
{
|
||||
da.SetData(0, result.RootObject);
|
||||
Message = "Done";
|
||||
}
|
||||
|
||||
protected override async Task<ReceiveComponentOutput> PerformScopedTask(
|
||||
SpeckleUrlModelResource input,
|
||||
IServiceScope scope,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
// TODO: Resolving dependencies here may be overkill in most cases. Must re-evaluate.
|
||||
var accountManager = scope.ServiceProvider.GetRequiredService<AccountService>();
|
||||
var clientFactory = scope.ServiceProvider.GetRequiredService<IClientFactory>();
|
||||
var receiveOperation = scope.ServiceProvider.GetRequiredService<GrasshopperReceiveOperation>();
|
||||
|
||||
// Do the thing 👇🏼
|
||||
|
||||
// TODO: Get any account for this server, as we don't have a mechanism yet to pass accountIds through
|
||||
var account = accountManager.GetAccountWithServerUrlFallback("", new Uri(input.Server));
|
||||
|
||||
if (account is null)
|
||||
{
|
||||
throw new SpeckleAccountManagerException($"No default account was found");
|
||||
}
|
||||
|
||||
using var client = clientFactory.Create(account);
|
||||
var receiveInfo = await input.GetReceiveInfo(client, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var progress = new Progress<CardProgress>(_ =>
|
||||
{
|
||||
// TODO: Progress only makes sense in non-blocking async receive, which is not supported yet.
|
||||
// Message = $"{progress.Status}: {progress.Progress}";
|
||||
});
|
||||
|
||||
var root = await receiveOperation
|
||||
.ReceiveCommitObject(receiveInfo, progress, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// We need to rethink these lovely unpackers, there's a bit too many of 'em
|
||||
var rootObjectUnpacker = scope.ServiceProvider.GetService<RootObjectUnpacker>();
|
||||
var localToGlobalUnpacker = new LocalToGlobalUnpacker();
|
||||
var traversalContextUnpacker = new TraversalContextUnpacker();
|
||||
|
||||
var unpackedRoot = rootObjectUnpacker.Unpack(root);
|
||||
|
||||
// "flatten" block instances
|
||||
var localToGlobalMaps = localToGlobalUnpacker.Unpack(
|
||||
unpackedRoot.DefinitionProxies,
|
||||
unpackedRoot.ObjectsToConvert.ToList()
|
||||
);
|
||||
|
||||
var collectionRebuilder = new GrasshopperCollectionRebuilder(
|
||||
(root as Collection) ?? new Collection() { name = "unnamed" }
|
||||
);
|
||||
|
||||
foreach (var map in localToGlobalMaps)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<GeometryBase> converted = Convert(map.AtomicObject);
|
||||
List<Collection> path = traversalContextUnpacker.GetCollectionPath(map.TraversalContext).ToList();
|
||||
|
||||
foreach (var matrix in map.Matrix)
|
||||
{
|
||||
var mat = GrasshopperHelpers.MatrixToTransform(matrix, "meters");
|
||||
converted.ForEach(res => res.Transform(mat));
|
||||
}
|
||||
|
||||
// get the collection
|
||||
SpeckleCollectionWrapper objectCollection = collectionRebuilder.GetOrCreateSpeckleCollectionFromPath(path);
|
||||
|
||||
// get the name and properties
|
||||
SpecklePropertyGroupGoo propertyGroup = new();
|
||||
string name = "";
|
||||
if (map.AtomicObject is Speckle.Objects.Data.DataObject da)
|
||||
{
|
||||
propertyGroup.CastFrom(da.properties);
|
||||
name = da.name;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (map.AtomicObject["properties"] is Dictionary<string, object?> props)
|
||||
{
|
||||
propertyGroup.CastFrom(props);
|
||||
}
|
||||
|
||||
if (map.AtomicObject["name"] is string n)
|
||||
{
|
||||
name = n;
|
||||
}
|
||||
}
|
||||
|
||||
// create objects for every value in converted. This is where one to many is not handled very nicely.
|
||||
foreach (var geometryBase in converted)
|
||||
{
|
||||
var gh = new SpeckleObjectWrapper()
|
||||
{
|
||||
Base = map.AtomicObject,
|
||||
Path = path.Select(p => p.name).ToList(),
|
||||
Parent = objectCollection,
|
||||
GeometryBase = geometryBase,
|
||||
Properties = propertyGroup,
|
||||
Name = name
|
||||
};
|
||||
|
||||
collectionRebuilder.AppendSpeckleGrasshopperObject(gh, path);
|
||||
}
|
||||
}
|
||||
catch (ConversionException)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
// var x = new SpeckleCollectionGoo { Value = collGen.RootCollection };
|
||||
var goo = new SpeckleCollectionWrapperGoo(collectionRebuilder.RootCollectionWrapper);
|
||||
return new ReceiveComponentOutput { RootObject = goo };
|
||||
}
|
||||
|
||||
private List<GeometryBase> Convert(Base input)
|
||||
{
|
||||
var result = ToSpeckleConversionContext.ToHostConverter.Convert(input);
|
||||
|
||||
if (result is GeometryBase geometry)
|
||||
{
|
||||
return [geometry];
|
||||
}
|
||||
if (result is List<GeometryBase> geometryList)
|
||||
{
|
||||
return geometryList;
|
||||
}
|
||||
if (result is List<(GeometryBase, Base)> fallbackConversionResult)
|
||||
{
|
||||
// note special handling for proxying render materials OR we don't care about revit
|
||||
return fallbackConversionResult.Select(t => t.Item1).ToList();
|
||||
}
|
||||
|
||||
throw new SpeckleException("Failed to convert input to rhino");
|
||||
}
|
||||
}
|
||||
+503
@@ -0,0 +1,503 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Timers;
|
||||
using Grasshopper.GUI;
|
||||
using Grasshopper.GUI.Canvas;
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Attributes;
|
||||
using GrasshopperAsyncComponent;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Rhino;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Grasshopper8.HostApp;
|
||||
using Speckle.Connectors.Grasshopper8.Parameters;
|
||||
using Speckle.Connectors.Grasshopper8.Registration;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Rhino;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Speckle.Sdk.Models.Extensions;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.Operations.Send;
|
||||
|
||||
[Guid("52481972-7867-404F-8D9F-E1481183F355")]
|
||||
public class SendAsyncComponent : GH_AsyncComponent
|
||||
{
|
||||
public SendAsyncComponent()
|
||||
: base(
|
||||
"Async Send",
|
||||
"aS",
|
||||
"Send objects async to speckle",
|
||||
ComponentCategories.PRIMARY_RIBBON,
|
||||
ComponentCategories.OPERATIONS
|
||||
)
|
||||
{
|
||||
BaseWorker = new SendComponentWorker(this);
|
||||
Attributes = new SendAsyncComponentAttributes(this);
|
||||
}
|
||||
|
||||
public override Guid ComponentGuid => GetType().GUID;
|
||||
|
||||
protected override Bitmap Icon => BitmapBuilder.CreateSquareIconBitmap("aS");
|
||||
|
||||
public ComponentState CurrentComponentState { get; set; } = ComponentState.NeedsInput;
|
||||
public bool AutoSend { get; set; }
|
||||
public bool JustPastedIn { get; set; }
|
||||
public double OverallProgress { get; set; }
|
||||
public string? Url { get; set; }
|
||||
public Client ApiClient { get; set; }
|
||||
public HostApp.SpeckleUrlModelResource? UrlModelResource { get; set; }
|
||||
public SpeckleCollectionWrapperGoo? RootCollectionWrapper { get; set; }
|
||||
|
||||
public SpeckleUrlModelResource? OutputParam { get; set; }
|
||||
public SendOperation<SpeckleCollectionWrapperGoo> SendOperation { get; private set; }
|
||||
public static IServiceScope? Scope { get; set; }
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(new SpeckleUrlModelResourceParam());
|
||||
pManager.AddParameter(
|
||||
new SpeckleCollectionParam(GH_ParamAccess.item),
|
||||
"Model",
|
||||
"model",
|
||||
"The collection model object to send",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(new SpeckleUrlModelResourceParam());
|
||||
}
|
||||
|
||||
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
|
||||
{
|
||||
static void Open(string url)
|
||||
{
|
||||
var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true };
|
||||
Process.Start(psi);
|
||||
}
|
||||
|
||||
base.AppendAdditionalMenuItems(menu);
|
||||
|
||||
Menu_AppendSeparator(menu);
|
||||
|
||||
var autoSendMi = Menu_AppendItem(
|
||||
menu,
|
||||
"Send automatically",
|
||||
(s, e) =>
|
||||
{
|
||||
AutoSend = !AutoSend;
|
||||
RhinoApp.InvokeOnUiThread(
|
||||
(Action)
|
||||
delegate
|
||||
{
|
||||
OnDisplayExpired(true);
|
||||
}
|
||||
);
|
||||
},
|
||||
true,
|
||||
AutoSend
|
||||
);
|
||||
autoSendMi.ToolTipText =
|
||||
"Toggle automatic data sending. If set, any change in any of the input parameters of this component will start sending.\n Please be aware that if a new send starts before an old one is finished, the previous operation is cancelled.";
|
||||
|
||||
if (Url != null)
|
||||
{
|
||||
Menu_AppendSeparator(menu);
|
||||
|
||||
Menu_AppendItem(menu, $"View created version online ↗", (s, e) => Open(Url));
|
||||
}
|
||||
|
||||
Menu_AppendSeparator(menu);
|
||||
|
||||
if (CurrentComponentState == ComponentState.Sending)
|
||||
{
|
||||
Menu_AppendItem(
|
||||
menu,
|
||||
"Cancel Send",
|
||||
(s, e) =>
|
||||
{
|
||||
CurrentComponentState = ComponentState.Expired;
|
||||
RequestCancellation();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SolveInstance(IGH_DataAccess da)
|
||||
{
|
||||
// Dependency Injection
|
||||
Scope = PriorityLoader.Container.CreateScope();
|
||||
SendOperation = Scope.ServiceProvider.GetRequiredService<SendOperation<SpeckleCollectionWrapperGoo>>();
|
||||
var rhinoConversionSettingsFactory = Scope.ServiceProvider.GetRequiredService<IRhinoConversionSettingsFactory>();
|
||||
Scope
|
||||
.ServiceProvider.GetRequiredService<IConverterSettingsStore<RhinoConversionSettings>>()
|
||||
.Initialize(rhinoConversionSettingsFactory.Create(RhinoDoc.ActiveDoc));
|
||||
|
||||
var accountManager = Scope.ServiceProvider.GetRequiredService<AccountService>();
|
||||
var clientFactory = Scope.ServiceProvider.GetRequiredService<IClientFactory>();
|
||||
|
||||
// We need to call this always in here to be able to react and set events :/
|
||||
ParseInput(da, accountManager, clientFactory);
|
||||
|
||||
if (
|
||||
(AutoSend || CurrentComponentState == ComponentState.Ready || CurrentComponentState == ComponentState.Sending)
|
||||
&& !JustPastedIn
|
||||
)
|
||||
{
|
||||
CurrentComponentState = ComponentState.Sending;
|
||||
|
||||
// Delegate control to parent async component.
|
||||
base.SolveInstance(da);
|
||||
return;
|
||||
}
|
||||
|
||||
if (JustPastedIn)
|
||||
{
|
||||
// Set output data in a "first run" event. Note: we are not persisting the actual "sent" object as it can be very big.
|
||||
base.SolveInstance(da);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
da.SetData(0, OutputParam);
|
||||
CurrentComponentState = ComponentState.Expired;
|
||||
Message = "Expired";
|
||||
OnDisplayExpired(true);
|
||||
}
|
||||
}
|
||||
|
||||
public override void RemovedFromDocument(GH_Document document)
|
||||
{
|
||||
RequestCancellation();
|
||||
Scope?.Dispose();
|
||||
base.RemovedFromDocument(document);
|
||||
}
|
||||
|
||||
public override void DisplayProgress(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
if (Workers.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Message = "";
|
||||
var total = 0.0;
|
||||
foreach (var kvp in ProgressReports)
|
||||
{
|
||||
Message += $"{kvp.Key}: {kvp.Value}\n";
|
||||
total += kvp.Value;
|
||||
}
|
||||
|
||||
OverallProgress = total / ProgressReports.Keys.Count;
|
||||
|
||||
RhinoApp.InvokeOnUiThread(
|
||||
(Action)
|
||||
delegate
|
||||
{
|
||||
OnDisplayExpired(true);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public override void DocumentContextChanged(GH_Document document, GH_DocumentContext context)
|
||||
{
|
||||
switch (context)
|
||||
{
|
||||
case GH_DocumentContext.Loaded:
|
||||
OnDisplayExpired(true);
|
||||
break;
|
||||
|
||||
case GH_DocumentContext.Unloaded:
|
||||
// Will execute every time a document becomes inactive (in background or closing file.)
|
||||
//Correctly dispose of the client when changing documents to prevent subscription handlers being called in background.
|
||||
RequestCancellation();
|
||||
break;
|
||||
}
|
||||
|
||||
base.DocumentContextChanged(document, context);
|
||||
}
|
||||
|
||||
private void ParseInput(IGH_DataAccess da, AccountService accountManager, IClientFactory clientFactory)
|
||||
{
|
||||
HostApp.SpeckleUrlModelResource? dataInput = null;
|
||||
da.GetData(0, ref dataInput);
|
||||
if (dataInput is null)
|
||||
{
|
||||
UrlModelResource = null;
|
||||
TriggerAutoSave();
|
||||
return;
|
||||
}
|
||||
|
||||
UrlModelResource = dataInput;
|
||||
try
|
||||
{
|
||||
// TODO: Get any account for this server, as we don't have a mechanism yet to pass accountIds through
|
||||
Account account = accountManager.GetAccountWithServerUrlFallback("", new Uri(dataInput.Server));
|
||||
if (account is null)
|
||||
{
|
||||
throw new SpeckleAccountManagerException($"No default account was found");
|
||||
}
|
||||
|
||||
ApiClient?.Dispose();
|
||||
ApiClient = clientFactory.Create(account);
|
||||
}
|
||||
catch (Exception e) when (!e.IsFatal())
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, e.ToFormattedString());
|
||||
}
|
||||
|
||||
SpeckleCollectionWrapperGoo rootCollectionWrapper = new();
|
||||
da.GetData(1, ref rootCollectionWrapper);
|
||||
if (rootCollectionWrapper is null)
|
||||
{
|
||||
RootCollectionWrapper = null;
|
||||
TriggerAutoSave();
|
||||
return;
|
||||
}
|
||||
RootCollectionWrapper = rootCollectionWrapper;
|
||||
}
|
||||
}
|
||||
|
||||
public class SendComponentWorker : WorkerInstance
|
||||
{
|
||||
public SendComponentWorker(GH_Component p)
|
||||
: base(p) { }
|
||||
|
||||
private Stopwatch _stopwatch;
|
||||
public SpeckleUrlModelResource? OutputParam { get; set; }
|
||||
private List<(GH_RuntimeMessageLevel, string)> RuntimeMessages { get; } = new();
|
||||
|
||||
public override WorkerInstance Duplicate()
|
||||
{
|
||||
return new SendComponentWorker(Parent);
|
||||
}
|
||||
|
||||
public override void GetData(IGH_DataAccess da, GH_ComponentParamServer p)
|
||||
{
|
||||
_stopwatch = new Stopwatch();
|
||||
_stopwatch.Start();
|
||||
}
|
||||
|
||||
public override void SetData(IGH_DataAccess da)
|
||||
{
|
||||
_stopwatch.Stop();
|
||||
|
||||
if (((SendAsyncComponent)Parent).JustPastedIn)
|
||||
{
|
||||
((SendAsyncComponent)Parent).JustPastedIn = false;
|
||||
da.SetData(0, ((SendAsyncComponent)Parent).OutputParam);
|
||||
return;
|
||||
}
|
||||
|
||||
if (CancellationToken.IsCancellationRequested)
|
||||
{
|
||||
((SendAsyncComponent)Parent).CurrentComponentState = ComponentState.Expired;
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (level, message) in RuntimeMessages)
|
||||
{
|
||||
Parent.AddRuntimeMessage(level, message);
|
||||
}
|
||||
|
||||
da.SetData(0, OutputParam);
|
||||
|
||||
((SendAsyncComponent)Parent).CurrentComponentState = ComponentState.UpToDate;
|
||||
((SendAsyncComponent)Parent).OutputParam = OutputParam; // ref the outputs in the parent too, so we can serialise them on write/read
|
||||
((SendAsyncComponent)Parent).OverallProgress = 0;
|
||||
|
||||
var hasWarnings = RuntimeMessages.Count > 0;
|
||||
if (!hasWarnings)
|
||||
{
|
||||
Parent.AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Remark,
|
||||
$"Successfully sent {((SendAsyncComponent)Parent).RootCollectionWrapper?.Value.GetTotalChildrenCount()} objects to Speckle."
|
||||
);
|
||||
Parent.AddRuntimeMessage(
|
||||
GH_RuntimeMessageLevel.Remark,
|
||||
$"Send duration: {_stopwatch.ElapsedMilliseconds / 1000f}s"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public override void DoWork(Action<string, double> reportProgress, Action done)
|
||||
{
|
||||
var sendComponent = (SendAsyncComponent)Parent;
|
||||
|
||||
if (sendComponent.JustPastedIn)
|
||||
{
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
if (CancellationToken.IsCancellationRequested)
|
||||
{
|
||||
sendComponent.CurrentComponentState = ComponentState.Expired;
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
SpeckleUrlModelResource? urlModelResource = sendComponent.UrlModelResource;
|
||||
if (urlModelResource is null)
|
||||
{
|
||||
throw new InvalidOperationException("Url Resource was null");
|
||||
}
|
||||
|
||||
SpeckleCollectionWrapperGoo? rootCollectionWrapper = sendComponent.RootCollectionWrapper;
|
||||
if (rootCollectionWrapper is null)
|
||||
{
|
||||
throw new InvalidOperationException("Root Collection was null");
|
||||
}
|
||||
|
||||
var t = Task.Run(async () =>
|
||||
{
|
||||
if (CancellationToken.IsCancellationRequested)
|
||||
{
|
||||
sendComponent.CurrentComponentState = ComponentState.Expired;
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 1 - SEND TO SERVER
|
||||
var sendInfo = await urlModelResource
|
||||
.GetSendInfo(sendComponent.ApiClient, CancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var progress = new Progress<CardProgress>(p =>
|
||||
{
|
||||
reportProgress(Id, p.Progress ?? 0);
|
||||
//sendComponent.Message = $"{p.Status}";
|
||||
});
|
||||
|
||||
var result = await sendComponent
|
||||
.SendOperation.Execute(
|
||||
new List<SpeckleCollectionWrapperGoo>() { rootCollectionWrapper },
|
||||
sendInfo,
|
||||
progress,
|
||||
CancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
// TODO: need the created version id here from the send result, not the rootobj id
|
||||
SpeckleUrlModelVersionResource? createdVersion =
|
||||
new(sendInfo.ServerUrl.ToString(), sendInfo.ProjectId, sendInfo.ModelId, result.RootObjId);
|
||||
OutputParam = createdVersion;
|
||||
sendComponent.Url = $"{createdVersion.Server}projects/{sendInfo.ProjectId}/models/{sendInfo.ModelId}"; // TODO: missing "@VersionId"
|
||||
|
||||
// DONE
|
||||
done();
|
||||
});
|
||||
t.Wait();
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
RuntimeMessages.Add((GH_RuntimeMessageLevel.Error, ex.ToFormattedString()));
|
||||
done();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SendAsyncComponentAttributes : GH_ComponentAttributes
|
||||
{
|
||||
private bool _selected;
|
||||
|
||||
public SendAsyncComponentAttributes(GH_Component owner)
|
||||
: base(owner) { }
|
||||
|
||||
private Rectangle ButtonBounds { get; set; }
|
||||
|
||||
public override bool Selected
|
||||
{
|
||||
get => _selected;
|
||||
set => _selected = value;
|
||||
}
|
||||
|
||||
protected override void Layout()
|
||||
{
|
||||
base.Layout();
|
||||
|
||||
var baseRec = GH_Convert.ToRectangle(Bounds);
|
||||
baseRec.Height += 26;
|
||||
|
||||
var btnRec = baseRec;
|
||||
btnRec.Y = btnRec.Bottom - 26;
|
||||
btnRec.Height = 26;
|
||||
btnRec.Inflate(-2, -2);
|
||||
|
||||
Bounds = baseRec;
|
||||
ButtonBounds = btnRec;
|
||||
}
|
||||
|
||||
protected override void Render(GH_Canvas canvas, Graphics graphics, GH_CanvasChannel channel)
|
||||
{
|
||||
base.Render(canvas, graphics, channel);
|
||||
|
||||
var state = ((SendAsyncComponent)Owner).CurrentComponentState;
|
||||
|
||||
if (channel == GH_CanvasChannel.Objects)
|
||||
{
|
||||
if (((SendAsyncComponent)Owner).AutoSend)
|
||||
{
|
||||
var autoSendButton = GH_Capsule.CreateTextCapsule(
|
||||
ButtonBounds,
|
||||
ButtonBounds,
|
||||
GH_Palette.Blue,
|
||||
"Auto Send",
|
||||
2,
|
||||
0
|
||||
);
|
||||
|
||||
autoSendButton.Render(graphics, Selected, Owner.Locked, false);
|
||||
autoSendButton.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
var palette =
|
||||
state == ComponentState.Expired || state == ComponentState.UpToDate
|
||||
? GH_Palette.Black
|
||||
: GH_Palette.Transparent;
|
||||
|
||||
var text = state == ComponentState.Sending ? "Sending..." : "Send";
|
||||
|
||||
var button = GH_Capsule.CreateTextCapsule(
|
||||
ButtonBounds,
|
||||
ButtonBounds,
|
||||
palette,
|
||||
text,
|
||||
2,
|
||||
state == ComponentState.Expired ? 10 : 0
|
||||
);
|
||||
button.Render(graphics, Selected, Owner.Locked, false);
|
||||
button.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override GH_ObjectResponse RespondToMouseDown(GH_Canvas sender, GH_CanvasMouseEvent e)
|
||||
{
|
||||
if (e.Button == MouseButtons.Left)
|
||||
{
|
||||
if (((RectangleF)ButtonBounds).Contains(e.CanvasLocation))
|
||||
{
|
||||
if (((SendAsyncComponent)Owner).AutoSend)
|
||||
{
|
||||
((SendAsyncComponent)Owner).AutoSend = false;
|
||||
Owner.OnDisplayExpired(true);
|
||||
return GH_ObjectResponse.Handled;
|
||||
}
|
||||
if (((SendAsyncComponent)Owner).CurrentComponentState == ComponentState.Sending)
|
||||
{
|
||||
return GH_ObjectResponse.Handled;
|
||||
}
|
||||
((SendAsyncComponent)Owner).CurrentComponentState = ComponentState.Ready;
|
||||
Owner.ExpireSolution(true);
|
||||
return GH_ObjectResponse.Handled;
|
||||
}
|
||||
}
|
||||
|
||||
return base.RespondToMouseDown(sender, e);
|
||||
}
|
||||
}
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
using System.Diagnostics;
|
||||
using Grasshopper.Kernel;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Rhino;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Grasshopper8.Components.BaseComponents;
|
||||
using Speckle.Connectors.Grasshopper8.HostApp;
|
||||
using Speckle.Connectors.Grasshopper8.Parameters;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Rhino;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Credentials;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.Operations.Send;
|
||||
|
||||
public class SendComponentInput
|
||||
{
|
||||
public SpeckleUrlModelResource Resource { get; }
|
||||
public SpeckleCollectionWrapperGoo Input { get; }
|
||||
|
||||
public SendComponentInput(SpeckleUrlModelResource resource, SpeckleCollectionWrapperGoo input)
|
||||
{
|
||||
Resource = resource;
|
||||
Input = input;
|
||||
}
|
||||
}
|
||||
|
||||
public class SendComponentOutput(SpeckleUrlModelResource resource)
|
||||
{
|
||||
public SpeckleUrlModelResource Resource { get; } = resource;
|
||||
}
|
||||
|
||||
public class SendComponent : SpeckleScopedTaskCapableComponent<SendComponentInput, SendComponentOutput>
|
||||
{
|
||||
public SendComponent()
|
||||
: base(
|
||||
"Send to Speckle",
|
||||
"STS",
|
||||
"Send objects to speckle",
|
||||
ComponentCategories.PRIMARY_RIBBON,
|
||||
ComponentCategories.OPERATIONS
|
||||
) { }
|
||||
|
||||
public override Guid ComponentGuid => new("0CF0D173-BDF0-4AC2-9157-02822B90E9FB");
|
||||
|
||||
public string? Url { get; private set; }
|
||||
|
||||
protected override Bitmap Icon => BitmapBuilder.CreateSquareIconBitmap("S");
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(new SpeckleUrlModelResourceParam());
|
||||
pManager.AddParameter(
|
||||
new SpeckleCollectionParam(GH_ParamAccess.item),
|
||||
"Model",
|
||||
"model",
|
||||
"The collection model object to send",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(new SpeckleUrlModelResourceParam());
|
||||
}
|
||||
|
||||
protected override SendComponentInput GetInput(IGH_DataAccess da)
|
||||
{
|
||||
if (da.Iteration != 0)
|
||||
{
|
||||
throw new SpeckleException("No more than 1 resource allowed");
|
||||
}
|
||||
|
||||
SpeckleUrlModelResource? resource = null;
|
||||
if (!da.GetData(0, ref resource))
|
||||
{
|
||||
throw new SpeckleException("Failed to get resource");
|
||||
}
|
||||
|
||||
SpeckleCollectionWrapperGoo rootCollectionWrapper = new();
|
||||
da.GetData(1, ref rootCollectionWrapper);
|
||||
|
||||
return new SendComponentInput(resource.NotNull(), rootCollectionWrapper);
|
||||
}
|
||||
|
||||
protected override void SetOutput(IGH_DataAccess da, SendComponentOutput result)
|
||||
{
|
||||
da.SetData(0, result.Resource);
|
||||
}
|
||||
|
||||
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
|
||||
{
|
||||
base.AppendAdditionalMenuItems(menu);
|
||||
|
||||
Menu_AppendSeparator(menu);
|
||||
if (Url != null)
|
||||
{
|
||||
Menu_AppendSeparator(menu);
|
||||
|
||||
Menu_AppendItem(menu, $"View created version online ↗", (s, e) => Open(Url));
|
||||
}
|
||||
|
||||
static void Open(string url)
|
||||
{
|
||||
var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true };
|
||||
Process.Start(psi);
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task<SendComponentOutput> PerformScopedTask(
|
||||
SendComponentInput input,
|
||||
IServiceScope scope,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
var rhinoConversionSettingsFactory = scope.ServiceProvider.GetRequiredService<IRhinoConversionSettingsFactory>();
|
||||
scope
|
||||
.ServiceProvider.GetRequiredService<IConverterSettingsStore<RhinoConversionSettings>>()
|
||||
.Initialize(rhinoConversionSettingsFactory.Create(RhinoDoc.ActiveDoc));
|
||||
|
||||
var accountManager = scope.ServiceProvider.GetRequiredService<AccountService>();
|
||||
var clientFactory = scope.ServiceProvider.GetRequiredService<IClientFactory>();
|
||||
var sendOperation = scope.ServiceProvider.GetRequiredService<SendOperation<SpeckleCollectionWrapperGoo>>();
|
||||
|
||||
// TODO: Get any account for this server, as we don't have a mechanism yet to pass accountIds through
|
||||
var account = accountManager.GetAccountWithServerUrlFallback("", new Uri(input.Resource.Server));
|
||||
|
||||
if (account is null)
|
||||
{
|
||||
throw new SpeckleAccountManagerException($"No default account was found");
|
||||
}
|
||||
|
||||
var progress = new Progress<CardProgress>(_ =>
|
||||
{
|
||||
// TODO: Progress only makes sense in non-blocking async receive, which is not supported yet.
|
||||
// Message = $"{progress.Status}: {progress.Progress}";
|
||||
});
|
||||
|
||||
using var client = clientFactory.Create(account);
|
||||
var sendInfo = await input.Resource.GetSendInfo(client, cancellationToken).ConfigureAwait(false);
|
||||
var result = await sendOperation
|
||||
.Execute(new List<SpeckleCollectionWrapperGoo>() { input.Input }, sendInfo, progress, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
SpeckleUrlLatestModelVersionResource createdVersionResource =
|
||||
new(sendInfo.ServerUrl.ToString(), sendInfo.ProjectId, sendInfo.ModelId);
|
||||
Url = $"{createdVersionResource.Server}projects/{sendInfo.ProjectId}/models/{sendInfo.ModelId}"; // TODO: missing "@VersionId"
|
||||
|
||||
return new SendComponentOutput(createdVersionResource);
|
||||
}
|
||||
}
|
||||
+507
@@ -0,0 +1,507 @@
|
||||
using GH_IO.Serialization;
|
||||
using Grasshopper.Kernel;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Grasshopper8.HostApp;
|
||||
using Speckle.Connectors.Grasshopper8.Parameters;
|
||||
using Speckle.Connectors.Grasshopper8.Registration;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL.Models;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Version = Speckle.Sdk.Api.GraphQL.Models.Version;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.Operations;
|
||||
|
||||
public class SpeckleSelectModelComponent : GH_Component
|
||||
{
|
||||
private Project? _project;
|
||||
private Model? _model;
|
||||
private Version? _version;
|
||||
private Account? _account;
|
||||
|
||||
private bool _justPastedIn;
|
||||
|
||||
private string? _storedUserId;
|
||||
private string? _storedServer;
|
||||
private string? _storedProjectId;
|
||||
private string? _storedModelId;
|
||||
private string? _storedVersionId;
|
||||
|
||||
private readonly AccountService _accountService;
|
||||
private readonly AccountManager _accountManager;
|
||||
private readonly IClientFactory _clientFactory;
|
||||
|
||||
public ResourceCollection<Project>? LastFetchedProjects { get; set; }
|
||||
public ResourceCollection<Model>? LastFetchedModels { get; set; }
|
||||
public ResourceCollection<Version>? LastFetchedVersions { get; set; }
|
||||
|
||||
public GhContextMenuButton ProjectContextMenuButton { get; set; }
|
||||
public GhContextMenuButton ModelContextMenuButton { get; set; }
|
||||
public GhContextMenuButton VersionContextMenuButton { get; set; }
|
||||
|
||||
protected override Bitmap Icon => BitmapBuilder.CreateSquareIconBitmap("URL");
|
||||
|
||||
public SpeckleSelectModelComponent()
|
||||
: base("Speckle Model URL", "URL", "User selectable model from speckle", "Speckle", "Models")
|
||||
{
|
||||
ProjectContextMenuButton = new GhContextMenuButton(
|
||||
"Select Project",
|
||||
"Select Project",
|
||||
"Right-click to select project",
|
||||
PopulateProjectMenu
|
||||
);
|
||||
ModelContextMenuButton = new GhContextMenuButton(
|
||||
"Select Model",
|
||||
"Select Project",
|
||||
"Right-click to select a model",
|
||||
PopulateModelMenu
|
||||
);
|
||||
VersionContextMenuButton = new GhContextMenuButton(
|
||||
"Select Version",
|
||||
"Select Version",
|
||||
"Right-click to select a version",
|
||||
PopulateVersionMenu
|
||||
);
|
||||
|
||||
Attributes = new SpeckleSelectModelComponentAttributes(this);
|
||||
_accountService = PriorityLoader.Container.GetRequiredService<AccountService>();
|
||||
_accountManager = PriorityLoader.Container.GetRequiredService<AccountManager>();
|
||||
_clientFactory = PriorityLoader.Container.GetRequiredService<IClientFactory>();
|
||||
var account = _accountManager.GetDefaultAccount();
|
||||
OnAccountSelected(account);
|
||||
}
|
||||
|
||||
private bool PopulateVersionMenu(ToolStripDropDown menu)
|
||||
{
|
||||
if (LastFetchedVersions is null)
|
||||
{
|
||||
Menu_AppendItem(menu, "No versions were fetched");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (LastFetchedVersions.items.Count == 0)
|
||||
{
|
||||
Menu_AppendItem(menu, "Model has no versions");
|
||||
return true;
|
||||
}
|
||||
|
||||
Menu_AppendItem(menu, "Search...", null, null, false, false);
|
||||
Menu_AppendSeparator(menu);
|
||||
Menu_AppendItem(
|
||||
menu,
|
||||
"Latest Version",
|
||||
(_, _) => OnVersionSelected(null),
|
||||
null,
|
||||
_version != null,
|
||||
_version == null
|
||||
);
|
||||
|
||||
foreach (var version in LastFetchedVersions.items)
|
||||
{
|
||||
var desc = string.IsNullOrEmpty(version.message) ? "No description" : version.message;
|
||||
|
||||
Menu_AppendItem(
|
||||
menu,
|
||||
$"{version.id} - {desc}",
|
||||
(_, _) => OnVersionSelected(version),
|
||||
null,
|
||||
_version?.id != version.id,
|
||||
_version?.id == version.id
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool PopulateModelMenu(ToolStripDropDown menu)
|
||||
{
|
||||
if (LastFetchedModels == null)
|
||||
{
|
||||
Menu_AppendItem(menu, "No models were fetched");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (LastFetchedModels.items.Count == 0)
|
||||
{
|
||||
Menu_AppendItem(menu, "Project has no models");
|
||||
return true;
|
||||
}
|
||||
|
||||
Menu_AppendItem(menu, "Search...", null, null, false, false);
|
||||
Menu_AppendSeparator(menu);
|
||||
|
||||
foreach (var model in LastFetchedModels.items)
|
||||
{
|
||||
var desc = string.IsNullOrEmpty(model.description) ? "No description" : model.description;
|
||||
|
||||
Menu_AppendItem(
|
||||
menu,
|
||||
$"{model.name} - {desc}",
|
||||
(_, _) => OnModelSelected(model),
|
||||
null,
|
||||
_model?.id != model.id,
|
||||
_model?.id == model.id
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool PopulateProjectMenu(ToolStripDropDown menu)
|
||||
{
|
||||
if (LastFetchedProjects == null)
|
||||
{
|
||||
Menu_AppendItem(menu, "No projects were fetched");
|
||||
return true;
|
||||
}
|
||||
|
||||
Menu_AppendItem(menu, "Search...", null, null, false, false);
|
||||
Menu_AppendSeparator(menu);
|
||||
|
||||
foreach (var project in LastFetchedProjects.items)
|
||||
{
|
||||
var desc = string.IsNullOrEmpty(project.description) ? "No description" : project.description;
|
||||
Menu_AppendItem(
|
||||
menu,
|
||||
$"{project.name} - {desc}",
|
||||
(_, _) => OnProjectSelected(project),
|
||||
_project?.id != project.id,
|
||||
_project?.id == project.id
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnAccountSelected(Account? account, bool expire = true, bool redraw = true)
|
||||
{
|
||||
_account = account;
|
||||
Message = _account != null ? $"{_account.serverInfo.url}\n{_account.userInfo.email}" : null;
|
||||
LastFetchedProjects = null;
|
||||
OnProjectSelected(null, expire, redraw);
|
||||
}
|
||||
|
||||
private void OnProjectSelected(Project? project, bool expire = true, bool redraw = true)
|
||||
{
|
||||
_project = project;
|
||||
var suffix = ProjectContextMenuButton.Enabled
|
||||
? "Right-click to select another project."
|
||||
: "Selection is disabled due to component input.";
|
||||
if (_project != null)
|
||||
{
|
||||
ProjectContextMenuButton.Name = _project.name;
|
||||
ProjectContextMenuButton.NickName = _project.id;
|
||||
ProjectContextMenuButton.Description = $"{_project.description ?? "No description"}\n\n{suffix}";
|
||||
}
|
||||
else
|
||||
{
|
||||
ProjectContextMenuButton.Name = "Select Project";
|
||||
ProjectContextMenuButton.NickName = "Project";
|
||||
ProjectContextMenuButton.Description = "Right-click to select project";
|
||||
}
|
||||
LastFetchedModels = null;
|
||||
OnModelSelected(null, expire, redraw);
|
||||
}
|
||||
|
||||
private void OnModelSelected(Model? model, bool expire = true, bool redraw = true)
|
||||
{
|
||||
_model = model;
|
||||
var suffix = ModelContextMenuButton.Enabled
|
||||
? "Right-click to select another model."
|
||||
: "Selection is disabled due to component input.";
|
||||
if (_model != null)
|
||||
{
|
||||
ModelContextMenuButton.Name = _model.name;
|
||||
ModelContextMenuButton.NickName = _model.id;
|
||||
ModelContextMenuButton.Description = $"{_model.description ?? "No description"}\n\n{suffix}";
|
||||
}
|
||||
else
|
||||
{
|
||||
ModelContextMenuButton.Name = "Select Model";
|
||||
ModelContextMenuButton.NickName = "Model";
|
||||
ModelContextMenuButton.Description = "Right-click to select model";
|
||||
}
|
||||
LastFetchedVersions = null;
|
||||
OnVersionSelected(null, expire, redraw);
|
||||
}
|
||||
|
||||
private void OnVersionSelected(Version? version, bool expire = true, bool redraw = true)
|
||||
{
|
||||
_version = version;
|
||||
var suffix = VersionContextMenuButton.Enabled
|
||||
? "Right-click to select another version."
|
||||
: "Selection is disabled due to component input.";
|
||||
if (_version != null)
|
||||
{
|
||||
VersionContextMenuButton.Name = _version.id;
|
||||
VersionContextMenuButton.NickName = _version.id;
|
||||
VersionContextMenuButton.Description = $"{_version.message ?? "No message"}\n\n{suffix}";
|
||||
}
|
||||
else if (_model != null)
|
||||
{
|
||||
VersionContextMenuButton.NickName = "Latest Version";
|
||||
VersionContextMenuButton.Name = "Latest Version";
|
||||
VersionContextMenuButton.Description = "Gets the latest version from the selected model";
|
||||
}
|
||||
else
|
||||
{
|
||||
VersionContextMenuButton.Name = "Select Version";
|
||||
VersionContextMenuButton.NickName = "Version";
|
||||
VersionContextMenuButton.Description = "Right-click to select version";
|
||||
}
|
||||
if (expire)
|
||||
{
|
||||
ExpirePreview(redraw);
|
||||
ExpireSolution(true);
|
||||
}
|
||||
}
|
||||
|
||||
public override Guid ComponentGuid => new("9638B3B5-C469-4570-B69F-686D8DA5C48D");
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
var urlIndex = pManager.AddTextParameter("Speckle Url", "Url", "Speckle URL", GH_ParamAccess.item);
|
||||
pManager[urlIndex].Optional = true;
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(new SpeckleUrlModelResourceParam());
|
||||
}
|
||||
|
||||
protected override void SolveInstance(IGH_DataAccess da)
|
||||
{
|
||||
// Deal with inputs
|
||||
string? urlInput = null;
|
||||
|
||||
// OPTION 1: Component has input wire connected
|
||||
if (da.GetData(0, ref urlInput))
|
||||
{
|
||||
//Lock button interactions before anything else, to ensure any input (even invalid ones) lock the state.
|
||||
SetComponentButtonsState(false);
|
||||
|
||||
if (urlInput == null || string.IsNullOrEmpty(urlInput))
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Input url was empty or null");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var resource = SolveInstanceWithUrlInput(urlInput);
|
||||
da.SetData(0, resource);
|
||||
}
|
||||
catch (SpeckleException e)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, e.Message);
|
||||
}
|
||||
return; // Fast exit!
|
||||
}
|
||||
|
||||
// OPTION 2: Component is running with no wires connected to input.
|
||||
|
||||
// Unlock button interactions when no input data is provided (no wires connected)
|
||||
SetComponentButtonsState(true);
|
||||
|
||||
if (_justPastedIn && _storedUserId != null && !string.IsNullOrEmpty(_storedUserId))
|
||||
{
|
||||
try
|
||||
{
|
||||
var account = _accountManager.GetAccount(_storedUserId);
|
||||
OnAccountSelected(account, false);
|
||||
}
|
||||
catch (SpeckleAccountManagerException e)
|
||||
{
|
||||
// Swallow and move onto checking server.
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
|
||||
if (_storedServer != null && _account == null)
|
||||
{
|
||||
var account = _accountService.GetAccountWithServerUrlFallback(_storedUserId ?? "", new Uri(_storedServer));
|
||||
OnAccountSelected(account, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate backing data
|
||||
if (_account == null)
|
||||
{
|
||||
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Please select an account in the right click menu");
|
||||
ProjectContextMenuButton.Enabled = false;
|
||||
ModelContextMenuButton.Enabled = false;
|
||||
VersionContextMenuButton.Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
Client client = _clientFactory.Create(_account);
|
||||
|
||||
LastFetchedProjects = client.ActiveUser.GetProjects(10, null, null).Result;
|
||||
ProjectContextMenuButton.Enabled = true;
|
||||
|
||||
if (_justPastedIn && !string.IsNullOrEmpty(_storedProjectId))
|
||||
{
|
||||
var project = client.Project.Get(_storedProjectId!).Result;
|
||||
OnProjectSelected(project, false);
|
||||
}
|
||||
|
||||
if (_project == null)
|
||||
{
|
||||
ModelContextMenuButton.Enabled = false;
|
||||
VersionContextMenuButton.Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
LastFetchedModels = client.Project.GetWithModels(_project.id, 10).Result.models;
|
||||
ModelContextMenuButton.Enabled = true;
|
||||
|
||||
if (_justPastedIn && !string.IsNullOrEmpty(_storedModelId))
|
||||
{
|
||||
var model = client.Model.Get(_storedModelId!, _project.id).Result;
|
||||
OnModelSelected(model, false);
|
||||
}
|
||||
|
||||
if (_model == null)
|
||||
{
|
||||
VersionContextMenuButton.Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
LastFetchedVersions = client.Model.GetWithVersions(_model.id, _project.id, 10).Result.versions;
|
||||
VersionContextMenuButton.Enabled = true;
|
||||
|
||||
if (_justPastedIn && !string.IsNullOrEmpty(_storedVersionId))
|
||||
{
|
||||
var version = client.Version.Get(_storedVersionId!, _project.id).Result;
|
||||
OnVersionSelected(version);
|
||||
}
|
||||
if (_version == null)
|
||||
{
|
||||
// If no version selected, output `latest` resource
|
||||
da.SetData(0, new SpeckleUrlLatestModelVersionResource(_account.serverInfo.url, _project.id, _model.id));
|
||||
return;
|
||||
}
|
||||
|
||||
// If all data points are selected, output specific version.
|
||||
da.SetData(0, new SpeckleUrlModelVersionResource(_account.serverInfo.url, _project.id, _model.id, _version.id));
|
||||
}
|
||||
|
||||
protected override void AfterSolveInstance()
|
||||
{
|
||||
// If the component runs once till the end, then it's no longer "just pasted in".
|
||||
_justPastedIn = false;
|
||||
base.AfterSolveInstance();
|
||||
}
|
||||
|
||||
private void SetComponentButtonsState(bool enabled)
|
||||
{
|
||||
ProjectContextMenuButton.Enabled = enabled;
|
||||
ModelContextMenuButton.Enabled = enabled;
|
||||
VersionContextMenuButton.Enabled = enabled;
|
||||
}
|
||||
|
||||
private SpeckleUrlModelResource SolveInstanceWithUrlInput(string urlInput)
|
||||
{
|
||||
// When input is provided, lock interaction of buttons so only text is shown (no context menu)
|
||||
// Should perform validation, fill in all internal data of the component (project, model, version, account)
|
||||
// Should notify user if any of this goes wrong.
|
||||
|
||||
var resources = SpeckleResourceBuilder.FromUrlString(urlInput);
|
||||
if (resources.Length == 0)
|
||||
{
|
||||
throw new SpeckleException($"Input url string was empty");
|
||||
}
|
||||
|
||||
if (resources.Length > 1)
|
||||
{
|
||||
throw new SpeckleException($"Input multi-model url is not supported");
|
||||
}
|
||||
|
||||
var resource = resources.First();
|
||||
|
||||
var account = _accountService.GetAccountWithServerUrlFallback(string.Empty, new Uri(resource.Server));
|
||||
OnAccountSelected(account, false);
|
||||
|
||||
if (_account == null)
|
||||
{
|
||||
throw new SpeckleException("No account found for server URL");
|
||||
}
|
||||
|
||||
Client client = _clientFactory.Create(_account);
|
||||
|
||||
var project = client.Project.Get(resource.ProjectId).Result;
|
||||
OnProjectSelected(project, false);
|
||||
|
||||
switch (resource)
|
||||
{
|
||||
case SpeckleUrlLatestModelVersionResource latestVersionResource:
|
||||
var model = client.Model.Get(latestVersionResource.ModelId, latestVersionResource.ProjectId).Result;
|
||||
OnModelSelected(model, false);
|
||||
break;
|
||||
case SpeckleUrlModelVersionResource versionResource:
|
||||
var m = client.Model.Get(versionResource.ModelId, versionResource.ProjectId).Result;
|
||||
OnModelSelected(m, false);
|
||||
var v = client.Version.Get(versionResource.VersionId, versionResource.ProjectId).Result;
|
||||
OnVersionSelected(v, false);
|
||||
break;
|
||||
case SpeckleUrlModelObjectResource:
|
||||
throw new SpeckleException("Object URLs are not supported");
|
||||
default:
|
||||
throw new SpeckleException("Unknown Speckle resource type");
|
||||
}
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
public override void AppendAdditionalMenuItems(ToolStripDropDown menu)
|
||||
{
|
||||
base.AppendAdditionalMenuItems(menu);
|
||||
var accountsMenu = Menu_AppendItem(menu, "Account");
|
||||
|
||||
foreach (var account in _accountManager.GetAccounts())
|
||||
{
|
||||
Menu_AppendItem(
|
||||
accountsMenu.DropDown,
|
||||
account.ToString(),
|
||||
(_, _) => OnAccountSelected(account),
|
||||
null,
|
||||
_account?.id != account.id,
|
||||
_account?.id == account.id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Write(GH_IWriter writer)
|
||||
{
|
||||
var baseRes = base.Write(writer);
|
||||
writer.SetString("Server", _account?.serverInfo.url);
|
||||
writer.SetString("User", _account?.id);
|
||||
writer.SetString("Project", _project?.id);
|
||||
writer.SetString("Model", _model?.id);
|
||||
writer.SetString("Version", _version?.id);
|
||||
|
||||
return baseRes;
|
||||
}
|
||||
|
||||
public override bool Read(GH_IReader reader)
|
||||
{
|
||||
var readRes = base.Read(reader);
|
||||
|
||||
reader.TryGetString("Server", ref _storedServer);
|
||||
reader.TryGetString("User", ref _storedUserId);
|
||||
reader.TryGetString("Project", ref _storedProjectId);
|
||||
reader.TryGetString("Model", ref _storedModelId);
|
||||
reader.TryGetString("Version", ref _storedVersionId);
|
||||
|
||||
_justPastedIn = true;
|
||||
return readRes;
|
||||
}
|
||||
|
||||
public override void ExpirePreview(bool redraw)
|
||||
{
|
||||
ProjectContextMenuButton.ExpirePreview(redraw);
|
||||
ModelContextMenuButton.ExpirePreview(redraw);
|
||||
VersionContextMenuButton.ExpirePreview(redraw);
|
||||
base.ExpirePreview(redraw);
|
||||
}
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
using Grasshopper.GUI.Canvas;
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Attributes;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.Operations;
|
||||
|
||||
public class SpeckleSelectModelComponentAttributes : GH_ComponentAttributes
|
||||
{
|
||||
private readonly SpeckleSelectModelComponent _typedOwner;
|
||||
|
||||
public SpeckleSelectModelComponentAttributes(IGH_Component component)
|
||||
: base(component)
|
||||
{
|
||||
_typedOwner = (SpeckleSelectModelComponent)component;
|
||||
}
|
||||
|
||||
public override void AppendToAttributeTree(List<IGH_Attributes> attributes)
|
||||
{
|
||||
base.AppendToAttributeTree(attributes);
|
||||
_typedOwner.ProjectContextMenuButton.Attributes?.AppendToAttributeTree(attributes);
|
||||
_typedOwner.ModelContextMenuButton.Attributes?.AppendToAttributeTree(attributes);
|
||||
_typedOwner.VersionContextMenuButton.Attributes?.AppendToAttributeTree(attributes);
|
||||
}
|
||||
|
||||
private void InitialiseAttributes()
|
||||
{
|
||||
if (_typedOwner.ProjectContextMenuButton.Attributes == null)
|
||||
{
|
||||
_typedOwner.ProjectContextMenuButton.Attributes = new GhContextMenuButtonAttributes(
|
||||
_typedOwner.ProjectContextMenuButton
|
||||
)
|
||||
{
|
||||
Parent = this,
|
||||
};
|
||||
}
|
||||
|
||||
if (_typedOwner.ModelContextMenuButton.Attributes == null)
|
||||
{
|
||||
_typedOwner.ModelContextMenuButton.Attributes = new GhContextMenuButtonAttributes(
|
||||
_typedOwner.ModelContextMenuButton
|
||||
)
|
||||
{
|
||||
Parent = this,
|
||||
Pivot = Pivot
|
||||
};
|
||||
}
|
||||
|
||||
if (_typedOwner.VersionContextMenuButton.Attributes == null)
|
||||
{
|
||||
_typedOwner.VersionContextMenuButton.Attributes = new GhContextMenuButtonAttributes(
|
||||
_typedOwner.VersionContextMenuButton
|
||||
)
|
||||
{
|
||||
Parent = this,
|
||||
Pivot = Pivot
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Layout()
|
||||
{
|
||||
base.Layout();
|
||||
var baseRec = GH_Convert.ToRectangle(Bounds);
|
||||
baseRec.Height += 26 * 3;
|
||||
|
||||
var btnRec = baseRec;
|
||||
btnRec.Y = baseRec.Bottom - 26 * 3;
|
||||
btnRec.Height = 26;
|
||||
btnRec.Inflate(-2, -2);
|
||||
|
||||
var btnRec2 = btnRec;
|
||||
btnRec2.Y = btnRec.Bottom + 2;
|
||||
|
||||
var btnRec3 = btnRec;
|
||||
btnRec3.Y = btnRec2.Bottom + 2;
|
||||
|
||||
Bounds = baseRec;
|
||||
InitialiseAttributes();
|
||||
// Both pivot and bounds require updating to proper render buttons on location
|
||||
_typedOwner.ProjectContextMenuButton.Attributes.Pivot = btnRec.Location;
|
||||
_typedOwner.ProjectContextMenuButton.Attributes.Bounds = btnRec;
|
||||
_typedOwner.ModelContextMenuButton.Attributes.Pivot = btnRec2.Location;
|
||||
_typedOwner.ModelContextMenuButton.Attributes.Bounds = btnRec2;
|
||||
_typedOwner.VersionContextMenuButton.Attributes.Pivot = btnRec3.Location;
|
||||
_typedOwner.VersionContextMenuButton.Attributes.Bounds = btnRec3;
|
||||
}
|
||||
|
||||
protected override void Render(GH_Canvas canvas, Graphics graphics, GH_CanvasChannel channel)
|
||||
{
|
||||
base.Render(canvas, graphics, channel);
|
||||
// Draw custom buttons and dropdowns
|
||||
|
||||
_typedOwner.ProjectContextMenuButton.Attributes.RenderToCanvas(canvas, channel);
|
||||
_typedOwner.ModelContextMenuButton.Attributes.RenderToCanvas(canvas, channel);
|
||||
_typedOwner.VersionContextMenuButton.Attributes.RenderToCanvas(canvas, channel);
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.Grasshopper8.HostApp;
|
||||
using Speckle.Connectors.Grasshopper8.Parameters;
|
||||
using Speckle.Sdk.Models;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.Properties;
|
||||
|
||||
[Guid("F9418610-ACAE-4417-B010-19EBEA6A121F")]
|
||||
public class CreateSpeckleObject : GH_Component
|
||||
{
|
||||
public CreateSpeckleObject()
|
||||
: base(
|
||||
"Create Speckle Object",
|
||||
"CSO",
|
||||
"Creates a Speckle Object",
|
||||
ComponentCategories.PRIMARY_RIBBON,
|
||||
ComponentCategories.OBJECTS
|
||||
) { }
|
||||
|
||||
public override Guid ComponentGuid => GetType().GUID;
|
||||
|
||||
protected override Bitmap Icon => BitmapBuilder.CreateCircleIconBitmap("cO");
|
||||
|
||||
protected override void RegisterInputParams(GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddGenericParameter("Geometry", "G", "The geometry of the new Speckle Object", GH_ParamAccess.item);
|
||||
|
||||
pManager.AddTextParameter("Name", "N", "Name of the new Speckle Object", GH_ParamAccess.item);
|
||||
Params.Input[1].Optional = true;
|
||||
|
||||
pManager.AddParameter(
|
||||
new SpecklePropertyGroupParam(),
|
||||
"Properties",
|
||||
"P",
|
||||
"The properties of the new Speckle Object",
|
||||
GH_ParamAccess.item
|
||||
);
|
||||
Params.Input[2].Optional = true;
|
||||
|
||||
// TODO: add render material and color
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
|
||||
{
|
||||
pManager.AddGenericParameter("Speckle Object", "SO", "The created Speckle Object", GH_ParamAccess.item);
|
||||
}
|
||||
|
||||
protected override void SolveInstance(IGH_DataAccess da)
|
||||
{
|
||||
object gooGeometry = new();
|
||||
da.GetData(0, ref gooGeometry);
|
||||
GeometryBase geometry = ((IGH_GeometricGoo)gooGeometry).GeometricGooToGeometryBase();
|
||||
|
||||
string name = "";
|
||||
da.GetData(1, ref name);
|
||||
|
||||
SpecklePropertyGroupGoo properties = new();
|
||||
da.GetData(2, ref properties);
|
||||
|
||||
// convert the properties
|
||||
Dictionary<string, object?> props = new();
|
||||
properties.CastTo(ref props);
|
||||
|
||||
// convert the geometries
|
||||
Base converted = ToSpeckleConversionContext.ToSpeckleConverter.Convert(geometry);
|
||||
|
||||
Objects.Data.DataObject grasshopperObject =
|
||||
new()
|
||||
{
|
||||
name = name,
|
||||
displayValue = new() { converted },
|
||||
properties = props
|
||||
};
|
||||
|
||||
SpeckleObjectWrapper so =
|
||||
new()
|
||||
{
|
||||
Base = grasshopperObject,
|
||||
GeometryBase = geometry,
|
||||
Properties = properties,
|
||||
Name = name
|
||||
};
|
||||
|
||||
da.SetData(0, new SpeckleObjectWrapperGoo(so));
|
||||
}
|
||||
}
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
using Grasshopper;
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Data;
|
||||
using Grasshopper.Kernel.Parameters;
|
||||
using Speckle.Connectors.Grasshopper8.Parameters;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.Properties;
|
||||
|
||||
public class FilterPropertiesByPropertyGroupPaths : GH_Component, IGH_VariableParameterComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the unique ID for this component. Do not change this ID after release.
|
||||
/// </summary>
|
||||
public override Guid ComponentGuid => new Guid("BF517D60-B853-4C61-9574-AD8A718B995B");
|
||||
|
||||
public FilterPropertiesByPropertyGroupPaths()
|
||||
: base(
|
||||
"FilterPropertiesByPropertyGroupPaths",
|
||||
"pgF",
|
||||
"Filters object properties by their property group path",
|
||||
"Speckle",
|
||||
"Properties"
|
||||
) { }
|
||||
|
||||
protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
|
||||
{
|
||||
pManager.AddParameter(
|
||||
new SpeckleObjectParam(),
|
||||
"Objects",
|
||||
"O",
|
||||
"Speckle Objects to filter properties from",
|
||||
GH_ParamAccess.list
|
||||
);
|
||||
pManager.AddTextParameter("Paths", "P", "Property Group paths to filter by", GH_ParamAccess.list);
|
||||
}
|
||||
|
||||
protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
|
||||
{
|
||||
// pManager.AddParameter( new SpecklePropertyParam(), "Properties", "P", "The properties of the selected Object", GH_ParamAccess.tree );
|
||||
}
|
||||
|
||||
protected override void SolveInstance(IGH_DataAccess da)
|
||||
{
|
||||
List<string> paths = new();
|
||||
da.GetDataList(1, paths);
|
||||
|
||||
if (paths.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<SpeckleObjectWrapperGoo> objectWrapperGoos = new();
|
||||
da.GetDataList(0, objectWrapperGoos);
|
||||
|
||||
if (objectWrapperGoos.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// we're creating an output param for every property path selected
|
||||
// we're creating a branch in the output tree for every object for that property
|
||||
|
||||
List<OutputParamWrapper> outputParams = new();
|
||||
foreach (string path in paths)
|
||||
{
|
||||
// create the output for this path
|
||||
DataTree<object?> paramResult = new();
|
||||
Param_GenericObject param =
|
||||
new()
|
||||
{
|
||||
Name = path,
|
||||
NickName = path,
|
||||
Access = GH_ParamAccess.tree
|
||||
};
|
||||
|
||||
// get the branch and property value for each input object
|
||||
for (int i = 0; i < objectWrapperGoos.Count; i++)
|
||||
{
|
||||
// create the result branch for this object
|
||||
SpeckleObjectWrapperGoo objectGoo = objectWrapperGoos[i];
|
||||
GH_Path objectPath = new GH_Path(i);
|
||||
|
||||
SpecklePropertyGroupGoo properties = objectGoo.Value.Properties;
|
||||
if (properties.Value.Count == 0)
|
||||
{
|
||||
paramResult.Add(null, objectPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
SpecklePropertyGoo objectProperty = FindProperty(properties, path);
|
||||
paramResult.Add(string.IsNullOrEmpty((string)objectProperty.Value) ? null : objectProperty.Value, objectPath);
|
||||
}
|
||||
|
||||
outputParams.Add(new OutputParamWrapper(param, paramResult));
|
||||
}
|
||||
|
||||
if (da.Iteration == 0 && OutputMismatch(outputParams))
|
||||
{
|
||||
OnPingDocument()
|
||||
.ScheduleSolution(
|
||||
5,
|
||||
_ =>
|
||||
{
|
||||
CreateOutputs(outputParams);
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < outputParams.Count; i++)
|
||||
{
|
||||
var outParam = Params.Output[i];
|
||||
var outParamWrapper = outputParams[i];
|
||||
switch (outParam.Access)
|
||||
{
|
||||
case GH_ParamAccess.item:
|
||||
da.SetData(i, outParamWrapper.Values);
|
||||
break;
|
||||
case GH_ParamAccess.tree:
|
||||
da.SetDataTree(i, (DataTree<object?>)outParamWrapper.Values);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private SpecklePropertyGoo FindProperty(SpecklePropertyGroupGoo root, string unifiedPath)
|
||||
{
|
||||
if (!root.Value.TryGetValue(unifiedPath, out SpecklePropertyGoo currentGoo))
|
||||
{
|
||||
return new() { Path = unifiedPath, Value = "" };
|
||||
}
|
||||
|
||||
return currentGoo;
|
||||
}
|
||||
|
||||
private bool OutputMismatch(List<OutputParamWrapper> outputParams)
|
||||
{
|
||||
if (Params.Output.Count != outputParams.Count)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var count = 0;
|
||||
foreach (var newParam in outputParams)
|
||||
{
|
||||
var oldParam = Params.Output[count];
|
||||
if (
|
||||
oldParam.NickName != newParam.Param.NickName
|
||||
|| oldParam.Name != newParam.Param.Name
|
||||
|| oldParam.Access != newParam.Param.Access
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void CreateOutputs(List<OutputParamWrapper> outputParams)
|
||||
{
|
||||
// TODO: better, nicer handling of creation/removal
|
||||
while (Params.Output.Count > 0)
|
||||
{
|
||||
Params.UnregisterOutputParameter(Params.Output[^1]);
|
||||
}
|
||||
|
||||
foreach (var newParam in outputParams)
|
||||
{
|
||||
var param = new Param_GenericObject
|
||||
{
|
||||
Name = newParam.Param.Name,
|
||||
NickName = newParam.Param.NickName,
|
||||
MutableNickName = false,
|
||||
Access = newParam.Param.Access
|
||||
};
|
||||
Params.RegisterOutputParam(param);
|
||||
}
|
||||
|
||||
Params.OnParametersChanged();
|
||||
VariableParameterMaintenance();
|
||||
ExpireSolution(false);
|
||||
}
|
||||
|
||||
public bool CanInsertParameter(GH_ParameterSide side, int index) => false;
|
||||
|
||||
public bool CanRemoveParameter(GH_ParameterSide side, int index) => false;
|
||||
|
||||
public IGH_Param CreateParameter(GH_ParameterSide side, int index)
|
||||
{
|
||||
var myParam = new Param_GenericObject
|
||||
{
|
||||
Name = GH_ComponentParamServer.InventUniqueNickname("ABCD", Params.Input),
|
||||
MutableNickName = true,
|
||||
Optional = true
|
||||
};
|
||||
myParam.NickName = myParam.Name;
|
||||
return myParam;
|
||||
}
|
||||
|
||||
public bool DestroyParameter(GH_ParameterSide side, int index) => side == GH_ParameterSide.Output;
|
||||
|
||||
public void VariableParameterMaintenance() { }
|
||||
}
|
||||
|
||||
public record OutputParamWrapper(Param_GenericObject Param, object Values);
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Grasshopper.Rhinoceros.Model;
|
||||
using Speckle.Connectors.Common.Extensions;
|
||||
using Speckle.Connectors.Grasshopper8.Components.BaseComponents;
|
||||
using Speckle.Connectors.Grasshopper8.Parameters;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Components.Properties;
|
||||
|
||||
public class PropertyGroupPathsSelector : ValueSet<IGH_Goo>
|
||||
{
|
||||
public PropertyGroupPathsSelector()
|
||||
: base(
|
||||
"Property Group Paths Selector",
|
||||
"Paths",
|
||||
"Allows you to select a set of property group paths for filtering",
|
||||
"Speckle",
|
||||
"Properties"
|
||||
) { }
|
||||
|
||||
public override Guid ComponentGuid => new Guid("8882BE3A-81F1-4416-B420-58D69E4CC8F1");
|
||||
|
||||
protected override void LoadVolatileData()
|
||||
{
|
||||
var objectPropertyGroups = VolatileData
|
||||
.AllData(true)
|
||||
.OfType<SpeckleObjectWrapperGoo>()
|
||||
.Select(goo => goo.Value.Properties.Value)
|
||||
.ToList();
|
||||
|
||||
// support model objects direct piping also
|
||||
if (objectPropertyGroups.Count != VolatileData.DataCount)
|
||||
{
|
||||
var modelObjects = VolatileData
|
||||
.AllData(true)
|
||||
.OfType<ModelObject>()
|
||||
.Select(mo => new SpeckleObjectWrapperGoo(mo).Value.Properties.Value)
|
||||
.ToList();
|
||||
objectPropertyGroups.AddRange(modelObjects);
|
||||
}
|
||||
|
||||
if (objectPropertyGroups.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var paths = GetPropertyPaths(objectPropertyGroups);
|
||||
m_data.AppendRange(paths.Select(s => new GH_String(s)));
|
||||
}
|
||||
|
||||
private static List<string> GetPropertyPaths(List<Dictionary<string, SpecklePropertyGoo>> objectPropertyGroups)
|
||||
{
|
||||
var result = new HashSet<string>();
|
||||
foreach (var dict in objectPropertyGroups)
|
||||
{
|
||||
result.AddRange(
|
||||
dict.Keys.Where(k => !(k.EndsWith(".name") || k.EndsWith(".units") || k.EndsWith(".internalDefinitionName")))
|
||||
);
|
||||
}
|
||||
return result.ToList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
using System.Drawing.Drawing2D;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.HostApp;
|
||||
|
||||
public static class BitmapBuilder
|
||||
{
|
||||
public static Bitmap CreateSquareIconBitmap(string text, int width = 24, int height = 24)
|
||||
{
|
||||
Bitmap bitmap = new(width, height);
|
||||
using Graphics graphics = Graphics.FromImage(bitmap);
|
||||
|
||||
// Enable high-quality rendering
|
||||
graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
|
||||
// Set background to transparent
|
||||
graphics.Clear(Color.Transparent);
|
||||
|
||||
// Rectangle with a 1px offset
|
||||
Rectangle squareRect = new(1, 1, width - 2, height - 2);
|
||||
|
||||
using (Brush blueBrush = new SolidBrush(Color.Blue))
|
||||
{
|
||||
graphics.FillRectangle(blueBrush, squareRect);
|
||||
}
|
||||
|
||||
// Draw white letters in the center
|
||||
using (Font font = new("Arial", 8, FontStyle.Bold, GraphicsUnit.Pixel))
|
||||
using (Brush whiteBrush = new SolidBrush(Color.White))
|
||||
{
|
||||
StringFormat format = new() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
|
||||
|
||||
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
|
||||
graphics.DrawString(text, font, whiteBrush, new RectangleF(1, 1, width - 2, height - 2), format);
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public static Bitmap CreateCircleIconBitmap(string text, int width = 24, int height = 24)
|
||||
{
|
||||
Bitmap bitmap = new(width, height);
|
||||
using Graphics graphics = Graphics.FromImage(bitmap);
|
||||
|
||||
// Enable high-quality rendering
|
||||
graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
|
||||
// Set background to transparent
|
||||
graphics.Clear(Color.Transparent);
|
||||
|
||||
// Rectangle with a 1px offset
|
||||
Rectangle squareRect = new(1, 1, width - 2, height - 2);
|
||||
|
||||
using (Brush blueBrush = new SolidBrush(Color.Blue))
|
||||
{
|
||||
graphics.FillEllipse(blueBrush, squareRect);
|
||||
}
|
||||
|
||||
// Draw white letters in the center
|
||||
using (Font font = new("Arial", 8, FontStyle.Bold, GraphicsUnit.Pixel))
|
||||
using (Brush whiteBrush = new SolidBrush(Color.White))
|
||||
{
|
||||
StringFormat format = new() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
|
||||
|
||||
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
|
||||
graphics.DrawString(text, font, whiteBrush, new RectangleF(1, 1, width - 2, height - 2), format);
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public static Bitmap CreateHexagonalBitmap(string text, int width = 24, int height = 24)
|
||||
{
|
||||
Bitmap bitmap = new(width, height);
|
||||
using Graphics graphics = Graphics.FromImage(bitmap);
|
||||
|
||||
// Enable high-quality rendering
|
||||
graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
|
||||
// Set background to transparent
|
||||
graphics.Clear(Color.Transparent);
|
||||
|
||||
// Calculate hexagon points centered within the bitmap
|
||||
float side = (width - 2) / 2.236f; // 2.236f approximates 4 / √3 for regular hex dimensions
|
||||
float h = side * (float)Math.Sqrt(3) / 2;
|
||||
float centerX = width / 2f;
|
||||
float centerY = height / 2f;
|
||||
|
||||
Point[] hexagonPoints =
|
||||
[
|
||||
new((int)(centerX - side / 2), (int)(centerY - h)),
|
||||
new((int)(centerX + side / 2), (int)(centerY - h)),
|
||||
new((int)(centerX + side), (int)centerY),
|
||||
new((int)(centerX + side / 2), (int)(centerY + h)),
|
||||
new((int)(centerX - side / 2), (int)(centerY + h)),
|
||||
new((int)(centerX - side), (int)centerY)
|
||||
];
|
||||
|
||||
using (Brush blueBrush = new SolidBrush(Color.Blue))
|
||||
{
|
||||
graphics.FillPolygon(blueBrush, hexagonPoints);
|
||||
}
|
||||
|
||||
// Draw white letters in the center
|
||||
using Font font = new("Monospace", 10, FontStyle.Bold, GraphicsUnit.Pixel);
|
||||
using Brush whiteBrush = new SolidBrush(Color.White);
|
||||
StringFormat format = new() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
|
||||
|
||||
graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
|
||||
graphics.DrawString(text, font, whiteBrush, new RectangleF(0, 1, width, height), format);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.HostApp.Extras;
|
||||
|
||||
/// <summary>
|
||||
/// Provides Debounce() and Throttle() methods.
|
||||
/// Use these methods to ensure that events aren't handled too frequently.
|
||||
///
|
||||
/// Throttle() ensures that events are throttled by the interval specified.
|
||||
/// Only the last event in the interval sequence of events fires.
|
||||
///
|
||||
/// Debounce() fires an event only after the specified interval has passed
|
||||
/// in which no other pending event has fired. Only the last event in the
|
||||
/// sequence is fired.
|
||||
/// </summary>
|
||||
public class DebounceDispatcher
|
||||
{
|
||||
private DispatcherTimer? _timer;
|
||||
private DateTime TimerStarted { get; set; } = DateTime.UtcNow.AddYears(-1);
|
||||
|
||||
/// <summary>
|
||||
/// Debounce an event by resetting the event timeout every time the event is
|
||||
/// fired. The behavior is that the Action passed is fired only after events
|
||||
/// stop firing for the given timeout period.
|
||||
///
|
||||
/// Use Debounce when you want events to fire only after events stop firing
|
||||
/// after the given interval timeout period.
|
||||
///
|
||||
/// Wrap the logic you would normally use in your event code into
|
||||
/// the Action you pass to this method to debounce the event.
|
||||
/// Example: https://gist.github.com/RickStrahl/0519b678f3294e27891f4d4f0608519a
|
||||
/// </summary>
|
||||
/// <param name="interval">Timeout in Milliseconds</param>
|
||||
/// <param name="action">Action<object> to fire when debounced event fires</object></param>
|
||||
/// <param name="param">optional parameter</param>
|
||||
/// <param name="priority">optional priorty for the dispatcher</param>
|
||||
/// <param name="disp">optional dispatcher. If not passed or null CurrentDispatcher is used.</param>
|
||||
public void Debounce(
|
||||
int interval,
|
||||
Action<object?> action,
|
||||
object? param = null,
|
||||
DispatcherPriority priority = DispatcherPriority.ApplicationIdle,
|
||||
Dispatcher? disp = null
|
||||
)
|
||||
{
|
||||
// kill pending timer and pending ticks
|
||||
_timer?.Stop();
|
||||
_timer = null;
|
||||
|
||||
if (disp == null)
|
||||
{
|
||||
disp = Dispatcher.CurrentDispatcher;
|
||||
}
|
||||
|
||||
// timer is recreated for each event and effectively
|
||||
// resets the timeout. Action only fires after timeout has fully
|
||||
// elapsed without other events firing in between
|
||||
_timer = new DispatcherTimer(
|
||||
TimeSpan.FromMilliseconds(interval),
|
||||
priority,
|
||||
(s, e) =>
|
||||
{
|
||||
if (_timer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_timer?.Stop();
|
||||
_timer = null;
|
||||
action.Invoke(param);
|
||||
},
|
||||
disp
|
||||
);
|
||||
|
||||
_timer.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method throttles events by allowing only 1 event to fire for the given
|
||||
/// timeout period. Only the last event fired is handled - all others are ignored.
|
||||
/// Throttle will fire events every timeout ms even if additional events are pending.
|
||||
///
|
||||
/// Use Throttle where you need to ensure that events fire at given intervals.
|
||||
/// </summary>
|
||||
/// <param name="interval">Timeout in Milliseconds</param>
|
||||
/// <param name="action">Action<object> to fire when debounced event fires</object></param>
|
||||
/// <param name="param">optional parameter</param>
|
||||
/// <param name="priority">optional priorty for the dispatcher</param>
|
||||
/// <param name="disp">optional dispatcher. If not passed or null CurrentDispatcher is used.</param>
|
||||
public void Throttle(
|
||||
int interval,
|
||||
Action<object?> action,
|
||||
object? param = null,
|
||||
DispatcherPriority priority = DispatcherPriority.ApplicationIdle,
|
||||
Dispatcher? disp = null
|
||||
)
|
||||
{
|
||||
// kill pending timer and pending ticks
|
||||
_timer?.Stop();
|
||||
_timer = null;
|
||||
|
||||
if (disp == null)
|
||||
{
|
||||
disp = Dispatcher.CurrentDispatcher;
|
||||
}
|
||||
|
||||
var curTime = DateTime.UtcNow;
|
||||
|
||||
// if timeout is not up yet - adjust timeout to fire
|
||||
// with potentially new Action parameters
|
||||
if (curTime.Subtract(TimerStarted).TotalMilliseconds < interval)
|
||||
{
|
||||
interval -= (int)curTime.Subtract(TimerStarted).TotalMilliseconds;
|
||||
}
|
||||
|
||||
_timer = new DispatcherTimer(
|
||||
TimeSpan.FromMilliseconds(interval),
|
||||
priority,
|
||||
(s, e) =>
|
||||
{
|
||||
if (_timer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_timer?.Stop();
|
||||
_timer = null;
|
||||
action.Invoke(param);
|
||||
},
|
||||
disp
|
||||
);
|
||||
|
||||
_timer.Start();
|
||||
TimerStarted = curTime;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Speckle.Connectors.Grasshopper8.HostApp;
|
||||
|
||||
public static class Constants
|
||||
{
|
||||
public const string LAYER_PATH_DELIMITER = "::";
|
||||
public const string PROPERTY_PATH_DELIMITER = ".";
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
using Grasshopper;
|
||||
using Grasshopper.Kernel;
|
||||
using Grasshopper.Kernel.Data;
|
||||
using Grasshopper.Kernel.Types;
|
||||
using Rhino;
|
||||
using Rhino.Geometry;
|
||||
using Speckle.DoubleNumerics;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Common;
|
||||
using Speckle.Sdk.Common.Exceptions;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.HostApp;
|
||||
|
||||
public static class GrasshopperHelpers
|
||||
{
|
||||
public static string ToSpeckleString(this UnitSystem unitSystem)
|
||||
{
|
||||
switch (unitSystem)
|
||||
{
|
||||
case UnitSystem.None:
|
||||
return Units.Meters;
|
||||
case UnitSystem.Millimeters:
|
||||
return Units.Millimeters;
|
||||
case UnitSystem.Centimeters:
|
||||
return Units.Centimeters;
|
||||
case UnitSystem.Meters:
|
||||
return Units.Meters;
|
||||
case UnitSystem.Kilometers:
|
||||
return Units.Kilometers;
|
||||
case UnitSystem.Inches:
|
||||
return Units.Inches;
|
||||
case UnitSystem.Feet:
|
||||
return Units.Feet;
|
||||
case UnitSystem.Yards:
|
||||
return Units.Yards;
|
||||
case UnitSystem.Miles:
|
||||
return Units.Miles;
|
||||
case UnitSystem.Unset:
|
||||
return Units.Meters;
|
||||
default:
|
||||
throw new UnitNotSupportedException($"The Unit System \"{unitSystem}\" is unsupported.");
|
||||
}
|
||||
}
|
||||
|
||||
public static Transform MatrixToTransform(Matrix4x4 matrix, string units)
|
||||
{
|
||||
var currentDoc = RhinoDoc.ActiveDoc; // POC: too much right now to interface around
|
||||
var conversionFactor = Units.GetConversionFactor(units, currentDoc.ModelUnitSystem.ToSpeckleString());
|
||||
|
||||
var t = Transform.Identity;
|
||||
t.M00 = matrix.M11;
|
||||
t.M01 = matrix.M12;
|
||||
t.M02 = matrix.M13;
|
||||
t.M03 = matrix.M14 * conversionFactor;
|
||||
|
||||
t.M10 = matrix.M21;
|
||||
t.M11 = matrix.M22;
|
||||
t.M12 = matrix.M23;
|
||||
t.M13 = matrix.M24 * conversionFactor;
|
||||
|
||||
t.M20 = matrix.M31;
|
||||
t.M21 = matrix.M32;
|
||||
t.M22 = matrix.M33;
|
||||
t.M23 = matrix.M34 * conversionFactor;
|
||||
|
||||
t.M30 = matrix.M41;
|
||||
t.M31 = matrix.M42;
|
||||
t.M32 = matrix.M43;
|
||||
t.M33 = matrix.M44;
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to cast the goo to a geometry base object.
|
||||
/// </summary>
|
||||
/// <param name="geoGeo"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="SpeckleException">If it fails to cast</exception>
|
||||
public static GeometryBase GeometricGooToGeometryBase(this IGH_GeometricGoo geoGeo)
|
||||
{
|
||||
var value = geoGeo.GetType().GetProperty("Value")?.GetValue(geoGeo);
|
||||
switch (value)
|
||||
{
|
||||
case GeometryBase gb:
|
||||
return gb;
|
||||
case Point3d pt:
|
||||
return new Rhino.Geometry.Point(pt);
|
||||
case Line ln:
|
||||
return new LineCurve(ln);
|
||||
case Rectangle3d rec:
|
||||
return rec.ToNurbsCurve();
|
||||
case Circle c:
|
||||
return new ArcCurve(c);
|
||||
case Arc ac:
|
||||
return new ArcCurve(ac);
|
||||
case Ellipse el:
|
||||
return el.ToNurbsCurve();
|
||||
case Sphere sp:
|
||||
return sp.ToBrep();
|
||||
}
|
||||
|
||||
throw new SpeckleException("Failed to cast IGH_GeometricGoo to geometry base");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a tree based of a string that encodes the grasshopper topology.
|
||||
/// </summary>
|
||||
/// <param name="topology"></param>
|
||||
/// <param name="subset"></param>
|
||||
/// <returns></returns>
|
||||
public static DataTree<object> CreateDataTreeFromTopologyAndItems(string topology, System.Collections.IList subset)
|
||||
{
|
||||
var tree = new DataTree<object>();
|
||||
var treeTopo = topology.Split(' ');
|
||||
int subsetCount = 0;
|
||||
foreach (var branch in treeTopo)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(branch))
|
||||
{
|
||||
var branchTopo = branch.Split('-')[0].Split(';');
|
||||
var branchIndexes = new List<int>();
|
||||
foreach (var t in branchTopo)
|
||||
{
|
||||
branchIndexes.Add(Convert.ToInt32(t));
|
||||
}
|
||||
|
||||
var elCount = Convert.ToInt32(branch.Split('-')[1]);
|
||||
var myPath = new GH_Path(branchIndexes.ToArray());
|
||||
|
||||
for (int i = 0; i < elCount; i++)
|
||||
{
|
||||
tree.EnsurePath(myPath).Add(new Grasshopper.Kernel.Types.GH_ObjectWrapper(subset[subsetCount + i]));
|
||||
}
|
||||
|
||||
subsetCount += elCount;
|
||||
}
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encodes a tree topology into an exhaustive string which can be used to recreate it using
|
||||
/// <see cref="CreateDataTreeFromTopologyAndItems"/>.
|
||||
/// </summary>
|
||||
/// <param name="param"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetParamTopology(IGH_Param param)
|
||||
{
|
||||
string topology = "";
|
||||
foreach (GH_Path myPath in param.VolatileData.Paths)
|
||||
{
|
||||
topology += myPath.ToString(false) + "-" + param.VolatileData.get_Branch(myPath).Count + " ";
|
||||
}
|
||||
return topology;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Api.GraphQL.Models;
|
||||
using Speckle.Sdk.Common;
|
||||
using Version = Speckle.Sdk.Api.GraphQL.Models.Version;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.HostApp;
|
||||
|
||||
public abstract record SpeckleUrlModelResource(string Server, string ProjectId)
|
||||
{
|
||||
public abstract Task<ReceiveInfo> GetReceiveInfo(Client client, CancellationToken cancellationToken = default);
|
||||
|
||||
public abstract Task<SendInfo> GetSendInfo(Client client, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
public record SpeckleUrlLatestModelVersionResource(string Server, string ProjectId, string ModelId)
|
||||
: SpeckleUrlModelResource(Server, ProjectId)
|
||||
{
|
||||
public override async Task<ReceiveInfo> GetReceiveInfo(Client client, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Project project = await client.Project.Get(ProjectId, cancellationToken).ConfigureAwait(false);
|
||||
ModelWithVersions model = await client
|
||||
.Model.GetWithVersions(ModelId, ProjectId, 1, null, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
Version version = model.versions.items[0];
|
||||
|
||||
var info = new ReceiveInfo(
|
||||
client.Account.id,
|
||||
new Uri(Server),
|
||||
ProjectId,
|
||||
project.name,
|
||||
ModelId,
|
||||
model.name,
|
||||
version.id,
|
||||
version.sourceApplication.NotNull()
|
||||
);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public override async Task<SendInfo> GetSendInfo(Client client, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// We don't care about the return info, we just want to be sure we have access and everything exists.
|
||||
await client.Project.Get(ProjectId, cancellationToken).ConfigureAwait(false);
|
||||
await client.Model.Get(ModelId, ProjectId, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return new SendInfo(
|
||||
client.Account.id,
|
||||
new Uri(Server),
|
||||
ProjectId,
|
||||
ModelId,
|
||||
"Grasshopper8" // TODO: Grab from the right place!
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public record SpeckleUrlModelVersionResource(string Server, string ProjectId, string ModelId, string VersionId)
|
||||
: SpeckleUrlModelResource(Server, ProjectId)
|
||||
{
|
||||
public override async Task<ReceiveInfo> GetReceiveInfo(Client client, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Project project = await client.Project.Get(ProjectId, cancellationToken).ConfigureAwait(false);
|
||||
Model model = await client.Model.Get(ModelId, ProjectId, cancellationToken).ConfigureAwait(false);
|
||||
Version version = await client.Version.Get(VersionId, ProjectId, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var info = new ReceiveInfo(
|
||||
client.Account.id,
|
||||
new Uri(Server),
|
||||
ProjectId,
|
||||
project.name,
|
||||
ModelId,
|
||||
model.name,
|
||||
VersionId,
|
||||
version.sourceApplication.NotNull()
|
||||
);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public override async Task<SendInfo> GetSendInfo(Client client, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// We don't care about the return info, we just want to be sure we have access and everything exists.
|
||||
await client.Project.Get(ProjectId, cancellationToken).ConfigureAwait(false);
|
||||
await client.Model.Get(ModelId, ProjectId, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return new SendInfo(
|
||||
client.Account.id,
|
||||
new Uri(Server),
|
||||
ProjectId,
|
||||
ModelId,
|
||||
"Grasshopper8" // TODO: Grab from the right place!
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public record SpeckleUrlModelObjectResource(string Server, string ProjectId, string ObjectId)
|
||||
: SpeckleUrlModelResource(Server, ProjectId)
|
||||
{
|
||||
public override Task<ReceiveInfo> GetReceiveInfo(Client client, CancellationToken cancellationToken = default) =>
|
||||
throw new NotImplementedException("Object Resources are not supported yet");
|
||||
|
||||
public override Task<SendInfo> GetSendInfo(Client client, CancellationToken cancellationToken = default) =>
|
||||
throw new NotImplementedException("Object Resources are not supported yet");
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Speckle.Sdk;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.HostApp;
|
||||
|
||||
public record SpeckleResourceBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The ReGex pattern to determine if a URL's AbsolutePath is a Frontend2 URL or not.
|
||||
/// </summary>
|
||||
private static readonly Regex s_fe2UrlRegex =
|
||||
new(
|
||||
@"/projects/(?<projectId>[\w\d]+)(?:/models/(?<model>[\w\d]+(?:@[\w\d]+)?)(?:,(?<additionalModels>[\w\d]+(?:@[\w\d]+)?))*)?"
|
||||
);
|
||||
|
||||
public static SpeckleUrlModelResource[] FromUrlString(string speckleModel)
|
||||
{
|
||||
var uri = new Uri(speckleModel);
|
||||
var serverUrl = uri.GetLeftPart(UriPartial.Authority);
|
||||
var match = s_fe2UrlRegex.Match(speckleModel);
|
||||
var result = ParseFe2RegexMatch(serverUrl, match);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static SpeckleUrlModelResource[] ParseFe2RegexMatch(string serverUrl, Match match)
|
||||
{
|
||||
var projectId = match.Groups["projectId"];
|
||||
var model = match.Groups["model"];
|
||||
var additionalModels = match.Groups["additionalModels"];
|
||||
|
||||
if (!projectId.Success)
|
||||
{
|
||||
throw new SpeckleException("The provided url is not a valid Speckle url");
|
||||
}
|
||||
|
||||
if (!model.Success)
|
||||
{
|
||||
throw new SpeckleException("The provided url is not pointing to any model in the project.");
|
||||
}
|
||||
|
||||
if (model.Value == "all")
|
||||
{
|
||||
throw new NotSupportedException("Fetching all models is not supported.");
|
||||
}
|
||||
|
||||
if (model.Value.StartsWith("$"))
|
||||
{
|
||||
throw new NotSupportedException("Federation model urls are not supported");
|
||||
}
|
||||
|
||||
var modelRes = GetUrlModelResource(serverUrl, projectId.Value, model.Value);
|
||||
|
||||
var result = new List<SpeckleUrlModelResource> { modelRes };
|
||||
|
||||
if (additionalModels.Success)
|
||||
{
|
||||
foreach (Capture additionalModelsCapture in additionalModels.Captures)
|
||||
{
|
||||
var extraModel = GetUrlModelResource(serverUrl, projectId.Value, additionalModelsCapture.Value);
|
||||
result.Add(extraModel);
|
||||
}
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private static SpeckleUrlModelResource GetUrlModelResource(string serverUrl, string projectId, string modelValue)
|
||||
{
|
||||
if (modelValue.Length == 32)
|
||||
{
|
||||
return new SpeckleUrlModelObjectResource(serverUrl, projectId, modelValue); // Model value is an ObjectID
|
||||
}
|
||||
|
||||
if (!modelValue.Contains('@'))
|
||||
{
|
||||
return new SpeckleUrlLatestModelVersionResource(serverUrl, projectId, modelValue); // Model has no version attached
|
||||
}
|
||||
|
||||
var res = modelValue.Split('@');
|
||||
return new SpeckleUrlModelVersionResource(serverUrl, projectId, res[0], res[1]);
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Rhino;
|
||||
using Speckle.Connectors.Grasshopper8.Registration;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Rhino;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.HostApp;
|
||||
|
||||
/// <summary>
|
||||
/// Handles grasshopper wide converters. We don't need new converters, unless the document changes - this class should handle this (untested).
|
||||
/// </summary>
|
||||
public static class ToSpeckleConversionContext
|
||||
{
|
||||
private static IServiceScope? Scope { get; set; }
|
||||
public static IRootToHostConverter ToHostConverter { get; private set; }
|
||||
public static IRootToSpeckleConverter ToSpeckleConverter { get; private set; }
|
||||
|
||||
static ToSpeckleConversionContext()
|
||||
{
|
||||
RhinoDoc.ActiveDocumentChanged += RhinoDocOnActiveDocumentChanged;
|
||||
InitializeConverters();
|
||||
}
|
||||
|
||||
private static void RhinoDocOnActiveDocumentChanged(object sender, DocumentEventArgs e) => InitializeConverters(); // note: untested, and wrong on mac
|
||||
|
||||
private static void InitializeConverters()
|
||||
{
|
||||
Scope?.Dispose();
|
||||
Scope = PriorityLoader.Container.CreateScope();
|
||||
|
||||
var rhinoConversionSettingsFactory = Scope.ServiceProvider.GetRequiredService<IRhinoConversionSettingsFactory>();
|
||||
Scope
|
||||
.ServiceProvider.GetRequiredService<IConverterSettingsStore<RhinoConversionSettings>>()
|
||||
.Initialize(rhinoConversionSettingsFactory.Create(RhinoDoc.ActiveDoc));
|
||||
|
||||
ToHostConverter = Scope.ServiceProvider.GetService<IRootToHostConverter>();
|
||||
ToSpeckleConverter = Scope.ServiceProvider.GetService<IRootToSpeckleConverter>();
|
||||
}
|
||||
}
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
using Rhino.Geometry;
|
||||
using Speckle.Connectors.Common.Builders;
|
||||
using Speckle.Connectors.Common.Conversion;
|
||||
using Speckle.Connectors.Common.Extensions;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Common.Operations.Receive;
|
||||
using Speckle.Converters.Common;
|
||||
using Speckle.Converters.Rhino;
|
||||
using Speckle.Sdk;
|
||||
using Speckle.Sdk.Logging;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
using Speckle.Sdk.Models.Instances;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Operations.Receive;
|
||||
|
||||
public sealed class GrasshopperReceiveConversionResult : ReceiveConversionResult
|
||||
{
|
||||
public object? Result { get; set; }
|
||||
public Base Source { get; set; }
|
||||
|
||||
public GrasshopperReceiveConversionResult(
|
||||
Status status,
|
||||
Base source,
|
||||
object? result,
|
||||
string? resultId = null,
|
||||
string? resultType = null,
|
||||
Exception? exception = null
|
||||
)
|
||||
: base(status, source, resultId, resultType, exception)
|
||||
{
|
||||
Result = result;
|
||||
Source = source;
|
||||
}
|
||||
}
|
||||
|
||||
public class GrasshopperHostObjectBuilder : IHostObjectBuilder
|
||||
{
|
||||
private readonly IRootToHostConverter _converter;
|
||||
private readonly IConverterSettingsStore<RhinoConversionSettings> _converterSettings;
|
||||
private readonly TraversalContextUnpacker _contextUnpacker;
|
||||
private readonly RootObjectUnpacker _rootObjectUnpacker;
|
||||
private readonly ISdkActivityFactory _activityFactory;
|
||||
|
||||
public GrasshopperHostObjectBuilder(
|
||||
IRootToHostConverter converter,
|
||||
IConverterSettingsStore<RhinoConversionSettings> converterSettings,
|
||||
RootObjectUnpacker rootObjectUnpacker,
|
||||
ISdkActivityFactory activityFactory,
|
||||
TraversalContextUnpacker contextUnpacker
|
||||
)
|
||||
{
|
||||
_converter = converter;
|
||||
_converterSettings = converterSettings;
|
||||
_rootObjectUnpacker = rootObjectUnpacker;
|
||||
_activityFactory = activityFactory;
|
||||
_contextUnpacker = contextUnpacker;
|
||||
}
|
||||
|
||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
public async Task<HostObjectBuilderResult> Build(
|
||||
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
Base rootObject,
|
||||
string projectName,
|
||||
string modelName,
|
||||
IProgress<CardProgress> onOperationProgressed,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
using var activity = _activityFactory.Start("Build");
|
||||
// POC: This is where the top level base-layer name is set. Could be abstracted or injected in the context?
|
||||
var baseLayerName = $"Project {projectName}: Model {modelName}";
|
||||
|
||||
// 1 - Unpack objects and proxies from root commit object
|
||||
|
||||
var unpackedRoot = _rootObjectUnpacker.Unpack(rootObject);
|
||||
|
||||
// 2 - Split atomic objects and instance components with their path
|
||||
var (atomicObjects, instanceComponents) = _rootObjectUnpacker.SplitAtomicObjectsAndInstances(
|
||||
unpackedRoot.ObjectsToConvert
|
||||
);
|
||||
|
||||
var atomicObjectsWithPath = _contextUnpacker.GetAtomicObjectsWithPath(atomicObjects);
|
||||
var instanceComponentsWithPath = _contextUnpacker.GetInstanceComponentsWithPath(instanceComponents);
|
||||
|
||||
// 2.1 - these are not captured by traversal, so we need to re-add them here
|
||||
if (unpackedRoot.DefinitionProxies != null && unpackedRoot.DefinitionProxies.Count > 0)
|
||||
{
|
||||
var transformed = unpackedRoot.DefinitionProxies.Select(proxy =>
|
||||
(Array.Empty<Collection>(), proxy as IInstanceComponent)
|
||||
);
|
||||
instanceComponentsWithPath.AddRange(transformed);
|
||||
}
|
||||
|
||||
// 3 - Bake materials and colors, as they are used later down the line by layers and objects
|
||||
onOperationProgressed.Report(new("Converting materials and colors", null));
|
||||
if (unpackedRoot.RenderMaterialProxies != null)
|
||||
{
|
||||
using var _ = _activityFactory.Start("Render Materials");
|
||||
//_materialBaker.BakeMaterials(unpackedRoot.RenderMaterialProxies, baseLayerName);
|
||||
}
|
||||
|
||||
if (unpackedRoot.ColorProxies != null)
|
||||
{
|
||||
//_colorBaker.ParseColors(unpackedRoot.ColorProxies);
|
||||
}
|
||||
|
||||
// 5 - Convert atomic objects
|
||||
List<ReceiveConversionResult> conversionResults = new();
|
||||
Dictionary<string, List<string>> applicationIdMap = new(); // This map is used in converting blocks in stage 2. keeps track of original app id => resulting new app ids post baking
|
||||
|
||||
int count = 0;
|
||||
using (var _ = _activityFactory.Start("Converting objects"))
|
||||
{
|
||||
foreach (var (path, obj) in atomicObjectsWithPath)
|
||||
{
|
||||
using (var convertActivity = _activityFactory.Start("Converting object"))
|
||||
{
|
||||
onOperationProgressed.Report(new("Converting objects", (double)++count / atomicObjects.Count));
|
||||
try
|
||||
{
|
||||
// 0: get pre-created layer from cache in layer baker
|
||||
// int layerIndex = _layerBaker.GetLayerIndex(path, baseLayerName);
|
||||
|
||||
// 1: create object attributes for baking
|
||||
string name = obj["name"] as string ?? "";
|
||||
|
||||
// 2: convert
|
||||
var result = _converter.Convert(obj);
|
||||
|
||||
// 3: bake
|
||||
if (result is GeometryBase geometryBase)
|
||||
{
|
||||
//var guid = BakeObject(geometryBase, obj, atts);
|
||||
}
|
||||
else if (result is List<GeometryBase> geometryBases) // one to many raw encoding case
|
||||
{
|
||||
foreach (var gb in geometryBases)
|
||||
{
|
||||
//var guid = BakeObject(gb, obj, atts);
|
||||
}
|
||||
}
|
||||
else if (result is IEnumerable<(object, Base)> fallbackConversionResult) // one to many fallback conversion
|
||||
{
|
||||
//var guids = BakeObjectsAsFallbackGroup(fallbackConversionResult, obj, atts, baseLayerName);
|
||||
}
|
||||
|
||||
// 4: log
|
||||
conversionResults.Add(
|
||||
new GrasshopperReceiveConversionResult(Status.SUCCESS, obj, result, null, result.GetType().ToString())
|
||||
);
|
||||
|
||||
// applicationIdMap[obj.applicationId ?? obj.id] = conversionIds;
|
||||
convertActivity?.SetStatus(SdkActivityStatusCode.Ok);
|
||||
}
|
||||
catch (Exception ex) when (!ex.IsFatal())
|
||||
{
|
||||
// TODO: No conversion report yet
|
||||
conversionResults.Add(new GrasshopperReceiveConversionResult(Status.ERROR, obj, null, null, null, ex));
|
||||
convertActivity?.SetStatus(SdkActivityStatusCode.Error);
|
||||
convertActivity?.RecordException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 6 - Convert instances
|
||||
using (var _ = _activityFactory.Start("Converting instances"))
|
||||
{
|
||||
// TODO: No instances yet
|
||||
// var (createdInstanceIds, consumedObjectIds, instanceConversionResults) = await _instanceBaker
|
||||
// .BakeInstances(instanceComponentsWithPath, applicationIdMap, baseLayerName, onOperationProgressed)
|
||||
// .ConfigureAwait(false);
|
||||
|
||||
// TODO: No conversion report yet
|
||||
// conversionResults.RemoveAll(result => result.ResultId != null && consumedObjectIds.Contains(result.ResultId)); // remove all conversion results for atomic objects that have been consumed (POC: not that cool, but prevents problems on object highlighting)
|
||||
// conversionResults.AddRange(instanceConversionResults); // add instance conversion results to our list
|
||||
}
|
||||
|
||||
// 7 - Create groups
|
||||
if (unpackedRoot.GroupProxies is not null)
|
||||
{
|
||||
// TODO: No groups yet
|
||||
// _groupBaker.BakeGroups(unpackedRoot.GroupProxies, applicationIdMap, baseLayerName);
|
||||
}
|
||||
|
||||
return new HostObjectBuilderResult([], conversionResults);
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Logging;
|
||||
using Speckle.Sdk.Api;
|
||||
using Speckle.Sdk.Credentials;
|
||||
using Speckle.Sdk.Logging;
|
||||
using Speckle.Sdk.Models;
|
||||
using Speckle.Sdk.Transports;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Operations.Receive;
|
||||
|
||||
public class GrasshopperReceiveOperation
|
||||
{
|
||||
private readonly AccountService _accountService;
|
||||
private readonly IServerTransportFactory _serverTransportFactory;
|
||||
private readonly IProgressDisplayManager _progressDisplayManager;
|
||||
private readonly ISdkActivityFactory _activityFactory;
|
||||
private readonly IOperations _operations;
|
||||
private readonly IClientFactory _clientFactory;
|
||||
|
||||
public GrasshopperReceiveOperation(
|
||||
AccountService accountService,
|
||||
IServerTransportFactory serverTransportFactory,
|
||||
IProgressDisplayManager progressDisplayManager,
|
||||
ISdkActivityFactory activityFactory,
|
||||
IOperations operations,
|
||||
IClientFactory clientFactory
|
||||
)
|
||||
{
|
||||
_accountService = accountService;
|
||||
_serverTransportFactory = serverTransportFactory;
|
||||
_progressDisplayManager = progressDisplayManager;
|
||||
_activityFactory = activityFactory;
|
||||
_operations = operations;
|
||||
_clientFactory = clientFactory;
|
||||
}
|
||||
|
||||
public async Task<Base> ReceiveCommitObject(
|
||||
ReceiveInfo receiveInfo,
|
||||
IProgress<CardProgress> onOperationProgressed,
|
||||
CancellationToken cancellationToken
|
||||
)
|
||||
{
|
||||
using var execute = _activityFactory.Start("Receive Operation");
|
||||
execute?.SetTag("receiveInfo", receiveInfo);
|
||||
// 2 - Check account exist
|
||||
Account account = _accountService.GetAccountWithServerUrlFallback(receiveInfo.AccountId, receiveInfo.ServerUrl);
|
||||
using Client apiClient = _clientFactory.Create(account);
|
||||
using var userScope = ActivityScope.SetTag(Consts.USER_ID, account.GetHashedEmail());
|
||||
|
||||
var version = await apiClient
|
||||
.Version.Get(receiveInfo.SelectedVersionId, receiveInfo.ProjectId, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
using var transport = _serverTransportFactory.Create(account, receiveInfo.ProjectId);
|
||||
|
||||
double? previousPercentage = null;
|
||||
_progressDisplayManager.Begin();
|
||||
Base commitObject = await _operations
|
||||
.Receive2(
|
||||
new Uri(account.serverInfo.url),
|
||||
receiveInfo.ProjectId,
|
||||
version.referencedObject,
|
||||
account.token,
|
||||
onProgressAction: new PassthroughProgress(args =>
|
||||
{
|
||||
if (args.ProgressEvent == ProgressEvent.CacheCheck || args.ProgressEvent == ProgressEvent.DownloadBytes)
|
||||
{
|
||||
switch (args.ProgressEvent)
|
||||
{
|
||||
case ProgressEvent.CacheCheck:
|
||||
previousPercentage = _progressDisplayManager.CalculatePercentage(args);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!_progressDisplayManager.ShouldUpdate())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (args.ProgressEvent)
|
||||
{
|
||||
case ProgressEvent.CacheCheck:
|
||||
case ProgressEvent.DownloadBytes:
|
||||
onOperationProgressed.Report(new("Checking and Downloading... ", previousPercentage));
|
||||
break;
|
||||
case ProgressEvent.DeserializeObject:
|
||||
onOperationProgressed.Report(new("Deserializing ...", _progressDisplayManager.CalculatePercentage(args)));
|
||||
break;
|
||||
}
|
||||
}),
|
||||
cancellationToken: cancellationToken
|
||||
)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
await apiClient
|
||||
.Version.Received(new(version.id, receiveInfo.ProjectId, receiveInfo.SourceApplication), cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
return commitObject;
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
using Speckle.Connectors.Common.Builders;
|
||||
using Speckle.Connectors.Common.Operations;
|
||||
using Speckle.Connectors.Grasshopper8.Parameters;
|
||||
using Speckle.Sdk.Models.Collections;
|
||||
|
||||
namespace Speckle.Connectors.Grasshopper8.Operations.Send;
|
||||
|
||||
public class GrasshopperRootObjectBuilder() : IRootObjectBuilder<SpeckleCollectionWrapperGoo>
|
||||
{
|
||||
public Task<RootObjectBuilderResult> Build(
|
||||
IReadOnlyList<SpeckleCollectionWrapperGoo> input,
|
||||
SendInfo sendInfo,
|
||||
IProgress<CardProgress> onOperationProgressed,
|
||||
CancellationToken ct = default
|
||||
)
|
||||
{
|
||||
// TODO: Send info is used in other connectors to get the project ID to populate the SendConversionCache
|
||||
Console.WriteLine($"Send Info {sendInfo}");
|
||||
|
||||
// set the input collection name to "Grasshopper Model"
|
||||
var rootCollection = new Collection { name = "Grasshopper model", elements = input[0].Value.Collection.elements };
|
||||
|
||||
// reconstruct the input collection by substituting all of the objectgoos with base
|
||||
var collection = ReplaceAndRebuild(rootCollection);
|
||||
|
||||
// TODO: Not getting any conversion results yet
|
||||
var result = new RootObjectBuilderResult(collection, []);
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unwraps collection wrappers and object wrapppers.
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
/// <returns></returns>
|
||||
private Collection ReplaceAndRebuild(Collection c)
|
||||
{
|
||||
// Iterate over the current collection's elements
|
||||
var myCollection = new Collection() { name = c.name };
|
||||
|
||||
if (c["topology"] is string topology)
|
||||
{
|
||||
myCollection["topology"] = topology;
|
||||
}
|
||||
|
||||
for (int i = 0; i < c.elements.Count; i++)
|
||||
{
|
||||
var element = c.elements[i];
|
||||
if (element is SpeckleCollectionWrapper collectionWrapper)
|
||||
{
|
||||
var newCollection = new Collection
|
||||
{
|
||||
name = collectionWrapper.Collection.name,
|
||||
["topology"] = collectionWrapper.Topology,
|
||||
elements = collectionWrapper.Collection.elements
|
||||
};
|
||||
var unwrapped = ReplaceAndRebuild(newCollection);
|
||||
myCollection.elements.Add(unwrapped);
|
||||
}
|
||||
else if (element is SpeckleObjectWrapper so)
|
||||
{
|
||||
// If it's not a Collection, replace the non-Collection element
|
||||
myCollection.elements.Add(so.Base);
|
||||
}
|
||||
}
|
||||
return myCollection;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
namespace Speckle.Connectors.Grasshopper8.Parameters;
|
||||
|
||||
internal interface ISpeckleGoo { }
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user