diff --git a/.github/workflows/continuous.yml b/.github/workflows/continuous.yml
index d9b93db1..6fa040bf 100644
--- a/.github/workflows/continuous.yml
+++ b/.github/workflows/continuous.yml
@@ -9,6 +9,8 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v1
+ - name: Run tests
+ run: ./build.cmd Test
- name: Run './build.cmd PublishGitHubRelease'
run: ./build.cmd PublishGitHubRelease
env:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b321de6b..a6cd9ad4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,37 @@
This is the Changelog for the OpenProject Revit Add-in. It follows the guidelines described
in https://keepachangelog.com/en/1.0.0/. The versions follow [semantic versioning](https://semver.org/).
+## Unreleased
+
+### Added
+
+- Amended README with information about reporting bugs and other useful information
+
+### Changed
+
+- Viewpoint snapshot data is given in an improper state to the OpenProject instance frontend. The current hack has to be
+ maintained until the related
+ [work package](https://community.openproject.org/projects/bcfier/work_packages/39135/activity) is resolved and the
+ solution deployed.
+
+### Fixed
+
+- Section boxes with infinity values, which can exist after importing viewpoints with less then 6 clipping planes, no
+ longer create invalid viewpoint data when generating a new viewpoint.
+- If there is a newer version of the AddIn released at GitHub, a notification dialog is displayed again
+- When exporting a viewpoint from a Revit view, elements hidden by category are interpreted as hidden. However, applying
+ this viewpoint does not apply the category visibility, but sets all contained elements to hidden, as BCF viewpoints
+ are not aware of Revit categories.
+
+## [2.3.1] - 2021-10-04
+
+### Fixed
+
+- Fixed an issue that led to an exception when installing the Revit Add-In for the very first time
+- Made the loading of the `OpenProject.Configuration.json` more robust, so that missing or outdated keys no longer lead
+ to errors
+- Improved logging and error dialog communication
+
## [2.3.0] - 2021-09-21
### Added
@@ -35,7 +66,7 @@ in https://keepachangelog.com/en/1.0.0/. The versions follow [semantic versionin
- Loading times for viewpoints were slightly improved, on machines without enough capability the asynchronous zoom for
orthogonal viewpoints can take several seconds (see this
[official issue](https://thebuildingcoder.typepad.com/blog/2020/10/save-and-restore-3d-view-camera-settings.html)).
-- An issue was fixed, causing the cursor in input fields in the embedded browser vanishing occasionally.
+- An issue was fixed, causing the cursor in input fields in the embedded browser vanishing occasionally.
## [2.2.5] - 2021-04-16
diff --git a/build/Build.cs b/build/Build.cs
index fb450d23..ddd87f67 100644
--- a/build/Build.cs
+++ b/build/Build.cs
@@ -12,10 +12,13 @@
using Nuke.Common.Utilities.Collections;
using Nuke.GitHub;
using System;
+using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
+using Nuke.Common.Tools.Git;
+using Octokit;
using static Nuke.Common.IO.CompressionTasks;
using static Nuke.Common.IO.FileSystemTasks;
using static Nuke.Common.IO.PathConstruction;
@@ -221,7 +224,7 @@ public static class RepositoryInfo
.Executes(() =>
{
DotNetTest(c => c
- .SetConfiguration("Debug")
+ .SetConfiguration("Debug-2022")
.SetProjectFile(RootDirectory / "test" / "OpenProject.Tests" / "OpenProject.Tests.csproj")
.SetTestAdapterPath(".")
.SetLoggers($"xunit;LogFilePath={OutputDirectory / "testresults.xml"}"));
@@ -281,7 +284,19 @@ public static class RepositoryInfo
SignFilesIfRequirementsMet(OutputDirectory / "OpenProject.Revit.exe");
});
+ static bool ShouldBuildRelease()
+ {
+ const string flag = "[release skip]";
+
+ var message = GitTasks.Git("log -1 --pretty=format:%B", logOutput: false)
+ .Select(output => output.Text)
+ .Aggregate((s, s1) => $"{s}\n{s1}");
+
+ return !message.Contains(flag);
+ }
+
Target PublishGitHubRelease => _ => _
+ .OnlyWhenDynamic(() => ShouldBuildRelease())
.DependsOn(CreateSetup)
.DependsOn(BuildDocumentation)
.Requires(() => GitHubAuthenticationToken)
@@ -290,10 +305,10 @@ public static class RepositoryInfo
var releaseTag = $"v{GitVersion.SemVer}";
var isStableRelease = GitVersion.BranchName.Equals("master") || GitVersion.BranchName.Equals("origin/master") || GitVersion.BranchName.Equals("main") || GitVersion.BranchName.Equals("origin/main");
- var repositoryInfo = GetGitHubRepositoryInfo(GitRepository);
+ var (gitHubOwner, repositoryName) = GetGitHubRepositoryInfo(GitRepository);
var installationFile = GlobFiles(OutputDirectory, "OpenProject.Revit.exe").NotEmpty().Single();
- var artifactPaths = new string[]
+ var artifactPaths = new[]
{
installationFile,
OutputDirectory / "InstallationInstructions.pdf",
@@ -304,8 +319,8 @@ await PublishRelease(x => x
.SetPrerelease(!isStableRelease)
.SetArtifactPaths(artifactPaths)
.SetCommitSha(GitVersion.Sha)
- .SetRepositoryName(repositoryInfo.repositoryName)
- .SetRepositoryOwner(repositoryInfo.gitHubOwner)
+ .SetRepositoryName(repositoryName)
+ .SetRepositoryOwner(gitHubOwner)
.SetTag(releaseTag)
.SetToken(GitHubAuthenticationToken));
});
diff --git a/global.json b/global.json
index 496b7c1a..200e186e 100644
--- a/global.json
+++ b/global.json
@@ -1,5 +1,5 @@
{
"sdk": {
- "version": "5.0.301"
+ "version": "5.0.402"
}
-}
\ No newline at end of file
+}
diff --git a/src/OpenProject.Browser/Models/Release.cs b/src/OpenProject.Browser/Models/Release.cs
new file mode 100644
index 00000000..8187118e
--- /dev/null
+++ b/src/OpenProject.Browser/Models/Release.cs
@@ -0,0 +1,25 @@
+using System;
+
+// ReSharper disable UnusedAutoPropertyAccessor.Global
+// ReSharper disable MemberCanBePrivate.Global
+// ReSharper disable InconsistentNaming
+
+namespace OpenProject.Browser.Models
+{
+ ///
+ /// Class model for deserialization of github release JSON data from the github rest API.
+ ///
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class Release
+ {
+ public string tag_name { private get; set; }
+ public DateTime published_at { private get; set; }
+ public string html_url { private get; set; }
+
+ public Version Version() => Models.Version.Parse(tag_name);
+
+ public DateTime PublishedAt() => published_at;
+
+ public string DownloadUrl() => html_url;
+ }
+}
diff --git a/src/OpenProject.Browser/Models/Version.cs b/src/OpenProject.Browser/Models/Version.cs
new file mode 100644
index 00000000..9c11d416
--- /dev/null
+++ b/src/OpenProject.Browser/Models/Version.cs
@@ -0,0 +1,92 @@
+using System;
+using Serilog;
+
+namespace OpenProject.Browser.Models
+{
+ ///
+ /// Immutable class representing semantic versions.
+ ///
+ public sealed class Version : IComparable
+ {
+ private readonly int _major;
+ private readonly int _minor;
+ private readonly int _patch;
+ private readonly string _suffix;
+
+ private Version(int major, int minor, int patch, string suffix)
+ {
+ _major = major;
+ _minor = minor;
+ _patch = patch;
+ _suffix = suffix;
+ }
+
+ ///
+ /// Parses a string into a semantic version. Returns 'v0.0.0' if an invalid string is given.
+ ///
+ /// The input string
+ /// A semantic version object
+ public static Version Parse(string versionString)
+ {
+ var separators = new[] { '.', '-' };
+ var split = versionString.Split(separators);
+
+ var major = 0;
+ var minor = 0;
+ var patch = 0;
+ var suffix = "";
+
+ try
+ {
+ if (split.Length >= 1)
+ {
+ var str = split[0].StartsWith("v", StringComparison.InvariantCultureIgnoreCase) ? split[0][1..] : split[0];
+ major = int.Parse(str);
+ }
+
+ if (split.Length >= 2)
+ minor = int.Parse(split[1]);
+ if (split.Length >= 3)
+ patch = int.Parse(split[2]);
+ if (split.Length >= 4)
+ suffix = split[3];
+ }
+ catch (FormatException)
+ {
+ Log.Error("{version} is no valid version string.", versionString);
+ return new Version(0, 0, 0, "");
+ }
+
+ return new Version(major, minor, patch, suffix);
+ }
+
+ ///
+ public int CompareTo(Version other)
+ {
+ if (ReferenceEquals(this, other)) return 0;
+ if (ReferenceEquals(null, other)) return 1;
+
+ if (_major > other._major)
+ return 1;
+ if (_major < other._major)
+ return -1;
+
+ if (_minor > other._minor)
+ return 1;
+ if (_minor < other._minor)
+ return -1;
+
+ if (_patch > other._patch)
+ return 1;
+ if (_patch < other._patch)
+ return -1;
+
+ var stringComparison = string.CompareOrdinal(_suffix, other._suffix);
+ return stringComparison > 0 ? 1 : stringComparison < 0 ? -1 : 0;
+ }
+
+ ///
+ public override string ToString() =>
+ _suffix.Length > 0 ? $"v{_major}.{_minor}.{_patch}-{_suffix}" : $"v{_major}.{_minor}.{_patch}";
+ }
+}
diff --git a/src/OpenProject.Browser/Services/ConfigurationHandler.cs b/src/OpenProject.Browser/Services/ConfigurationHandler.cs
index 1c5f8b94..3d592613 100644
--- a/src/OpenProject.Browser/Services/ConfigurationHandler.cs
+++ b/src/OpenProject.Browser/Services/ConfigurationHandler.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.IO;
using Config.Net;
using Newtonsoft.Json;
@@ -25,11 +24,7 @@ static ConfigurationHandler()
}
}
- private static IOpenProjectSettings Settings { get; }
-
- public static bool ShouldEnableDevelopmentTools() => Settings.EnableDevelopmentTools;
-
- public static List LoadAllInstances() => Settings.GetOpenProjectInstances();
+ public static IOpenProjectSettings Settings { get; }
public static void RemoveSavedInstance(string instanceUrl)
{
diff --git a/src/OpenProject.Browser/Services/GitHubService.cs b/src/OpenProject.Browser/Services/GitHubService.cs
new file mode 100644
index 00000000..53412aa4
--- /dev/null
+++ b/src/OpenProject.Browser/Services/GitHubService.cs
@@ -0,0 +1,39 @@
+using System.Net;
+using OpenProject.Browser.Models;
+using OpenProject.Shared;
+using Optional;
+using RestSharp;
+
+namespace OpenProject.Browser.Services
+{
+ public sealed class GitHubService : IGitHubService
+ {
+ private RestClient _client;
+
+ private RestClient Client
+ {
+ get
+ {
+ if (_client != null)
+ return _client;
+
+ _client = new RestClient(@"https://api.github.com/");
+ return _client;
+ }
+ }
+
+ ///
+ public Option GetLatestRelease()
+ {
+ var request =
+ new RestRequest($"repos/{RepositoryInfo.GitHubOwner}/{RepositoryInfo.GitHubRepository}/releases/latest", Method.GET);
+ request.AddHeader("Content-Type", "application/json");
+ request.RequestFormat = DataFormat.Json;
+ request.OnBeforeDeserialization = resp => { resp.ContentType = "application/json"; };
+
+ var response = Client.Execute(request);
+
+ return response.StatusCode == HttpStatusCode.OK ? response.Data.SomeNotNull() : Option.None();
+ }
+ }
+}
diff --git a/src/OpenProject.Browser/Services/Interfaces/IGitHubService.cs b/src/OpenProject.Browser/Services/Interfaces/IGitHubService.cs
new file mode 100644
index 00000000..17ffa0a0
--- /dev/null
+++ b/src/OpenProject.Browser/Services/Interfaces/IGitHubService.cs
@@ -0,0 +1,17 @@
+using OpenProject.Browser.Models;
+using Optional;
+
+namespace OpenProject.Browser.Services
+{
+ ///
+ /// A service for communication to the repository on github.
+ ///
+ public interface IGitHubService
+ {
+ ///
+ /// Get the latest published released of the OpenProject Revit AddIn.
+ ///
+ /// A release object.
+ Option GetLatestRelease();
+ }
+}
diff --git a/src/OpenProject.Browser/Services/MessageHandler.cs b/src/OpenProject.Browser/Services/MessageHandler.cs
index cc8a5294..b36cd97f 100644
--- a/src/OpenProject.Browser/Services/MessageHandler.cs
+++ b/src/OpenProject.Browser/Services/MessageHandler.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics;
using System.Windows;
using Serilog;
@@ -17,5 +18,31 @@ public static void ShowError(Exception exception, string message)
Log.Error(exception, message);
MessageBox.Show(message, "ERROR", MessageBoxButton.OK, MessageBoxImage.Error);
}
+
+ ///
+ /// Displays an update dialog, which indicates that a newer add-in version is available for download.
+ /// User can decide whether to download update or not.
+ ///
+ /// The string representation of the currently installed version.
+ /// The string representation of the new version.
+ /// The release date of the new version.
+ /// The URL where the new version can be downloaded from.
+ public static void ShowUpdateDialog(string oldVersion, string newVersion, DateTime releaseDate, string downloadUrl)
+ {
+ Log.Information("New version {new} available. Currently installed: {old}", newVersion, oldVersion);
+ var messageBoxText = $"A new release '{newVersion}' was released at {releaseDate.ToLongDateString()}. " +
+ $"'{oldVersion}' is currently installed.\nDo you want to download the new version?";
+ MessageBoxResult messageBoxResult = MessageBox.Show(
+ messageBoxText,
+ "New version available",
+ MessageBoxButton.YesNo,
+ MessageBoxImage.Information
+ );
+
+ if (messageBoxResult != MessageBoxResult.Yes) return;
+
+ Process.Start(new ProcessStartInfo("cmd", $"/c start {downloadUrl}") { CreateNoWindow = true });
+ Log.Information("Download of new version {new} started.", newVersion);
+ }
}
}
diff --git a/src/OpenProject.Browser/Services/ServiceProviderConfiguration.cs b/src/OpenProject.Browser/Services/ServiceProviderConfiguration.cs
index fc357c1c..da3506f6 100644
--- a/src/OpenProject.Browser/Services/ServiceProviderConfiguration.cs
+++ b/src/OpenProject.Browser/Services/ServiceProviderConfiguration.cs
@@ -32,6 +32,7 @@ internal static IServiceCollection ConfigureIoCContainer()
services.AddSingleton();
// Interface implementations
+ services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
diff --git a/src/OpenProject.Browser/Settings/IOpenProjectSettings.cs b/src/OpenProject.Browser/Settings/IOpenProjectSettings.cs
index 2ebd9d73..88c5e86e 100644
--- a/src/OpenProject.Browser/Settings/IOpenProjectSettings.cs
+++ b/src/OpenProject.Browser/Settings/IOpenProjectSettings.cs
@@ -5,5 +5,6 @@ public interface IOpenProjectSettings
public bool EnableDevelopmentTools { get; set; }
public string OpenProjectInstances { get; set; }
public string LastVisitedPage { get; set; }
+ public bool CheckForUpdates { get; set; }
}
}
diff --git a/src/OpenProject.Browser/Settings/OpenProject.Configuration.json b/src/OpenProject.Browser/Settings/OpenProject.Configuration.json
index e4bfa639..31c0d277 100644
--- a/src/OpenProject.Browser/Settings/OpenProject.Configuration.json
+++ b/src/OpenProject.Browser/Settings/OpenProject.Configuration.json
@@ -1,4 +1,5 @@
{
"EnableDevelopmentTools": false,
- "OpenProjectInstances": []
+ "OpenProjectInstances": [],
+ "CheckForUpdates": true
}
diff --git a/src/OpenProject.Browser/ViewModels/MainWindowViewModel.cs b/src/OpenProject.Browser/ViewModels/MainWindowViewModel.cs
index da36f036..d4fc92e2 100644
--- a/src/OpenProject.Browser/ViewModels/MainWindowViewModel.cs
+++ b/src/OpenProject.Browser/ViewModels/MainWindowViewModel.cs
@@ -9,7 +9,7 @@ namespace OpenProject.Browser.ViewModels
///
public sealed class MainWindowViewModel
{
- private const double _windowMinWidth = 800.00;
+ private const double _windowMinWidth = 320.00;
///
/// The view of the nested bcfier panel.
@@ -19,7 +19,12 @@ public sealed class MainWindowViewModel
///
/// The main window calculated width.
///
- public double Width => Math.Max(_windowMinWidth, SystemParameters.PrimaryScreenHeight * 0.25);
+ public double MinWidth => _windowMinWidth;
+
+ ///
+ /// The main window calculated width.
+ ///
+ public double Width => Math.Max(_windowMinWidth, SystemParameters.PrimaryScreenWidth * 0.33);
///
/// The main window default height.
diff --git a/src/OpenProject.Browser/ViewModels/WebViewModel.cs b/src/OpenProject.Browser/ViewModels/WebViewModel.cs
index 87bd7125..86415694 100644
--- a/src/OpenProject.Browser/ViewModels/WebViewModel.cs
+++ b/src/OpenProject.Browser/ViewModels/WebViewModel.cs
@@ -1,24 +1,30 @@
using System;
using System.Linq;
using CefSharp.Wpf;
+using OpenProject.Browser.Services;
using OpenProject.Browser.WebViewIntegration;
+using OpenProject.Shared;
+using Version = OpenProject.Browser.Models.Version;
namespace OpenProject.Browser.ViewModels
{
public sealed class WebViewModel
{
private readonly JavaScriptBridge _javaScriptBridge;
+ private readonly IGitHubService _gitHubService;
private readonly BrowserManager _browserManager;
public ChromiumWebBrowser Browser => _browserManager.Browser;
- public WebViewModel(BrowserManager browserManager, JavaScriptBridge javaScriptBridge)
+ public WebViewModel(IGitHubService gitHubService, BrowserManager browserManager, JavaScriptBridge javaScriptBridge)
{
+ _gitHubService = gitHubService;
_browserManager = browserManager;
_javaScriptBridge = javaScriptBridge;
_browserManager.Initialize();
InitializeIpcConnection();
+ CheckForUpdates();
}
private void InitializeIpcConnection()
@@ -34,5 +40,27 @@ private void InitializeIpcConnection()
var clientPort = int.Parse(args[1]);
IpcManager.StartIpcCommunication(_javaScriptBridge, serverPort, clientPort);
}
+
+ private void CheckForUpdates()
+ {
+ if (!ConfigurationHandler.Settings.CheckForUpdates)
+ return;
+
+ var latestRelease = _gitHubService.GetLatestRelease();
+
+ latestRelease.MatchSome(release =>
+ {
+ Version latestVersion = release.Version();
+ Version currentVersion = Version.Parse(VersionsService.Version);
+ if (latestVersion.CompareTo(currentVersion) > 0)
+ {
+ MessageHandler.ShowUpdateDialog(
+ currentVersion.ToString(),
+ latestVersion.ToString(),
+ release.PublishedAt(),
+ release.DownloadUrl());
+ }
+ });
+ }
}
}
diff --git a/src/OpenProject.Browser/Views/MainWindowView.xaml.cs b/src/OpenProject.Browser/Views/MainWindowView.xaml.cs
index 2a27e362..e651f4e7 100644
--- a/src/OpenProject.Browser/Views/MainWindowView.xaml.cs
+++ b/src/OpenProject.Browser/Views/MainWindowView.xaml.cs
@@ -20,6 +20,7 @@ public MainWindowView(MainWindowViewModel viewModel, JavaScriptBridge javaScript
// We have to set layout here, as the layout in wpf doesn't work with bindings at runtime.
Width = viewModel.Width;
+ MinWidth = viewModel.MinWidth;
Height = viewModel.Height;
Top = viewModel.Top;
Left = viewModel.Left;
diff --git a/src/OpenProject.Browser/WebViewIntegration/BrowserManager.cs b/src/OpenProject.Browser/WebViewIntegration/BrowserManager.cs
index 957d0851..5e302d71 100644
--- a/src/OpenProject.Browser/WebViewIntegration/BrowserManager.cs
+++ b/src/OpenProject.Browser/WebViewIntegration/BrowserManager.cs
@@ -6,6 +6,7 @@
using CefSharp;
using CefSharp.Wpf;
using OpenProject.Browser.Services;
+using OpenProject.Browser.Settings;
namespace OpenProject.Browser.WebViewIntegration
{
@@ -39,7 +40,7 @@ public void Initialize()
{
Browser = _chromiumWebBrowser;
- var knownGoodUrls = ConfigurationHandler.LoadAllInstances();
+ var knownGoodUrls = ConfigurationHandler.Settings.GetOpenProjectInstances();
var lastVisitedPage = ConfigurationHandler.LastVisitedPage();
var isWhiteListedUrl = knownGoodUrls.Any(goodUrl =>
lastVisitedPage.StartsWith(goodUrl, StringComparison.InvariantCultureIgnoreCase));
@@ -78,7 +79,7 @@ public void Initialize()
}
});
- if (!ConfigurationHandler.ShouldEnableDevelopmentTools()) return;
+ if (!ConfigurationHandler.Settings.EnableDevelopmentTools) return;
var devToolsEnabled = false;
Browser.IsBrowserInitializedChanged += (s, e) =>
diff --git a/src/OpenProject.Browser/WebViewIntegration/IpcManager.cs b/src/OpenProject.Browser/WebViewIntegration/IpcManager.cs
index ae92247a..2ad4eab7 100644
--- a/src/OpenProject.Browser/WebViewIntegration/IpcManager.cs
+++ b/src/OpenProject.Browser/WebViewIntegration/IpcManager.cs
@@ -13,14 +13,14 @@ public static void StartIpcCommunication(JavaScriptBridge javaScriptBridge, int
server.Start(serverPort);
server.ReceivedRequest += (sender, e) =>
{
- var deserialized = JsonConvert.DeserializeObject(e.Request);
+ var deserialized = JsonConvert.DeserializeObject(e.Request);
javaScriptBridge.SendMessageToOpenProject(
deserialized.MessageType, deserialized.TrackingId, deserialized.MessagePayload);
};
var client = new IpcClient();
client.Initialize(clientPort);
- javaScriptBridge.OnWebUIMessageReveived += (s, e) => client.Send(JsonConvert.SerializeObject(e));
+ javaScriptBridge.OnWebUiMessageReceived += (s, e) => client.Send(JsonConvert.SerializeObject(e));
}
}
}
diff --git a/src/OpenProject.Browser/WebViewIntegration/JavaScriptBridge.cs b/src/OpenProject.Browser/WebViewIntegration/JavaScriptBridge.cs
index 5f0d1a17..7c9c78bf 100644
--- a/src/OpenProject.Browser/WebViewIntegration/JavaScriptBridge.cs
+++ b/src/OpenProject.Browser/WebViewIntegration/JavaScriptBridge.cs
@@ -5,6 +5,7 @@
using CefSharp.Wpf;
using Newtonsoft.Json;
using OpenProject.Browser.Services;
+using OpenProject.Browser.Settings;
using OpenProject.Shared;
using Serilog;
@@ -32,9 +33,9 @@ public JavaScriptBridge(OpenProjectInstanceValidator instanceValidator)
private ChromiumWebBrowser _webBrowser;
- public event WebUIMessageReceivedEventHandler OnWebUIMessageReveived;
+ public event WebUiMessageReceivedEventHandler OnWebUiMessageReceived;
- public delegate void WebUIMessageReceivedEventHandler(object sender, WebUIMessageEventArgs e);
+ public delegate void WebUiMessageReceivedEventHandler(object sender, WebUiMessageEventArgs e);
public event AppForegroundRequestReceivedEventHandler OnAppForegroundRequestReceived;
@@ -83,7 +84,7 @@ public void SendMessageToRevit(string messageType, string trackingId, string mes
break;
case MessageTypes.ALL_INSTANCES_REQUESTED:
{
- var allInstances = JsonConvert.SerializeObject(ConfigurationHandler.LoadAllInstances());
+ var allInstances = JsonConvert.SerializeObject(ConfigurationHandler.Settings.GetOpenProjectInstances());
SendMessageToOpenProject(MessageTypes.ALL_INSTANCES, trackingId, allInstances);
break;
}
@@ -101,8 +102,8 @@ public void SendMessageToRevit(string messageType, string trackingId, string mes
break;
default:
{
- var eventArgs = new WebUIMessageEventArgs(messageType, trackingId, messagePayload);
- OnWebUIMessageReveived?.Invoke(this, eventArgs);
+ var eventArgs = new WebUiMessageEventArgs(messageType, trackingId, messagePayload);
+ OnWebUiMessageReceived?.Invoke(this, eventArgs);
// For some UI operations, revit should be focused
RevitMainWindowHandler.SetFocusToRevit();
break;
diff --git a/src/OpenProject.Browser/WebViewIntegration/LandingPage/LandingPage.zip b/src/OpenProject.Browser/WebViewIntegration/LandingPage/LandingPage.zip
index e5fd955b..54c61667 100644
Binary files a/src/OpenProject.Browser/WebViewIntegration/LandingPage/LandingPage.zip and b/src/OpenProject.Browser/WebViewIntegration/LandingPage/LandingPage.zip differ
diff --git a/src/OpenProject.Browser/WebViewIntegration/OpenProjectBrowserRequestHandler.cs b/src/OpenProject.Browser/WebViewIntegration/OpenProjectBrowserRequestHandler.cs
index 0ef4bc19..d23c59be 100644
--- a/src/OpenProject.Browser/WebViewIntegration/OpenProjectBrowserRequestHandler.cs
+++ b/src/OpenProject.Browser/WebViewIntegration/OpenProjectBrowserRequestHandler.cs
@@ -4,6 +4,7 @@
using System.Security.Cryptography.X509Certificates;
using CefSharp;
using OpenProject.Browser.Services;
+using OpenProject.Browser.Settings;
namespace OpenProject.Browser.WebViewIntegration
{
@@ -60,7 +61,7 @@ private static bool ShouldOpenRequestInEmbeddedBrowser(IRequest request, bool is
if (isValidLocalUrl)
return true;
- var knownGoodUrls = ConfigurationHandler.LoadAllInstances();
+ var knownGoodUrls = ConfigurationHandler.Settings.GetOpenProjectInstances();
var isValidExternalUrl = knownGoodUrls.Any(goodUrl =>
request.Url.StartsWith(goodUrl, StringComparison.InvariantCultureIgnoreCase));
diff --git a/src/OpenProject.Revit/Data/ProjectPositionWrapper.cs b/src/OpenProject.Revit/Data/ProjectPositionWrapper.cs
index 85b7b195..ff2178fe 100644
--- a/src/OpenProject.Revit/Data/ProjectPositionWrapper.cs
+++ b/src/OpenProject.Revit/Data/ProjectPositionWrapper.cs
@@ -1,5 +1,4 @@
-using System;
-using Autodesk.Revit.DB;
+using Autodesk.Revit.DB;
using OpenProject.Shared.Math3D;
namespace OpenProject.Revit.Data
@@ -47,10 +46,10 @@ public ProjectPositionWrapper()
public ProjectPositionWrapper(ProjectPosition projectPosition)
{
- EastWest = Convert.ToDecimal(projectPosition.EastWest);
- NorthSouth = Convert.ToDecimal(projectPosition.NorthSouth);
- Elevation = Convert.ToDecimal(projectPosition.Elevation);
- Angle = Convert.ToDecimal(projectPosition.Angle);
+ EastWest = projectPosition.EastWest.ToDecimal();
+ NorthSouth = projectPosition.NorthSouth.ToDecimal();
+ Elevation = projectPosition.Elevation.ToDecimal();
+ Angle = projectPosition.Angle.ToDecimal();
}
///
diff --git a/src/OpenProject.Revit/Data/RevitUtils.cs b/src/OpenProject.Revit/Data/RevitUtils.cs
index 05ab834e..8a9f33bc 100644
--- a/src/OpenProject.Revit/Data/RevitUtils.cs
+++ b/src/OpenProject.Revit/Data/RevitUtils.cs
@@ -1,7 +1,6 @@
using System;
using Autodesk.Revit.DB;
using OpenProject.Shared.Math3D;
-
using decMath = DecimalMath.DecimalEx;
namespace OpenProject.Revit.Data
@@ -26,7 +25,7 @@ public static Position TransformCameraPosition(
var i = reverse ? -1 : 1;
Vector3 translation = projectBase.GetTranslation() * i;
- var rotation = i * Convert.ToDecimal(projectBase.Angle);
+ var rotation = i * projectBase.Angle;
// do translation before rotation if we transform from global to local coordinates
Vector3 center = reverse
@@ -83,10 +82,7 @@ public static XYZ ToRevitXyz(this Vector3 vec) =>
///
/// The vector3 object
/// The Revit vector object
- public static Vector3 ToVector3(this XYZ vec) =>
- new(Convert.ToDecimal(vec.X),
- Convert.ToDecimal(vec.Y),
- Convert.ToDecimal(vec.Z));
+ public static Vector3 ToVector3(this XYZ vec) => new(vec.X.ToDecimal(), vec.Y.ToDecimal(), vec.Z.ToDecimal());
///
/// Converts some basic revit view values to a view box height and a view box width.
@@ -96,7 +92,7 @@ public static Vector3 ToVector3(this XYZ vec) =>
/// The bottom left corner of the revit view.
/// The right direction of the revit view.
/// A tuple of the height and the width of the view box.
- public static ( double viewBoxHeight, double viewBoxWidth) ConvertToViewBoxValues(
+ public static (double viewBoxHeight, double viewBoxWidth) ConvertToViewBoxValues(
XYZ topRight, XYZ bottomLeft, XYZ right)
{
XYZ diagonal = topRight.Subtract(bottomLeft);
@@ -142,20 +138,28 @@ public static double ToInternalRevitUnit(this double meters)
///
/// The vector with values in feet
/// The vector with values in meter
- public static Vector3 ToMeters(this Vector3 vec) =>
- new(Convert.ToDecimal(Convert.ToDouble(vec.X).ToMeters()),
- Convert.ToDecimal(Convert.ToDouble(vec.Y).ToMeters()),
- Convert.ToDecimal(Convert.ToDouble(vec.Z).ToMeters()));
+ public static Vector3 ToMeters(this Vector3 vec)
+ {
+ var x = vec.X.IsFinite() ? Convert.ToDouble(vec.X).ToMeters().ToDecimal() : vec.X;
+ var y = vec.Y.IsFinite() ? Convert.ToDouble(vec.Y).ToMeters().ToDecimal() : vec.Y;
+ var z = vec.Z.IsFinite() ? Convert.ToDouble(vec.Z).ToMeters().ToDecimal() : vec.Z;
+
+ return new Vector3(x, y, z);
+ }
///
/// Converts a vector containing values in meter units to feet. Feet are the internal Revit units.
///
/// The vector with values in meter
/// The vector with values in feet
- public static Vector3 ToInternalUnits(this Vector3 vec) =>
- new(Convert.ToDecimal(Convert.ToDouble(vec.X).ToInternalRevitUnit()),
- Convert.ToDecimal(Convert.ToDouble(vec.Y).ToInternalRevitUnit()),
- Convert.ToDecimal(Convert.ToDouble(vec.Z).ToInternalRevitUnit()));
+ public static Vector3 ToInternalUnits(this Vector3 vec)
+ {
+ var x = vec.X.IsFinite() ? Convert.ToDouble(vec.X).ToInternalRevitUnit().ToDecimal() : vec.X;
+ var y = vec.Y.IsFinite() ? Convert.ToDouble(vec.Y).ToInternalRevitUnit().ToDecimal() : vec.Y;
+ var z = vec.Z.IsFinite() ? Convert.ToDouble(vec.Z).ToInternalRevitUnit().ToDecimal() : vec.Z;
+
+ return new Vector3(x, y, z);
+ }
///
/// Converts a position containing values in feet units to meter. Feet are the internal Revit units.
diff --git a/src/OpenProject.Revit/Data/RevitView.cs b/src/OpenProject.Revit/Data/RevitView.cs
deleted file mode 100644
index 93d186ca..00000000
--- a/src/OpenProject.Revit/Data/RevitView.cs
+++ /dev/null
@@ -1,282 +0,0 @@
-using Autodesk.Revit.DB;
-using Autodesk.Revit.UI;
-using OpenProject.Shared;
-using OpenProject.Shared.ViewModels.Bcf;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using OpenProject.Shared.Math3D;
-
-namespace OpenProject.Revit.Data
-{
- //Methods for working with views
- public static class RevitView
- {
- //
- //Generate a VisualizationInfo of the current view
- //
- //
- public static BcfViewpointViewModel GenerateViewpoint(UIDocument uiDoc)
- {
- try
- {
- Document doc = uiDoc.Document;
- var bcfViewpoint = new BcfViewpointViewModel();
-
- //Corners of the active UI view
- XYZ bottomLeft = uiDoc.GetOpenUIViews()[0].GetZoomCorners()[0];
- XYZ topRight = uiDoc.GetOpenUIViews()[0].GetZoomCorners()[1];
- ProjectPosition projectPosition = doc.ActiveProjectLocation.GetProjectPosition(XYZ.Zero);
-
- //It's a 3d view
- if (uiDoc.ActiveView.ViewType == ViewType.ThreeD)
- {
- var view3D = (View3D)uiDoc.ActiveView;
-
- // it is a orthogonal view
- if (!view3D.IsPerspective)
- {
- //center of the UI view
- var viewCenter = new XYZ((topRight.X + bottomLeft.X) / 2,
- (topRight.Y + bottomLeft.Y) / 2,
- (topRight.Z + bottomLeft.Z) / 2);
-
- var (viewBoxHeight, _) = RevitUtils.ConvertToViewBoxValues(topRight, bottomLeft, view3D.RightDirection);
-
- Position cameraPosition = RevitUtils.TransformCameraPosition(
- new ProjectPositionWrapper(projectPosition),
- new Position(
- viewCenter.ToVector3(),
- uiDoc.ActiveView.ViewDirection.ToVector3(),
- uiDoc.ActiveView.UpDirection.ToVector3()));
-
- Vector3 c = cameraPosition.Center.ToMeters();
- Vector3 vi = cameraPosition.Forward;
- Vector3 up = cameraPosition.Up;
-
- bcfViewpoint.Viewpoint = new iabi.BCF.APIObjects.V21.Viewpoint_GET
- {
- Orthogonal_camera = new iabi.BCF.APIObjects.V21.Orthogonal_camera
- {
- View_to_world_scale = Convert.ToSingle(viewBoxHeight.ToMeters()),
- Camera_view_point =
- new iabi.BCF.APIObjects.V21.Point
- {
- X = Convert.ToSingle(c.X),
- Y = Convert.ToSingle(c.Y),
- Z = Convert.ToSingle(c.Z)
- },
- Camera_up_vector =
- new iabi.BCF.APIObjects.V21.Direction
- {
- X = Convert.ToSingle(up.X),
- Y = Convert.ToSingle(up.Y),
- Z = Convert.ToSingle(up.Z)
- },
- Camera_direction = new iabi.BCF.APIObjects.V21.Direction
- {
- X = Convert.ToSingle(vi.X * -1),
- Y = Convert.ToSingle(vi.Y * -1),
- Z = Convert.ToSingle(vi.Z * -1)
- }
- }
- };
- }
- // it is a perspective view
- else
- {
- XYZ viewCenter = uiDoc.ActiveView.Origin;
-
- Position cameraPosition = RevitUtils.TransformCameraPosition(
- new ProjectPositionWrapper(projectPosition),
- new Position(
- viewCenter.ToVector3(),
- uiDoc.ActiveView.ViewDirection.ToVector3(),
- uiDoc.ActiveView.UpDirection.ToVector3()));
-
- Vector3 c = cameraPosition.Center.ToMeters();
- Vector3 vi = cameraPosition.Forward;
- Vector3 up = cameraPosition.Up;
-
- bcfViewpoint.Viewpoint = new iabi.BCF.APIObjects.V21.Viewpoint_GET
- {
- Perspective_camera = new iabi.BCF.APIObjects.V21.Perspective_camera
- {
- Field_of_view = 45, //revit default value
- Camera_view_point =
- new iabi.BCF.APIObjects.V21.Point
- {
- X = Convert.ToSingle(c.X),
- Y = Convert.ToSingle(c.Y),
- Z = Convert.ToSingle(c.Z)
- },
- Camera_up_vector =
- new iabi.BCF.APIObjects.V21.Direction
- {
- X = Convert.ToSingle(up.X),
- Y = Convert.ToSingle(up.Y),
- Z = Convert.ToSingle(up.Z)
- },
- Camera_direction = new iabi.BCF.APIObjects.V21.Direction
- {
- X = Convert.ToSingle(vi.X * -1),
- Y = Convert.ToSingle(vi.Y * -1),
- Z = Convert.ToSingle(vi.Z * -1)
- }
- }
- };
- }
- }
-
- SetViewpointComponents(bcfViewpoint, doc, uiDoc);
- SetViewpointClippingPlanes(bcfViewpoint, uiDoc);
-
- return bcfViewpoint;
- }
- catch (Exception ex1)
- {
- TaskDialog.Show("Error generating viewpoint", "exception: " + ex1);
- }
-
- return null;
- }
-
- private static void SetViewpointComponents(BcfViewpointViewModel bcfViewpoint, Document doc, UIDocument uiDoc)
- {
- var versionName = doc.Application.VersionName;
-
- var visibleElems = new FilteredElementCollector(doc, doc.ActiveView.Id)
- .WhereElementIsNotElementType()
- .WhereElementIsViewIndependent()
- .ToElementIds();
- var hiddenElems = new FilteredElementCollector(doc)
- .WhereElementIsNotElementType()
- .WhereElementIsViewIndependent()
- .Where(x => x.IsHidden(doc.ActiveView)
- || !doc.ActiveView.IsElementVisibleInTemporaryViewMode(TemporaryViewMode.TemporaryHideIsolate,
- x.Id)).Select(x => x.Id)
- .ToList(); //would need to check how much this is affecting performance
-
- var selectedElems = uiDoc.Selection.GetElementIds();
-
- bcfViewpoint.Components = new iabi.BCF.APIObjects.V21.Components();
- if (hiddenElems.Count > visibleElems.Count)
- {
- bcfViewpoint.Components.Visibility = new iabi.BCF.APIObjects.V21.Visibility
- {
- Default_visibility = false,
- Exceptions = visibleElems
- .Select(visibleComponent => new iabi.BCF.APIObjects.V21.Component
- {
- Originating_system = versionName,
- Ifc_guid = IfcGuid.ToIfcGuid(ExportUtils.GetExportId(doc, visibleComponent)),
- Authoring_tool_id = visibleComponent.IntegerValue.ToString()
- })
- .ToList()
- };
- }
- else
- {
- bcfViewpoint.Components.Visibility = new iabi.BCF.APIObjects.V21.Visibility
- {
- Default_visibility = true,
- Exceptions = hiddenElems
- .Select(hiddenComponent => new iabi.BCF.APIObjects.V21.Component
- {
- Originating_system = versionName,
- Ifc_guid = IfcGuid.ToIfcGuid(ExportUtils.GetExportId(doc, hiddenComponent)),
- Authoring_tool_id = hiddenComponent.IntegerValue.ToString()
- })
- .ToList()
- };
- }
-
- if (selectedElems.Any())
- {
- bcfViewpoint.Components.Selection = new List();
- foreach (var selectedComponent in selectedElems)
- {
- bcfViewpoint.Components.Selection.Add(new iabi.BCF.APIObjects.V21.Component
- {
- Originating_system = versionName,
- Ifc_guid = IfcGuid.ToIfcGuid(ExportUtils.GetExportId(doc, selectedComponent)),
- Authoring_tool_id = selectedComponent.IntegerValue.ToString()
- });
- }
- }
- }
-
- private static void SetViewpointClippingPlanes(BcfViewpointViewModel bcfViewpoint, UIDocument uiDoc)
- {
- // There are actually no clipping planes in a 3d view in revit, rather it's a
- // section box, meaning a set of 6 clipping planes that wrap the visible part
- // of a model inside a cuboid shape.
- if (uiDoc.ActiveView is not View3D { IsSectionBoxActive: true } view3d)
- return;
-
- bcfViewpoint.Viewpoint.Clipping_planes = new List();
- var revitSectionBox = view3d.GetSectionBox();
-
- // The Min and Max represent two corners of the section box on opposite
- // sides, so they're describing a cuboid 3D geometry, which we can now
- // use to derive the six actual planes from
- XYZ transformedMin = revitSectionBox.Transform.OfPoint(revitSectionBox.Min);
- XYZ transformedMax = revitSectionBox.Transform.OfPoint(revitSectionBox.Max);
-
- // A clipping plane is defined as a point and a direction, so we now
- // can use each point (Min and Max) three times, each with one direction in
- // X, Y and Z pointing outwards from the center
- // The vectors for the Min point should point away from the center (negative)
- // and the vectors for the Max point should be positive
- bcfViewpoint.Viewpoint.Clipping_planes = new[]
- {
- new
- {
- Location = transformedMin,
- Direction = revitSectionBox.Transform.OfVector(new XYZ(-1, 0, 0))
- },
- new
- {
- Location = transformedMin,
- Direction = revitSectionBox.Transform.OfVector(new XYZ(0, -1, 0))
- },
- new
- {
- Location = transformedMin,
- Direction = revitSectionBox.Transform.OfVector(new XYZ(0, 0, -1))
- },
- new
- {
- Location = transformedMax,
- Direction = revitSectionBox.Transform.OfVector(new XYZ(1, 0, 0))
- },
- new
- {
- Location = transformedMax,
- Direction = revitSectionBox.Transform.OfVector(new XYZ(0, 1, 0))
- },
- new
- {
- Location = transformedMax,
- Direction = revitSectionBox.Transform.OfVector(new XYZ(0, 0, 1))
- }
- }
- .Select(cp => new iabi.BCF.APIObjects.V21.Clipping_plane
- {
- Location = new iabi.BCF.APIObjects.V21.Location
- {
- X = Convert.ToSingle(cp.Location.X.ToMeters()),
- Y = Convert.ToSingle(cp.Location.Y.ToMeters()),
- Z = Convert.ToSingle(cp.Location.Z.ToMeters())
- },
- Direction = new iabi.BCF.APIObjects.V21.Direction
- {
- X = Convert.ToSingle(cp.Direction.X),
- Y = Convert.ToSingle(cp.Direction.Y),
- Z = Convert.ToSingle(cp.Direction.Z)
- }
- })
- .ToList();
- }
- }
-}
diff --git a/src/OpenProject.Revit/Entry/IpcHandler.cs b/src/OpenProject.Revit/Entry/IpcHandler.cs
index 6576f92a..8243bb46 100644
--- a/src/OpenProject.Revit/Entry/IpcHandler.cs
+++ b/src/OpenProject.Revit/Entry/IpcHandler.cs
@@ -1,16 +1,17 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
-using OpenProject.Revit.Data;
-using OpenProject.Shared.ViewModels.Bcf;
using OpenProject.Shared;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
-using System.Windows;
+using iabi.BCF.APIObjects.V21;
+using Newtonsoft.Json.Converters;
+using OpenProject.Revit.Extensions;
+using OpenProject.Revit.Services;
+using OpenProject.Shared.BcfApi;
using ZetaIpc.Runtime.Client;
using ZetaIpc.Runtime.Server;
using ZetaIpc.Runtime.Helper;
@@ -46,35 +47,57 @@ public int StartLocalServerAndReturnPort()
server.Start(freePort);
server.ReceivedRequest += (_, e) =>
{
- var eventArgs = JsonConvert.DeserializeObject(e.Request);
+ var eventArgs = JsonConvert.DeserializeObject(e.Request);
+ if (eventArgs == null) return;
+
var localMessageType = eventArgs.MessageType;
var localTrackingId = eventArgs.TrackingId;
var localMessagePayload = eventArgs.MessagePayload;
- _callbackStack.Push(() =>
+
+ lock (_callbackStackLock)
{
- switch (localMessageType)
+ _callbackStack.Push(() =>
{
- case MessageTypes.VIEWPOINT_DATA:
+ switch (localMessageType)
{
- var bcfViewpoint = MessageDeserializer.DeserializeBcfViewpoint(
- new WebUIMessageEventArgs(localMessageType, localTrackingId, localMessagePayload));
- OpenView(bcfViewpoint);
- break;
+ case MessageTypes.VIEWPOINT_DATA:
+ {
+ try
+ {
+ BcfViewpointWrapper bcfViewpoint = MessageDeserializer.DeserializeBcfViewpoint(
+ new WebUiMessageEventArgs(localMessageType, localTrackingId, localMessagePayload));
+ OpenViewpointEventHandler.ShowBcfViewpoint(bcfViewpoint);
+ }
+ catch (Exception exception)
+ {
+ MessageHandler.ShowError(exception, "Error opening a viewpoint.");
+ }
+
+ break;
+ }
+ case MessageTypes.VIEWPOINT_GENERATION_REQUESTED:
+ try
+ {
+ AddViewpoint(localTrackingId);
+ }
+ catch (Exception exception)
+ {
+ MessageHandler.ShowError(exception, "Error generating a viewpoint.");
+ }
+
+ break;
}
- case MessageTypes.VIEWPOINT_GENERATION_REQUESTED:
- AddView(localTrackingId);
- break;
- }
- });
+ });
+ }
};
return freePort;
}
- public void StartLocalClient(int bcfierWinServerPort)
+ public void StartLocalClient(int ipcWinServerPort)
{
var client = new IpcClient();
- client.Initialize(bcfierWinServerPort);
+ client.Initialize(ipcWinServerPort);
_sendData = message =>
{
try
@@ -92,144 +115,60 @@ public void StartLocalClient(int bcfierWinServerPort)
public void SendShutdownRequestToDesktopApp()
{
- var eventArgs = new WebUIMessageEventArgs(MessageTypes.CLOSE_DESKTOP_APPLICATION, "0", string.Empty);
+ var eventArgs = new WebUiMessageEventArgs(MessageTypes.CLOSE_DESKTOP_APPLICATION, "0", string.Empty);
var jsonEventArgs = JsonConvert.SerializeObject(eventArgs);
_sendData(jsonEventArgs);
}
- ///
- /// Raises the External Event to accomplish a transaction in a modeless window
- /// http://help.autodesk.com/view/RVT/2014/ENU/?guid=GUID-0A0D656E-5C44-49E8-A891-6C29F88E35C0
- /// http://matteocominetti.com/starting-a-transaction-from-an-external-application-running-outside-of-api-context-is-not-allowed/
- ///
- private void OpenView(BcfViewpointViewModel bcfViewpoint)
- {
- try
- {
- var uiDoc = _uiApp.ActiveUIDocument;
-
- if (uiDoc.ActiveView.ViewType == ViewType.Schedule)
- {
- MessageBox.Show("BCFier can't take snapshots of schedules.",
- "Warning!", MessageBoxButton.OK, MessageBoxImage.Warning);
- return;
- }
-
- OpenViewpointEventHandler.ShowBcfViewpoint(bcfViewpoint);
- }
- catch (Exception ex1)
- {
- TaskDialog.Show("Error opening a View!", "exception: " + ex1);
- }
- }
-
public void SendOpenSettingsRequestToDesktopApp()
{
- var eventArgs = new WebUIMessageEventArgs(MessageTypes.GO_TO_SETTINGS, "0", string.Empty);
+ var eventArgs = new WebUiMessageEventArgs(MessageTypes.GO_TO_SETTINGS, "0", string.Empty);
var jsonEventArgs = JsonConvert.SerializeObject(eventArgs);
_sendData(jsonEventArgs);
}
public void SendBringBrowserToForegroundRequestToDesktopApp()
{
- var eventArgs = new WebUIMessageEventArgs(MessageTypes.SET_BROWSER_TO_FOREGROUND, "0", string.Empty);
+ var eventArgs = new WebUiMessageEventArgs(MessageTypes.SET_BROWSER_TO_FOREGROUND, "0", string.Empty);
var jsonEventArgs = JsonConvert.SerializeObject(eventArgs);
_sendData(jsonEventArgs);
}
///
- /// Same as in the windows app, but here we generate a VisInfo that is attached to the view
+ /// Generates a viewpoint from the active view and sends the data as an event to bridge.
///
/// The local message tracking id.
- private void AddView(string trackingId)
+ private void AddViewpoint(string trackingId)
{
- try
+ if (_uiApp.ActiveUIDocument.ActiveView.ViewType != ViewType.ThreeD)
{
- if (_uiApp.ActiveUIDocument.ActiveView.ViewType != ViewType.ThreeD)
- {
- TaskDialog.Show("Invalid active UI document",
- "To capture viewpoints the active document must be a 3D view.");
- return;
- }
-
- var generatedViewpoint = RevitView.GenerateViewpoint(_uiApp.ActiveUIDocument);
- var snapshot = GetRevitSnapshot(_uiApp.ActiveUIDocument.Document);
- var messageContent = new ViewpointGeneratedApiMessage
- {
- SnapshotPngBase64 = "data:image/png;base64," + ConvertToBase64(snapshot),
- Viewpoint = MessageSerializer.SerializeBcfViewpoint(generatedViewpoint)
- };
-
- var serializerSettings = new JsonSerializerSettings
- {
- ContractResolver = new CamelCasePropertyNamesContractResolver()
- };
+ MessageHandler.ShowWarning(
+ "Invalid view",
+ "Active UI document is not a 3D view",
+ "In order to capture BCF viewpoints, the OpenProject Revit Add-In requires a 3D view.");
+ return;
+ }
- var jsonPayload =
- JObject.Parse(JsonConvert.SerializeObject(messageContent.Viewpoint.Viewpoint, serializerSettings));
- if (messageContent.Viewpoint.Components != null)
- {
- jsonPayload["components"] =
- JObject.Parse(JsonConvert.SerializeObject(messageContent.Viewpoint.Components, serializerSettings));
- }
+ JObject payload = GenerateJsonViewpoint();
- jsonPayload["snapshot"] = messageContent.SnapshotPngBase64;
- var payloadString = jsonPayload.ToString();
+ // TODO: remove hack of snapshot data once #39135 is deployed
+ payload["snapshot"] = payload["snapshot"]?["snapshot_data"];
- var eventArgs = new WebUIMessageEventArgs(MessageTypes.VIEWPOINT_GENERATED, trackingId, payloadString);
- var jsonEventArgs = JsonConvert.SerializeObject(eventArgs);
- _sendData(jsonEventArgs);
- }
- catch (Exception exception)
- {
- TaskDialog.Show("Error adding a View!", "exception: " + exception);
- }
+ var eventArgs = new WebUiMessageEventArgs(MessageTypes.VIEWPOINT_GENERATED, trackingId, payload.ToString());
+ _sendData(JsonConvert.SerializeObject(eventArgs));
}
- private static Stream GetRevitSnapshot(Document doc)
+ private JObject GenerateJsonViewpoint()
{
- try
- {
- var tempPath = Path.Combine(Path.GetTempPath(), "BCFier");
- Directory.CreateDirectory(tempPath);
- var tempImg = Path.Combine(tempPath, Path.GetTempFileName() + ".png");
- var options = new ImageExportOptions
- {
- FilePath = tempImg,
- HLRandWFViewsFileType = ImageFileType.PNG,
- ShadowViewsFileType = ImageFileType.PNG,
- ExportRange = ExportRange.VisibleRegionOfCurrentView,
- ZoomType = ZoomFitType.FitToPage,
- ImageResolution = ImageResolution.DPI_72,
- PixelSize = 1000
- };
- doc.ExportImage(options);
-
- var memStream = new MemoryStream();
- using (var fs = File.OpenRead(tempImg))
- {
- fs.CopyTo(memStream);
- }
-
- File.Delete(tempImg);
-
- memStream.Position = 0;
- return memStream;
- }
- catch (Exception exception)
+ var serializerSettings = new JsonSerializerSettings
{
- TaskDialog.Show("Error!", "exception: " + exception);
- throw;
- }
- }
+ ContractResolver = new CamelCasePropertyNamesContractResolver(),
+ };
- private static string ConvertToBase64(Stream stream)
- {
- using var memoryStream = new MemoryStream();
- stream.CopyTo(memoryStream);
- var bytes = memoryStream.ToArray();
+ serializerSettings.Converters.Add(new StringEnumConverter(new SnakeCaseNamingStrategy(), false));
- return Convert.ToBase64String(bytes);
+ Viewpoint_POST viewpoint = _uiApp.ActiveUIDocument.GenerateViewpoint();
+ return JObject.Parse(JsonConvert.SerializeObject(viewpoint, serializerSettings));
}
}
}
diff --git a/src/OpenProject.Revit/Entry/OpenViewpointEventHandler.cs b/src/OpenProject.Revit/Entry/OpenViewpointEventHandler.cs
index 6a4a5546..fcbb1cee 100644
--- a/src/OpenProject.Revit/Entry/OpenViewpointEventHandler.cs
+++ b/src/OpenProject.Revit/Entry/OpenViewpointEventHandler.cs
@@ -2,11 +2,11 @@
using Autodesk.Revit.UI;
using OpenProject.Revit.Data;
using OpenProject.Revit.Extensions;
-using OpenProject.Shared.ViewModels.Bcf;
using System;
using System.Collections.Generic;
using System.Linq;
using OpenProject.Revit.Services;
+using OpenProject.Shared.BcfApi;
using OpenProject.Shared.Math3D;
using OpenProject.Shared.Math3D.Enumeration;
using Serilog;
@@ -37,7 +37,7 @@ public void Execute(UIApplication app)
///
public string GetName() => nameof(OpenViewpointEventHandler);
- private BcfViewpointViewModel _bcfViewpoint;
+ private BcfViewpointWrapper _bcfViewpoint;
private static OpenViewpointEventHandler _instance;
@@ -60,8 +60,12 @@ private static OpenViewpointEventHandler Instance
/// Wraps the raising of the external event and thus the execution of the event callback,
/// that show given bcf viewpoint.
///
+ ///
+ /// http://help.autodesk.com/view/RVT/2014/ENU/?guid=GUID-0A0D656E-5C44-49E8-A891-6C29F88E35C0
+ /// http://matteocominetti.com/starting-a-transaction-from-an-external-application-running-outside-of-api-context-is-not-allowed/
+ ///
/// The bcf viewpoint to be shown in current view.
- public static void ShowBcfViewpoint(BcfViewpointViewModel bcfViewpoint)
+ public static void ShowBcfViewpoint(BcfViewpointWrapper bcfViewpoint)
{
Log.Information("Received 'Opening BCF Viewpoint event'. Attempting to open viewpoint ...");
Instance._bcfViewpoint = bcfViewpoint;
@@ -135,13 +139,7 @@ private static void ResetView(UIDocument uiDocument, View3D view)
view.DisableTemporaryViewMode(TemporaryViewMode.RevealHiddenElements);
view.IsSectionBoxActive = false;
- var currentlyHiddenElements = new FilteredElementCollector(uiDocument.Document)
- .WhereElementIsNotElementType()
- .WhereElementIsViewIndependent()
- .Where(element => element.IsHidden(view))
- .Select(element => element.Id)
- .ToList();
-
+ var currentlyHiddenElements = uiDocument.Document.GetHiddenElementsOfView(view).ToList();
if (currentlyHiddenElements.Any())
{
Log.Information("Unhide {n} currently hidden elements ...", currentlyHiddenElements.Count);
@@ -177,13 +175,7 @@ private void ApplyViewOrientationAndVisibility(UIDocument uiDocument, View3D vie
view.SetOrientation(viewOrientation3D);
Log.Information("Applying element visibility ...");
- var currentlyVisibleElements = new FilteredElementCollector(uiDocument.Document, view.Id)
- .WhereElementIsNotElementType()
- .WhereElementIsViewIndependent()
- .Where(element => element.CanBeHidden(view))
- .Select(element => element.Id)
- .ToList();
-
+ var currentlyVisibleElements = uiDocument.Document.GetVisibleElementsOfView(view);
var map = uiDocument.Document.GetIfcGuidElementIdMap(currentlyVisibleElements);
var exceptionElements = GetViewpointVisibilityExceptions(map);
var selectedElements = GetViewpointSelection(map);
diff --git a/src/OpenProject.Revit/Extensions/BcfExtensions.cs b/src/OpenProject.Revit/Extensions/BcfExtensions.cs
new file mode 100644
index 00000000..c8f8daf9
--- /dev/null
+++ b/src/OpenProject.Revit/Extensions/BcfExtensions.cs
@@ -0,0 +1,203 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Autodesk.Revit.DB;
+using Autodesk.Revit.UI;
+using iabi.BCF.APIObjects.V21;
+using OpenProject.Revit.Data;
+using OpenProject.Shared;
+using OpenProject.Shared.BcfApi;
+using OpenProject.Shared.Math3D;
+using OpenProject.Shared.Math3D.Enumeration;
+
+namespace OpenProject.Revit.Extensions
+{
+ ///
+ /// A namespace for static extensions for Revit document classes with focus on getting BCF data out
+ /// of the current state.
+ ///
+ public static class BcfExtensions
+ {
+ ///
+ /// Generates a model meant to send a viewpoint creation request to the BCF API from a Revit UI document.
+ ///
+ /// The Revit UI document
+ /// A BCF model for creating viewpoints.
+ public static Viewpoint_POST GenerateViewpoint(this UIDocument uiDocument)
+ {
+ (Orthogonal_camera ortho, Perspective_camera perspective) = uiDocument.GetBcfCameraValues();
+
+ return new Viewpoint_POST
+ {
+ Clipping_planes = uiDocument.GetBcfClippingPlanes(),
+ Snapshot = uiDocument.GetBcfSnapshotData(),
+ Components = uiDocument.GetBcfComponents(),
+ Orthogonal_camera = ortho,
+ Perspective_camera = perspective
+ };
+ }
+
+ private static List GetBcfClippingPlanes(this UIDocument uiDocument)
+ {
+ if (uiDocument.ActiveView is not View3D view3D)
+ return new List();
+
+ BoundingBoxXYZ sectionBox = view3D.GetSectionBox();
+ XYZ transformedMin = sectionBox.Transform.OfPoint(sectionBox.Min);
+ XYZ transformedMax = sectionBox.Transform.OfPoint(sectionBox.Max);
+ Vector3 minCorner = transformedMin.ToVector3().ToMeters();
+ Vector3 maxCorner = transformedMax.ToVector3().ToMeters();
+
+ return new AxisAlignedBoundingBox(minCorner, maxCorner).ToClippingPlanes();
+ }
+
+ private static Snapshot_POST GetBcfSnapshotData(this UIDocument uiDocument)
+ {
+ return new Snapshot_POST
+ {
+ Snapshot_type = Snapshot_type.Png,
+ Snapshot_data = "data:image/png;base64," + uiDocument.GetBase64RevitSnapshot()
+ };
+ }
+
+ private static Components GetBcfComponents(this UIDocument uiDocument)
+ {
+ var hiddenElementsOfView = uiDocument.Document.GetHiddenElementsOfView(uiDocument.ActiveView).ToList();
+ var visibleElementsOfView = uiDocument.Document.GetVisibleElementsOfView(uiDocument.ActiveView).ToList();
+
+ var defaultVisibility = visibleElementsOfView.Count >= hiddenElementsOfView.Count;
+ var exceptions = (defaultVisibility ? hiddenElementsOfView : visibleElementsOfView)
+ .Select(uiDocument.Document.ElementIdToComponentSelector())
+ .ToList();
+
+ var selectedComponents = uiDocument.Selection.GetElementIds()
+ .Select(uiDocument.Document.ElementIdToComponentSelector())
+ .ToList();
+
+ return new Components
+ {
+ Selection = selectedComponents,
+ Visibility = new iabi.BCF.APIObjects.V21.Visibility
+ {
+ Default_visibility = defaultVisibility,
+ Exceptions = exceptions
+ }
+ };
+ }
+
+ private static (Orthogonal_camera orthogonalCamera, Perspective_camera perspectiveCamera) GetBcfCameraValues(
+ this UIDocument uiDocument)
+ {
+ if (uiDocument.ActiveView is not View3D view3D)
+ return (null, null);
+
+ CameraType cameraType = view3D.IsPerspective ? CameraType.Perspective : CameraType.Orthogonal;
+ Position cameraPosition = uiDocument.GetCameraPosition(view3D.IsPerspective);
+ Vector3 center = cameraPosition.Center.ToMeters();
+
+ var cameraViewpoint = new iabi.BCF.APIObjects.V21.Point
+ {
+ X = Convert.ToSingle(center.X),
+ Y = Convert.ToSingle(center.Y),
+ Z = Convert.ToSingle(center.Z)
+ };
+
+ var cameraUpVector = new Direction
+ {
+ X = Convert.ToSingle(cameraPosition.Up.X),
+ Y = Convert.ToSingle(cameraPosition.Up.Y),
+ Z = Convert.ToSingle(cameraPosition.Up.Z)
+ };
+
+ var cameraDirection = new Direction
+ {
+ X = Convert.ToSingle(cameraPosition.Forward.X * -1),
+ Y = Convert.ToSingle(cameraPosition.Forward.Y * -1),
+ Z = Convert.ToSingle(cameraPosition.Forward.Z * -1)
+ };
+
+ Orthogonal_camera ortho = null;
+ Perspective_camera perspective = null;
+
+ switch (cameraType)
+ {
+ case CameraType.Perspective:
+ perspective = new Perspective_camera
+ {
+ Field_of_view = 45, // revit default value
+ Camera_direction = cameraDirection,
+ Camera_up_vector = cameraUpVector,
+ Camera_view_point = cameraViewpoint
+ };
+ break;
+ case CameraType.Orthogonal:
+ ortho = new Orthogonal_camera
+ {
+ View_to_world_scale = uiDocument.GetViewBoxHeight(),
+ Camera_direction = cameraDirection,
+ Camera_up_vector = cameraUpVector,
+ Camera_view_point = cameraViewpoint
+ };
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(cameraType), cameraType, "invalid camera type");
+ }
+
+ return (ortho, perspective);
+ }
+
+ private static float GetViewBoxHeight(this UIDocument uiDocument)
+ {
+ var zoomCorners = uiDocument.GetOpenUIViews()[0].GetZoomCorners();
+ XYZ bottomLeft = zoomCorners[0];
+ XYZ topRight = zoomCorners[1];
+ var (viewBoxHeight, _) =
+ RevitUtils.ConvertToViewBoxValues(topRight, bottomLeft, uiDocument.ActiveView.RightDirection);
+
+ return Convert.ToSingle(viewBoxHeight.ToMeters());
+ }
+
+ private static Position GetCameraPosition(this UIDocument uiDocument, bool isPerspective)
+ {
+ ProjectPosition projectPosition = uiDocument.Document.ActiveProjectLocation.GetProjectPosition(XYZ.Zero);
+ var zoomCorners = uiDocument.GetOpenUIViews()[0].GetZoomCorners();
+ XYZ bottomLeft = zoomCorners[0];
+ XYZ topRight = zoomCorners[1];
+ XYZ viewCenter = uiDocument.ActiveView.Origin;
+ if (!isPerspective)
+ viewCenter = new XYZ((topRight.X + bottomLeft.X) / 2,
+ (topRight.Y + bottomLeft.Y) / 2,
+ (topRight.Z + bottomLeft.Z) / 2);
+
+ return RevitUtils.TransformCameraPosition(
+ new ProjectPositionWrapper(projectPosition),
+ new Position(
+ viewCenter.ToVector3(),
+ uiDocument.ActiveView.ViewDirection.ToVector3(),
+ uiDocument.ActiveView.UpDirection.ToVector3()));
+ }
+
+ private static string GetBase64RevitSnapshot(this UIDocument uiDocument)
+ {
+ var tmpPath = ConfigurationConstant.OpenProjectTempDirectory;
+ Directory.CreateDirectory(tmpPath);
+ var tempImg = Path.Combine(tmpPath, Path.GetTempFileName() + ".png");
+ var options = new ImageExportOptions
+ {
+ FilePath = tempImg,
+ HLRandWFViewsFileType = ImageFileType.PNG,
+ ShadowViewsFileType = ImageFileType.PNG,
+ ExportRange = ExportRange.VisibleRegionOfCurrentView,
+ ZoomType = ZoomFitType.FitToPage,
+ ImageResolution = ImageResolution.DPI_72,
+ PixelSize = 1000
+ };
+ uiDocument.Document.ExportImage(options);
+
+ var bytes = File.ReadAllBytes(tempImg);
+ File.Delete(tempImg);
+ return Convert.ToBase64String(bytes);
+ }
+ }
+}
diff --git a/src/OpenProject.Revit/Extensions/RevitExtensions.cs b/src/OpenProject.Revit/Extensions/RevitDocumentExtensions.cs
similarity index 61%
rename from src/OpenProject.Revit/Extensions/RevitExtensions.cs
rename to src/OpenProject.Revit/Extensions/RevitDocumentExtensions.cs
index 3f44dd9f..5568460a 100644
--- a/src/OpenProject.Revit/Extensions/RevitExtensions.cs
+++ b/src/OpenProject.Revit/Extensions/RevitDocumentExtensions.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.DB;
+using iabi.BCF.APIObjects.V21;
using OpenProject.Shared;
using OpenProject.Shared.Math3D.Enumeration;
using Serilog;
@@ -9,9 +10,9 @@
namespace OpenProject.Revit.Extensions
{
///
- /// Extension written for handling of classes of the Revit API.
+ /// Extensions written for handling of classes of the Revit API.
///
- public static class RevitExtensions
+ public static class RevitDocumentExtensions
{
private const string _openProjectOrthogonalViewName = "OpenProject Orthogonal";
private const string _openProjectPerspectiveViewName = "OpenProject Perspective";
@@ -22,7 +23,8 @@ public static class RevitExtensions
/// A revit document
/// A list of element ids
/// The map between IFC GUIDs and revit element ids.
- public static Dictionary GetIfcGuidElementIdMap(this Document doc, IEnumerable elements)
+ public static Dictionary GetIfcGuidElementIdMap(this Document doc,
+ IEnumerable elements)
{
var map = new Dictionary();
foreach (ElementId element in elements)
@@ -35,6 +37,55 @@ public static Dictionary GetIfcGuidElementIdMap(this Document
return map;
}
+ ///
+ /// Gets all visible elements in the given view of the document.
+ ///
+ /// The Revit document
+ /// The Revit view
+ /// A list of element ids of all elements, that are currently visible.
+ public static IEnumerable GetVisibleElementsOfView(this Document doc, View view) =>
+ new FilteredElementCollector(doc, view.Id)
+ .WhereElementIsNotElementType()
+ .WhereElementIsViewIndependent()
+ .Where(element => element.CanBeHidden(view))
+ .Select(element => element.Id);
+
+ ///
+ /// Gets all invisible elements in the given view of the document.
+ ///
+ /// The Revit document
+ /// The Revit view
+ /// A list of element ids of all elements, that are currently hidden.
+ public static IEnumerable GetHiddenElementsOfView(this Document doc, View view)
+ {
+ bool ElementIsHiddenInView(Element element) =>
+ element.IsHidden(view) ||
+ element.Category is { CategoryType: CategoryType.Model } &&
+ view.GetCategoryHidden(element.Category.Id);
+
+ return new FilteredElementCollector(doc)
+ .WhereElementIsNotElementType()
+ .WhereElementIsViewIndependent()
+ .Where(ElementIsHiddenInView)
+ .Select(element => element.Id);
+ }
+
+ ///
+ /// Gets a selector, that converts Revit element ids into BCF API components.
+ /// This is done in the context of a specific Revit Document.
+ ///
+ /// The Revit document
+ /// A selector converting to .
+ public static Func ElementIdToComponentSelector(this Document doc)
+ {
+ return id => new Component
+ {
+ Originating_system = doc.Application.VersionName,
+ Ifc_guid = IfcGuid.ToIfcGuid(ExportUtils.GetExportId(doc, id)),
+ Authoring_tool_id = id.ToString()
+ };
+ }
+
///
/// Gets the correct 3D view for displaying OpenProject content. The type of the view is dependent of the requested
/// camera type, either orthogonal or perspective. If the view is not yet available, it is created.
@@ -42,7 +93,7 @@ public static Dictionary GetIfcGuidElementIdMap(this Document
/// The current revit document.
/// The camera type for the requested view.
/// A with the correct settings to display OpenProject content.
- /// Throws, if camera type is neither orthogonal nor perspective.
+ /// Throws, if camera type is neither orthogonal nor perspective.
public static View3D GetOpenProjectView(this Document doc, CameraType type)
{
var viewName = type switch
diff --git a/src/OpenProject.Shared/BcfApi/BcfApiExtensions.cs b/src/OpenProject.Shared/BcfApi/BcfApiExtensions.cs
index 8990e46f..38f4d388 100644
--- a/src/OpenProject.Shared/BcfApi/BcfApiExtensions.cs
+++ b/src/OpenProject.Shared/BcfApi/BcfApiExtensions.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using iabi.BCF.APIObjects.V21;
using OpenProject.Shared.Math3D;
@@ -8,14 +9,118 @@ public static class BcfApiExtensions
{
public static Vector3 ToVector3(this Direction direction) =>
new Vector3(
- Convert.ToDecimal(direction.X),
- Convert.ToDecimal(direction.Y),
- Convert.ToDecimal(direction.Z));
+ direction.X.ToDecimal(),
+ direction.Y.ToDecimal(),
+ direction.Z.ToDecimal());
public static Vector3 ToVector3(this Point point) =>
new Vector3(
- Convert.ToDecimal(point.X),
- Convert.ToDecimal(point.Y),
- Convert.ToDecimal(point.Z));
+ point.X.ToDecimal(),
+ point.Y.ToDecimal(),
+ point.Z.ToDecimal());
+
+ ///
+ /// Converts a axis aligned bounding box into a list of bcf api clipping planes.
+ ///
+ /// The bounding box that defines the clipping. Can contain infinite values,
+ /// which are interpreted as if the view is not clipped in that direction.
+ /// An optional clipping center. Important for positioning the clipping planes not
+ /// too far away from the model. If no clipping center is given, the center of the clipping box is used, which
+ /// can result in very odd clipping plane locations, if the clipping box contains infinite values.
+ /// A list of clipping planes.
+ public static List ToClippingPlanes(
+ this AxisAlignedBoundingBox clippingBox,
+ Vector3 clippingCenter = null)
+ {
+ Vector3 center = clippingCenter ?? (clippingBox.Min + clippingBox.Max) * 0.5m;
+
+ var planes = new List();
+
+ if (clippingBox.Min.X.IsFinite())
+ {
+ planes.Add(new Clipping_plane
+ {
+ Location = new Location
+ {
+ X = Convert.ToSingle(clippingBox.Min.X),
+ Y = Convert.ToSingle(center.Y),
+ Z = Convert.ToSingle(center.Z)
+ },
+ Direction = new Direction { X = -1, Y = 0, Z = 0 }
+ });
+ }
+
+ if (clippingBox.Min.Y.IsFinite())
+ {
+ planes.Add(new Clipping_plane
+ {
+ Location = new Location
+ {
+ X = Convert.ToSingle(center.X),
+ Y = Convert.ToSingle(clippingBox.Min.Y),
+ Z = Convert.ToSingle(center.Z)
+ },
+ Direction = new Direction { X = 0, Y = -1, Z = 0 }
+ });
+ }
+
+ if (clippingBox.Min.Z.IsFinite())
+ {
+ planes.Add(new Clipping_plane
+ {
+ Location = new Location
+ {
+ X = Convert.ToSingle(center.X),
+ Y = Convert.ToSingle(center.Y),
+ Z = Convert.ToSingle(clippingBox.Min.Z)
+ },
+ Direction = new Direction { X = 0, Y = 0, Z = -1 }
+ });
+ }
+
+ if (clippingBox.Max.X.IsFinite())
+ {
+ planes.Add(new Clipping_plane
+ {
+ Location = new Location
+ {
+ X = Convert.ToSingle(clippingBox.Max.X),
+ Y = Convert.ToSingle(center.Y),
+ Z = Convert.ToSingle(center.Z)
+ },
+ Direction = new Direction { X = 1, Y = 0, Z = 0 }
+ });
+ }
+
+ if (clippingBox.Max.Y.IsFinite())
+ {
+ planes.Add(new Clipping_plane
+ {
+ Location = new Location
+ {
+ X = Convert.ToSingle(center.X),
+ Y = Convert.ToSingle(clippingBox.Max.Y),
+ Z = Convert.ToSingle(center.Z)
+ },
+ Direction = new Direction { X = 0, Y = 1, Z = 0 }
+ });
+ }
+
+ if (clippingBox.Max.Z.IsFinite())
+ {
+ planes.Add(new Clipping_plane
+ {
+ Location = new Location
+ {
+ X = Convert.ToSingle(center.X),
+ Y = Convert.ToSingle(center.Y),
+ Z = Convert.ToSingle(clippingBox.Max.Z)
+ },
+ Direction = new Direction { X = 0, Y = 0, Z = 1 }
+ });
+ }
+
+ return planes;
+ }
}
}
diff --git a/src/OpenProject.Shared/ViewModels/Bcf/BcfViewpointViewModel.cs b/src/OpenProject.Shared/BcfApi/BcfViewpointWrapper.cs
similarity index 73%
rename from src/OpenProject.Shared/ViewModels/Bcf/BcfViewpointViewModel.cs
rename to src/OpenProject.Shared/BcfApi/BcfViewpointWrapper.cs
index e3fdd036..8056dc4e 100644
--- a/src/OpenProject.Shared/ViewModels/Bcf/BcfViewpointViewModel.cs
+++ b/src/OpenProject.Shared/BcfApi/BcfViewpointWrapper.cs
@@ -1,22 +1,17 @@
-using System;
-using System.Collections.Generic;
-using Dangl;
+using System.Collections.Generic;
using iabi.BCF.APIObjects.V21;
-using OpenProject.Shared.BcfApi;
using OpenProject.Shared.Math3D;
using Optional;
-namespace OpenProject.Shared.ViewModels.Bcf
+namespace OpenProject.Shared.BcfApi
{
///
/// A view model for BCF viewpoints.
///
- public sealed class BcfViewpointViewModel : BindableBase
+ public sealed class BcfViewpointWrapper
{
public Viewpoint_GET Viewpoint { get; set; }
- public string SnapshotData { get; set; }
-
public Components Components { get; set; }
///
@@ -33,7 +28,7 @@ public Option GetCamera()
var c = new PerspectiveCamera();
Perspective_camera bcfPerspective = Viewpoint.Perspective_camera;
- c.FieldOfView = Convert.ToDecimal(bcfPerspective.Field_of_view);
+ c.FieldOfView = bcfPerspective.Field_of_view.ToDecimal();
c.Position = new Position(
bcfPerspective.Camera_view_point.ToVector3(),
bcfPerspective.Camera_direction.ToVector3(),
@@ -47,7 +42,7 @@ public Option GetCamera()
var c = new OrthogonalCamera();
Orthogonal_camera bcfOrthogonal = Viewpoint.Orthogonal_camera;
- c.ViewToWorldScale = Convert.ToDecimal(bcfOrthogonal.View_to_world_scale);
+ c.ViewToWorldScale = bcfOrthogonal.View_to_world_scale.ToDecimal();
c.Position = new Position(
bcfOrthogonal.Camera_view_point.ToVector3(),
bcfOrthogonal.Camera_direction.ToVector3(),
@@ -62,7 +57,8 @@ public Option GetCamera()
///
/// Gets the list of visibility exceptions or an empty list if any path element is null.
///
- public List GetVisibilityExceptions() => Components?.Visibility?.Exceptions ?? new List();
+ public IEnumerable GetVisibilityExceptions() =>
+ Components?.Visibility?.Exceptions ?? new List();
///
/// Gets the visibility default, or false, if any path element is null.
@@ -72,11 +68,11 @@ public Option GetCamera()
///
/// Gets the list of selected components or an empty list if any path element is null.
///
- public List GetSelection() => Components?.Selection ?? new List();
+ public IEnumerable GetSelection() => Components?.Selection ?? new List();
///
/// Gets the list of viewpoint clipping planes or an empty list if any path element is null.
///
- public List GetClippingPlanes() => Viewpoint?.Clipping_planes ?? new List();
+ public IEnumerable GetClippingPlanes() => Viewpoint?.Clipping_planes ?? new List();
}
}
diff --git a/src/OpenProject.Shared/ConfigurationConstant.cs b/src/OpenProject.Shared/ConfigurationConstant.cs
index 1cc12ea3..9fb40251 100644
--- a/src/OpenProject.Shared/ConfigurationConstant.cs
+++ b/src/OpenProject.Shared/ConfigurationConstant.cs
@@ -18,5 +18,8 @@ public static class ConfigurationConstant
public static readonly string OpenProjectBrowserExecutablePath =
Path.Combine(_openProjectBrowserFolderName, _openProjectBrowserExecutableName);
+
+ public static readonly string OpenProjectTempDirectory =
+ Path.Combine(Path.GetTempPath(), _openProjectRevitAddInFolderName);
}
}
diff --git a/src/OpenProject.Shared/Math3D/Extensions.cs b/src/OpenProject.Shared/Math3D/Extensions.cs
new file mode 100644
index 00000000..c917975e
--- /dev/null
+++ b/src/OpenProject.Shared/Math3D/Extensions.cs
@@ -0,0 +1,42 @@
+using System;
+
+namespace OpenProject.Shared.Math3D
+{
+ public static class Extensions
+ {
+ ///
+ /// Checks a decimal number against infinity.
+ ///
+ /// A decimal value
+ /// true, if a decimal value is finite, false otherwise
+ public static bool IsFinite(this decimal number) => number > decimal.MinValue && number < decimal.MaxValue;
+
+ ///
+ /// Converts a double value to a decimal value without throwing a .
+ /// If the double is bigger then the maximal value of decimals, the decimal maximal value is returned.
+ ///
+ /// The double value
+ /// The converted decimal value
+ public static decimal ToDecimal(this double @double)
+ {
+ if (@double < 0)
+ return @double < (double)decimal.MinValue ? decimal.MinValue : (decimal)@double;
+
+ return @double > (double)decimal.MaxValue ? decimal.MaxValue : (decimal)@double;
+ }
+
+ ///
+ /// Converts a float value to a decimal value without throwing a .
+ /// If the float is bigger then the maximal value of decimals, the decimal maximal value is returned.
+ ///
+ /// The float value
+ /// The converted decimal value
+ public static decimal ToDecimal(this float @float)
+ {
+ if (@float < 0)
+ return @float < (float)decimal.MinValue ? decimal.MinValue : (decimal)@float;
+
+ return @float > (float)decimal.MaxValue ? decimal.MaxValue : (decimal)@float;
+ }
+ }
+}
diff --git a/src/OpenProject.Shared/Math3D/OrthogonalCamera.cs b/src/OpenProject.Shared/Math3D/OrthogonalCamera.cs
index 87a57e3f..8d88984d 100644
--- a/src/OpenProject.Shared/Math3D/OrthogonalCamera.cs
+++ b/src/OpenProject.Shared/Math3D/OrthogonalCamera.cs
@@ -1,5 +1,4 @@
-using System.Runtime.CompilerServices;
-using OpenProject.Shared.Math3D.Enumeration;
+using OpenProject.Shared.Math3D.Enumeration;
namespace OpenProject.Shared.Math3D
{
diff --git a/src/OpenProject.Shared/Math3D/Vector3.cs b/src/OpenProject.Shared/Math3D/Vector3.cs
index 23f6eed5..21fb9025 100644
--- a/src/OpenProject.Shared/Math3D/Vector3.cs
+++ b/src/OpenProject.Shared/Math3D/Vector3.cs
@@ -48,6 +48,14 @@ public decimal AngleBetween(Vector3 vec)
return DecimalMath.DecimalEx.ACos(this * vec / (Euclidean() * vec.Euclidean()));
}
+ ///
+ /// Calculates the sum of this vector and the given one.
+ ///
+ /// The left vector.
+ /// The right vector.
+ /// The dot product value.
+ public static Vector3 operator +(Vector3 v1, Vector3 v2) => new Vector3(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z);
+
///
/// Calculates the dot product of this vector and the given one.
///
diff --git a/src/OpenProject.Shared/MessageDeserializer.cs b/src/OpenProject.Shared/MessageDeserializer.cs
index b9720d33..814ed419 100644
--- a/src/OpenProject.Shared/MessageDeserializer.cs
+++ b/src/OpenProject.Shared/MessageDeserializer.cs
@@ -1,29 +1,24 @@
using iabi.BCF.APIObjects.V21;
using Newtonsoft.Json.Linq;
-using OpenProject.Shared.ViewModels.Bcf;
using System;
+using OpenProject.Shared.BcfApi;
namespace OpenProject.Shared
{
public static class MessageDeserializer
{
- public static BcfViewpointViewModel DeserializeBcfViewpoint(WebUIMessageEventArgs webUIMessage)
+ public static BcfViewpointWrapper DeserializeBcfViewpoint(WebUiMessageEventArgs webUiMessage)
{
- if (webUIMessage.MessageType != MessageTypes.VIEWPOINT_DATA)
- {
+ if (webUiMessage.MessageType != MessageTypes.VIEWPOINT_DATA)
throw new InvalidOperationException("Tried to deserialize a message with the wrong data type");
- }
- var jObject = JObject.Parse(webUIMessage.MessagePayload.Trim('"').Replace("\\\"", "\""));
+ JObject jObject = JObject.Parse(webUiMessage.MessagePayload.Trim('"').Replace("\\\"", "\""));
- var bcfViewpoint = new BcfViewpointViewModel
+ return new BcfViewpointWrapper
{
Viewpoint = jObject.ToObject(),
- SnapshotData = jObject["snapshot"]?["snapshot_data"]?.ToString(),
Components = jObject["components"]?.ToObject()
};
-
- return bcfViewpoint;
}
}
}
diff --git a/src/OpenProject.Shared/MessageSerializer.cs b/src/OpenProject.Shared/MessageSerializer.cs
deleted file mode 100644
index 0993e598..00000000
--- a/src/OpenProject.Shared/MessageSerializer.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using OpenProject.Shared.ViewModels.Bcf;
-
-namespace OpenProject.Shared
-{
- public static class MessageSerializer
- {
- public static ViewpointApiMessage SerializeBcfViewpoint(BcfViewpointViewModel bcfViewpointViewModel)
- {
- var apiViewpoint = new ViewpointApiMessage
- {
- Viewpoint = bcfViewpointViewModel.Viewpoint,
- Components = bcfViewpointViewModel.Components
- };
- return apiViewpoint;
- }
- }
-}
diff --git a/src/OpenProject.Shared/OpenProject.Shared.csproj b/src/OpenProject.Shared/OpenProject.Shared.csproj
index 86f71560..c8edcdb7 100644
--- a/src/OpenProject.Shared/OpenProject.Shared.csproj
+++ b/src/OpenProject.Shared/OpenProject.Shared.csproj
@@ -6,7 +6,6 @@
-
diff --git a/src/OpenProject.Shared/VersionsService.cs b/src/OpenProject.Shared/VersionsService.cs
index e72d5b1b..1f431a7e 100644
--- a/src/OpenProject.Shared/VersionsService.cs
+++ b/src/OpenProject.Shared/VersionsService.cs
@@ -5,11 +5,11 @@ namespace OpenProject.Shared
[System.CodeDom.Compiler.GeneratedCode("GitVersionBuild", "")]
public static class VersionsService
{
- public static string Version => "2.3.1-beta0001";
- public static string CommitInfo => "Branch.dev.Sha.25c297551e0ff3a048396401e89b6fdf8abe9d0c";
- public static string CommitDate => "2021-09-22";
- public static string CommitHash => "25c297551e0ff3a048396401e89b6fdf8abe9d0c";
- public static string InformationalVersion => "2.3.1-beta.1+Branch.dev.Sha.25c297551e0ff3a048396401e89b6fdf8abe9d0c";
- public static DateTime BuildDateUtc { get; } = new DateTime(2021, 9, 30, 13, 53, 42, DateTimeKind.Utc);
+ public static string Version => "2.3.3-beta0018";
+ public static string CommitInfo => "Branch.dev.Sha.d52a81904aad56d88abd4b2ad4d5969d350c52ae";
+ public static string CommitDate => "2021-11-03";
+ public static string CommitHash => "d52a81904aad56d88abd4b2ad4d5969d350c52ae";
+ public static string InformationalVersion => "2.3.3-beta.18+Branch.dev.Sha.d52a81904aad56d88abd4b2ad4d5969d350c52ae";
+ public static DateTime BuildDateUtc { get; } = new DateTime(2021, 11, 3, 14, 47, 10, DateTimeKind.Utc);
}
}
\ No newline at end of file
diff --git a/src/OpenProject.Shared/ViewModels/Bcf/BcfCommentViewModel.cs b/src/OpenProject.Shared/ViewModels/Bcf/BcfCommentViewModel.cs
deleted file mode 100644
index 0a7d55f3..00000000
--- a/src/OpenProject.Shared/ViewModels/Bcf/BcfCommentViewModel.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using Dangl;
-using System;
-
-namespace OpenProject.Shared.ViewModels.Bcf
-{
- public class BcfCommentViewModel : BindableBase
- {
- private string _author;
- private string _modifiedAuthor;
- private DateTime _creationDate = DateTime.UtcNow;
- private DateTime? _modifiedDate;
- private string _text;
- private Guid _id = Guid.NewGuid();
- private string _status;
- private Guid? _viewpointId;
-
- public string Author
- {
- get => _author;
- set => SetProperty(ref _author, value);
- }
-
- public string ModifiedAuthor
- {
- get => _modifiedAuthor;
- set => SetProperty(ref _modifiedAuthor, value);
- }
-
- public DateTime CreationDate
- {
- get => _creationDate;
- set => SetProperty(ref _creationDate, value);
- }
-
- public DateTime? ModifiedDate
- {
- get => _modifiedDate;
- set => SetProperty(ref _modifiedDate, value);
- }
-
- public string Text
- {
- get => _text;
- set => SetProperty(ref _text, value);
- }
-
- public Guid Id
- {
- get => _id;
- set => SetProperty(ref _id, value);
- }
-
- public string Status
- {
- get => _status;
- set => SetProperty(ref _status, value);
- }
-
- public Guid? ViewpointId
- {
- get => _viewpointId;
- set => SetProperty(ref _viewpointId, value);
- }
- }
-}
diff --git a/src/OpenProject.Shared/ViewModels/Bcf/BcfFileViewModel.cs b/src/OpenProject.Shared/ViewModels/Bcf/BcfFileViewModel.cs
deleted file mode 100644
index 674f5b05..00000000
--- a/src/OpenProject.Shared/ViewModels/Bcf/BcfFileViewModel.cs
+++ /dev/null
@@ -1,143 +0,0 @@
-using Dangl;
-using System;
-using System.Collections.ObjectModel;
-using System.Linq;
-
-namespace OpenProject.Shared.ViewModels.Bcf
-{
- public class BcfFileViewModel : BindableBase
- {
- public BcfFileViewModel()
- {
- BcfIssues.CollectionChanged += (s, e) =>
- {
- FilterTopics();
-
- if (e.NewItems?.Count > 0)
- {
- foreach (var bcfIssue in e.NewItems.OfType())
- {
- bcfIssue.PropertyChanged += BcfIssue_PropertyChanged;
- }
- }
- if (e.OldItems?.Count > 0)
- {
- foreach (var bcfIssue in e.OldItems.OfType())
- {
- bcfIssue.PropertyChanged -= BcfIssue_PropertyChanged;
- }
- }
- };
- }
-
- private void BcfIssue_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
- {
- if (e.PropertyName == nameof(BcfIssueViewModel.IsModified)
- && (sender as BcfIssueViewModel).IsModified)
- {
- IsModified = true;
- }
- }
-
- private string _fileName;
- private string _fullName;
- private bool _isModified;
- private Guid _id = Guid.NewGuid();
- private BcfVersion _bcfVersion;
- private string _projectId;
- private string _projectName;
- private BcfIssueViewModel _selectedBcfIssue;
- private string _textSearch;
-
- public string FileName
- {
- get => _fileName;
- set => SetProperty(ref _fileName, value);
- }
-
- public string FullName
- {
- get => _fullName;
- set => SetProperty(ref _fullName, value);
- }
-
- public bool IsModified
- {
- get => _isModified;
- set => SetProperty(ref _isModified, value);
- }
-
- public Guid Id
- {
- get => _id;
- set => SetProperty(ref _id, value);
- }
-
- public BcfVersion BcfVersion
- {
- get => _bcfVersion;
- set => SetProperty(ref _bcfVersion, value);
- }
-
- public ObservableCollection BcfIssues { get; } = new ObservableCollection();
-
- public ObservableCollection BcfIssuesFiltered { get; } = new ObservableCollection();
-
- public string ProjectId
- {
- get => _projectId;
- set => SetProperty(ref _projectId, value);
- }
-
- public string ProjectName
- {
- get => _projectName;
- set => SetProperty(ref _projectName, value);
- }
-
- public BcfIssueViewModel SelectedBcfIssue
- {
- get => _selectedBcfIssue;
- set => SetProperty(ref _selectedBcfIssue, value);
- }
-
- public string TextSearch
- {
- get => _textSearch;
- set
- {
- if (SetProperty(ref _textSearch, value))
- {
- FilterTopics();
- }
- }
- }
-
- private void FilterTopics()
- {
- BcfIssuesFiltered.Clear();
-
- var searchString = TextSearch?.ToLowerInvariant();
- if (string.IsNullOrWhiteSpace(searchString))
- {
- foreach (var issue in BcfIssues)
- {
- BcfIssuesFiltered.Add(issue);
- }
- return;
- }
-
- foreach (var issue in BcfIssues)
- {
- var shouldShowIssue = (issue.Markup?.BcfTopic?.Title?.ToLowerInvariant().Contains(searchString) ?? false)
- || (issue.Markup?.BcfTopic?.Description?.ToLowerInvariant().Contains(searchString) ?? false)
- || (issue.Markup?.Comments?.Any(c => c.Text?.ToLowerInvariant()?.Contains(searchString) ?? false) ?? false);
-
- if (shouldShowIssue)
- {
- BcfIssuesFiltered.Add(issue);
- }
- }
- }
- }
-}
diff --git a/src/OpenProject.Shared/ViewModels/Bcf/BcfHeaderFileViewModel.cs b/src/OpenProject.Shared/ViewModels/Bcf/BcfHeaderFileViewModel.cs
deleted file mode 100644
index 02815e1e..00000000
--- a/src/OpenProject.Shared/ViewModels/Bcf/BcfHeaderFileViewModel.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using Dangl;
-using System;
-
-namespace OpenProject.Shared.ViewModels.Bcf
-{
- public class BcfHeaderFileViewModel : BindableBase
- {
- private DateTime? _fileDate;
- private string _fileName;
- private string _ifcProject;
- private string _ifcSpatialStructureElement;
- private bool _isExternal;
- private string _reference;
-
- public DateTime? FileDate
- {
- get => _fileDate;
- set => SetProperty(ref _fileDate, value);
- }
-
- public string FileName
- {
- get => _fileName;
- set => SetProperty(ref _fileName, value);
- }
-
- public string IfcProject
- {
- get => _ifcProject;
- set => SetProperty(ref _ifcProject, value);
- }
-
- public string IfcSpatialStructureElement
- {
- get => _ifcSpatialStructureElement;
- set => SetProperty(ref _ifcSpatialStructureElement, value);
- }
-
- public bool IsExternal
- {
- get => _isExternal;
- set => SetProperty(ref _isExternal, value);
- }
-
- public string Reference
- {
- get => _reference;
- set => SetProperty(ref _reference, value);
- }
- }
-}
diff --git a/src/OpenProject.Shared/ViewModels/Bcf/BcfIssueViewModel.cs b/src/OpenProject.Shared/ViewModels/Bcf/BcfIssueViewModel.cs
deleted file mode 100644
index de3f0f96..00000000
--- a/src/OpenProject.Shared/ViewModels/Bcf/BcfIssueViewModel.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using Dangl;
-using OpenProject.Shared.ViewModels.ChangeListeners;
-using System.Collections.ObjectModel;
-
-namespace OpenProject.Shared.ViewModels.Bcf
-{
- public class BcfIssueViewModel : BindableBase
- {
- private BcfMarkupViewModel _markup;
- private bool _isModified;
- private BcfIssueViewModelChangeListener _changeListener;
-
- public BcfIssueViewModel()
- {
- _changeListener = new BcfIssueViewModelChangeListener(this);
- }
-
- public bool DisableListeningForChanges
- {
- get => _changeListener.DisableListening;
- set => _changeListener.DisableListening = value;
- }
-
- public BcfMarkupViewModel Markup
- {
- get => _markup;
- set => SetProperty(ref _markup, value);
- }
-
- public bool IsModified
- {
- get => _isModified;
- internal set => SetProperty(ref _isModified, value);
- }
-
- public ObservableCollection Viewpoints { get; } = new ObservableCollection();
- }
-}
diff --git a/src/OpenProject.Shared/ViewModels/Bcf/BcfMarkupViewModel.cs b/src/OpenProject.Shared/ViewModels/Bcf/BcfMarkupViewModel.cs
deleted file mode 100644
index 86987ee8..00000000
--- a/src/OpenProject.Shared/ViewModels/Bcf/BcfMarkupViewModel.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using Dangl;
-using System.Collections.ObjectModel;
-
-namespace OpenProject.Shared.ViewModels.Bcf
-{
- public class BcfMarkupViewModel : BindableBase
- {
- private BcfTopicViewModel _bcfTopic;
-
- public BcfTopicViewModel BcfTopic
- {
- get => _bcfTopic;
- set => SetProperty(ref _bcfTopic, value);
- }
-
- public ObservableCollection Comments { get; } = new ObservableCollection();
-
- public ObservableCollection HeaderFiles { get; } = new ObservableCollection();
- public ObservableCollection ViewpointReferences { get; } = new ObservableCollection();
- }
-}
diff --git a/src/OpenProject.Shared/ViewModels/Bcf/BcfMarkupViewpointReferenceViewModel.cs b/src/OpenProject.Shared/ViewModels/Bcf/BcfMarkupViewpointReferenceViewModel.cs
deleted file mode 100644
index b43ea275..00000000
--- a/src/OpenProject.Shared/ViewModels/Bcf/BcfMarkupViewpointReferenceViewModel.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using Dangl;
-using System;
-
-namespace OpenProject.Shared.ViewModels.Bcf
-{
- public class BcfMarkupViewpointReferenceViewModel : BindableBase
- {
- private Guid _id = Guid.NewGuid();
- private byte[] _snapshot;
-
- public Guid Id
- {
- get => _id;
- set => SetProperty(ref _id, value);
- }
-
- public byte[] Snapshot
- {
- get => _snapshot;
- set => SetProperty(ref _snapshot, value);
- }
- }
-}
diff --git a/src/OpenProject.Shared/ViewModels/Bcf/BcfPointOrVectorViewModel.cs b/src/OpenProject.Shared/ViewModels/Bcf/BcfPointOrVectorViewModel.cs
deleted file mode 100644
index 23a55d15..00000000
--- a/src/OpenProject.Shared/ViewModels/Bcf/BcfPointOrVectorViewModel.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using Dangl;
-
-namespace OpenProject.Shared.ViewModels.Bcf
-{
- public class BcfPointOrVectorViewModel : BindableBase
- {
- private double _x;
- private double _y;
- private double _z;
-
- public double X
- {
- get => _x;
- set => SetProperty(ref _x, value);
- }
-
- public double Y
- {
- get => _y;
- set => SetProperty(ref _y, value);
- }
-
- public double Z
- {
- get => _z;
- set => SetProperty(ref _z, value);
- }
- }
-}
diff --git a/src/OpenProject.Shared/ViewModels/Bcf/BcfTopicViewModel.cs b/src/OpenProject.Shared/ViewModels/Bcf/BcfTopicViewModel.cs
deleted file mode 100644
index cce92980..00000000
--- a/src/OpenProject.Shared/ViewModels/Bcf/BcfTopicViewModel.cs
+++ /dev/null
@@ -1,92 +0,0 @@
-using Dangl;
-using System;
-using System.Collections.ObjectModel;
-
-namespace OpenProject.Shared.ViewModels.Bcf
-{
- public class BcfTopicViewModel : BindableBase
- {
- private Guid _id = Guid.NewGuid();
- private string _author;
- private string _modifiedAuthor;
- private DateTime _creationDate = DateTime.UtcNow;
- private DateTime? _modifiedDate;
- private string _assignedTo;
- private string _description;
- private string _title;
- private string _status;
- private string _type;
- private string _priority;
-
- public Guid Id
- {
- get => _id;
- set => SetProperty(ref _id, value);
- }
-
- public string Author
- {
- get => _author;
- set => SetProperty(ref _author, value);
- }
-
- public string ModifiedAuthor
- {
- get => _modifiedAuthor;
- set => SetProperty(ref _modifiedAuthor, value);
- }
-
- public DateTime CreationDate
- {
- get => _creationDate;
- set => SetProperty(ref _creationDate, value);
- }
-
- public DateTime? ModifiedDate
- {
- get => _modifiedDate;
- set => SetProperty(ref _modifiedDate, value);
- }
-
- public string AssignedTo
- {
- get => _assignedTo;
- set => SetProperty(ref _assignedTo, value);
- }
-
- public string Description
- {
- get => _description;
- set => SetProperty(ref _description, value);
- }
-
- public string Title
- {
- get => _title;
- set => SetProperty(ref _title, value);
- }
-
- public string Status
- {
- get => _status;
- set => SetProperty(ref _status, value);
- }
-
- public string Type
- {
- get => _type;
- set => SetProperty(ref _type, value);
- }
-
- public string Priority
- {
- get => _priority;
- set => SetProperty(ref _priority, value);
- }
-
- public ObservableCollection Labels { get; } = new ObservableCollection();
-
- public ObservableCollection AvailableStati { get; } = new ObservableCollection();
- public ObservableCollection AvailableTypes { get; } = new ObservableCollection();
- }
-}
diff --git a/src/OpenProject.Shared/ViewModels/Bcf/BcfVersion.cs b/src/OpenProject.Shared/ViewModels/Bcf/BcfVersion.cs
deleted file mode 100644
index 4d737a00..00000000
--- a/src/OpenProject.Shared/ViewModels/Bcf/BcfVersion.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace OpenProject.Shared.ViewModels.Bcf
-{
- public enum BcfVersion
- {
- Unknown,
- V20,
- V21
- }
-}
diff --git a/src/OpenProject.Shared/ViewModels/Bcf/BcfViewpointCameraViewModelBase.cs b/src/OpenProject.Shared/ViewModels/Bcf/BcfViewpointCameraViewModelBase.cs
deleted file mode 100644
index e4b4451a..00000000
--- a/src/OpenProject.Shared/ViewModels/Bcf/BcfViewpointCameraViewModelBase.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-using Dangl;
-
-namespace OpenProject.Shared.ViewModels.Bcf
-{
- public abstract class BcfViewpointCameraViewModelBase : BindableBase
- {
- private double _directionX;
- private double _directionY;
- private double _directionZ;
- private double _upX;
- private double _upY;
- private double _upZ;
- private double _viewPointX;
- private double _viewPointY;
- private double _viewPointZ;
-
- public double DirectionX
- {
- get => _directionX;
- set => SetProperty(ref _directionX, value);
- }
-
- public double DirectionY
- {
- get => _directionY;
- set => SetProperty(ref _directionY, value);
- }
-
- public double DirectionZ
- {
- get => _directionZ;
- set => SetProperty(ref _directionZ, value);
- }
-
- public double UpX
- {
- get => _upX;
- set => SetProperty(ref _upX, value);
- }
-
- public double UpY
- {
- get => _upY;
- set => SetProperty(ref _upY, value);
- }
-
- public double UpZ
- {
- get => _upZ;
- set => SetProperty(ref _upZ, value);
- }
-
- public double ViewPointX
- {
- get => _viewPointX;
- set => SetProperty(ref _viewPointX, value);
- }
-
- public double ViewPointY
- {
- get => _viewPointY;
- set => SetProperty(ref _viewPointY, value);
- }
-
- public double ViewPointZ
- {
- get => _viewPointZ;
- set => SetProperty(ref _viewPointZ, value);
- }
- }
-}
diff --git a/src/OpenProject.Shared/ViewModels/Bcf/BcfViewpointClippingPlaneViewModel.cs b/src/OpenProject.Shared/ViewModels/Bcf/BcfViewpointClippingPlaneViewModel.cs
deleted file mode 100644
index 7eb3ed7f..00000000
--- a/src/OpenProject.Shared/ViewModels/Bcf/BcfViewpointClippingPlaneViewModel.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using Dangl;
-
-namespace OpenProject.Shared.ViewModels.Bcf
-{
- public class BcfViewpointClippingPlaneViewModel : BindableBase
- {
- private BcfPointOrVectorViewModel _direction;
- private BcfPointOrVectorViewModel _location;
-
- public BcfPointOrVectorViewModel Direction
- {
- get => _direction;
- set => SetProperty(ref _direction, value);
- }
-
- public BcfPointOrVectorViewModel Location
- {
- get => _location;
- set => SetProperty(ref _location, value);
- }
- }
-}
diff --git a/src/OpenProject.Shared/ViewModels/Bcf/BcfViewpointComponentViewModel.cs b/src/OpenProject.Shared/ViewModels/Bcf/BcfViewpointComponentViewModel.cs
deleted file mode 100644
index f253bef9..00000000
--- a/src/OpenProject.Shared/ViewModels/Bcf/BcfViewpointComponentViewModel.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using Dangl;
-
-namespace OpenProject.Shared.ViewModels.Bcf
-{
- public class BcfViewpointComponentViewModel : BindableBase
- {
- private string _authoringToolId;
- private byte[] _color;
- private string _ifcGuid;
- private string _originatingSystem;
- private bool _isSelected;
- private bool _isVisible;
-
- public string AuthoringToolId
- {
- get => _authoringToolId;
- set => SetProperty(ref _authoringToolId, value);
- }
-
- public byte[] Color
- {
- get => _color;
- set => SetProperty(ref _color, value);
- }
-
- public string IfcGuid
- {
- get => _ifcGuid;
- set => SetProperty(ref _ifcGuid, value);
- }
-
- public string OriginatingSystem
- {
- get => _originatingSystem;
- set => SetProperty(ref _originatingSystem, value);
- }
-
- public bool IsSelected
- {
- get => _isSelected;
- set => SetProperty(ref _isSelected, value);
- }
-
- public bool IsVisible
- {
- get => _isVisible;
- set => SetProperty(ref _isVisible, value);
- }
- }
-}
diff --git a/src/OpenProject.Shared/ViewModels/Bcf/BcfViewpointLineViewModel.cs b/src/OpenProject.Shared/ViewModels/Bcf/BcfViewpointLineViewModel.cs
deleted file mode 100644
index 82582d47..00000000
--- a/src/OpenProject.Shared/ViewModels/Bcf/BcfViewpointLineViewModel.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using Dangl;
-
-namespace OpenProject.Shared.ViewModels.Bcf
-{
- public class BcfViewpointLineViewModel : BindableBase
- {
- private BcfPointOrVectorViewModel _start;
- private BcfPointOrVectorViewModel _end;
-
- public BcfPointOrVectorViewModel Start
- {
- get => _start;
- set => SetProperty(ref _start, value);
- }
-
- public BcfPointOrVectorViewModel End
- {
- get => _end;
- set => SetProperty(ref _end, value);
- }
- }
-}
diff --git a/src/OpenProject.Shared/ViewModels/Bcf/BcfViewpointOrthogonalCameraViewModel.cs b/src/OpenProject.Shared/ViewModels/Bcf/BcfViewpointOrthogonalCameraViewModel.cs
deleted file mode 100644
index 965e8f46..00000000
--- a/src/OpenProject.Shared/ViewModels/Bcf/BcfViewpointOrthogonalCameraViewModel.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace OpenProject.Shared.ViewModels.Bcf
-{
- public class BcfViewpointOrthogonalCameraViewModel : BcfViewpointCameraViewModelBase
- {
- private double _viewToWorldScale;
-
- public double ViewToWorldScale
- {
- get => _viewToWorldScale;
- set => SetProperty(ref _viewToWorldScale, value);
- }
- }
-}
diff --git a/src/OpenProject.Shared/ViewModels/Bcf/BcfViewpointPerspectiveCameraViewModel.cs b/src/OpenProject.Shared/ViewModels/Bcf/BcfViewpointPerspectiveCameraViewModel.cs
deleted file mode 100644
index 7563eda8..00000000
--- a/src/OpenProject.Shared/ViewModels/Bcf/BcfViewpointPerspectiveCameraViewModel.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-namespace OpenProject.Shared.ViewModels.Bcf
-{
- public class BcfViewpointPerspectiveCameraViewModel : BcfViewpointCameraViewModelBase
- {
- private double _fieldOfView;
-
- public double FieldOfView
- {
- get => _fieldOfView;
- set => SetProperty(ref _fieldOfView, value);
- }
- }
-}
diff --git a/src/OpenProject.Shared/ViewModels/ChangeListeners/BcfIssueViewModelChangeListener.cs b/src/OpenProject.Shared/ViewModels/ChangeListeners/BcfIssueViewModelChangeListener.cs
deleted file mode 100644
index 3ee70f6b..00000000
--- a/src/OpenProject.Shared/ViewModels/ChangeListeners/BcfIssueViewModelChangeListener.cs
+++ /dev/null
@@ -1,81 +0,0 @@
-using OpenProject.Shared.ViewModels.Bcf;
-using System;
-using System.ComponentModel;
-
-namespace OpenProject.Shared.ViewModels.ChangeListeners
-{
- internal class BcfIssueViewModelChangeListener
- {
- private readonly BcfIssueViewModel _bcfIssue;
-
- public BcfIssueViewModelChangeListener(BcfIssueViewModel bcfIssue)
- {
- bcfIssue.PropertyChanged += BcfIssue_PropertyChanged;
- _bcfIssue = bcfIssue;
-
- _collectionChangeListener = new CollectionChangeListener(_bcfIssue.Viewpoints);
- _collectionChangeListener.PropertyChanged += (s, e) =>
- {
- if (!DisableListening)
- {
- UpdateModifiedDate();
- }
- };
- }
-
- public bool DisableListening { get; set; }
-
- private ChangeListener _markupChangeListener;
- private CollectionChangeListener _collectionChangeListener;
-
- private void UpdateModifiedDate()
- {
- var disableListeningWasActivated = DisableListening;
- DisableListening = true;
-
- _bcfIssue.IsModified = true;
- if (_bcfIssue.Markup == null)
- {
- _bcfIssue.Markup = new BcfMarkupViewModel();
- }
- if (_bcfIssue.Markup.BcfTopic == null)
- {
- _bcfIssue.Markup.BcfTopic = new BcfTopicViewModel();
- }
-
- _bcfIssue.Markup.BcfTopic.ModifiedDate = DateTime.UtcNow;
- _bcfIssue.IsModified = true;
-
- if (!disableListeningWasActivated)
- {
- DisableListening = false;
- }
- }
-
- private void BcfIssue_PropertyChanged(object sender, PropertyChangedEventArgs e)
- {
- if (!DisableListening)
- {
- UpdateModifiedDate();
- }
-
- if (e.PropertyName == nameof(BcfIssueViewModel.Markup))
- {
- if (_markupChangeListener != null)
- {
- _markupChangeListener.PropertyChanged -= MarkupChangeListener_PropertyChanged;
- }
- _markupChangeListener = new ChangeListener(_bcfIssue.Markup);
- _markupChangeListener.PropertyChanged += MarkupChangeListener_PropertyChanged;
- }
- }
-
- private void MarkupChangeListener_PropertyChanged(object sender, PropertyChangedEventArgs e)
- {
- if (!DisableListening)
- {
- UpdateModifiedDate();
- }
- }
- }
-}
diff --git a/src/OpenProject.Shared/ViewModels/ChangeListeners/ChangeListener.cs b/src/OpenProject.Shared/ViewModels/ChangeListeners/ChangeListener.cs
deleted file mode 100644
index a4aa8573..00000000
--- a/src/OpenProject.Shared/ViewModels/ChangeListeners/ChangeListener.cs
+++ /dev/null
@@ -1,95 +0,0 @@
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.ComponentModel;
-using System.Linq;
-
-namespace OpenProject.Shared.ViewModels.ChangeListeners
-{
- public class ChangeListener : INotifyPropertyChanged
- {
- public INotifyPropertyChanged NotifyPropertyChanged { get; }
-
- public ChangeListener(INotifyPropertyChanged notifyPropertyChanged)
- {
- NotifyPropertyChanged = notifyPropertyChanged;
- NotifyPropertyChanged.PropertyChanged += NotifyPropertyChanged_PropertyChanged;
-
- var childProperties = notifyPropertyChanged.GetType()
- .GetProperties()
- .Where(cp => cp.PropertyType.GetInterfaces().Any(i => i == typeof(INotifyPropertyChanged)))
- .Where(cp => cp.PropertyType.GetInterfaces().All(i => i != typeof(INotifyCollectionChanged)))
- .ToList();
- foreach (var childProperty in childProperties)
- {
- if (childProperty.PropertyType.GetInterfaces().Contains(typeof(INotifyPropertyChanged)))
- {
- var propertyValue = childProperty.GetValue(notifyPropertyChanged) as INotifyPropertyChanged;
- if (propertyValue != null)
- {
- ListenToChangesInChild(childProperty.Name, propertyValue);
- }
- }
- }
-
- var childCollections = NotifyPropertyChanged.GetType()
- .GetProperties()
- .Where(cp => cp.PropertyType.GetInterfaces().Any(i => i == typeof(INotifyCollectionChanged)))
- .ToList();
- foreach (var childCollection in childCollections)
- {
- var childCollectionChanged = childCollection.GetValue(notifyPropertyChanged) as INotifyCollectionChanged;
- if (childCollectionChanged != null)
- {
- var collectionChangeListener = new CollectionChangeListener(childCollectionChanged);
- _collectionChangeListenersByPropertyName.Add(childCollection.Name, collectionChangeListener);
- collectionChangeListener.PropertyChanged += (s, e) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ChildModified)));
- }
- }
- }
-
- public event PropertyChangedEventHandler PropertyChanged;
-
- public bool ChildModified { get; private set; }
- private readonly Dictionary _changeListenersByPropertyName = new Dictionary();
- private readonly Dictionary _collectionChangeListenersByPropertyName = new Dictionary();
-
- private void NotifyPropertyChanged_PropertyChanged(object sender, PropertyChangedEventArgs e)
- {
- ChildModified = true;
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ChildModified)));
-
- if (e.PropertyName == nameof(ChildModified))
- {
- return;
- }
-
- var propertyValue = sender.GetType()
- .GetProperties()
- .FirstOrDefault(p => p.Name == e.PropertyName)
- .GetValue(sender);
-
- if (propertyValue is INotifyPropertyChanged notifiyPropertyChanged)
- {
- ListenToChangesInChild(e.PropertyName, notifiyPropertyChanged);
- }
- }
-
- private void ListenToChangesInChild(string propertyName, INotifyPropertyChanged childPropertyValue)
- {
- if (_changeListenersByPropertyName.ContainsKey(propertyName)
- && _changeListenersByPropertyName[propertyName].NotifyPropertyChanged != childPropertyValue)
- {
- _changeListenersByPropertyName[propertyName].PropertyChanged -= NotifyPropertyChanged_PropertyChanged;
- var newListener = new ChangeListener(childPropertyValue);
- newListener.PropertyChanged += NotifyPropertyChanged_PropertyChanged;
- _changeListenersByPropertyName[propertyName] = newListener;
- }
- else if (!_changeListenersByPropertyName.ContainsKey(propertyName))
- {
- var newListener = new ChangeListener(childPropertyValue);
- newListener.PropertyChanged += NotifyPropertyChanged_PropertyChanged;
- _changeListenersByPropertyName.Add(propertyName, newListener);
- }
- }
- }
-}
diff --git a/src/OpenProject.Shared/ViewModels/ChangeListeners/CollectionChangeListener.cs b/src/OpenProject.Shared/ViewModels/ChangeListeners/CollectionChangeListener.cs
deleted file mode 100644
index fc2f4c30..00000000
--- a/src/OpenProject.Shared/ViewModels/ChangeListeners/CollectionChangeListener.cs
+++ /dev/null
@@ -1,93 +0,0 @@
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.ComponentModel;
-
-namespace OpenProject.Shared.ViewModels.ChangeListeners
-{
- public class CollectionChangeListener : INotifyPropertyChanged
- {
- public event PropertyChangedEventHandler PropertyChanged;
-
- private Dictionary