diff --git a/RevitLookup.sln b/RevitLookup.sln index c0767de4..42ff0e5d 100644 --- a/RevitLookup.sln +++ b/RevitLookup.sln @@ -38,8 +38,6 @@ Global Benchmark|Any CPU = Benchmark|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {05C77115-2277-4DFC-8F95-BE37E5F1130F}.UI.Demo Debug|Any CPU.ActiveCfg = Debug R22|Any CPU - {05C77115-2277-4DFC-8F95-BE37E5F1130F}.UI.Demo Debug|Any CPU.Build.0 = Debug R22|Any CPU {05C77115-2277-4DFC-8F95-BE37E5F1130F}.UI.Demo Release|Any CPU.ActiveCfg = Release R22|Any CPU {05C77115-2277-4DFC-8F95-BE37E5F1130F}.UI.Demo Release|Any CPU.Build.0 = Release R22|Any CPU {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Benchmark|Any CPU.ActiveCfg = Release R24|Any CPU @@ -64,6 +62,8 @@ Global {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Release R24|Any CPU.Build.0 = Release R24|Any CPU {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Release R25|Any CPU.ActiveCfg = Release R25|Any CPU {05C77115-2277-4DFC-8F95-BE37E5F1130F}.Release R25|Any CPU.Build.0 = Release R25|Any CPU + {05C77115-2277-4DFC-8F95-BE37E5F1130F}.UI.Demo Debug|Any CPU.ActiveCfg = Debug R25|Any CPU + {05C77115-2277-4DFC-8F95-BE37E5F1130F}.UI.Demo Debug|Any CPU.Build.0 = Debug R25|Any CPU {64D9C223-D070-46CD-A635-04FD4E1BEC3C}.UI.Demo Debug|Any CPU.ActiveCfg = Debug|Any CPU {64D9C223-D070-46CD-A635-04FD4E1BEC3C}.UI.Demo Release|Any CPU.ActiveCfg = Release|Any CPU {64D9C223-D070-46CD-A635-04FD4E1BEC3C}.Installer|Any CPU.ActiveCfg = Release|Any CPU @@ -141,8 +141,8 @@ Global {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.Benchmark|Any CPU.Build.0 = Release|Any CPU {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.Installer|Any CPU.ActiveCfg = Release|Any CPU {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.Debug R21|Any CPU.ActiveCfg = Debug|Any CPU - {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.Debug R22|Any CPU.ActiveCfg = Debug|Any CPU - {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.Debug R23|Any CPU.ActiveCfg = Debug|Any CPU + {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.Debug R22|Any CPU.ActiveCfg = Debug|Any CPU + {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.Debug R23|Any CPU.ActiveCfg = Debug|Any CPU {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.Debug R24|Any CPU.ActiveCfg = Debug|Any CPU {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.Debug R25|Any CPU.ActiveCfg = Debug|Any CPU {827646DF-D373-4D40-8F0B-1E9842E3CE9A}.Release R21|Any CPU.ActiveCfg = Release|Any CPU diff --git a/source/RevitLookup.UI.Demo/App.xaml.cs b/source/RevitLookup.UI.Demo/App.xaml.cs index 14eda461..721197b4 100644 --- a/source/RevitLookup.UI.Demo/App.xaml.cs +++ b/source/RevitLookup.UI.Demo/App.xaml.cs @@ -42,7 +42,7 @@ private void OnStartup(object sender, StartupEventArgs e) private void OnExit(object sender, ExitEventArgs e) { var settingsService = Host.GetService(); - settingsService.Save(); + settingsService.SaveSettings(); Host.Stop(); } @@ -50,12 +50,12 @@ private void OnExit(object sender, ExitEventArgs e) private Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args) { var assemblyName = new AssemblyName(args.Name); - var path = _revitPath ?? $@"C:\Program Files\Autodesk\Revit 20{assemblyName.Version.Major}"; + var path = _revitPath ?? $@"C:\Program Files\Autodesk\Revit 20{assemblyName.Version!.Major}"; if (!Directory.Exists(path)) return null; _revitPath = path; return Directory.EnumerateFiles(_revitPath, $"{assemblyName.Name}.dll") - .Select(Assembly.LoadFile) + .Select(Assembly.LoadFrom) .First(); } } \ No newline at end of file diff --git a/source/RevitLookup.UI.Demo/HostProvider.cs b/source/RevitLookup.UI.Demo/HostProvider.cs index 6777568e..1ca042b1 100644 --- a/source/RevitLookup.UI.Demo/HostProvider.cs +++ b/source/RevitLookup.UI.Demo/HostProvider.cs @@ -30,7 +30,7 @@ public static IHost CreateHost() //Logging builder.Logging.ClearProviders(); - builder.Logging.AddLoggerConfiguration(); + builder.Logging.AddSerilogConfiguration(); //Configuration builder.Services.AddOptions(builder.Configuration); diff --git a/source/RevitLookup.UI.Demo/Mock/Services/MockLookupService.cs b/source/RevitLookup.UI.Demo/Mock/Services/MockLookupService.cs index bb2b2d42..28f9c8fc 100644 --- a/source/RevitLookup.UI.Demo/Mock/Services/MockLookupService.cs +++ b/source/RevitLookup.UI.Demo/Mock/Services/MockLookupService.cs @@ -46,7 +46,7 @@ public ILookupServiceDependsStage Snoop(SnoopableObject snoopableObject) return this; } - public ILookupServiceDependsStage Snoop(IReadOnlyCollection snoopableObjects) + public ILookupServiceDependsStage Snoop(IList snoopableObjects) { _scope.ServiceProvider.GetService()!.Snoop(snoopableObjects); return this; @@ -54,7 +54,7 @@ public ILookupServiceDependsStage Snoop(IReadOnlyCollection sno public ILookupServiceShowStage DependsOn(IServiceProvider provider) { - _owner = (Window) provider.GetService(); + _owner = (Window)provider.GetService(); return this; } @@ -86,7 +86,7 @@ public void Execute(Action handler) where T : class private void ShowPage() where T : Page { - var window = (Window) _scope.ServiceProvider.GetService(); + var window = (Window)_scope.ServiceProvider.GetService(); window.Closed += OnWindowClosed; if (_owner is null) diff --git a/source/RevitLookup.UI.Demo/Mock/ViewModels/MockEventsViewModel.cs b/source/RevitLookup.UI.Demo/Mock/ViewModels/MockEventsViewModel.cs index 16bcdf4e..afa41fda 100644 --- a/source/RevitLookup.UI.Demo/Mock/ViewModels/MockEventsViewModel.cs +++ b/source/RevitLookup.UI.Demo/Mock/ViewModels/MockEventsViewModel.cs @@ -19,11 +19,12 @@ public sealed partial class MockEventsViewModel( { private readonly CancellationTokenSource _cancellationTokenSource = new(); private readonly Stack _events = new(); + [ObservableProperty] private string _searchText = string.Empty; - [ObservableProperty] private IReadOnlyCollection _snoopableObjects = Array.Empty(); - [ObservableProperty] private IReadOnlyCollection _filteredSnoopableObjects = Array.Empty(); - [ObservableProperty] private IReadOnlyCollection _snoopableData; - [ObservableProperty] private IReadOnlyCollection _filteredSnoopableData; + [ObservableProperty] private IList _snoopableObjects = []; + [ObservableProperty] private IList _filteredSnoopableObjects = []; + [ObservableProperty] private IList _filteredSnoopableData; + [ObservableProperty] private IList _snoopableData; public SnoopableObject SelectedObject { get; set; } public IServiceProvider ServiceProvider { get; } = provider; @@ -36,7 +37,7 @@ public void Navigate(SnoopableObject selectedItem) .Show(); } - public void Navigate(IReadOnlyCollection selectedItems) + public void Navigate(IList selectedItems) { Host.GetService() .Snoop(selectedItems) @@ -44,18 +45,31 @@ public void Navigate(IReadOnlyCollection selectedItems) .Show(); } + public void RemoveObject(object obj) + { + var snoopableObject = obj switch + { + SnoopableObject snoopable => snoopable, + Descriptor descriptor => descriptor.Value.Descriptor.Value, + _ => throw new NotSupportedException($"Type {obj.GetType().Name} removing not supported") + }; + + SnoopableObjects.Remove(snoopableObject); + FilteredSnoopableObjects.Remove(snoopableObject); + } + partial void OnSearchTextChanged(string value) { UpdateSearchResults(SearchOption.Objects); } - partial void OnSnoopableObjectsChanged(IReadOnlyCollection value) + partial void OnSnoopableObjectsChanged(IList value) { SelectedObject = null; UpdateSearchResults(SearchOption.Objects); } - partial void OnSnoopableDataChanged(IReadOnlyCollection value) + partial void OnSnoopableDataChanged(IList value) { UpdateSearchResults(SearchOption.Selection); } diff --git a/source/RevitLookup.UI.Demo/RevitLookup.UI.Demo.csproj b/source/RevitLookup.UI.Demo/RevitLookup.UI.Demo.csproj index 7a5b3642..a38e07ad 100644 --- a/source/RevitLookup.UI.Demo/RevitLookup.UI.Demo.csproj +++ b/source/RevitLookup.UI.Demo/RevitLookup.UI.Demo.csproj @@ -5,7 +5,7 @@ latest x64 true - net48 + net8.0-windows app.manifest 2023.0.0 diff --git a/source/RevitLookup.UI/SimpleContentDialogCreateOptions.cs b/source/RevitLookup.UI/SimpleContentDialogCreateOptions.cs index 041df7e4..1c805dd7 100644 --- a/source/RevitLookup.UI/SimpleContentDialogCreateOptions.cs +++ b/source/RevitLookup.UI/SimpleContentDialogCreateOptions.cs @@ -50,10 +50,12 @@ public class SimpleContentDialogCreateOptions /// /// Gets or sets a dialog Horizontal Alignment. /// + /// HorizontalAlignment.Stretch by default public HorizontalAlignment DialogHorizontalAlignment { get; set; } = HorizontalAlignment.Stretch; - + /// /// Gets or sets a dialog Vertical Alignment. /// + /// VerticalAlignment.Stretch by default public VerticalAlignment DialogVerticalAlignment { get; set; } = VerticalAlignment.Stretch; -} +} \ No newline at end of file diff --git a/source/RevitLookup/Application.cs b/source/RevitLookup/Application.cs index 00649cc7..11b51a73 100644 --- a/source/RevitLookup/Application.cs +++ b/source/RevitLookup/Application.cs @@ -24,6 +24,7 @@ using Nice3point.Revit.Toolkit.External; using Nice3point.Revit.Toolkit.External.Handlers; using RevitLookup.Core.Objects; +using RevitLookup.Models.Settings; using RevitLookup.Services.Contracts; using RevitLookup.Utils; @@ -45,8 +46,8 @@ public override void OnStartup() var settingsService = Host.GetService(); var updateService = Host.GetService(); - EnableHardwareRendering(settingsService); - RibbonController.CreatePanel(Application, settingsService); + EnableHardwareRendering(settingsService.GeneralSettings); + RibbonController.CreatePanel(Application, settingsService.GeneralSettings); updateService.CheckUpdates(); } @@ -67,21 +68,21 @@ private static void UpdateSoftware() private static void SaveSettings() { var settingsService = Host.GetService(); - settingsService.Save(); + settingsService.SaveSettings(); } - public static void EnableHardwareRendering(ISettingsService settingsService) + public static void EnableHardwareRendering(GeneralSettings settings) { - if (!settingsService.UseHardwareRendering) return; + if (!settings.UseHardwareRendering) return; //Revit overrides render mode during initialization //EventHandler is called after initialization ActionEventHandler.Raise(_ => RenderOptions.ProcessRenderMode = RenderMode.Default); } - public static void DisableHardwareRendering(ISettingsService settingsService) + public static void DisableHardwareRendering(GeneralSettings settings) { - if (settingsService.UseHardwareRendering) return; + if (settings.UseHardwareRendering) return; RenderOptions.ProcessRenderMode = RenderMode.SoftwareOnly; } diff --git a/source/RevitLookup/Config/GeneralConfiguration.cs b/source/RevitLookup/Config/GeneralConfiguration.cs new file mode 100644 index 00000000..9f38ef72 --- /dev/null +++ b/source/RevitLookup/Config/GeneralConfiguration.cs @@ -0,0 +1,55 @@ +// Copyright 2003-2024 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using System.Text.Json.Serialization; +using Wpf.Ui.Appearance; +using Wpf.Ui.Controls; + +namespace RevitLookup.Config; + +/// +/// Settings options saved on disk +/// +[Serializable] +public sealed class GeneralConfiguration +{ + public int DefaultTransitionDuration => 200; + + [JsonPropertyName("Theme")] public ApplicationTheme Theme { get; set; } = ApplicationTheme.Light; + [JsonPropertyName("Background")] public WindowBackdropType Background { get; set; } = WindowBackdropType.None; + [JsonPropertyName("TransitionDuration")] public int TransitionDuration { get; set; } //= SettingsService.DefaultTransitionDuration; + + [JsonPropertyName("WindowWidth")] public double WindowWidth { get; set; } + [JsonPropertyName("WindowHeight")] public double WindowHeight { get; set; } + + [JsonPropertyName("IsUnsupportedAllowed")] public bool IncludeUnsupported { get; set; } + [JsonPropertyName("IsPrivateAllowed")] public bool IncludePrivate { get; set; } + [JsonPropertyName("IsStaticAllowed")] public bool IncludeStatic { get; set; } + [JsonPropertyName("IsFieldsAllowed")] public bool IncludeFields { get; set; } + [JsonPropertyName("IsEventsAllowed")] public bool IncludeEvents { get; set; } + [JsonPropertyName("IsExtensionsAllowed")] public bool IncludeExtensions { get; set; } + [JsonPropertyName("IsRootHierarchyAllowed")] public bool IncludeRootHierarchy { get; set; } + + [JsonPropertyName("IsHardwareRenderingAllowed")] public bool UseHardwareRendering { get; set; } = true; + [JsonPropertyName("IsTimeColumnAllowed")] public bool ShowTimeColumn { get; set; } + [JsonPropertyName("ShowMemoryColumn")] public bool ShowMemoryColumn { get; set; } + [JsonPropertyName("UseSizeRestoring")] public bool UseSizeRestoring { get; set; } + [JsonPropertyName("IsModifyTabAllowed")] public bool UseModifyTab { get; set; } +} \ No newline at end of file diff --git a/source/RevitLookup/Config/OptionsConfiguration.cs b/source/RevitLookup/Config/OptionsConfiguration.cs index 9ab737d2..a9eabcde 100644 --- a/source/RevitLookup/Config/OptionsConfiguration.cs +++ b/source/RevitLookup/Config/OptionsConfiguration.cs @@ -37,7 +37,8 @@ public static void AddOptions(this IServiceCollection services, ConfigurationMan options.RootFolder = rootPath; options.ConfigFolder = Path.Combine(rootPath, "Config"); options.DownloadFolder = Path.Combine(rootPath, "Downloads"); - options.SettingsPath = Path.Combine(rootPath, "Config", "Settings.cfg"); + options.GeneralSettingsPath = Path.Combine(rootPath, "Config", "Settings.cfg"); + options.RenderSettingsPath = Path.Combine(rootPath, "Config", "RenderSettings.cfg"); }); services.Configure(options => diff --git a/source/RevitLookup/Config/RenderConfiguration.cs b/source/RevitLookup/Config/RenderConfiguration.cs new file mode 100644 index 00000000..9746716f --- /dev/null +++ b/source/RevitLookup/Config/RenderConfiguration.cs @@ -0,0 +1,134 @@ +// Copyright 2003-2024 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using System.Text.Json.Serialization; +using System.Windows.Media; +using Color = System.Windows.Media.Color; + +namespace RevitLookup.Config; + +/// +/// Settings options saved on disk +/// +[Serializable] +public sealed class RenderConfiguration +{ + [JsonPropertyName("BoundingBoxSettings")] public BoundingBoxVisualizationSettings BoundingBoxSettings { get; set; } = new(); + [JsonPropertyName("FaceSettings")] public FaceVisualizationSettings FaceSettings { get; set; } = new(); + [JsonPropertyName("MeshSettings")] public MeshVisualizationSettings MeshSettings { get; set; } = new(); + [JsonPropertyName("PolylineSettings")] public PolylineVisualizationSettings PolylineSettings { get; set; } = new(); + [JsonPropertyName("SolidSettings")] public SolidVisualizationSettings SolidSettings { get; set; } = new(); + [JsonPropertyName("XyzSettings")] public XyzVisualizationSettings XyzSettings { get; set; } = new(); +} + +[Serializable] +public class BoundingBoxVisualizationSettings +{ + [JsonPropertyName("Transparency")] public double Transparency { get; set; } = 60; + + [JsonPropertyName("SurfaceColor")] public Color SurfaceColor { get; set; } = Colors.DodgerBlue; + [JsonPropertyName("EdgeColor")] public Color EdgeColor { get; set; } = Color.FromArgb(255, 30, 81, 255); + [JsonPropertyName("AxisColor")] public Color AxisColor { get; set; } = Color.FromArgb(255, 255, 89, 30); + + [JsonPropertyName("ShowSurface")] public bool ShowSurface { get; set; } = true; + [JsonPropertyName("ShowEdge")] public bool ShowEdge { get; set; } = true; + [JsonPropertyName("ShowAxis")] public bool ShowAxis { get; set; } = true; +} + +[Serializable] +public class FaceVisualizationSettings +{ + [JsonPropertyName("Transparency")] public double Transparency { get; set; } = 20; + [JsonPropertyName("Extrusion")] public double Extrusion { get; set; } = Context.Application.VertexTolerance * 12; + [JsonPropertyName("MinExtrusion")] public double MinExtrusion { get; set; } = Context.Application.VertexTolerance * 12; + + [JsonPropertyName("SurfaceColor")] public Color SurfaceColor { get; set; } = Colors.DodgerBlue; + [JsonPropertyName("MeshColor")] public Color MeshColor { get; set; } = Color.FromArgb(255, 30, 81, 255); + [JsonPropertyName("NormalVectorColor")] public Color NormalVectorColor { get; set; } = Color.FromArgb(255, 255, 89, 30); + + [JsonPropertyName("ShowSurface")] public bool ShowSurface { get; set; } = true; + [JsonPropertyName("ShowMeshGrid")] public bool ShowMeshGrid { get; set; } = true; + [JsonPropertyName("ShowNormalVector")] public bool ShowNormalVector { get; set; } = true; +} + +[Serializable] +public class MeshVisualizationSettings +{ + [JsonPropertyName("Transparency")] public double Transparency { get; set; } = 20; + [JsonPropertyName("Extrusion")] public double Extrusion { get; set; } = Context.Application.VertexTolerance * 12; + [JsonPropertyName("MinExtrusion")] public double MinExtrusion { get; set; } = Context.Application.VertexTolerance * 12; + + [JsonPropertyName("SurfaceColor")] public Color SurfaceColor { get; set; } = Colors.DodgerBlue; + [JsonPropertyName("MeshColor")] public Color MeshColor { get; set; } = Color.FromArgb(255, 30, 81, 255); + [JsonPropertyName("NormalVectorColor")] public Color NormalVectorColor { get; set; } = Color.FromArgb(255, 255, 89, 30); + + [JsonPropertyName("ShowSurface")] public bool ShowSurface { get; set; } = true; + [JsonPropertyName("ShowMeshGrid")] public bool ShowMeshGrid { get; set; } = true; + [JsonPropertyName("ShowNormalVector")] public bool ShowNormalVector { get; set; } = true; +} + +[Serializable] +public class PolylineVisualizationSettings +{ + [JsonPropertyName("Transparency")] public double Transparency { get; set; } = 20; + [JsonPropertyName("Diameter")] public double Diameter { get; set; } = 2; + [JsonPropertyName("MinThickness")] public double MinThickness { get; set; } = 0.1; + + [JsonPropertyName("SurfaceColor")] public Color SurfaceColor { get; set; } = Colors.DodgerBlue; + [JsonPropertyName("CurveColor")] public Color CurveColor { get; set; } = Color.FromArgb(255, 30, 81, 255); + [JsonPropertyName("DirectionColor")] public Color DirectionColor { get; set; } = Color.FromArgb(255, 255, 89, 30); + + [JsonPropertyName("ShowSurface")] public bool ShowSurface { get; set; } = true; + [JsonPropertyName("ShowCurve")] public bool ShowCurve { get; set; } = true; + [JsonPropertyName("ShowDirection")] public bool ShowDirection { get; set; } = true; +} + +public class SolidVisualizationSettings +{ + [JsonPropertyName("Transparency")] public double Transparency { get; set; } = 20; + [JsonPropertyName("CageTransparency")] public double CageTransparency { get; set; } = 90; + [JsonPropertyName("CageSize")] public double CageSize { get; set; } = 1; + + [JsonPropertyName("FaceColor")] public Color FaceColor { get; set; } = Colors.DodgerBlue; + [JsonPropertyName("EdgeColor")] public Color EdgeColor { get; set; } = Color.FromArgb(255, 30, 81, 255); + [JsonPropertyName("CageSurfaceColor")] public Color CageSurfaceColor { get; set; } = Color.FromArgb(255, 175, 175, 175); + [JsonPropertyName("CageFrameColor")] public Color CageFrameColor { get; set; } = Colors.Black; + + [JsonPropertyName("ShowFace")] public bool ShowFace { get; set; } = true; + [JsonPropertyName("ShowEdge")] public bool ShowEdge { get; set; } = true; + [JsonPropertyName("ShowCageSurface")] public bool ShowCageSurface { get; set; } = true; +} + +[Serializable] +public class XyzVisualizationSettings +{ + [JsonPropertyName("Transparency")] public double Transparency { get; set; } = 0; + [JsonPropertyName("AxisLength")] public double AxisLength { get; set; } = 6; + [JsonPropertyName("MinAxisLength")] public double MinAxisLength { get; set; } = 0.1; + + [JsonPropertyName("XColor")] public Color XColor { get; set; } = Color.FromArgb(255, 30, 227, 255); + [JsonPropertyName("YColor")] public Color YColor { get; set; } = Color.FromArgb(255, 30, 144, 255); + [JsonPropertyName("ZColor")] public Color ZColor { get; set; } = Color.FromArgb(255, 30, 81, 255); + + [JsonPropertyName("ShowPlane")] public bool ShowPlane { get; set; } = true; + [JsonPropertyName("ShowXAxis")] public bool ShowXAxis { get; set; } = true; + [JsonPropertyName("ShowYAxis")] public bool ShowYAxis { get; set; } = true; + [JsonPropertyName("ShowZAxis")] public bool ShowZAxis { get; set; } = true; +} \ No newline at end of file diff --git a/source/RevitLookup/Core/ComponentModel/DescriptorMap.cs b/source/RevitLookup/Core/ComponentModel/DescriptorMap.cs index 05835fc0..fabe663c 100644 --- a/source/RevitLookup/Core/ComponentModel/DescriptorMap.cs +++ b/source/RevitLookup/Core/ComponentModel/DescriptorMap.cs @@ -65,6 +65,7 @@ public static Descriptor FindDescriptor(object obj, Type type) ElementId value when type is null || type == typeof(ElementId) => new ElementIdDescriptor(value), GuidEnum value when type is null || type == typeof(GuidEnum) => new GuidEnumDescriptor(value), Definition value when type is null || type == typeof(Definition) => new DefinitionDescriptor(value), + XYZ value when type is null || type == typeof(XYZ) => new XyzDescriptor(value), //Enumerator DefinitionBindingMapIterator value => new DefinitionBindingMapIteratorDescriptor(value), @@ -80,6 +81,7 @@ public static Descriptor FindDescriptor(object obj, Type type) Curve value when type is null || type == typeof(Curve) => new CurveDescriptor(value), Edge value when type is null || type == typeof(Edge) => new EdgeDescriptor(value), Solid value when type is null || type == typeof(Solid) => new SolidDescriptor(value), + Mesh value when type is null || type == typeof(Mesh) => new MeshDescriptor(value), Face value when type is null || type == typeof(Face) => new FaceDescriptor(value), City value when type is null || type == typeof(City) => new CityDescriptor(value), PaperSize value when type is null || type == typeof(PaperSize) => new PaperSizeDescriptor(value), diff --git a/source/RevitLookup/Core/ComponentModel/Descriptors/BoundingBoxXyzDescriptor.cs b/source/RevitLookup/Core/ComponentModel/Descriptors/BoundingBoxXyzDescriptor.cs index c29e7878..40d067c7 100644 --- a/source/RevitLookup/Core/ComponentModel/Descriptors/BoundingBoxXyzDescriptor.cs +++ b/source/RevitLookup/Core/ComponentModel/Descriptors/BoundingBoxXyzDescriptor.cs @@ -19,12 +19,18 @@ // (Rights in Technical Data and Computer Software), as applicable. using System.Reflection; +using System.Windows.Controls; +using System.Windows.Input; +using Microsoft.Extensions.Logging; using RevitLookup.Core.Contracts; using RevitLookup.Core.Objects; +using RevitLookup.ViewModels.Contracts; +using RevitLookup.Views.Dialogs.Visualization; +using RevitLookup.Views.Extensions; namespace RevitLookup.Core.ComponentModel.Descriptors; -public sealed class BoundingBoxXyzDescriptor(BoundingBoxXYZ box) : Descriptor, IDescriptorResolver, IDescriptorExtension +public sealed class BoundingBoxXyzDescriptor(BoundingBoxXYZ box) : Descriptor, IDescriptorResolver, IDescriptorExtension, IDescriptorConnector { public Func Resolve(Document context, string target, ParameterInfo[] parameters) { @@ -99,6 +105,7 @@ public void RegisterExtensions(IExtensionManager manager) .Add(new XYZ(box.Max.X, box.Min.Y, box.Max.Z)) .Add(new XYZ(box.Max.X, box.Max.Y, box.Min.Z)) .Add(new XYZ(box.Max.X, box.Max.Y, box.Max.Z))); + manager.Register("Volume", _ => { var length = box.Max.X - box.Min.X; @@ -107,6 +114,7 @@ public void RegisterExtensions(IExtensionManager manager) return length * width * height; }); + manager.Register("SurfaceArea", _ => { var length = box.Max.X - box.Min.X; @@ -120,4 +128,27 @@ public void RegisterExtensions(IExtensionManager manager) return 2 * (area1 + area2 + area3); }); } + + public void RegisterMenu(ContextMenu contextMenu) + { + contextMenu.AddMenuItem("VisualizeMenuItem") + .SetCommand(box, async boxArg => + { + if (Context.UiDocument is null) return; + + var context = (ISnoopViewModel) contextMenu.DataContext; + + try + { + var dialog = context.ServiceProvider.GetService(); + await dialog.ShowAsync(boxArg); + } + catch (Exception exception) + { + var logger = context.ServiceProvider.GetService>(); + logger.LogError(exception, "VisualizationDialog error"); + } + }) + .SetShortcut(Key.F8); + } } \ No newline at end of file diff --git a/source/RevitLookup/Core/ComponentModel/Descriptors/CurveDescriptor.cs b/source/RevitLookup/Core/ComponentModel/Descriptors/CurveDescriptor.cs index eca716f9..ad99c818 100644 --- a/source/RevitLookup/Core/ComponentModel/Descriptors/CurveDescriptor.cs +++ b/source/RevitLookup/Core/ComponentModel/Descriptors/CurveDescriptor.cs @@ -21,12 +21,13 @@ using System.Globalization; using System.Reflection; using System.Windows.Controls; -#if REVIT2023_OR_GREATER using System.Windows.Input; -using RevitLookup.Views.Extensions; -#endif +using Microsoft.Extensions.Logging; using RevitLookup.Core.Contracts; using RevitLookup.Core.Objects; +using RevitLookup.ViewModels.Contracts; +using RevitLookup.Views.Dialogs.Visualization; +using RevitLookup.Views.Extensions; namespace RevitLookup.Core.ComponentModel.Descriptors; @@ -43,8 +44,17 @@ public CurveDescriptor(Curve curve) public void RegisterMenu(ContextMenu contextMenu) { #if REVIT2023_OR_GREATER + contextMenu.AddMenuItem("SelectMenuItem") + .SetCommand(_curve, curve => + { + if (Context.UiDocument is null) return; + if (curve.Reference is null) return; + + Application.ActionEventHandler.Raise(_ => { Context.UiDocument.Selection.SetReferences([curve.Reference]); }); + }) + .SetShortcut(Key.F6); + contextMenu.AddMenuItem("ShowMenuItem") - .SetHeader("Show curve") .SetCommand(_curve, curve => { if (Context.UiDocument is null) return; @@ -57,22 +67,29 @@ public void RegisterMenu(ContextMenu contextMenu) Context.UiDocument.Selection.SetReferences([curve.Reference]); }); }) - .SetShortcut(ModifierKeys.Alt, Key.F7); + .SetShortcut(Key.F7); +#endif - contextMenu.AddMenuItem("SelectMenuItem") - .SetHeader("Select curve") - .SetCommand(_curve, curve => + contextMenu.AddMenuItem("VisualizeMenuItem") + .SetAvailability(_curve.ApproximateLength > 1e-6) + .SetCommand(_curve, async curve => { if (Context.UiDocument is null) return; - if (curve.Reference is null) return; - Application.ActionEventHandler.Raise(_ => + var context = (ISnoopViewModel) contextMenu.DataContext; + + try { - Context.UiDocument.Selection.SetReferences([curve.Reference]); - }); + var dialog = context.ServiceProvider.GetService(); + await dialog.ShowAsync(curve); + } + catch (Exception exception) + { + var logger = context.ServiceProvider.GetService>(); + logger.LogError(exception, "VisualizationDialog error"); + } }) - .SetShortcut(ModifierKeys.Alt, Key.F8); -#endif + .SetShortcut(Key.F8); } public Func Resolve(Document context, string target, ParameterInfo[] parameters) diff --git a/source/RevitLookup/Core/ComponentModel/Descriptors/EdgeDescriptor.cs b/source/RevitLookup/Core/ComponentModel/Descriptors/EdgeDescriptor.cs index 4f501458..d482e7a3 100644 --- a/source/RevitLookup/Core/ComponentModel/Descriptors/EdgeDescriptor.cs +++ b/source/RevitLookup/Core/ComponentModel/Descriptors/EdgeDescriptor.cs @@ -20,34 +20,40 @@ using System.Globalization; using System.Windows.Controls; -#if REVIT2023_OR_GREATER using System.Windows.Input; -using RevitLookup.Views.Extensions; -#endif +using Microsoft.Extensions.Logging; using RevitLookup.Core.Contracts; using RevitLookup.Core.Objects; +using RevitLookup.ViewModels.Contracts; +using RevitLookup.Views.Dialogs.Visualization; +using RevitLookup.Views.Extensions; namespace RevitLookup.Core.ComponentModel.Descriptors; public sealed class EdgeDescriptor : Descriptor, IDescriptorCollector, IDescriptorConnector { -#if REVIT2023_OR_GREATER private readonly Edge _edge; - -#endif + public EdgeDescriptor(Edge edge) { -#if REVIT2023_OR_GREATER _edge = edge; -#endif Name = $"{edge.ApproximateLength.ToString(CultureInfo.InvariantCulture)} ft"; } - + public void RegisterMenu(ContextMenu contextMenu) { #if REVIT2023_OR_GREATER + contextMenu.AddMenuItem("SelectMenuItem") + .SetCommand(_edge, edge => + { + if (Context.UiDocument is null) return; + if (edge.Reference is null) return; + + Application.ActionEventHandler.Raise(_ => { Context.UiDocument.Selection.SetReferences([edge.Reference]); }); + }) + .SetShortcut(Key.F6); + contextMenu.AddMenuItem("ShowMenuItem") - .SetHeader("Show edge") .SetCommand(_edge, edge => { if (Context.UiDocument is null) return; @@ -60,21 +66,28 @@ public void RegisterMenu(ContextMenu contextMenu) Context.UiDocument.Selection.SetReferences([edge.Reference]); }); }) - .SetShortcut(ModifierKeys.Alt, Key.F7); + .SetShortcut(Key.F7); +#endif - contextMenu.AddMenuItem("SelectMenuItem") - .SetHeader("Select edge") - .SetCommand(_edge, edge => + contextMenu.AddMenuItem("VisualizeMenuItem") + .SetAvailability(_edge.ApproximateLength > 1e-6) + .SetCommand(_edge, async edge => { if (Context.UiDocument is null) return; - if (edge.Reference is null) return; - Application.ActionEventHandler.Raise(_ => + var context = (ISnoopViewModel) contextMenu.DataContext; + + try { - Context.UiDocument.Selection.SetReferences([edge.Reference]); - }); + var dialog = context.ServiceProvider.GetService(); + await dialog.ShowAsync(edge); + } + catch (Exception exception) + { + var logger = context.ServiceProvider.GetService>(); + logger.LogError(exception, "VisualizationDialog error"); + } }) - .SetShortcut(ModifierKeys.Alt, Key.F8); -#endif + .SetShortcut(Key.F8); } } \ No newline at end of file diff --git a/source/RevitLookup/Core/ComponentModel/Descriptors/ElementDescriptor.cs b/source/RevitLookup/Core/ComponentModel/Descriptors/ElementDescriptor.cs index f282e533..7066fc0b 100644 --- a/source/RevitLookup/Core/ComponentModel/Descriptors/ElementDescriptor.cs +++ b/source/RevitLookup/Core/ComponentModel/Descriptors/ElementDescriptor.cs @@ -286,10 +286,22 @@ public virtual void RegisterExtensions(IExtensionManager manager) public virtual void RegisterMenu(ContextMenu contextMenu) { + contextMenu.AddMenuItem("SelectMenuItem") + .SetCommand(_element, element => + { + if (Context.UiDocument is null) return; + if (!element.IsValidObject) return; + + Application.ActionEventHandler.Raise(_ => + { + Context.UiDocument.Selection.SetElementIds([element.Id]); + }); + }) + .SetShortcut(Key.F6); + if (_element is not ElementType && _element is not Family) { contextMenu.AddMenuItem("ShowMenuItem") - .SetHeader("Show element") .SetCommand(_element, element => { if (Context.UiDocument is null) return; @@ -301,25 +313,10 @@ public virtual void RegisterMenu(ContextMenu contextMenu) Context.UiDocument.Selection.SetElementIds([element.Id]); }); }) - .SetShortcut(ModifierKeys.Alt, Key.F7); + .SetShortcut(Key.F7); } - contextMenu.AddMenuItem("SelectMenuItem") - .SetHeader("Select element") - .SetCommand(_element, element => - { - if (Context.UiDocument is null) return; - if (!element.IsValidObject) return; - - Application.ActionEventHandler.Raise(_ => - { - Context.UiDocument.Selection.SetElementIds([element.Id]); - }); - }) - .SetShortcut(ModifierKeys.Alt, Key.F8); - contextMenu.AddMenuItem("DeleteMenuItem") - .SetHeader("Delete") .SetCommand(_element, async element => { if (Context.UiDocument is null) return; diff --git a/source/RevitLookup/Core/ComponentModel/Descriptors/FaceDescriptor.cs b/source/RevitLookup/Core/ComponentModel/Descriptors/FaceDescriptor.cs index bed28d2b..e7b6473d 100644 --- a/source/RevitLookup/Core/ComponentModel/Descriptors/FaceDescriptor.cs +++ b/source/RevitLookup/Core/ComponentModel/Descriptors/FaceDescriptor.cs @@ -20,34 +20,40 @@ using System.Globalization; using System.Windows.Controls; -#if REVIT2023_OR_GREATER using System.Windows.Input; -using RevitLookup.Views.Extensions; -#endif +using Microsoft.Extensions.Logging; using RevitLookup.Core.Contracts; using RevitLookup.Core.Objects; +using RevitLookup.ViewModels.Contracts; +using RevitLookup.Views.Dialogs.Visualization; +using RevitLookup.Views.Extensions; namespace RevitLookup.Core.ComponentModel.Descriptors; public sealed class FaceDescriptor : Descriptor, IDescriptorCollector, IDescriptorConnector { -#if REVIT2023_OR_GREATER private readonly Face _face; - -#endif + public FaceDescriptor(Face face) { -#if REVIT2023_OR_GREATER _face = face; -#endif Name = $"{face.Area.ToString(CultureInfo.InvariantCulture)} ft²"; } - + public void RegisterMenu(ContextMenu contextMenu) { #if REVIT2023_OR_GREATER + contextMenu.AddMenuItem("SelectMenuItem") + .SetCommand(_face, face => + { + if (Context.UiDocument is null) return; + if (face.Reference is null) return; + + Application.ActionEventHandler.Raise(_ => Context.UiDocument.Selection.SetReferences([face.Reference])); + }) + .SetShortcut(Key.F6); + contextMenu.AddMenuItem("ShowMenuItem") - .SetHeader("Show face") .SetCommand(_face, face => { if (Context.UiDocument is null) return; @@ -60,21 +66,28 @@ public void RegisterMenu(ContextMenu contextMenu) Context.UiDocument.Selection.SetReferences([face.Reference]); }); }) - .SetShortcut(ModifierKeys.Alt, Key.F7); + .SetShortcut(Key.F7); +#endif - contextMenu.AddMenuItem("SelectMenuItem") - .SetHeader("Select face") - .SetCommand(_face, face => + contextMenu.AddMenuItem("VisualizeMenuItem") + .SetAvailability(_face.Area > 1e-6) + .SetCommand(_face, async face => { if (Context.UiDocument is null) return; - if (face.Reference is null) return; - Application.ActionEventHandler.Raise(_ => + var context = (ISnoopViewModel) contextMenu.DataContext; + + try { - Context.UiDocument.Selection.SetReferences([face.Reference]); - }); + var dialog = context.ServiceProvider.GetService(); + await dialog.ShowAsync(face); + } + catch (Exception exception) + { + var logger = context.ServiceProvider.GetService>(); + logger.LogError(exception, "VisualizationDialog error"); + } }) - .SetShortcut(ModifierKeys.Alt, Key.F8); -#endif + .SetShortcut(Key.F8); } } \ No newline at end of file diff --git a/source/RevitLookup/Core/ComponentModel/Descriptors/FamilyDescriptor.cs b/source/RevitLookup/Core/ComponentModel/Descriptors/FamilyDescriptor.cs index 9cf113f8..9f7dc749 100644 --- a/source/RevitLookup/Core/ComponentModel/Descriptors/FamilyDescriptor.cs +++ b/source/RevitLookup/Core/ComponentModel/Descriptors/FamilyDescriptor.cs @@ -28,10 +28,7 @@ public sealed class FamilyDescriptor(Family family) : ElementDescriptor(family) { public override Func Resolve(Document context, string target, ParameterInfo[] parameters) { - return target switch - { - _ => null - }; + return null; } public override void RegisterExtensions(IExtensionManager manager) diff --git a/source/RevitLookup/Core/ComponentModel/Descriptors/FamilySizeTableDescriptor.cs b/source/RevitLookup/Core/ComponentModel/Descriptors/FamilySizeTableDescriptor.cs index 0da47ac6..fbddb8db 100644 --- a/source/RevitLookup/Core/ComponentModel/Descriptors/FamilySizeTableDescriptor.cs +++ b/source/RevitLookup/Core/ComponentModel/Descriptors/FamilySizeTableDescriptor.cs @@ -83,8 +83,8 @@ public void RegisterMenu(ContextMenu contextMenu) } catch (Exception exception) { - var logger = context.ServiceProvider.GetService>(); - logger.LogError(exception, "Initialize EditParameterDialog error"); + var logger = context.ServiceProvider.GetService>(); + logger.LogError(exception, "FamilySizeTableDialog error"); } }); } diff --git a/source/RevitLookup/Core/ComponentModel/Descriptors/MeshDescriptor.cs b/source/RevitLookup/Core/ComponentModel/Descriptors/MeshDescriptor.cs new file mode 100644 index 00000000..faaf85e1 --- /dev/null +++ b/source/RevitLookup/Core/ComponentModel/Descriptors/MeshDescriptor.cs @@ -0,0 +1,57 @@ +// Copyright 2003-2024 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using System.Windows.Controls; +using System.Windows.Input; +using Microsoft.Extensions.Logging; +using RevitLookup.Core.Contracts; +using RevitLookup.Core.Objects; +using RevitLookup.ViewModels.Contracts; +using RevitLookup.Views.Dialogs.Visualization; +using RevitLookup.Views.Extensions; + +namespace RevitLookup.Core.ComponentModel.Descriptors; + +public class MeshDescriptor(Mesh mesh) : Descriptor, IDescriptorCollector, IDescriptorConnector +{ + public void RegisterMenu(ContextMenu contextMenu) + { + contextMenu.AddMenuItem("VisualizeMenuItem") + .SetAvailability(mesh.Vertices.Count > 0) + .SetCommand(mesh, async meshArg => + { + if (Context.UiDocument is null) return; + + var context = (ISnoopViewModel) contextMenu.DataContext; + + try + { + var dialog = context.ServiceProvider.GetService(); + await dialog.ShowAsync(meshArg); + } + catch (Exception exception) + { + var logger = context.ServiceProvider.GetService>(); + logger.LogError(exception, "VisualizationDialog error"); + } + }) + .SetShortcut(Key.F8); + } +} \ No newline at end of file diff --git a/source/RevitLookup/Core/ComponentModel/Descriptors/ParameterDescriptor.cs b/source/RevitLookup/Core/ComponentModel/Descriptors/ParameterDescriptor.cs index ec627543..e6b6cd69 100644 --- a/source/RevitLookup/Core/ComponentModel/Descriptors/ParameterDescriptor.cs +++ b/source/RevitLookup/Core/ComponentModel/Descriptors/ParameterDescriptor.cs @@ -64,7 +64,7 @@ public Func Resolve(Document context, string target, ParameterInfo[] public void RegisterMenu(ContextMenu contextMenu) { - contextMenu.AddMenuItem() + contextMenu.AddMenuItem("EditMenuItem") .SetHeader("Edit value") .SetAvailability(!_parameter.IsReadOnly && _parameter.StorageType != StorageType.None) .SetCommand(_parameter, async parameter => diff --git a/source/RevitLookup/Core/ComponentModel/Descriptors/SolidDescriptor.cs b/source/RevitLookup/Core/ComponentModel/Descriptors/SolidDescriptor.cs index e7282a32..8e0ec5fa 100644 --- a/source/RevitLookup/Core/ComponentModel/Descriptors/SolidDescriptor.cs +++ b/source/RevitLookup/Core/ComponentModel/Descriptors/SolidDescriptor.cs @@ -20,12 +20,13 @@ using System.Globalization; using System.Windows.Controls; -#if REVIT2023_OR_GREATER using System.Windows.Input; -using RevitLookup.Views.Extensions; -#endif +using Microsoft.Extensions.Logging; using RevitLookup.Core.Contracts; using RevitLookup.Core.Objects; +using RevitLookup.ViewModels.Contracts; +using RevitLookup.Views.Dialogs.Visualization; +using RevitLookup.Views.Extensions; namespace RevitLookup.Core.ComponentModel.Descriptors; @@ -42,8 +43,7 @@ public SolidDescriptor(Solid solid) public void RegisterMenu(ContextMenu contextMenu) { #if REVIT2023_OR_GREATER - contextMenu.AddMenuItem("ShowMenuItem") - .SetHeader("Show solid") + contextMenu.AddMenuItem("SelectMenuItem") .SetCommand(_solid, solid => { if (Context.UiDocument is null) return; @@ -58,15 +58,12 @@ public void RegisterMenu(ContextMenu contextMenu) if (references.Count == 0) return; - var element = references[0].ElementId.ToElement(Context.Document); - if (element is not null) Context.UiDocument.ShowElements(element); Context.UiDocument.Selection.SetReferences(references); }); }) - .SetShortcut(ModifierKeys.Alt, Key.F7); + .SetShortcut(Key.F6); - contextMenu.AddMenuItem("SelectMenuItem") - .SetHeader("Select solid") + contextMenu.AddMenuItem("ShowMenuItem") .SetCommand(_solid, solid => { if (Context.UiDocument is null) return; @@ -81,11 +78,34 @@ public void RegisterMenu(ContextMenu contextMenu) if (references.Count == 0) return; + var element = references[0].ElementId.ToElement(Context.Document); + if (element is not null) Context.UiDocument.ShowElements(element); Context.UiDocument.Selection.SetReferences(references); }); }) - .SetShortcut(ModifierKeys.Alt, Key.F8); + .SetShortcut(Key.F7); #endif + + contextMenu.AddMenuItem("VisualizeMenuItem") + .SetAvailability(_solid.IsValidForTessellation()) + .SetCommand(_solid, async solid => + { + if (Context.UiDocument is null) return; + + var context = (ISnoopViewModel) contextMenu.DataContext; + + try + { + var dialog = context.ServiceProvider.GetService(); + await dialog.ShowAsync(solid); + } + catch (Exception exception) + { + var logger = context.ServiceProvider.GetService>(); + logger.LogError(exception, "VisualizationDialog error"); + } + }) + .SetShortcut(Key.F8); } public void RegisterExtensions(IExtensionManager manager) diff --git a/source/RevitLookup/Core/ComponentModel/Descriptors/XyzDescriptor.cs b/source/RevitLookup/Core/ComponentModel/Descriptors/XyzDescriptor.cs new file mode 100644 index 00000000..28c29a72 --- /dev/null +++ b/source/RevitLookup/Core/ComponentModel/Descriptors/XyzDescriptor.cs @@ -0,0 +1,65 @@ +// Copyright 2003-2024 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using System.Windows.Controls; +using System.Windows.Input; +using Microsoft.Extensions.Logging; +using RevitLookup.Core.Contracts; +using RevitLookup.Core.Objects; +using RevitLookup.ViewModels.Contracts; +using RevitLookup.Views.Dialogs.Visualization; +using RevitLookup.Views.Extensions; + +namespace RevitLookup.Core.ComponentModel.Descriptors; + +public class XyzDescriptor : Descriptor, IDescriptorConnector +{ + private readonly XYZ _point; + + public XyzDescriptor(XYZ point) + { + _point = point; + Name = point.ToString(); + } + + public void RegisterMenu(ContextMenu contextMenu) + { + contextMenu.AddMenuItem("VisualizeMenuItem") + .SetAvailability(!_point.IsUnitLength()) + .SetCommand(_point, async point => + { + if (Context.UiDocument is null) return; + + var context = (ISnoopViewModel) contextMenu.DataContext; + + try + { + var dialog = context.ServiceProvider.GetService(); + await dialog.ShowAsync(point); + } + catch (Exception exception) + { + var logger = context.ServiceProvider.GetService>(); + logger.LogError(exception, "VisualizationDialog error"); + } + }) + .SetShortcut(Key.F8); + } +} \ No newline at end of file diff --git a/source/RevitLookup/Core/Engine/DescriptorBuilder.cs b/source/RevitLookup/Core/Engine/DescriptorBuilder.cs index 5e56f2f3..1d1fdc54 100644 --- a/source/RevitLookup/Core/Engine/DescriptorBuilder.cs +++ b/source/RevitLookup/Core/Engine/DescriptorBuilder.cs @@ -20,6 +20,7 @@ using RevitLookup.Core.Diagnostic; using RevitLookup.Core.Objects; +using RevitLookup.Models.Settings; using RevitLookup.Services.Contracts; namespace RevitLookup.Core.Engine; @@ -27,7 +28,7 @@ namespace RevitLookup.Core.Engine; public sealed partial class DescriptorBuilder { private readonly List _descriptors; - private readonly ISettingsService _settings; + private readonly GeneralSettings _settings; private Descriptor _currentDescriptor; private object _obj; private Type _type; @@ -35,12 +36,12 @@ public sealed partial class DescriptorBuilder private readonly ClockDiagnoser _clockDiagnoser = new(); private readonly MemoryDiagnoser _memoryDiagnoser = new(); - + private DescriptorBuilder() { _descriptors = new List(16); - _settings = Host.GetService(); + _settings = Host.GetService().GeneralSettings; } - + public Document Context { get; private set; } } \ No newline at end of file diff --git a/source/RevitLookup/Core/Visualization/BoundingBoxVisualizationServer.cs b/source/RevitLookup/Core/Visualization/BoundingBoxVisualizationServer.cs new file mode 100644 index 00000000..7a823cf6 --- /dev/null +++ b/source/RevitLookup/Core/Visualization/BoundingBoxVisualizationServer.cs @@ -0,0 +1,287 @@ +// Copyright 2003-2024 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using Autodesk.Revit.DB.DirectContext3D; +using Autodesk.Revit.DB.ExternalService; +using RevitLookup.Core.Visualization.Helpers; +using RevitLookup.Models.Render; + +namespace RevitLookup.Core.Visualization; + +public sealed class BoundingBoxVisualizationServer : IDirectContext3DServer +{ + private BoundingBoxXYZ _box; + private bool _hasGeometryUpdates = true; + private bool _hasEffectsUpdates = true; + + private readonly Guid _guid = Guid.NewGuid(); + private readonly RenderingBufferStorage _surfaceBuffer = new(); + private readonly RenderingBufferStorage _edgeBuffer = new(); + + private readonly RenderingBufferStorage[] _axisBuffers = Enumerable.Range(0, 6) + .Select(_ => new RenderingBufferStorage()) + .ToArray(); + + private readonly XYZ[] _normals = + [ + XYZ.BasisX, + XYZ.BasisY, + XYZ.BasisZ + ]; + + private double _transparency; + + private bool _drawSurface; + private bool _drawEdge; + private bool _drawAxis; + + private Color _surfaceColor; + private Color _edgeColor; + private Color _axisColor; + + public Guid GetServerId() => _guid; + public string GetVendorId() => "RevitLookup"; + public string GetName() => "BoundingBoxXYZ visualization server"; + public string GetDescription() => "BoundingBoxXYZ geometry visualization"; + public ExternalServiceId GetServiceId() => ExternalServices.BuiltInExternalServices.DirectContext3DService; + public string GetApplicationId() => string.Empty; + public string GetSourceId() => string.Empty; + public bool UsesHandles() => false; + public bool CanExecute(View view) => true; + public bool UseInTransparentPass(View view) => _drawSurface && _transparency > 0; + + public Outline GetBoundingBox(View view) + { + return new Outline(_box.Min, _box.Max); + } + + public void RenderScene(View view, DisplayStyle displayStyle) + { + try + { + if (_hasGeometryUpdates || !_surfaceBuffer.IsValid() || !_edgeBuffer.IsValid()) + { + MapGeometryBuffer(); + _hasGeometryUpdates = false; + } + + if (_hasEffectsUpdates) + { + UpdateEffects(); + _hasEffectsUpdates = false; + } + + if (_drawSurface) + { + var isTransparentPass = DrawContext.IsTransparentPass(); + if (isTransparentPass && _transparency > 0 || !isTransparentPass && _transparency == 0) + { + DrawContext.FlushBuffer(_surfaceBuffer.VertexBuffer, + _surfaceBuffer.VertexBufferCount, + _surfaceBuffer.IndexBuffer, + _surfaceBuffer.IndexBufferCount, + _surfaceBuffer.VertexFormat, + _surfaceBuffer.EffectInstance, PrimitiveType.TriangleList, 0, + _surfaceBuffer.PrimitiveCount); + } + } + + if (_drawEdge) + { + DrawContext.FlushBuffer(_edgeBuffer.VertexBuffer, + _edgeBuffer.VertexBufferCount, + _edgeBuffer.IndexBuffer, + _edgeBuffer.IndexBufferCount, + _edgeBuffer.VertexFormat, + _edgeBuffer.EffectInstance, PrimitiveType.LineList, 0, + _edgeBuffer.PrimitiveCount); + } + + if (_drawAxis) + { + foreach (var buffer in _axisBuffers) + { + DrawContext.FlushBuffer(buffer.VertexBuffer, + buffer.VertexBufferCount, + buffer.IndexBuffer, + buffer.IndexBufferCount, + buffer.VertexFormat, + buffer.EffectInstance, PrimitiveType.LineList, 0, + buffer.PrimitiveCount); + } + } + } + catch (Exception exception) + { + RenderFailed?.Invoke(this, new RenderFailedEventArgs + { + Exception = exception + }); + } + } + + private void MapGeometryBuffer() + { + RenderHelper.MapBoundingBoxSurfaceBuffer(_surfaceBuffer, _box); + RenderHelper.MapBoundingBoxEdgeBuffer(_edgeBuffer, _box); + MapAxisBuffers(); + } + + private void MapAxisBuffers() + { + var unitVector = new XYZ(1, 1, 1); + var minPoint = _box.Transform.OfPoint(_box.Min); + var maxPoint = _box.Transform.OfPoint(_box.Max); + var axisLength = RenderGeometryHelper.InterpolateAxisLengthByPoints(minPoint, maxPoint); + + for (var i = 0; i < _normals.Length; i++) + { + var normal = _normals[i]; + var minBuffer = _axisBuffers[i]; + var maxBuffer = _axisBuffers[i + _normals.Length]; + + RenderHelper.MapNormalVectorBuffer(minBuffer, minPoint - unitVector * Context.Application.ShortCurveTolerance, normal, axisLength); + RenderHelper.MapNormalVectorBuffer(maxBuffer, maxPoint + unitVector * Context.Application.ShortCurveTolerance, -normal, axisLength); + } + } + + private void UpdateEffects() + { + _surfaceBuffer.EffectInstance ??= new EffectInstance(_surfaceBuffer.FormatBits); + _surfaceBuffer.EffectInstance.SetColor(_surfaceColor); + _surfaceBuffer.EffectInstance.SetTransparency(_transparency); + + _edgeBuffer.EffectInstance ??= new EffectInstance(_edgeBuffer.FormatBits); + _edgeBuffer.EffectInstance.SetColor(_edgeColor); + + foreach (var buffer in _axisBuffers) + { + buffer.EffectInstance ??= new EffectInstance(_edgeBuffer.FormatBits); + buffer.EffectInstance.SetColor(_axisColor); + } + } + + public void UpdateSurfaceColor(Color color) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _surfaceColor = color; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateEdgeColor(Color color) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _edgeColor = color; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateAxisColor(Color color) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _axisColor = color; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateTransparency(double value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _transparency = value; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + + public void UpdateSurfaceVisibility(bool visible) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _drawSurface = visible; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateEdgeVisibility(bool visible) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _drawEdge = visible; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateAxisVisibility(bool visible) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _drawAxis = visible; + + uiDocument.UpdateAllOpenViews(); + } + + public void Register(BoundingBoxXYZ box) + { + _box = box; + + Application.ActionEventHandler.Raise(application => + { + if (application.ActiveUIDocument is null) return; + + var directContextService = (MultiServerService) ExternalServiceRegistry.GetService(ExternalServices.BuiltInExternalServices.DirectContext3DService); + var serverIds = directContextService.GetActiveServerIds(); + + directContextService.AddServer(this); + serverIds.Add(GetServerId()); + directContextService.SetActiveServers(serverIds); + + application.ActiveUIDocument.UpdateAllOpenViews(); + }); + } + + public void Unregister() + { + Application.ActionEventHandler.Raise(application => + { + var directContextService = (MultiServerService) ExternalServiceRegistry.GetService(ExternalServices.BuiltInExternalServices.DirectContext3DService); + directContextService.RemoveServer(GetServerId()); + + application.ActiveUIDocument?.UpdateAllOpenViews(); + }); + } + + public event EventHandler RenderFailed; +} \ No newline at end of file diff --git a/source/RevitLookup/Core/Visualization/FaceVisualizationServer.cs b/source/RevitLookup/Core/Visualization/FaceVisualizationServer.cs new file mode 100644 index 00000000..57ec3744 --- /dev/null +++ b/source/RevitLookup/Core/Visualization/FaceVisualizationServer.cs @@ -0,0 +1,279 @@ +// Copyright 2003-2024 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using Autodesk.Revit.DB.DirectContext3D; +using Autodesk.Revit.DB.ExternalService; +using RevitLookup.Core.Visualization.Helpers; +using RevitLookup.Models.Render; + +namespace RevitLookup.Core.Visualization; + +public sealed class FaceVisualizationServer : IDirectContext3DServer +{ + private Face _face; + private bool _hasEffectsUpdates = true; + private bool _hasGeometryUpdates = true; + + private readonly Guid _guid = Guid.NewGuid(); + private readonly RenderingBufferStorage _meshGridBuffer = new(); + private readonly RenderingBufferStorage _normalBuffer = new(); + private readonly RenderingBufferStorage _surfaceBuffer = new(); + + private double _extrusion; + private double _transparency; + + private Color _meshColor; + private Color _normalColor; + private Color _surfaceColor; + + private bool _drawMeshGrid; + private bool _drawNormalVector; + private bool _drawSurface; + + public Guid GetServerId() => _guid; + public string GetVendorId() => "RevitLookup"; + public string GetName() => "Face visualization server"; + public string GetDescription() => "Face geometry visualization"; + public ExternalServiceId GetServiceId() => ExternalServices.BuiltInExternalServices.DirectContext3DService; + public string GetApplicationId() => string.Empty; + public string GetSourceId() => string.Empty; + public bool UsesHandles() => false; + public bool CanExecute(View view) => true; + public bool UseInTransparentPass(View view) => _drawSurface && _transparency > 0; + + public Outline GetBoundingBox(View view) + { + if (_face.Reference is null) return null; + + var element = _face.Reference.ElementId.ToElement(view.Document)!; + var boundingBox = element.get_BoundingBox(null) ?? element.get_BoundingBox(view); + if (boundingBox is null) return null; + + var minPoint = boundingBox.Transform.OfPoint(boundingBox.Min); + var maxPoint = boundingBox.Transform.OfPoint(boundingBox.Max); + + return new Outline(minPoint, maxPoint); + } + + public void RenderScene(View view, DisplayStyle displayStyle) + { + try + { + if (_hasGeometryUpdates || !_surfaceBuffer.IsValid() || !_meshGridBuffer.IsValid() || !_normalBuffer.IsValid()) + { + MapGeometryBuffer(); + _hasGeometryUpdates = false; + } + + if (_hasEffectsUpdates) + { + UpdateEffects(); + _hasEffectsUpdates = false; + } + + if (_drawSurface) + { + var isTransparentPass = DrawContext.IsTransparentPass(); + if (isTransparentPass && _transparency > 0 || !isTransparentPass && _transparency == 0) + { + DrawContext.FlushBuffer(_surfaceBuffer.VertexBuffer, + _surfaceBuffer.VertexBufferCount, + _surfaceBuffer.IndexBuffer, + _surfaceBuffer.IndexBufferCount, + _surfaceBuffer.VertexFormat, + _surfaceBuffer.EffectInstance, PrimitiveType.TriangleList, 0, + _surfaceBuffer.PrimitiveCount); + } + } + + if (_drawMeshGrid) + { + DrawContext.FlushBuffer(_meshGridBuffer.VertexBuffer, + _meshGridBuffer.VertexBufferCount, + _meshGridBuffer.IndexBuffer, + _meshGridBuffer.IndexBufferCount, + _meshGridBuffer.VertexFormat, + _meshGridBuffer.EffectInstance, PrimitiveType.LineList, 0, + _meshGridBuffer.PrimitiveCount); + } + + if (_drawNormalVector) + { + DrawContext.FlushBuffer(_normalBuffer.VertexBuffer, + _normalBuffer.VertexBufferCount, + _normalBuffer.IndexBuffer, + _normalBuffer.IndexBufferCount, + _normalBuffer.VertexFormat, + _normalBuffer.EffectInstance, PrimitiveType.LineList, 0, + _normalBuffer.PrimitiveCount); + } + } + catch (Exception exception) + { + RenderFailed?.Invoke(this, new RenderFailedEventArgs + { + Exception = exception + }); + } + } + + private void MapGeometryBuffer() + { + var mesh = _face.Triangulate(); + var faceBox = _face.GetBoundingBox(); + var center = (faceBox.Min + faceBox.Max) / 2; + var normal = _face.ComputeNormal(center); + var offset = RenderGeometryHelper.InterpolateOffsetByArea(_face.Area); + var normalLength = RenderGeometryHelper.InterpolateAxisLengthByArea(_face.Area); + + RenderHelper.MapSurfaceBuffer(_surfaceBuffer, mesh, _extrusion); + RenderHelper.MapMeshGridBuffer(_meshGridBuffer, mesh, _extrusion); + RenderHelper.MapNormalVectorBuffer(_normalBuffer, _face.Evaluate(center) + normal * (offset + _extrusion), normal, normalLength); + } + + private void UpdateEffects() + { + _surfaceBuffer.EffectInstance ??= new EffectInstance(_surfaceBuffer.FormatBits); + _meshGridBuffer.EffectInstance ??= new EffectInstance(_surfaceBuffer.FormatBits); + _normalBuffer.EffectInstance ??= new EffectInstance(_surfaceBuffer.FormatBits); + + _surfaceBuffer.EffectInstance.SetColor(_surfaceColor); + _meshGridBuffer.EffectInstance.SetColor(_meshColor); + _normalBuffer.EffectInstance.SetColor(_normalColor); + _surfaceBuffer.EffectInstance.SetTransparency(_transparency); + } + + public void UpdateSurfaceColor(Color value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _surfaceColor = value; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateMeshGridColor(Color value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _meshColor = value; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateNormalVectorColor(Color value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _normalColor = value; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateExtrusion(double value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _extrusion = value; + _hasGeometryUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateTransparency(double value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _transparency = value; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateSurfaceVisibility(bool visible) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _drawSurface = visible; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateMeshGridVisibility(bool visible) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _drawMeshGrid = visible; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateNormalVectorVisibility(bool visible) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _drawNormalVector = visible; + + uiDocument.UpdateAllOpenViews(); + } + + public void Register(Face face) + { + _face = face; + + Application.ActionEventHandler.Raise(application => + { + if (application.ActiveUIDocument is null) return; + + var directContextService = (MultiServerService) ExternalServiceRegistry.GetService(ExternalServices.BuiltInExternalServices.DirectContext3DService); + var serverIds = directContextService.GetActiveServerIds(); + + directContextService.AddServer(this); + serverIds.Add(GetServerId()); + directContextService.SetActiveServers(serverIds); + + application.ActiveUIDocument.UpdateAllOpenViews(); + }); + } + + public void Unregister() + { + Application.ActionEventHandler.Raise(application => + { + var directContextService = (MultiServerService) ExternalServiceRegistry.GetService(ExternalServices.BuiltInExternalServices.DirectContext3DService); + directContextService.RemoveServer(GetServerId()); + + application.ActiveUIDocument?.UpdateAllOpenViews(); + }); + } + + public event EventHandler RenderFailed; +} \ No newline at end of file diff --git a/source/RevitLookup/Core/Visualization/Helpers/RenderGeometryHelper.cs b/source/RevitLookup/Core/Visualization/Helpers/RenderGeometryHelper.cs new file mode 100644 index 00000000..846c2b86 --- /dev/null +++ b/source/RevitLookup/Core/Visualization/Helpers/RenderGeometryHelper.cs @@ -0,0 +1,201 @@ +// Copyright 2003-2024 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +namespace RevitLookup.Core.Visualization.Helpers; + +public static class RenderGeometryHelper +{ + public static List> GetSegmentationTube(IList vertices, double diameter) + { + var points = new List>(); + + for (var i = 0; i < vertices.Count; i++) + { + var center = vertices[i]; + XYZ normal; + if (i == 0) + { + normal = (vertices[i + 1] - center).Normalize(); + } + else if (i == vertices.Count - 1) + { + normal = (center - vertices[i - 1]).Normalize(); + } + else + { + normal = ((vertices[i + 1] - vertices[i - 1]) / 2.0).Normalize(); + } + + points.Add(TessellateCircle(center, normal, diameter / 2)); + } + + return points; + } + + public static XYZ GetMeshVertexNormal(Mesh mesh, int index, DistributionOfNormals normalDistribution) + { + switch (normalDistribution) + { + case DistributionOfNormals.AtEachPoint: + return mesh.GetNormal(index); + case DistributionOfNormals.OnEachFacet: + var vertex = mesh.Vertices[index]; + for (var i = 0; i < mesh.NumTriangles; i++) + { + var triangle = mesh.get_Triangle(i); + var triangleVertex = triangle.get_Vertex(0); + if (triangleVertex.IsAlmostEqualTo(vertex)) return mesh.GetNormal(i); + triangleVertex = triangle.get_Vertex(1); + if (triangleVertex.IsAlmostEqualTo(vertex)) return mesh.GetNormal(i); + triangleVertex = triangle.get_Vertex(2); + if (triangleVertex.IsAlmostEqualTo(vertex)) return mesh.GetNormal(i); + } + + return XYZ.Zero; + case DistributionOfNormals.OnePerFace: + return mesh.GetNormal(0); + default: + throw new ArgumentOutOfRangeException(nameof(normalDistribution), normalDistribution, null); + } + } + + public static List TessellateCircle(XYZ center, XYZ normal, double radius) + { + var vertices = new List(); + var segmentCount = InterpolateSegmentsCount(radius); + var xDirection = normal.CrossProduct(XYZ.BasisZ).Normalize() * radius; + if (xDirection.IsZeroLength()) + { + xDirection = normal.CrossProduct(XYZ.BasisX).Normalize() * radius; + } + + var yDirection = normal.CrossProduct(xDirection).Normalize() * radius; + + for (var i = 0; i < segmentCount; i++) + { + var angle = 2 * Math.PI * i / segmentCount; + var vertex = center + xDirection * Math.Cos(angle) + yDirection * Math.Sin(angle); + vertices.Add(vertex); + } + + return vertices; + } + + public static int InterpolateSegmentsCount(double diameter) + { + const int minSegments = 6; + const int maxSegments = 33; + const double minDiameter = 0.1 / 12d; + const double maxDiameter = 3 / 12d; + + if (diameter <= minDiameter) return minSegments; + if (diameter >= maxDiameter) return maxSegments; + + var normalDiameter = (diameter - minDiameter) / (maxDiameter - minDiameter); + return (int) (minSegments + normalDiameter * (maxSegments - minSegments)); + } + + public static double InterpolateOffsetByDiameter(double diameter) + { + const double minOffset = 0.01d; + const double maxOffset = 0.1d; + const double minDiameter = 0.1 / 12d; + const double maxDiameter = 3 / 12d; + + if (diameter <= minDiameter) return minOffset; + if (diameter >= maxDiameter) return maxOffset; + + var normalOffset = (diameter - minDiameter) / (maxDiameter - minDiameter); + return minOffset + normalOffset * (maxOffset - minOffset); + } + + public static double InterpolateOffsetByArea(double area) + { + const double minOffset = 0.01d; + const double maxOffset = 0.1d; + const double minArea = 0.01d; + const double maxArea = 1d; + + if (area <= minArea) return minOffset; + if (area >= maxArea) return maxOffset; + + var normalOffset = (area - minArea) / (maxArea - minArea); + return minOffset + normalOffset * (maxOffset - minOffset); + } + + public static double InterpolateAxisLengthByArea(double area) + { + const double minLength = 0.1d; + const double maxLength = 1d; + const double minArea = 0.01d; + const double maxArea = 1d; + + if (area <= minArea) return minLength; + if (area >= maxArea) return maxLength; + + var normalOffset = (area - minArea) / (maxArea - minArea); + return minLength + normalOffset * (maxLength - minLength); + } + + public static double InterpolateAxisLengthByPoints(XYZ min, XYZ max) + { + const double maxLength = 1d; + + var width = max.X - min.X; + var height = max.Y - min.Y; + var depth = max.Z - min.Z; + + var maxSize = Math.Max(width, Math.Max(height, depth)); + + if (maxLength * 2 < maxSize) + { + return maxLength; + } + + return maxSize * 0.35; + } + + public static double ComputeMeshSurfaceArea(Mesh mesh) + { +#if REVIT2024_OR_GREATER + return mesh.ComputeSurfaceArea(); +#else + var surfaceArea = 0.0; + + for (var i = 0; i < mesh.NumTriangles; i++) + { + var triangle = mesh.get_Triangle(i); + + var vertex0 = triangle.get_Vertex(0); + var vertex1 = triangle.get_Vertex(1); + var vertex2 = triangle.get_Vertex(2); + + var side1 = vertex1 - vertex0; + var side2 = vertex2 - vertex0; + + var crossProduct = side1.CrossProduct(side2); + + surfaceArea += crossProduct.GetLength() / 2.0; + } + + return surfaceArea; +#endif + } +} \ No newline at end of file diff --git a/source/RevitLookup/Core/Visualization/Helpers/RenderHelper.cs b/source/RevitLookup/Core/Visualization/Helpers/RenderHelper.cs new file mode 100644 index 00000000..bca04c90 --- /dev/null +++ b/source/RevitLookup/Core/Visualization/Helpers/RenderHelper.cs @@ -0,0 +1,514 @@ +// Copyright 2003-2024 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using Autodesk.Revit.DB.DirectContext3D; +using RevitLookup.Models.Render; + +namespace RevitLookup.Core.Visualization.Helpers; + +public static class RenderHelper +{ + public static void MapSurfaceBuffer(RenderingBufferStorage buffer, Mesh mesh, double thickness) + { + var vertexCount = mesh.Vertices.Count; + var triangleCount = mesh.NumTriangles; + + buffer.VertexBufferCount = vertexCount; + buffer.PrimitiveCount = triangleCount; + + var vertexBufferSizeInFloats = VertexPosition.GetSizeInFloats() * buffer.VertexBufferCount; + buffer.FormatBits = VertexFormatBits.Position; + buffer.VertexBuffer = new VertexBuffer(vertexBufferSizeInFloats); + buffer.VertexBuffer.Map(vertexBufferSizeInFloats); + + var vertexStream = buffer.VertexBuffer.GetVertexStreamPosition(); + var normals = new List(mesh.NumberOfNormals); + + for (var i = 0; i < mesh.Vertices.Count; i++) + { + var normal = RenderGeometryHelper.GetMeshVertexNormal(mesh, i, mesh.DistributionOfNormals); + normals.Add(normal); + } + + for (var i = 0; i < mesh.Vertices.Count; i++) + { + var vertex = mesh.Vertices[i]; + var normal = normals[i]; + var offsetVertex = vertex + normal * thickness; + var vertexPosition = new VertexPosition(offsetVertex); + vertexStream.AddVertex(vertexPosition); + } + + buffer.VertexBuffer.Unmap(); + buffer.IndexBufferCount = triangleCount * IndexTriangle.GetSizeInShortInts(); + buffer.IndexBuffer = new IndexBuffer(buffer.IndexBufferCount); + buffer.IndexBuffer.Map(buffer.IndexBufferCount); + + var indexStream = buffer.IndexBuffer.GetIndexStreamTriangle(); + + for (var i = 0; i < triangleCount; i++) + { + var meshTriangle = mesh.get_Triangle(i); + var index0 = (int) meshTriangle.get_Index(0); + var index1 = (int) meshTriangle.get_Index(1); + var index2 = (int) meshTriangle.get_Index(2); + indexStream.AddTriangle(new IndexTriangle(index0, index1, index2)); + } + + buffer.IndexBuffer.Unmap(); + buffer.VertexFormat = new VertexFormat(buffer.FormatBits); + } + + public static void MapCurveBuffer(RenderingBufferStorage buffer, IList vertices) + { + var vertexCount = vertices.Count; + + buffer.VertexBufferCount = vertexCount; + buffer.PrimitiveCount = vertexCount - 1; + + var vertexBufferSizeInFloats = VertexPosition.GetSizeInFloats() * buffer.VertexBufferCount; + buffer.FormatBits = VertexFormatBits.Position; + buffer.VertexBuffer = new VertexBuffer(vertexBufferSizeInFloats); + buffer.VertexBuffer.Map(vertexBufferSizeInFloats); + + var vertexStream = buffer.VertexBuffer.GetVertexStreamPosition(); + + foreach (var vertex in vertices) + { + var vertexPosition = new VertexPosition(vertex); + vertexStream.AddVertex(vertexPosition); + } + + buffer.VertexBuffer.Unmap(); + buffer.IndexBufferCount = (vertexCount - 1) * IndexLine.GetSizeInShortInts(); + buffer.IndexBuffer = new IndexBuffer(buffer.IndexBufferCount); + buffer.IndexBuffer.Map(buffer.IndexBufferCount); + + var indexStream = buffer.IndexBuffer.GetIndexStreamLine(); + + for (var i = 0; i < vertexCount - 1; i++) + { + indexStream.AddLine(new IndexLine(i, i + 1)); + } + + buffer.IndexBuffer.Unmap(); + buffer.VertexFormat = new VertexFormat(buffer.FormatBits); + } + + public static void MapCurveBuffer(RenderingBufferStorage buffer, IList vertices, double diameter) + { + var tubeSegments = RenderGeometryHelper.GetSegmentationTube(vertices, diameter); + var segmentVerticesCount = tubeSegments[0].Count; + var newVertexCount = vertices.Count * segmentVerticesCount; + + buffer.VertexBufferCount = newVertexCount; + buffer.PrimitiveCount = (vertices.Count - 1) * segmentVerticesCount * 4; + + var vertexBufferSizeInFloats = VertexPosition.GetSizeInFloats() * buffer.VertexBufferCount; + buffer.FormatBits = VertexFormatBits.Position; + buffer.VertexBuffer = new VertexBuffer(vertexBufferSizeInFloats); + buffer.VertexBuffer.Map(vertexBufferSizeInFloats); + + var vertexStream = buffer.VertexBuffer.GetVertexStreamPosition(); + + foreach (var segment in tubeSegments) + { + foreach (var point in segment) + { + var vertexPosition = new VertexPosition(point); + vertexStream.AddVertex(vertexPosition); + } + } + + buffer.VertexBuffer.Unmap(); + + buffer.IndexBufferCount = (vertices.Count - 1) * segmentVerticesCount * 4 * IndexLine.GetSizeInShortInts(); + buffer.IndexBuffer = new IndexBuffer(buffer.IndexBufferCount); + buffer.IndexBuffer.Map(buffer.IndexBufferCount); + + var indexStream = buffer.IndexBuffer.GetIndexStreamLine(); + + for (var i = 0; i < vertices.Count - 1; i++) + { + for (var j = 0; j < segmentVerticesCount; j++) + { + var currentStart = i * segmentVerticesCount + j; + var nextStart = (i + 1) * segmentVerticesCount + j; + var currentEnd = i * segmentVerticesCount + (j + 1) % segmentVerticesCount; + var nextEnd = (i + 1) * segmentVerticesCount + (j + 1) % segmentVerticesCount; + + // First triangle + indexStream.AddLine(new IndexLine(currentStart, nextStart)); + indexStream.AddLine(new IndexLine(nextStart, nextEnd)); + + // Second triangle + indexStream.AddLine(new IndexLine(nextEnd, currentEnd)); + indexStream.AddLine(new IndexLine(currentEnd, currentStart)); + } + } + + buffer.IndexBuffer.Unmap(); + buffer.VertexFormat = new VertexFormat(buffer.FormatBits); + } + + public static void MapCurveSurfaceBuffer(RenderingBufferStorage buffer, IList vertices, double diameter) + { + var tubeSegments = RenderGeometryHelper.GetSegmentationTube(vertices, diameter); + var segmentVerticesCount = tubeSegments[0].Count; + var newVertexCount = vertices.Count * segmentVerticesCount; + + buffer.VertexBufferCount = newVertexCount; + buffer.PrimitiveCount = (vertices.Count - 1) * segmentVerticesCount * 2; + + var vertexBufferSizeInFloats = VertexPosition.GetSizeInFloats() * buffer.VertexBufferCount; + buffer.FormatBits = VertexFormatBits.Position; + buffer.VertexBuffer = new VertexBuffer(vertexBufferSizeInFloats); + buffer.VertexBuffer.Map(vertexBufferSizeInFloats); + + var vertexStream = buffer.VertexBuffer.GetVertexStreamPosition(); + + foreach (var segment in tubeSegments) + { + foreach (var point in segment) + { + var vertexPosition = new VertexPosition(point); + vertexStream.AddVertex(vertexPosition); + } + } + + buffer.VertexBuffer.Unmap(); + + buffer.IndexBufferCount = (vertices.Count - 1) * segmentVerticesCount * 6 * IndexTriangle.GetSizeInShortInts(); + buffer.IndexBuffer = new IndexBuffer(buffer.IndexBufferCount); + buffer.IndexBuffer.Map(buffer.IndexBufferCount); + + var indexStream = buffer.IndexBuffer.GetIndexStreamTriangle(); + + for (var i = 0; i < vertices.Count - 1; i++) + { + for (var j = 0; j < segmentVerticesCount; j++) + { + var currentStart = i * segmentVerticesCount + j; + var nextStart = (i + 1) * segmentVerticesCount + j; + var currentEnd = i * segmentVerticesCount + (j + 1) % segmentVerticesCount; + var nextEnd = (i + 1) * segmentVerticesCount + (j + 1) % segmentVerticesCount; + + // First triangle + indexStream.AddTriangle(new IndexTriangle(currentStart, nextStart, nextEnd)); + + // Second triangle + indexStream.AddTriangle(new IndexTriangle(nextEnd, currentEnd, currentStart)); + } + } + + buffer.IndexBuffer.Unmap(); + buffer.VertexFormat = new VertexFormat(buffer.FormatBits); + } + + public static void MapMeshGridBuffer(RenderingBufferStorage buffer, Mesh mesh, double thickness) + { + var vertexCount = mesh.Vertices.Count; + var triangleCount = mesh.NumTriangles; + + buffer.VertexBufferCount = vertexCount * 2; + buffer.PrimitiveCount = 3 * triangleCount * 2 + mesh.Vertices.Count; + + var vertexBufferSizeInFloats = VertexPosition.GetSizeInFloats() * buffer.VertexBufferCount; + buffer.FormatBits = VertexFormatBits.Position; + buffer.VertexBuffer = new VertexBuffer(vertexBufferSizeInFloats); + buffer.VertexBuffer.Map(vertexBufferSizeInFloats); + + var vertexStream = buffer.VertexBuffer.GetVertexStreamPosition(); + var normals = new List(mesh.NumberOfNormals); + + for (var i = 0; i < mesh.Vertices.Count; i++) + { + var normal = RenderGeometryHelper.GetMeshVertexNormal(mesh, i, mesh.DistributionOfNormals); + normals.Add(normal); + } + + foreach (var vertex in mesh.Vertices) + { + var vertexPosition = new VertexPosition(vertex); + vertexStream.AddVertex(vertexPosition); + } + + for (var i = 0; i < mesh.Vertices.Count; i++) + { + var vertex = mesh.Vertices[i]; + var normal = normals[i]; + var offsetVertex = vertex + normal * thickness; + var vertexPosition = new VertexPosition(offsetVertex); + vertexStream.AddVertex(vertexPosition); + } + + buffer.VertexBuffer.Unmap(); + buffer.IndexBufferCount = (3 * triangleCount * 2 + mesh.Vertices.Count) * IndexLine.GetSizeInShortInts(); + buffer.IndexBuffer = new IndexBuffer(buffer.IndexBufferCount); + buffer.IndexBuffer.Map(buffer.IndexBufferCount); + + var indexStream = buffer.IndexBuffer.GetIndexStreamLine(); + + for (var i = 0; i < triangleCount; i++) + { + var meshTriangle = mesh.get_Triangle(i); + var index0 = (int) meshTriangle.get_Index(0); + var index1 = (int) meshTriangle.get_Index(1); + var index2 = (int) meshTriangle.get_Index(2); + + indexStream.AddLine(new IndexLine(index0, index1)); + indexStream.AddLine(new IndexLine(index1, index2)); + indexStream.AddLine(new IndexLine(index2, index0)); + } + + for (var i = 0; i < triangleCount; i++) + { + var meshTriangle = mesh.get_Triangle(i); + var index0 = (int) meshTriangle.get_Index(0) + vertexCount; + var index1 = (int) meshTriangle.get_Index(1) + vertexCount; + var index2 = (int) meshTriangle.get_Index(2) + vertexCount; + + indexStream.AddLine(new IndexLine(index0, index1)); + indexStream.AddLine(new IndexLine(index1, index2)); + indexStream.AddLine(new IndexLine(index2, index0)); + } + + for (var i = 0; i < mesh.Vertices.Count; i++) + { + indexStream.AddLine(new IndexLine(i, i + mesh.Vertices.Count)); + } + + buffer.IndexBuffer.Unmap(); + buffer.VertexFormat = new VertexFormat(buffer.FormatBits); + } + + public static void MapSideBuffer(RenderingBufferStorage buffer, XYZ min, XYZ max) + { + var vertexCount = 4; + var normal = (max - min).Normalize(); + var length = (max - min).GetLength() / 2; + + XYZ point1; + XYZ point2; + XYZ point3; + XYZ point4; + if (normal.IsAlmostEqualTo(XYZ.BasisX)) + { + point1 = new XYZ(min.X, min.Y - length, min.Z); + point2 = new XYZ(min.X, min.Y + length, min.Z); + point3 = new XYZ(max.X, max.Y - length, max.Z); + point4 = new XYZ(max.X, max.Y + length, max.Z); + } + else if (normal.IsAlmostEqualTo(XYZ.BasisY)) + { + point1 = new XYZ(min.X, min.Y, min.Z - length); + point2 = new XYZ(min.X, min.Y, min.Z + length); + point3 = new XYZ(max.X, max.Y, max.Z - length); + point4 = new XYZ(max.X, max.Y, max.Z + length); + } + else + { + point1 = new XYZ(min.X - length, min.Y, min.Z); + point2 = new XYZ(min.X + length, min.Y, min.Z); + point3 = new XYZ(max.X - length, max.Y, max.Z); + point4 = new XYZ(max.X + length, max.Y, max.Z); + } + + buffer.VertexBufferCount = vertexCount; + buffer.PrimitiveCount = 2; + + var vertexBufferSizeInFloats = VertexPosition.GetSizeInFloats() * buffer.VertexBufferCount; + buffer.FormatBits = VertexFormatBits.Position; + buffer.VertexBuffer = new VertexBuffer(vertexBufferSizeInFloats); + buffer.VertexBuffer.Map(vertexBufferSizeInFloats); + + var vertexStream = buffer.VertexBuffer.GetVertexStreamPosition(); + vertexStream.AddVertex(new VertexPosition(point1)); + vertexStream.AddVertex(new VertexPosition(point2)); + vertexStream.AddVertex(new VertexPosition(point3)); + vertexStream.AddVertex(new VertexPosition(point4)); + + buffer.VertexBuffer.Unmap(); + buffer.IndexBufferCount = 2 * IndexTriangle.GetSizeInShortInts(); + buffer.IndexBuffer = new IndexBuffer(buffer.IndexBufferCount); + buffer.IndexBuffer.Map(buffer.IndexBufferCount); + + var indexStream = buffer.IndexBuffer.GetIndexStreamTriangle(); + indexStream.AddTriangle(new IndexTriangle(0, 1, 2)); + indexStream.AddTriangle(new IndexTriangle(1, 2, 3)); + + buffer.IndexBuffer.Unmap(); + buffer.VertexFormat = new VertexFormat(buffer.FormatBits); + } + + public static void MapBoundingBoxSurfaceBuffer(RenderingBufferStorage buffer, BoundingBoxXYZ box) + { + var minPoint = box.Transform.OfPoint(box.Min); + var maxPoint = box.Transform.OfPoint(box.Max); + + XYZ[] corners = + [ + new XYZ(minPoint.X, minPoint.Y, minPoint.Z), + new XYZ(maxPoint.X, minPoint.Y, minPoint.Z), + new XYZ(maxPoint.X, maxPoint.Y, minPoint.Z), + new XYZ(minPoint.X, maxPoint.Y, minPoint.Z), + new XYZ(minPoint.X, minPoint.Y, maxPoint.Z), + new XYZ(maxPoint.X, minPoint.Y, maxPoint.Z), + new XYZ(maxPoint.X, maxPoint.Y, maxPoint.Z), + new XYZ(minPoint.X, maxPoint.Y, maxPoint.Z) + ]; + + int[] triangles = + [ + 0, 1, 2, 2, 3, 0, // bottom face + 4, 5, 6, 6, 7, 4, // top face + 0, 4, 5, 5, 1, 0, // front face + 1, 5, 6, 6, 2, 1, // right face + 2, 6, 7, 7, 3, 2, // back face + 3, 7, 4, 4, 0, 3 // left face + ]; + + buffer.VertexBufferCount = corners.Length; + buffer.PrimitiveCount = triangles.Length / 3; + + var vertexBufferSizeInFloats = VertexPosition.GetSizeInFloats() * buffer.VertexBufferCount; + buffer.FormatBits = VertexFormatBits.Position; + buffer.VertexBuffer = new VertexBuffer(vertexBufferSizeInFloats); + buffer.VertexBuffer.Map(vertexBufferSizeInFloats); + + var vertexStream = buffer.VertexBuffer.GetVertexStreamPosition(); + + foreach (var corner in corners) + { + var vertexPosition = new VertexPosition(corner); + vertexStream.AddVertex(vertexPosition); + } + + buffer.VertexBuffer.Unmap(); + + buffer.IndexBufferCount = triangles.Length * IndexTriangle.GetSizeInShortInts(); + buffer.IndexBuffer = new IndexBuffer(buffer.IndexBufferCount); + buffer.IndexBuffer.Map(buffer.IndexBufferCount); + + var indexStream = buffer.IndexBuffer.GetIndexStreamTriangle(); + + for (var i = 0; i < triangles.Length; i += 3) + { + indexStream.AddTriangle(new IndexTriangle(triangles[i], triangles[i + 1], triangles[i + 2])); + } + + buffer.IndexBuffer.Unmap(); + buffer.VertexFormat = new VertexFormat(buffer.FormatBits); + } + + public static void MapBoundingBoxEdgeBuffer(RenderingBufferStorage buffer, BoundingBoxXYZ box) + { + var minPoint = box.Transform.OfPoint(box.Min); + var maxPoint = box.Transform.OfPoint(box.Max); + + XYZ[] corners = + [ + new XYZ(minPoint.X, minPoint.Y, minPoint.Z), + new XYZ(maxPoint.X, minPoint.Y, minPoint.Z), + new XYZ(maxPoint.X, maxPoint.Y, minPoint.Z), + new XYZ(minPoint.X, maxPoint.Y, minPoint.Z), + new XYZ(minPoint.X, minPoint.Y, maxPoint.Z), + new XYZ(maxPoint.X, minPoint.Y, maxPoint.Z), + new XYZ(maxPoint.X, maxPoint.Y, maxPoint.Z), + new XYZ(minPoint.X, maxPoint.Y, maxPoint.Z) + ]; + + int[] edges = + [ + 0, 1, 1, 2, 2, 3, 3, 0, // bottom face + 4, 5, 5, 6, 6, 7, 7, 4, // top face + 0, 4, 1, 5, 2, 6, 3, 7 // vertical edges + ]; + + buffer.VertexBufferCount = corners.Length; + buffer.PrimitiveCount = edges.Length / 2; + + var vertexBufferSizeInFloats = VertexPosition.GetSizeInFloats() * buffer.VertexBufferCount; + buffer.FormatBits = VertexFormatBits.Position; + buffer.VertexBuffer = new VertexBuffer(vertexBufferSizeInFloats); + buffer.VertexBuffer.Map(vertexBufferSizeInFloats); + + var vertexStream = buffer.VertexBuffer.GetVertexStreamPosition(); + + foreach (var corner in corners) + { + var vertexPosition = new VertexPosition(corner); + vertexStream.AddVertex(vertexPosition); + } + + buffer.VertexBuffer.Unmap(); + + buffer.IndexBufferCount = edges.Length * IndexLine.GetSizeInShortInts(); + buffer.IndexBuffer = new IndexBuffer(buffer.IndexBufferCount); + buffer.IndexBuffer.Map(buffer.IndexBufferCount); + + var indexStream = buffer.IndexBuffer.GetIndexStreamLine(); + + for (var i = 0; i < edges.Length; i += 2) + { + indexStream.AddLine(new IndexLine(edges[i], edges[i + 1])); + } + + buffer.IndexBuffer.Unmap(); + buffer.VertexFormat = new VertexFormat(buffer.FormatBits); + } + + public static void MapNormalVectorBuffer(RenderingBufferStorage buffer, XYZ origin, XYZ vector, double length) + { + var headSize = length > 1 ? 0.2 : length * 0.2; + + var endPoint = origin + vector * length; + var arrowHeadBase = endPoint - vector * headSize; + var basisVector = Math.Abs(vector.Z).IsAlmostEqual(1) ? XYZ.BasisX : XYZ.BasisZ; + var perpendicular1 = vector.CrossProduct(basisVector).Normalize().Multiply(headSize * 0.5); + + buffer.VertexBufferCount = 4; + buffer.PrimitiveCount = 3; + + var vertexBufferSizeInFloats = 4 * VertexPosition.GetSizeInFloats(); + buffer.FormatBits = VertexFormatBits.Position; + buffer.VertexBuffer = new VertexBuffer(vertexBufferSizeInFloats); + buffer.VertexBuffer.Map(vertexBufferSizeInFloats); + + var vertexStream = buffer.VertexBuffer.GetVertexStreamPosition(); + vertexStream.AddVertex(new VertexPosition(origin)); + vertexStream.AddVertex(new VertexPosition(endPoint)); + vertexStream.AddVertex(new VertexPosition(arrowHeadBase + perpendicular1)); + vertexStream.AddVertex(new VertexPosition(arrowHeadBase - perpendicular1)); + + buffer.VertexBuffer.Unmap(); + buffer.IndexBufferCount = 3 * IndexLine.GetSizeInShortInts(); + buffer.IndexBuffer = new IndexBuffer(buffer.IndexBufferCount); + buffer.IndexBuffer.Map(buffer.IndexBufferCount); + + var indexStream = buffer.IndexBuffer.GetIndexStreamLine(); + indexStream.AddLine(new IndexLine(0, 1)); + indexStream.AddLine(new IndexLine(1, 2)); + indexStream.AddLine(new IndexLine(1, 3)); + + buffer.IndexBuffer.Unmap(); + buffer.VertexFormat = new VertexFormat(buffer.FormatBits); + } +} \ No newline at end of file diff --git a/source/RevitLookup/Core/Visualization/MeshVisualizationServer.cs b/source/RevitLookup/Core/Visualization/MeshVisualizationServer.cs new file mode 100644 index 00000000..43311c02 --- /dev/null +++ b/source/RevitLookup/Core/Visualization/MeshVisualizationServer.cs @@ -0,0 +1,287 @@ +// Copyright 2003-2024 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using Autodesk.Revit.DB.DirectContext3D; +using Autodesk.Revit.DB.ExternalService; +using RevitLookup.Core.Visualization.Helpers; +using RevitLookup.Models.Render; + +namespace RevitLookup.Core.Visualization; + +public sealed class MeshVisualizationServer : IDirectContext3DServer +{ + private Mesh _mesh; + private bool _hasEffectsUpdates = true; + private bool _hasGeometryUpdates = true; + + private readonly Guid _guid = Guid.NewGuid(); + + private RenderingBufferStorage[] _normalBuffers; + private readonly RenderingBufferStorage _surfaceBuffer = new(); + private readonly RenderingBufferStorage _meshGridBuffer = new(); + + private double _extrusion; + private double _transparency; + + private bool _drawMeshGrid; + private bool _drawNormalVector; + private bool _drawSurface; + + private Color _meshColor; + private Color _normalColor; + private Color _surfaceColor; + + public Guid GetServerId() => _guid; + public string GetVendorId() => "RevitLookup"; + public string GetName() => "Mesh visualization server"; + public string GetDescription() => "Mesh geometry visualization"; + public ExternalServiceId GetServiceId() => ExternalServices.BuiltInExternalServices.DirectContext3DService; + public string GetApplicationId() => string.Empty; + public string GetSourceId() => string.Empty; + public bool UsesHandles() => false; + public bool CanExecute(View view) => true; + public bool UseInTransparentPass(View view) => _drawSurface && _transparency > 0; + public Outline GetBoundingBox(View view) => null; + + public void RenderScene(View view, DisplayStyle displayStyle) + { + try + { + if (_hasGeometryUpdates || !_surfaceBuffer.IsValid() || !_meshGridBuffer.IsValid() || _normalBuffers.Any(storage => !storage.IsValid())) + { + MapGeometryBuffer(); + _hasGeometryUpdates = false; + } + + if (_hasEffectsUpdates) + { + UpdateEffects(); + _hasEffectsUpdates = false; + } + + if (_drawSurface) + { + var isTransparentPass = DrawContext.IsTransparentPass(); + if (isTransparentPass && _transparency > 0 || !isTransparentPass && _transparency == 0) + { + DrawContext.FlushBuffer(_surfaceBuffer.VertexBuffer, + _surfaceBuffer.VertexBufferCount, + _surfaceBuffer.IndexBuffer, + _surfaceBuffer.IndexBufferCount, + _surfaceBuffer.VertexFormat, + _surfaceBuffer.EffectInstance, PrimitiveType.TriangleList, 0, + _surfaceBuffer.PrimitiveCount); + } + } + + if (_drawMeshGrid) + { + DrawContext.FlushBuffer(_meshGridBuffer.VertexBuffer, + _meshGridBuffer.VertexBufferCount, + _meshGridBuffer.IndexBuffer, + _meshGridBuffer.IndexBufferCount, + _meshGridBuffer.VertexFormat, + _meshGridBuffer.EffectInstance, PrimitiveType.LineList, 0, + _meshGridBuffer.PrimitiveCount); + } + + if (_drawNormalVector) + { + foreach (var buffer in _normalBuffers) + { + DrawContext.FlushBuffer(buffer.VertexBuffer, + buffer.VertexBufferCount, + buffer.IndexBuffer, + buffer.IndexBufferCount, + buffer.VertexFormat, + buffer.EffectInstance, PrimitiveType.LineList, 0, + buffer.PrimitiveCount); + } + } + } + catch (Exception exception) + { + RenderFailed?.Invoke(this, new RenderFailedEventArgs + { + Exception = exception + }); + } + } + + private void MapGeometryBuffer() + { + RenderHelper.MapSurfaceBuffer(_surfaceBuffer, _mesh, _extrusion); + RenderHelper.MapMeshGridBuffer(_meshGridBuffer, _mesh, _extrusion); + MapNormalsBuffer(); + } + + private void MapNormalsBuffer() + { + var area = RenderGeometryHelper.ComputeMeshSurfaceArea(_mesh); + var offset = RenderGeometryHelper.InterpolateOffsetByArea(area); + var normalLength = RenderGeometryHelper.InterpolateAxisLengthByArea(area); + + for (var i = 0; i < _mesh.Vertices.Count; i++) + { + var vertex = _mesh.Vertices[i]; + var buffer = _normalBuffers[i]; + var normal = RenderGeometryHelper.GetMeshVertexNormal(_mesh, i, _mesh.DistributionOfNormals); + + RenderHelper.MapNormalVectorBuffer(buffer, vertex + normal * (offset + _extrusion), normal, normalLength); + } + } + + private void UpdateEffects() + { + _surfaceBuffer.EffectInstance ??= new EffectInstance(_surfaceBuffer.FormatBits); + _meshGridBuffer.EffectInstance ??= new EffectInstance(_surfaceBuffer.FormatBits); + + _surfaceBuffer.EffectInstance.SetColor(_surfaceColor); + _meshGridBuffer.EffectInstance.SetColor(_meshColor); + _surfaceBuffer.EffectInstance.SetTransparency(_transparency); + + foreach (var normalBuffer in _normalBuffers) + { + normalBuffer.EffectInstance ??= new EffectInstance(_surfaceBuffer.FormatBits); + normalBuffer.EffectInstance.SetColor(_normalColor); + } + } + + public void UpdateSurfaceColor(Color value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _surfaceColor = value; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateMeshGridColor(Color value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _meshColor = value; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateNormalVectorColor(Color value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _normalColor = value; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateExtrusion(double value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _extrusion = value; + _hasGeometryUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateTransparency(double value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _transparency = value; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + + public void UpdateSurfaceVisibility(bool visible) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _drawSurface = visible; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateMeshGridVisibility(bool visible) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _drawMeshGrid = visible; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateNormalVectorVisibility(bool visible) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _drawNormalVector = visible; + + uiDocument.UpdateAllOpenViews(); + } + + public void Register(Mesh mesh) + { + _mesh = mesh; + _normalBuffers = Enumerable.Range(0, _mesh.Vertices.Count) + .Select(_ => new RenderingBufferStorage()) + .ToArray(); + + Application.ActionEventHandler.Raise(application => + { + if (application.ActiveUIDocument is null) return; + + var directContextService = (MultiServerService) ExternalServiceRegistry.GetService(ExternalServices.BuiltInExternalServices.DirectContext3DService); + var serverIds = directContextService.GetActiveServerIds(); + + directContextService.AddServer(this); + serverIds.Add(GetServerId()); + directContextService.SetActiveServers(serverIds); + + application.ActiveUIDocument.UpdateAllOpenViews(); + }); + } + + public void Unregister() + { + Application.ActionEventHandler.Raise(application => + { + var directContextService = (MultiServerService) ExternalServiceRegistry.GetService(ExternalServices.BuiltInExternalServices.DirectContext3DService); + directContextService.RemoveServer(GetServerId()); + + application.ActiveUIDocument?.UpdateAllOpenViews(); + }); + } + + public event EventHandler RenderFailed; +} \ No newline at end of file diff --git a/source/RevitLookup/Core/Visualization/PolylineVisualizationServer.cs b/source/RevitLookup/Core/Visualization/PolylineVisualizationServer.cs new file mode 100644 index 00000000..6d9b8ca5 --- /dev/null +++ b/source/RevitLookup/Core/Visualization/PolylineVisualizationServer.cs @@ -0,0 +1,318 @@ +// Copyright 2003-2024 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using Autodesk.Revit.DB.DirectContext3D; +using Autodesk.Revit.DB.ExternalService; +using RevitLookup.Core.Visualization.Helpers; +using RevitLookup.Models.Render; + +namespace RevitLookup.Core.Visualization; + +public sealed class PolylineVisualizationServer : IDirectContext3DServer +{ + private IList _vertices; + private bool _hasEffectsUpdates = true; + private bool _hasGeometryUpdates = true; + + private readonly Guid _guid = Guid.NewGuid(); + + private readonly RenderingBufferStorage _surfaceBuffer = new(); + private readonly RenderingBufferStorage _curveBuffer = new(); + private readonly List _normalsBuffers = new(1); + + private double _transparency; + private double _diameter; + + private Color _surfaceColor; + private Color _curveColor; + private Color _directionColor; + + private bool _drawCurve; + private bool _drawDirection; + private bool _drawSurface; + + public Guid GetServerId() => _guid; + public string GetVendorId() => "RevitLookup"; + public string GetName() => "Polyline visualization server"; + public string GetDescription() => "Polyline geometry visualization"; + public ExternalServiceId GetServiceId() => ExternalServices.BuiltInExternalServices.DirectContext3DService; + public string GetApplicationId() => string.Empty; + public string GetSourceId() => string.Empty; + public bool UsesHandles() => false; + public bool CanExecute(View view) => true; + public bool UseInTransparentPass(View view) => _drawSurface && _transparency > 0; + public Outline GetBoundingBox(View view) => null; + + public void RenderScene(View view, DisplayStyle displayStyle) + { + try + { + if (_hasGeometryUpdates || !_surfaceBuffer.IsValid() || !_curveBuffer.IsValid() || _normalsBuffers.Any(storage => !storage.IsValid())) + { + MapGeometryBuffer(); + _hasGeometryUpdates = false; + } + + if (_hasEffectsUpdates) + { + UpdateEffects(); + _hasEffectsUpdates = false; + } + + if (_drawSurface) + { + var isTransparentPass = DrawContext.IsTransparentPass(); + if (isTransparentPass && _transparency > 0 || !isTransparentPass && _transparency == 0) + { + DrawContext.FlushBuffer(_surfaceBuffer.VertexBuffer, + _surfaceBuffer.VertexBufferCount, + _surfaceBuffer.IndexBuffer, + _surfaceBuffer.IndexBufferCount, + _surfaceBuffer.VertexFormat, + _surfaceBuffer.EffectInstance, PrimitiveType.TriangleList, 0, + _surfaceBuffer.PrimitiveCount); + } + } + + if (_drawCurve) + { + DrawContext.FlushBuffer(_curveBuffer.VertexBuffer, + _curveBuffer.VertexBufferCount, + _curveBuffer.IndexBuffer, + _curveBuffer.IndexBufferCount, + _curveBuffer.VertexFormat, + _curveBuffer.EffectInstance, PrimitiveType.LineList, 0, + _curveBuffer.PrimitiveCount); + } + + if (_drawDirection) + { + foreach (var buffer in _normalsBuffers) + { + DrawContext.FlushBuffer(buffer.VertexBuffer, + buffer.VertexBufferCount, + buffer.IndexBuffer, + buffer.IndexBufferCount, + buffer.VertexFormat, + buffer.EffectInstance, PrimitiveType.LineList, 0, + buffer.PrimitiveCount); + } + } + } + catch (Exception exception) + { + RenderFailed?.Invoke(this, new RenderFailedEventArgs + { + Exception = exception + }); + } + } + + private void MapGeometryBuffer() + { + RenderHelper.MapCurveSurfaceBuffer(_surfaceBuffer, _vertices, _diameter); + RenderHelper.MapCurveBuffer(_curveBuffer, _vertices, _diameter); + MapDirectionsBuffer(); + } + + private void MapDirectionsBuffer() + { + var verticalOffset = 0d; + + for (var i = 0; i < _vertices.Count - 1; i++) + { + var startPoint = _vertices[i]; + var endPoint = _vertices[i + 1]; + var centerPoint = (startPoint + endPoint) / 2; + var buffer = CreateNormalBuffer(i); + + var segmentVector = endPoint - startPoint; + var segmentLength = segmentVector.GetLength(); + var segmentDirection = segmentVector.Normalize(); + if (verticalOffset == 0) + { + verticalOffset = RenderGeometryHelper.InterpolateOffsetByDiameter(_diameter) + _diameter / 2d; + } + + var arrowLength = segmentLength > 1 ? 1d : segmentLength * 0.6; + + var offsetVector = XYZ.BasisX.CrossProduct(segmentDirection).Normalize() * verticalOffset; + if (offsetVector.IsZeroLength()) + { + offsetVector = XYZ.BasisY.CrossProduct(segmentDirection).Normalize() * verticalOffset; + } + + var arrowOrigin = centerPoint + offsetVector - segmentDirection * (arrowLength / 2); + + RenderHelper.MapNormalVectorBuffer(buffer, arrowOrigin, segmentDirection, arrowLength); + } + } + + + private RenderingBufferStorage CreateNormalBuffer(int vertexIndex) + { + RenderingBufferStorage buffer; + if (_normalsBuffers.Count > vertexIndex) + { + buffer = _normalsBuffers[vertexIndex]; + } + else + { + buffer = new RenderingBufferStorage(); + _normalsBuffers.Add(buffer); + } + + return buffer; + } + + private void UpdateEffects() + { + _surfaceBuffer.EffectInstance ??= new EffectInstance(_surfaceBuffer.FormatBits); + _surfaceBuffer.EffectInstance.SetColor(_surfaceColor); + _surfaceBuffer.EffectInstance.SetTransparency(_transparency); + + _curveBuffer.EffectInstance ??= new EffectInstance(_curveBuffer.FormatBits); + _curveBuffer.EffectInstance.SetColor(_curveColor); + + foreach (var buffer in _normalsBuffers) + { + buffer.EffectInstance ??= new EffectInstance(buffer.FormatBits); + buffer.EffectInstance.SetColor(_directionColor); + } + } + + public void UpdateSurfaceColor(Color value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _surfaceColor = value; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateCurveColor(Color value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _curveColor = value; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateDirectionColor(Color value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _directionColor = value; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateDiameter(double value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _diameter = value; + _hasGeometryUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateTransparency(double value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _transparency = value; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + + public void UpdateSurfaceVisibility(bool visible) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _drawSurface = visible; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateCurveVisibility(bool visible) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _drawCurve = visible; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateDirectionVisibility(bool visible) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _drawDirection = visible; + + uiDocument.UpdateAllOpenViews(); + } + + public void Register(IList vertices) + { + _vertices = vertices; + + Application.ActionEventHandler.Raise(application => + { + if (application.ActiveUIDocument is null) return; + + var directContextService = (MultiServerService) ExternalServiceRegistry.GetService(ExternalServices.BuiltInExternalServices.DirectContext3DService); + var serverIds = directContextService.GetActiveServerIds(); + + directContextService.AddServer(this); + serverIds.Add(GetServerId()); + directContextService.SetActiveServers(serverIds); + + application.ActiveUIDocument.UpdateAllOpenViews(); + }); + } + + public void Unregister() + { + Application.ActionEventHandler.Raise(application => + { + var directContextService = (MultiServerService) ExternalServiceRegistry.GetService(ExternalServices.BuiltInExternalServices.DirectContext3DService); + directContextService.RemoveServer(GetServerId()); + + application.ActiveUIDocument?.UpdateAllOpenViews(); + }); + } + + public event EventHandler RenderFailed; +} \ No newline at end of file diff --git a/source/RevitLookup/Core/Visualization/SolidVisualizationServer.cs b/source/RevitLookup/Core/Visualization/SolidVisualizationServer.cs new file mode 100644 index 00000000..bb53e36b --- /dev/null +++ b/source/RevitLookup/Core/Visualization/SolidVisualizationServer.cs @@ -0,0 +1,383 @@ +// Copyright 2003-2024 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using Autodesk.Revit.DB.DirectContext3D; +using Autodesk.Revit.DB.ExternalService; +using RevitLookup.Core.Visualization.Helpers; +using RevitLookup.Models.Render; + +namespace RevitLookup.Core.Visualization; + +public sealed class SolidVisualizationServer : IDirectContext3DServer +{ + private Solid _solid; + private bool _hasEffectsUpdates = true; + private bool _hasGeometryUpdates = true; + private bool _hasCageUpdates = true; + + private readonly Guid _guid = Guid.NewGuid(); + private readonly List _faceBuffers = new(4); + private readonly List _edgeBuffers = new(8); + private readonly RenderingBufferStorage _cageSurfaceBuffer = new(); + private readonly RenderingBufferStorage _cageFrameBuffer = new(); + + private double _transparency; + private double _cageTransparency; + private double _cageSize; + + private Color _faceColor; + private Color _edgeColor; + private Color _cageSurfaceColor; + private Color _cageFrameColor; + + private bool _drawFace; + private bool _drawEdge; + private bool _drawCageSurface; + + public Guid GetServerId() => _guid; + public string GetVendorId() => "RevitLookup"; + public string GetName() => "Solid visualization server"; + public string GetDescription() => "Solid geometry visualization"; + public ExternalServiceId GetServiceId() => ExternalServices.BuiltInExternalServices.DirectContext3DService; + public string GetApplicationId() => string.Empty; + public string GetSourceId() => string.Empty; + public bool UsesHandles() => false; + public bool CanExecute(View view) => true; + public bool UseInTransparentPass(View view) => _drawFace && _transparency > 0 || _drawCageSurface && _cageTransparency > 0; + + public Outline GetBoundingBox(View view) + { + var boundingBox = _solid.GetBoundingBox(); + var minPoint = boundingBox.Transform.OfPoint(boundingBox.Min); + var maxPoint = boundingBox.Transform.OfPoint(boundingBox.Max); + + return new Outline(minPoint, maxPoint); + } + + public void RenderScene(View view, DisplayStyle displayStyle) + { + try + { + if (_hasGeometryUpdates || _faceBuffers.Any(storage => !storage.IsValid()) || _edgeBuffers.Any(storage => !storage.IsValid())) + { + MapGeometryBuffer(); + _hasGeometryUpdates = false; + } + + if (_hasCageUpdates || !_cageSurfaceBuffer.IsValid() || !_cageFrameBuffer.IsValid()) + { + MapCageBuffers(); + _hasCageUpdates = false; + } + + if (_hasEffectsUpdates) + { + UpdateEffects(); + _hasEffectsUpdates = false; + } + + if (_drawFace) + { + var isTransparentPass = DrawContext.IsTransparentPass(); + if (isTransparentPass && _transparency > 0 || !isTransparentPass && _transparency == 0) + { + foreach (var buffer in _faceBuffers) + { + DrawContext.FlushBuffer(buffer.VertexBuffer, + buffer.VertexBufferCount, + buffer.IndexBuffer, + buffer.IndexBufferCount, + buffer.VertexFormat, + buffer.EffectInstance, PrimitiveType.TriangleList, 0, + buffer.PrimitiveCount); + } + } + } + + if (_drawEdge) + { + foreach (var buffer in _edgeBuffers) + { + DrawContext.FlushBuffer(buffer.VertexBuffer, + buffer.VertexBufferCount, + buffer.IndexBuffer, + buffer.IndexBufferCount, + buffer.VertexFormat, + buffer.EffectInstance, PrimitiveType.LineList, 0, + buffer.PrimitiveCount); + } + } + + if (_drawCageSurface) + { + var isTransparentPass = DrawContext.IsTransparentPass(); + if (isTransparentPass && _cageTransparency > 0 || !isTransparentPass && _cageTransparency == 0) + { + DrawContext.FlushBuffer(_cageSurfaceBuffer.VertexBuffer, + _cageSurfaceBuffer.VertexBufferCount, + _cageSurfaceBuffer.IndexBuffer, + _cageSurfaceBuffer.IndexBufferCount, + _cageSurfaceBuffer.VertexFormat, + _cageSurfaceBuffer.EffectInstance, PrimitiveType.TriangleList, 0, + _cageSurfaceBuffer.PrimitiveCount); + } + + DrawContext.FlushBuffer(_cageFrameBuffer.VertexBuffer, + _cageFrameBuffer.VertexBufferCount, + _cageFrameBuffer.IndexBuffer, + _cageFrameBuffer.IndexBufferCount, + _cageFrameBuffer.VertexFormat, + _cageFrameBuffer.EffectInstance, PrimitiveType.LineList, 0, + _cageFrameBuffer.PrimitiveCount); + } + } + catch (Exception exception) + { + RenderFailed?.Invoke(this, new RenderFailedEventArgs + { + Exception = exception + }); + } + } + + private void MapGeometryBuffer() + { + var faceIndex = 0; + foreach (Face face in _solid.Faces) + { + var buffer = CreateOrUpdateBuffer(_faceBuffers, faceIndex++); + MapFaceBuffers(buffer, face); + } + + var edgeIndex = 0; + foreach (Edge edge in _solid.Edges) + { + var buffer = CreateOrUpdateBuffer(_edgeBuffers, edgeIndex++); + MapEdgeBuffers(buffer, edge); + } + } + + private void MapCageBuffers() + { + var maxScaleVector = new XYZ(1, 1, 1); + var minScaleVector = -maxScaleVector; + + var box = _solid.GetBoundingBox(); + var scaledBox = new BoundingBoxXYZ + { + Min = box.Transform.OfPoint(box.Min) + minScaleVector * _cageSize, + Max = box.Transform.OfPoint(box.Max) + maxScaleVector * _cageSize + }; + + RenderHelper.MapBoundingBoxSurfaceBuffer(_cageSurfaceBuffer, scaledBox); + RenderHelper.MapBoundingBoxEdgeBuffer(_cageFrameBuffer, scaledBox); + } + + private void MapFaceBuffers(RenderingBufferStorage buffer, Face face) + { + var mesh = face.Triangulate(); + RenderHelper.MapSurfaceBuffer(buffer, mesh, 0); + } + + private void MapEdgeBuffers(RenderingBufferStorage buffer, Edge edge) + { + var mesh = edge.Tessellate(); + RenderHelper.MapCurveBuffer(buffer, mesh); + } + + private RenderingBufferStorage CreateOrUpdateBuffer(List buffers, int index) + { + RenderingBufferStorage buffer; + if (buffers.Count > index) + { + buffer = buffers[index]; + } + else + { + buffer = new RenderingBufferStorage(); + buffers.Add(buffer); + } + + return buffer; + } + + private void UpdateEffects() + { + foreach (var buffer in _faceBuffers) + { + buffer.EffectInstance ??= new EffectInstance(buffer.FormatBits); + buffer.EffectInstance.SetColor(_faceColor); + buffer.EffectInstance.SetTransparency(_transparency); + } + + foreach (var buffer in _edgeBuffers) + { + buffer.EffectInstance ??= new EffectInstance(buffer.FormatBits); + buffer.EffectInstance.SetColor(_edgeColor); + } + + _cageSurfaceBuffer.EffectInstance ??= new EffectInstance(_cageSurfaceBuffer.FormatBits); + _cageSurfaceBuffer.EffectInstance.SetColor(_cageSurfaceColor); + _cageSurfaceBuffer.EffectInstance.SetTransparency(_cageTransparency); + + _cageFrameBuffer.EffectInstance ??= new EffectInstance(_cageFrameBuffer.FormatBits); + _cageFrameBuffer.EffectInstance.SetColor(_cageFrameColor); + } + + public void UpdateFaceColor(Color value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _faceColor = value; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateEdgeColor(Color value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _edgeColor = value; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + + public void UpdateCageSurfaceColor(Color value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _cageSurfaceColor = value; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateCageFrameColor(Color value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _cageFrameColor = value; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateTransparency(double value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _transparency = value; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateCageTransparency(double value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _cageTransparency = value; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateCageSize(double value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _cageSize = value; + _hasCageUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateFaceVisibility(bool value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _drawFace = value; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateEdgeVisibility(bool value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _drawEdge = value; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateCageSurfaceVisibility(bool value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _drawCageSurface = value; + + uiDocument.UpdateAllOpenViews(); + } + + public void Register(Solid solid) + { + _solid = solid; + + Application.ActionEventHandler.Raise(application => + { + if (application.ActiveUIDocument is null) return; + + var directContextService = (MultiServerService) ExternalServiceRegistry.GetService(ExternalServices.BuiltInExternalServices.DirectContext3DService); + var serverIds = directContextService.GetActiveServerIds(); + + directContextService.AddServer(this); + serverIds.Add(GetServerId()); + directContextService.SetActiveServers(serverIds); + + application.ActiveUIDocument.UpdateAllOpenViews(); + }); + } + + public void Unregister() + { + Application.ActionEventHandler.Raise(application => + { + var directContextService = (MultiServerService) ExternalServiceRegistry.GetService(ExternalServices.BuiltInExternalServices.DirectContext3DService); + directContextService.RemoveServer(GetServerId()); + + application.ActiveUIDocument?.UpdateAllOpenViews(); + }); + } + + public event EventHandler RenderFailed; +} \ No newline at end of file diff --git a/source/RevitLookup/Core/Visualization/XyzVisualizationServer.cs b/source/RevitLookup/Core/Visualization/XyzVisualizationServer.cs new file mode 100644 index 00000000..c1465c52 --- /dev/null +++ b/source/RevitLookup/Core/Visualization/XyzVisualizationServer.cs @@ -0,0 +1,328 @@ +// Copyright 2003-2024 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using Autodesk.Revit.DB.DirectContext3D; +using Autodesk.Revit.DB.ExternalService; +using RevitLookup.Core.Visualization.Helpers; +using RevitLookup.Models.Render; + +namespace RevitLookup.Core.Visualization; + +public sealed class XyzVisualizationServer : IDirectContext3DServer +{ + private XYZ _point; + private bool _hasEffectsUpdates = true; + private bool _hasGeometryUpdates = true; + + private readonly Guid _guid = Guid.NewGuid(); + + private readonly RenderingBufferStorage[] _planeBuffers = Enumerable.Range(0, 3) + .Select(_ => new RenderingBufferStorage()) + .ToArray(); + + private readonly RenderingBufferStorage[] _axisBuffers = Enumerable.Range(0, 3) + .Select(_ => new RenderingBufferStorage()) + .ToArray(); + + private readonly XYZ[] _normals = + [ + XYZ.BasisX, + XYZ.BasisY, + XYZ.BasisZ + ]; + + private double _transparency; + private double _axisLength; + + private Color _xColor; + private Color _yColor; + private Color _zColor; + + private bool _drawPlane; + private bool _drawXAxis; + private bool _drawYAxis; + private bool _drawZAxis; + + public Guid GetServerId() => _guid; + public string GetVendorId() => "RevitLookup"; + public string GetName() => "XYZ visualization server"; + public string GetDescription() => "XYZ geometry visualization"; + public ExternalServiceId GetServiceId() => ExternalServices.BuiltInExternalServices.DirectContext3DService; + public string GetApplicationId() => string.Empty; + public string GetSourceId() => string.Empty; + public bool UsesHandles() => false; + public bool CanExecute(View view) => true; + public bool UseInTransparentPass(View view) => _drawPlane && _transparency > 0; + + public Outline GetBoundingBox(View view) + { + var minPoint = new XYZ(_point.X - _axisLength, _point.Y - _axisLength, _point.Z - _axisLength); + var maxPoint = new XYZ(_point.X + _axisLength, _point.Y + _axisLength, _point.Z + _axisLength); + + return new Outline(minPoint, maxPoint); + } + + public void RenderScene(View view, DisplayStyle displayStyle) + { + try + { + if (_hasGeometryUpdates || _planeBuffers.Any(storage => !storage.IsValid()) || _axisBuffers.Any(storage => !storage.IsValid())) + { + UpdateGeometryBuffer(); + _hasGeometryUpdates = false; + } + + if (_hasEffectsUpdates) + { + UpdateEffects(); + _hasEffectsUpdates = false; + } + + if (_drawXAxis) + { + RenderAxisBuffer(_axisBuffers[0]); + RenderPlaneBuffer(_planeBuffers[0]); + } + + if (_drawYAxis) + { + RenderAxisBuffer(_axisBuffers[1]); + RenderPlaneBuffer(_planeBuffers[1]); + } + + if (_drawZAxis) + { + RenderAxisBuffer(_axisBuffers[2]); + RenderPlaneBuffer(_planeBuffers[2]); + } + } + catch (Exception exception) + { + RenderFailed?.Invoke(this, new RenderFailedEventArgs + { + Exception = exception + }); + } + } + + private void RenderPlaneBuffer(RenderingBufferStorage buffer) + { + if (!_drawPlane) return; + + var isTransparentPass = DrawContext.IsTransparentPass(); + if (isTransparentPass && _transparency > 0 || !isTransparentPass && _transparency == 0) + { + DrawContext.FlushBuffer(buffer.VertexBuffer, + buffer.VertexBufferCount, + buffer.IndexBuffer, + buffer.IndexBufferCount, + buffer.VertexFormat, + buffer.EffectInstance, PrimitiveType.TriangleList, 0, + buffer.PrimitiveCount); + } + } + + private void RenderAxisBuffer(RenderingBufferStorage buffer) + { + DrawContext.FlushBuffer(buffer.VertexBuffer, + buffer.VertexBufferCount, + buffer.IndexBuffer, + buffer.IndexBufferCount, + buffer.VertexFormat, + buffer.EffectInstance, PrimitiveType.LineList, 0, + buffer.PrimitiveCount); + } + + private void UpdateGeometryBuffer() + { + MapNormalBuffer(); + MapPlaneBuffer(); + } + + private void MapNormalBuffer() + { + var normalExtendLength = _axisLength > 1 ? 0.8 : _axisLength * 0.8; + for (var i = 0; i < _normals.Length; i++) + { + var normal = _normals[i]; + var buffer = _axisBuffers[i]; + RenderHelper.MapNormalVectorBuffer(buffer, _point - normal * (_axisLength + normalExtendLength), normal, 2 * (_axisLength + normalExtendLength)); + } + } + + private void MapPlaneBuffer() + { + for (var i = 0; i < _normals.Length; i++) + { + var normal = _normals[i]; + var buffer = _planeBuffers[i]; + RenderHelper.MapSideBuffer(buffer, _point - normal * _axisLength, _point + normal * _axisLength); + } + } + + private void UpdateEffects() + { + foreach (var buffer in _planeBuffers) + { + buffer.EffectInstance ??= new EffectInstance(buffer.FormatBits); + buffer.EffectInstance.SetTransparency(_transparency); + } + + _planeBuffers[0].EffectInstance.SetColor(_xColor); + _planeBuffers[1].EffectInstance.SetColor(_yColor); + _planeBuffers[2].EffectInstance.SetColor(_zColor); + + foreach (var buffer in _axisBuffers) + { + buffer.EffectInstance ??= new EffectInstance(buffer.FormatBits); + } + + _axisBuffers[0].EffectInstance.SetColor(_xColor); + _axisBuffers[1].EffectInstance.SetColor(_yColor); + _axisBuffers[2].EffectInstance.SetColor(_zColor); + } + + public void UpdateXColor(Color value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _xColor = value; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateYColor(Color value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _yColor = value; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateZColor(Color value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _zColor = value; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateAxisLength(double value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _axisLength = value; + _hasGeometryUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateTransparency(double value) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _transparency = value; + _hasEffectsUpdates = true; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdatePlaneVisibility(bool visible) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _drawPlane = visible; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateXAxisVisibility(bool visible) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _drawXAxis = visible; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateYAxisVisibility(bool visible) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _drawYAxis = visible; + + uiDocument.UpdateAllOpenViews(); + } + + public void UpdateZAxisVisibility(bool visible) + { + var uiDocument = Context.UiDocument; + if (uiDocument is null) return; + + _drawZAxis = visible; + + uiDocument.UpdateAllOpenViews(); + } + + public void Register(XYZ point) + { + _point = point; + + Application.ActionEventHandler.Raise(application => + { + if (application.ActiveUIDocument is null) return; + + var directContextService = (MultiServerService) ExternalServiceRegistry.GetService(ExternalServices.BuiltInExternalServices.DirectContext3DService); + var serverIds = directContextService.GetActiveServerIds(); + + directContextService.AddServer(this); + serverIds.Add(GetServerId()); + directContextService.SetActiveServers(serverIds); + + application.ActiveUIDocument.UpdateAllOpenViews(); + }); + } + + public void Unregister() + { + Application.ActionEventHandler.Raise(application => + { + var directContextService = (MultiServerService) ExternalServiceRegistry.GetService(ExternalServices.BuiltInExternalServices.DirectContext3DService); + directContextService.RemoveServer(GetServerId()); + + application.ActiveUIDocument?.UpdateAllOpenViews(); + }); + } + + public event EventHandler RenderFailed; +} \ No newline at end of file diff --git a/source/RevitLookup/Host.cs b/source/RevitLookup/Host.cs index 059ac3ec..e1b3d2df 100644 --- a/source/RevitLookup/Host.cs +++ b/source/RevitLookup/Host.cs @@ -7,8 +7,10 @@ using RevitLookup.Services; using RevitLookup.Services.Contracts; using RevitLookup.ViewModels.Contracts; +using RevitLookup.ViewModels.Dialogs.Visualization; using RevitLookup.ViewModels.Pages; using RevitLookup.Views; +using RevitLookup.Views.Dialogs.Visualization; using RevitLookup.Views.Pages; using Wpf.Ui; @@ -17,7 +19,7 @@ namespace RevitLookup; public static class Host { private static IHost _host; - + public static void Start() { var builder = new HostApplicationBuilder(new HostApplicationBuilderSettings @@ -25,24 +27,24 @@ public static void Start() ContentRootPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly()!.Location), DisableDefaults = true }); - + //Logging builder.Logging.ClearProviders(); builder.Logging.AddSerilogConfiguration(); - + //Configuration builder.Services.AddOptions(builder.Configuration); - + //App services builder.Services.AddSingleton(); builder.Services.AddSingleton(); - + //UI services builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); - + //Views builder.Services.AddScoped(); builder.Services.AddScoped(); @@ -56,30 +58,44 @@ public static void Start() builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); - + + //Dialogs + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + //Startup view builder.Services.AddTransient(); - + _host = builder.Build(); _host.Start(); } - + public static void Start(IHost host) { _host = host; host.Start(); } - + public static void Stop() { _host.StopAsync(); } - + public static T GetService() where T : class { return _host.Services.GetService(typeof(T)) as T; } - + public static T GetService(this IServiceProvider serviceProvider) where T : class { return serviceProvider.GetService(typeof(T)) as T; diff --git a/source/RevitLookup/Models/Options/FolderLocations.cs b/source/RevitLookup/Models/Options/FolderLocations.cs index 9459f4a1..fab9b3d1 100644 --- a/source/RevitLookup/Models/Options/FolderLocations.cs +++ b/source/RevitLookup/Models/Options/FolderLocations.cs @@ -25,5 +25,6 @@ public sealed class FolderLocations public string RootFolder { get; set; } public string ConfigFolder { get; set; } public string DownloadFolder { get; set; } - public string SettingsPath { get; set; } + public string GeneralSettingsPath { get; set; } + public string RenderSettingsPath { get; set; } } \ No newline at end of file diff --git a/source/RevitLookup/Models/Render/RenderFailedEventArgs.cs b/source/RevitLookup/Models/Render/RenderFailedEventArgs.cs new file mode 100644 index 00000000..1b935460 --- /dev/null +++ b/source/RevitLookup/Models/Render/RenderFailedEventArgs.cs @@ -0,0 +1,26 @@ +// Copyright 2003-2024 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +namespace RevitLookup.Models.Render; + +public class RenderFailedEventArgs +{ + public required Exception Exception { get; set; } +} \ No newline at end of file diff --git a/source/RevitLookup/Models/Render/RenderingBufferStorage.cs b/source/RevitLookup/Models/Render/RenderingBufferStorage.cs new file mode 100644 index 00000000..5b9e65f2 --- /dev/null +++ b/source/RevitLookup/Models/Render/RenderingBufferStorage.cs @@ -0,0 +1,25 @@ +using Autodesk.Revit.DB.DirectContext3D; + +namespace RevitLookup.Models.Render; + +public sealed class RenderingBufferStorage +{ + public VertexFormatBits FormatBits { get; set; } + public int PrimitiveCount { get; set; } + public int VertexBufferCount { get; set; } + public int IndexBufferCount { get; set; } + public VertexBuffer VertexBuffer { get; set; } + public IndexBuffer IndexBuffer { get; set; } + public VertexFormat VertexFormat { get; set; } + public EffectInstance EffectInstance { get; set; } + + public bool IsValid() + { + if (!VertexBuffer.IsValid()) return false; + if (!IndexBuffer.IsValid()) return false; + if (!VertexFormat.IsValid()) return false; + if (!EffectInstance.IsValid()) return false; + + return true; + } +} \ No newline at end of file diff --git a/source/RevitLookup/Models/Settings/GeneralSettings.cs b/source/RevitLookup/Models/Settings/GeneralSettings.cs new file mode 100644 index 00000000..e64161d7 --- /dev/null +++ b/source/RevitLookup/Models/Settings/GeneralSettings.cs @@ -0,0 +1,173 @@ +// Copyright 2003-2024 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using System.IO; +using System.Text.Json; +using Microsoft.Extensions.Options; +using RevitLookup.Config; +using RevitLookup.Services.Contracts; +using Wpf.Ui.Appearance; +using Wpf.Ui.Controls; + +namespace RevitLookup.Models.Settings; + +public class GeneralSettings(string path, IOptions jsonOptions) : ISettings +{ + private GeneralConfiguration _settings; + + public ApplicationTheme Theme + { + get => _settings.Theme; + set => _settings.Theme = value; + } + + public WindowBackdropType Background + { + get => _settings.Background; + set => _settings.Background = value; + } + + public int TransitionDuration + { + get => _settings.TransitionDuration; + private set => _settings.TransitionDuration = value; + } + + public bool UseHardwareRendering + { + get => _settings.UseHardwareRendering; + set => _settings.UseHardwareRendering = value; + } + + public bool ShowTimeColumn + { + get => _settings.ShowTimeColumn; + set => _settings.ShowTimeColumn = value; + } + + public bool ShowMemoryColumn + { + get => _settings.ShowMemoryColumn; + set => _settings.ShowMemoryColumn = value; + } + + public bool UseModifyTab + { + get => _settings.UseModifyTab; + set => _settings.UseModifyTab = value; + } + + public bool UseSizeRestoring + { + get => _settings.UseSizeRestoring; + set => _settings.UseSizeRestoring = value; + } + + public double WindowWidth + { + get => _settings.WindowWidth; + set => _settings.WindowWidth = value; + } + + public double WindowHeight + { + get => _settings.WindowHeight; + set => _settings.WindowHeight = value; + } + + public bool IncludeUnsupported + { + get => _settings.IncludeUnsupported; + set => _settings.IncludeUnsupported = value; + } + + public bool IncludePrivate + { + get => _settings.IncludePrivate; + set => _settings.IncludePrivate = value; + } + + public bool IncludeStatic + { + get => _settings.IncludeStatic; + set => _settings.IncludeStatic = value; + } + + public bool IncludeFields + { + get => _settings.IncludeFields; + set => _settings.IncludeFields = value; + } + + public bool IncludeEvents + { + get => _settings.IncludeEvents; + set => _settings.IncludeEvents = value; + } + + public bool IncludeExtensions + { + get => _settings.IncludeExtensions; + set => _settings.IncludeExtensions = value; + } + + public bool IncludeRootHierarchy + { + get => _settings.IncludeRootHierarchy; + set => _settings.IncludeRootHierarchy = value; + } + + public int ApplyTransition(bool value) + { + return TransitionDuration = value ? _settings.DefaultTransitionDuration : 0; + } + + public void Save() + { + Directory.CreateDirectory(Path.GetDirectoryName(path)!); + + var json = JsonSerializer.Serialize(_settings, jsonOptions.Value); + File.WriteAllText(path, json); + } + + public void Load() + { + if (!File.Exists(path)) + { + SetDefault(); + return; + } + + try + { + using var config = File.OpenRead(path); + _settings = JsonSerializer.Deserialize(config, jsonOptions.Value); + } + catch + { + SetDefault(); + } + } + + public void SetDefault() + { + _settings = new GeneralConfiguration(); + } +} \ No newline at end of file diff --git a/source/RevitLookup/Models/Settings/RenderSettings.cs b/source/RevitLookup/Models/Settings/RenderSettings.cs new file mode 100644 index 00000000..ef782304 --- /dev/null +++ b/source/RevitLookup/Models/Settings/RenderSettings.cs @@ -0,0 +1,72 @@ +// Copyright 2003-2024 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using System.IO; +using System.Text.Json; +using Microsoft.Extensions.Options; +using RevitLookup.Config; +using RevitLookup.Services.Contracts; + +namespace RevitLookup.Models.Settings; + +public class RenderSettings(string savingPath, IOptions jsonOptions) : ISettings +{ + private RenderConfiguration _settings; + + public BoundingBoxVisualizationSettings BoundingBoxSettings => _settings.BoundingBoxSettings; + public FaceVisualizationSettings FaceSettings => _settings.FaceSettings; + public MeshVisualizationSettings MeshSettings => _settings.MeshSettings; + public PolylineVisualizationSettings PolylineSettings => _settings.PolylineSettings; + public SolidVisualizationSettings SolidSettings => _settings.SolidSettings; + public XyzVisualizationSettings XyzSettings => _settings.XyzSettings; + + public void Save() + { + Directory.CreateDirectory(Path.GetDirectoryName(savingPath)!); + + var json = JsonSerializer.Serialize(_settings, jsonOptions.Value); + File.WriteAllText(savingPath, json); + } + + public void Load() + { + if (!File.Exists(savingPath)) + { + SetDefault(); + return; + } + + try + { + using var config = File.OpenRead(savingPath); + _settings = JsonSerializer.Deserialize(config, jsonOptions.Value); + } + catch + { + SetDefault(); + throw; + } + } + + public void SetDefault() + { + _settings = new RenderConfiguration(); + } +} \ No newline at end of file diff --git a/source/RevitLookup/RibbonController.cs b/source/RevitLookup/RibbonController.cs index 91685b91..a83ac8e3 100644 --- a/source/RevitLookup/RibbonController.cs +++ b/source/RevitLookup/RibbonController.cs @@ -22,7 +22,7 @@ using Autodesk.Windows; using RevitLookup.Commands; using RevitLookup.Core; -using RevitLookup.Services.Contracts; +using RevitLookup.Models.Settings; using RevitLookup.Utils; namespace RevitLookup; @@ -30,14 +30,14 @@ namespace RevitLookup; public static class RibbonController { private const string PanelName = "Revit Lookup"; - - public static void CreatePanel(UIControlledApplication application, ISettingsService settingsService) + + public static void CreatePanel(UIControlledApplication application, GeneralSettings settingsService) { var addinPanel = application.CreatePanel("Revit Lookup"); var pullButton = addinPanel.AddPullDownButton("RevitLookupButton", "RevitLookup"); pullButton.SetImage("/RevitLookup;component/Resources/Images/RibbonIcon16.png"); pullButton.SetLargeImage("/RevitLookup;component/Resources/Images/RibbonIcon32.png"); - + pullButton.AddPushButton("Dashboard"); ResolveSelectionButton(settingsService, pullButton); pullButton.AddPushButton("Snoop Active view"); @@ -50,33 +50,33 @@ public static void CreatePanel(UIControlledApplication application, ISettingsSer pullButton.AddPushButton("Search Elements"); pullButton.AddPushButton("Event monitor"); } - - private static void ResolveSelectionButton(ISettingsService settingsService, PulldownButton parentButton) + + private static void ResolveSelectionButton(GeneralSettings settings, PulldownButton parentButton) { - if (!settingsService.UseModifyTab) + if (!settings.UseModifyTab) { parentButton.AddPushButton("Snoop Selection"); return; } - + var modifyTab = ComponentManager.Ribbon.FindTab("Modify"); var modifyPanel = modifyTab.CreatePanel(PanelName); - + var button = modifyPanel.AddPushButton("Snoop\nSelection"); button.SetImage("/RevitLookup;component/Resources/Images/RibbonIcon16.png"); button.SetLargeImage("/RevitLookup;component/Resources/Images/RibbonIcon32.png"); } - - public static void ReloadPanels(ISettingsService settingsService) + + public static void ReloadPanels(GeneralSettings settings) { Application.ActionEventHandler.Raise(_ => { RibbonUtils.RemovePanel("CustomCtrl_%CustomCtrl_%Add-Ins%Revit Lookup%RevitLookupButton", PanelName); RibbonUtils.RemovePanel("CustomCtrl_%Revit Lookup%RevitLookup.Commands.SnoopSelectionCommand", PanelName); - + var controlledApplication = RevitShell.CreateUiControlledApplication(); - CreatePanel(controlledApplication, settingsService); - + CreatePanel(controlledApplication, settings); + RibbonUtils.ReloadShortcuts(); }); } diff --git a/source/RevitLookup/Services/Contracts/ISettings.cs b/source/RevitLookup/Services/Contracts/ISettings.cs new file mode 100644 index 00000000..8ca23cf6 --- /dev/null +++ b/source/RevitLookup/Services/Contracts/ISettings.cs @@ -0,0 +1,28 @@ +// Copyright 2003-2024 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +namespace RevitLookup.Services.Contracts; + +public interface ISettings +{ + void Save(); + void Load(); + void SetDefault(); +} \ No newline at end of file diff --git a/source/RevitLookup/Services/Contracts/ISettingsService.cs b/source/RevitLookup/Services/Contracts/ISettingsService.cs index a0dad019..ea2471a3 100644 --- a/source/RevitLookup/Services/Contracts/ISettingsService.cs +++ b/source/RevitLookup/Services/Contracts/ISettingsService.cs @@ -18,38 +18,13 @@ // Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) // (Rights in Technical Data and Computer Software), as applicable. -using Wpf.Ui.Appearance; -using Wpf.Ui.Controls; +using RevitLookup.Models.Settings; namespace RevitLookup.Services.Contracts; public interface ISettingsService { - //User interface - ApplicationTheme Theme { get; set; } - WindowBackdropType Background { get; set; } - int TransitionDuration { get; } - bool UseHardwareRendering { get; set; } - bool ShowTimeColumn { get; set; } - bool ShowMemoryColumn { get; set; } - - //Window - bool UseSizeRestoring { get; set; } - double WindowWidth { get; set; } - double WindowHeight { get; set; } - - // Descriptor builder - bool IncludeUnsupported { get; set; } - bool IncludePrivate { get; set; } - bool IncludeStatic { get; set; } - bool IncludeFields { get; set; } - bool IncludeEvents { get; set; } - bool IncludeExtensions { get; set; } - bool IncludeRootHierarchy { get; set; } - - // Ribbon - bool UseModifyTab { get; set; } - - int ApplyTransition(bool value); - void Save(); + public GeneralSettings GeneralSettings { get; } + public RenderSettings RenderSettings { get; } + void SaveSettings(); } \ No newline at end of file diff --git a/source/RevitLookup/Services/Contracts/IWindow.cs b/source/RevitLookup/Services/Contracts/IWindow.cs index 026b04c4..b809ce47 100644 --- a/source/RevitLookup/Services/Contracts/IWindow.cs +++ b/source/RevitLookup/Services/Contracts/IWindow.cs @@ -19,7 +19,7 @@ // (Rights in Technical Data and Computer Software), as applicable. using System.Windows; -using Wpf.Ui.Controls; +using System.Windows.Threading; using Visibility = System.Windows.Visibility; namespace RevitLookup.Services.Contracts; @@ -28,10 +28,10 @@ public interface IWindow { bool IsLoaded { get; } Visibility Visibility { get; set; } - WindowBackdropType WindowBackdropType { get; set; } - + Dispatcher Dispatcher { get; } + void EnableSizeTracking(); void DisableSizeTracking(); - + event RoutedEventHandler Loaded; } \ No newline at end of file diff --git a/source/RevitLookup/Services/LookupService.cs b/source/RevitLookup/Services/LookupService.cs index f6b1f988..f7a84e5f 100644 --- a/source/RevitLookup/Services/LookupService.cs +++ b/source/RevitLookup/Services/LookupService.cs @@ -51,7 +51,7 @@ public LookupService(IServiceScopeFactory scopeFactory) } else { - _dispatcher.InvokeAsync(() => _lookupService = new LookupServiceImpl(scopeFactory)).Wait(); + _dispatcher.Invoke(() => _lookupService = new LookupServiceImpl(scopeFactory)); } } @@ -63,7 +63,7 @@ public ILookupServiceDependsStage Snoop(SnoopableType snoopableType) } else { - _dispatcher.InvokeAsync(() => _lookupService.Snoop(snoopableType)).Wait(); + _dispatcher.Invoke(() => _lookupService.Snoop(snoopableType)); } return this; @@ -77,7 +77,7 @@ public ILookupServiceDependsStage Snoop(SnoopableObject snoopableObject) } else { - _dispatcher.InvokeAsync(() => _lookupService.Snoop(snoopableObject)).Wait(); + _dispatcher.Invoke(() => _lookupService.Snoop(snoopableObject)); } return this; @@ -91,7 +91,7 @@ public ILookupServiceDependsStage Snoop(IList snoopableObjects) } else { - _dispatcher.InvokeAsync(() => _lookupService.Snoop(snoopableObjects)).Wait(); + _dispatcher.Invoke(() => _lookupService.Snoop(snoopableObjects)); } return this; @@ -105,7 +105,7 @@ public ILookupServiceShowStage DependsOn(IServiceProvider provider) } else { - _dispatcher.InvokeAsync(() => _lookupService.DependsOn(provider)).Wait(); + _dispatcher.Invoke(() => _lookupService.DependsOn(provider)); } return this; @@ -119,7 +119,7 @@ public ILookupServiceExecuteStage Show() where T : Page } else { - _dispatcher.InvokeAsync(() => _lookupService.Show()).Wait(); + _dispatcher.Invoke(() => _lookupService.Show()); } return this; @@ -133,7 +133,7 @@ public void Execute(Action handler) where T : class } else { - _dispatcher.InvokeAsync(() => _lookupService.Execute(handler)).Wait(); + _dispatcher.Invoke(() => _lookupService.Execute(handler)); } } diff --git a/source/RevitLookup/Services/NotificationService.cs b/source/RevitLookup/Services/NotificationService.cs index ca39fa2b..d1d6a41b 100644 --- a/source/RevitLookup/Services/NotificationService.cs +++ b/source/RevitLookup/Services/NotificationService.cs @@ -28,8 +28,56 @@ namespace RevitLookup.Services; public sealed class NotificationService(ISnackbarService snackbarService, IWindow window) { private Action _pendingNotifications; - + public void ShowSuccess(string title, string message) + { + if (window.Dispatcher.CheckAccess()) + { + PushSuccessMessage(title, message); + } + else + { + window.Dispatcher.Invoke(() => PushSuccessMessage(title, message)); + } + } + + public void ShowWarning(string title, string message) + { + if (window.Dispatcher.CheckAccess()) + { + PushWarningMessage(title, message); + } + else + { + window.Dispatcher.Invoke(() => PushWarningMessage(title, message)); + } + } + + public void ShowError(string title, string message) + { + if (window.Dispatcher.CheckAccess()) + { + PushErrorMessage(title, message); + } + else + { + window.Dispatcher.Invoke(() => PushErrorMessage(title, message)); + } + } + + public void ShowError(string title, Exception exception) + { + if (window.Dispatcher.CheckAccess()) + { + PushErrorMessage(title, exception.Message); + } + else + { + window.Dispatcher.Invoke(() => PushErrorMessage(title, exception.Message)); + } + } + + private void PushSuccessMessage(string title, string message) { if (!window.IsLoaded) { @@ -41,8 +89,8 @@ public void ShowSuccess(string title, string message) ShowSuccessBar(title, message); } } - - public void ShowWarning(string title, string message) + + private void PushWarningMessage(string title, string message) { if (!window.IsLoaded) { @@ -54,13 +102,8 @@ public void ShowWarning(string title, string message) ShowWarningBar(title, message); } } - - public void ShowError(string title, Exception exception) - { - ShowError(title, exception.Message); - } - - public void ShowError(string title, string message) + + private void PushErrorMessage(string title, string message) { if (!window.IsLoaded) { @@ -72,7 +115,7 @@ public void ShowError(string title, string message) ShowErrorBar(title, message); } } - + private void ShowSuccessBar(string title, string message) { snackbarService.Show( @@ -82,7 +125,7 @@ private void ShowSuccessBar(string title, string message) new SymbolIcon(SymbolRegular.ChatWarning24, 24), snackbarService.DefaultTimeOut); } - + private void ShowWarningBar(string title, string message) { snackbarService.Show( @@ -92,7 +135,7 @@ private void ShowWarningBar(string title, string message) new SymbolIcon(SymbolRegular.Warning24, 24), snackbarService.DefaultTimeOut); } - + private void ShowErrorBar(string title, string message) { snackbarService.Show( @@ -102,7 +145,7 @@ private void ShowErrorBar(string title, string message) new SymbolIcon(SymbolRegular.ErrorCircle24, 24), snackbarService.DefaultTimeOut); } - + private void ShowPendingNotifications(object sender, RoutedEventArgs args) { window.Loaded -= ShowPendingNotifications; diff --git a/source/RevitLookup/Services/SettingsService.cs b/source/RevitLookup/Services/SettingsService.cs index f26dccbf..25eab9ce 100644 --- a/source/RevitLookup/Services/SettingsService.cs +++ b/source/RevitLookup/Services/SettingsService.cs @@ -18,196 +18,70 @@ // Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) // (Rights in Technical Data and Computer Software), as applicable. -using System.IO; using System.Text.Json; -using System.Text.Json.Serialization; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using RevitLookup.Models.Options; +using RevitLookup.Models.Settings; using RevitLookup.Services.Contracts; -using Wpf.Ui.Appearance; -using Wpf.Ui.Controls; namespace RevitLookup.Services; -/// -/// Settings options saved to disk -/// -[Serializable] -internal sealed class Settings -{ - [JsonPropertyName("Theme")] public ApplicationTheme Theme { get; set; } = ApplicationTheme.Light; - [JsonPropertyName("Background")] public WindowBackdropType Background { get; set; } = WindowBackdropType.None; - [JsonPropertyName("TransitionDuration")] public int TransitionDuration { get; set; } //= SettingsService.DefaultTransitionDuration; - [JsonPropertyName("IsHardwareRenderingAllowed")] public bool UseHardwareRendering { get; set; } = true; - [JsonPropertyName("IsTimeColumnAllowed")] public bool ShowTimeColumn { get; set; } - [JsonPropertyName("ShowMemoryColumn")] public bool ShowMemoryColumn { get; set; } - [JsonPropertyName("UseSizeRestoring")] public bool UseSizeRestoring { get; set; } - [JsonPropertyName("WindowWidth")] public double WindowWidth { get; set; } - [JsonPropertyName("WindowHeight")] public double WindowHeight { get; set; } - [JsonPropertyName("IsUnsupportedAllowed")] public bool IncludeUnsupported { get; set; } - [JsonPropertyName("IsPrivateAllowed")] public bool IncludePrivate { get; set; } - [JsonPropertyName("IsStaticAllowed")] public bool IncludeStatic { get; set; } - [JsonPropertyName("IsFieldsAllowed")] public bool IncludeFields { get; set; } - [JsonPropertyName("IsEventsAllowed")] public bool IncludeEvents { get; set; } - [JsonPropertyName("IsExtensionsAllowed")] public bool IncludeExtensions { get; set; } - [JsonPropertyName("IsRootHierarchyAllowed")] public bool IncludeRootHierarchy { get; set; } - [JsonPropertyName("IsModifyTabAllowed")] public bool UseModifyTab { get; set; } -} - public sealed class SettingsService : ISettingsService { - private const int DefaultTransitionDuration = 200; - - private readonly FolderLocations _folderLocations; - private readonly JsonSerializerOptions _jsonSerializerOptions; private readonly ILogger _logger; - private readonly Settings _settings; public SettingsService(IOptions foldersOptions, IOptions jsonOptions, ILogger logger) { - _folderLocations = foldersOptions.Value; - _jsonSerializerOptions = jsonOptions.Value; _logger = logger; - _settings = LoadSettings(); - } - - public ApplicationTheme Theme - { - get => _settings.Theme; - set => _settings.Theme = value; - } - - public WindowBackdropType Background - { - get => _settings.Background; - set => _settings.Background = value; - } - - public int TransitionDuration - { - get => _settings.TransitionDuration; - private set => _settings.TransitionDuration = value; - } - - public bool UseHardwareRendering - { - get => _settings.UseHardwareRendering; - set => _settings.UseHardwareRendering = value; - } - - public bool ShowTimeColumn - { - get => _settings.ShowTimeColumn; - set => _settings.ShowTimeColumn = value; - } - - public bool ShowMemoryColumn - { - get => _settings.ShowMemoryColumn; - set => _settings.ShowMemoryColumn = value; - } - - public bool UseModifyTab - { - get => _settings.UseModifyTab; - set => _settings.UseModifyTab = value; - } - - public bool UseSizeRestoring - { - get => _settings.UseSizeRestoring; - set => _settings.UseSizeRestoring = value; - } - - public double WindowWidth - { - get => _settings.WindowWidth; - set => _settings.WindowWidth = value; - } - - public double WindowHeight - { - get => _settings.WindowHeight; - set => _settings.WindowHeight = value; - } - - public bool IncludeUnsupported - { - get => _settings.IncludeUnsupported; - set => _settings.IncludeUnsupported = value; - } - - public bool IncludePrivate - { - get => _settings.IncludePrivate; - set => _settings.IncludePrivate = value; - } - - public bool IncludeStatic - { - get => _settings.IncludeStatic; - set => _settings.IncludeStatic = value; - } - - public bool IncludeFields - { - get => _settings.IncludeFields; - set => _settings.IncludeFields = value; - } - - public bool IncludeEvents - { - get => _settings.IncludeEvents; - set => _settings.IncludeEvents = value; - } - - public bool IncludeExtensions - { - get => _settings.IncludeExtensions; - set => _settings.IncludeExtensions = value; - } - - public bool IncludeRootHierarchy - { - get => _settings.IncludeRootHierarchy; - set => _settings.IncludeRootHierarchy = value; + GeneralSettings = new GeneralSettings(foldersOptions.Value.GeneralSettingsPath, jsonOptions); + RenderSettings = new RenderSettings(foldersOptions.Value.RenderSettingsPath, jsonOptions); + + LoadSettings(); } - public int ApplyTransition(bool value) - { - return TransitionDuration = value ? DefaultTransitionDuration : 0; - } + public GeneralSettings GeneralSettings { get; } + public RenderSettings RenderSettings { get; } - public void Save() + public void SaveSettings() { - Directory.CreateDirectory(Path.GetDirectoryName(_folderLocations.SettingsPath)!); + try + { + GeneralSettings.Save(); + } + catch (Exception exception) + { + _logger.LogError(exception, "General settings deserializing error"); + } try { - var json = JsonSerializer.Serialize(_settings, _jsonSerializerOptions); - File.WriteAllText(_folderLocations.SettingsPath, json); + RenderSettings.Save(); } catch (Exception exception) { - _logger.LogError(exception, "Settings serializing error"); + _logger.LogError(exception, "Render settings deserializing error"); } } - private Settings LoadSettings() + public void LoadSettings() { - if (!File.Exists(_folderLocations.SettingsPath)) return new Settings(); - try { - using var config = File.OpenRead(_folderLocations.SettingsPath); - return JsonSerializer.Deserialize(config, _jsonSerializerOptions); + GeneralSettings.Load(); } catch (Exception exception) { - _logger.LogError(exception, "Settings deserializing error"); + _logger.LogError(exception, "General settings serializing error"); } - return new Settings(); + try + { + RenderSettings.Load(); + } + catch (Exception exception) + { + _logger.LogError(exception, "Render settings serializing error"); + } } } \ No newline at end of file diff --git a/source/RevitLookup/ViewModels/Dialogs/Visualization/BoundingBoxVisualizationViewModel.cs b/source/RevitLookup/ViewModels/Dialogs/Visualization/BoundingBoxVisualizationViewModel.cs new file mode 100644 index 00000000..b860b541 --- /dev/null +++ b/source/RevitLookup/ViewModels/Dialogs/Visualization/BoundingBoxVisualizationViewModel.cs @@ -0,0 +1,152 @@ +// Copyright 2003-2024 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using Microsoft.Extensions.Logging; +using RevitLookup.Core.Visualization; +using RevitLookup.Models.Render; +using RevitLookup.Services; +using RevitLookup.Services.Contracts; +using Color = Autodesk.Revit.DB.Color; + +namespace RevitLookup.ViewModels.Dialogs.Visualization; + +public sealed partial class BoundingBoxVisualizationViewModel( + NotificationService notificationService, + ISettingsService settingsService, + ILogger logger) + : ObservableObject +{ + private readonly BoundingBoxVisualizationServer _server = new(); + + [ObservableProperty] private double _transparency = settingsService.RenderSettings.BoundingBoxSettings.Transparency; + + [ObservableProperty] private System.Windows.Media.Color _surfaceColor = settingsService.RenderSettings.BoundingBoxSettings.SurfaceColor; + [ObservableProperty] private System.Windows.Media.Color _edgeColor = settingsService.RenderSettings.BoundingBoxSettings.EdgeColor; + [ObservableProperty] private System.Windows.Media.Color _axisColor = settingsService.RenderSettings.BoundingBoxSettings.AxisColor; + + [ObservableProperty] private bool _showSurface = settingsService.RenderSettings.BoundingBoxSettings.ShowSurface; + [ObservableProperty] private bool _showEdge = settingsService.RenderSettings.BoundingBoxSettings.ShowEdge; + [ObservableProperty] private bool _showAxis = settingsService.RenderSettings.BoundingBoxSettings.ShowAxis; + + public void RegisterServer(BoundingBoxXYZ box) + { + UpdateShowSurface(ShowSurface); + UpdateShowEdge(ShowEdge); + UpdateShowAxis(ShowAxis); + + UpdateSurfaceColor(SurfaceColor); + UpdateEdgeColor(EdgeColor); + UpdateAxisColor(AxisColor); + + UpdateTransparency(Transparency); + + _server.RenderFailed += HandleRenderFailure; + _server.Register(box); + } + + public void UnregisterServer() + { + _server.RenderFailed -= HandleRenderFailure; + _server.Unregister(); + } + + private void HandleRenderFailure(object sender, RenderFailedEventArgs args) + { + logger.LogError(args.Exception, "Render error"); + notificationService.ShowError("Render error", args.Exception); + } + + partial void OnTransparencyChanged(double value) + { + settingsService.RenderSettings.BoundingBoxSettings.Transparency = value; + UpdateTransparency(value); + } + + partial void OnSurfaceColorChanged(System.Windows.Media.Color value) + { + settingsService.RenderSettings.BoundingBoxSettings.SurfaceColor = value; + UpdateSurfaceColor(value); + } + + partial void OnEdgeColorChanged(System.Windows.Media.Color value) + { + settingsService.RenderSettings.BoundingBoxSettings.EdgeColor = value; + UpdateEdgeColor(value); + } + + partial void OnAxisColorChanged(System.Windows.Media.Color value) + { + settingsService.RenderSettings.BoundingBoxSettings.AxisColor = value; + UpdateAxisColor(value); + } + + partial void OnShowSurfaceChanged(bool value) + { + settingsService.RenderSettings.BoundingBoxSettings.ShowSurface = value; + UpdateShowSurface(value); + } + + partial void OnShowEdgeChanged(bool value) + { + settingsService.RenderSettings.BoundingBoxSettings.ShowEdge = value; + UpdateShowEdge(value); + } + + partial void OnShowAxisChanged(bool value) + { + settingsService.RenderSettings.BoundingBoxSettings.ShowEdge = value; + UpdateShowAxis(value); + } + + private void UpdateSurfaceColor(System.Windows.Media.Color value) + { + _server.UpdateSurfaceColor(new Color(value.R, value.G, value.B)); + } + + private void UpdateEdgeColor(System.Windows.Media.Color value) + { + _server.UpdateEdgeColor(new Color(value.R, value.G, value.B)); + } + + private void UpdateAxisColor(System.Windows.Media.Color value) + { + _server.UpdateAxisColor(new Color(value.R, value.G, value.B)); + } + + private void UpdateTransparency(double value) + { + _server.UpdateTransparency(value / 100); + } + + private void UpdateShowSurface(bool value) + { + _server.UpdateSurfaceVisibility(value); + } + + private void UpdateShowEdge(bool value) + { + _server.UpdateEdgeVisibility(value); + } + + private void UpdateShowAxis(bool value) + { + _server.UpdateAxisVisibility(value); + } +} \ No newline at end of file diff --git a/source/RevitLookup/ViewModels/Dialogs/Visualization/FaceVisualizationViewModel.cs b/source/RevitLookup/ViewModels/Dialogs/Visualization/FaceVisualizationViewModel.cs new file mode 100644 index 00000000..1579fcb3 --- /dev/null +++ b/source/RevitLookup/ViewModels/Dialogs/Visualization/FaceVisualizationViewModel.cs @@ -0,0 +1,167 @@ +// Copyright 2003-2024 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using Microsoft.Extensions.Logging; +using RevitLookup.Core.Visualization; +using RevitLookup.Models.Render; +using RevitLookup.Services; +using RevitLookup.Services.Contracts; +using Color = Autodesk.Revit.DB.Color; + +namespace RevitLookup.ViewModels.Dialogs.Visualization; + +public sealed partial class FaceVisualizationViewModel( + NotificationService notificationService, + ISettingsService settingsService, + ILogger logger) + : ObservableObject +{ + private readonly FaceVisualizationServer _server = new(); + + [ObservableProperty] private double _extrusion = settingsService.RenderSettings.FaceSettings.Extrusion; + [ObservableProperty] private double _transparency = settingsService.RenderSettings.FaceSettings.Transparency; + + [ObservableProperty] private System.Windows.Media.Color _surfaceColor = settingsService.RenderSettings.FaceSettings.SurfaceColor; + [ObservableProperty] private System.Windows.Media.Color _meshColor = settingsService.RenderSettings.FaceSettings.MeshColor; + [ObservableProperty] private System.Windows.Media.Color _normalVectorColor = settingsService.RenderSettings.FaceSettings.NormalVectorColor; + + [ObservableProperty] private bool _showSurface = settingsService.RenderSettings.FaceSettings.ShowSurface; + [ObservableProperty] private bool _showMeshGrid = settingsService.RenderSettings.FaceSettings.ShowMeshGrid; + [ObservableProperty] private bool _showNormalVector = settingsService.RenderSettings.FaceSettings.ShowNormalVector; + + public double MinExtrusion => settingsService.RenderSettings.FaceSettings.MinExtrusion; + + public void RegisterServer(Face face) + { + UpdateShowSurface(ShowSurface); + UpdateShowMeshGrid(ShowMeshGrid); + UpdateShowNormalVector(ShowNormalVector); + + UpdateSurfaceColor(SurfaceColor); + UpdateMeshColor(MeshColor); + UpdateNormalVectorColor(NormalVectorColor); + + UpdateTransparency(Transparency); + UpdateExtrusion(Extrusion); + + _server.RenderFailed += HandleRenderFailure; + _server.Register(face); + } + + public void UnregisterServer() + { + _server.RenderFailed -= HandleRenderFailure; + _server.Unregister(); + } + + private void HandleRenderFailure(object sender, RenderFailedEventArgs args) + { + logger.LogError(args.Exception, "Render error"); + notificationService.ShowError("Render error", args.Exception); + } + + partial void OnSurfaceColorChanged(System.Windows.Media.Color value) + { + settingsService.RenderSettings.FaceSettings.SurfaceColor = value; + UpdateSurfaceColor(value); + } + + partial void OnMeshColorChanged(System.Windows.Media.Color value) + { + settingsService.RenderSettings.FaceSettings.MeshColor = value; + UpdateMeshColor(value); + } + + partial void OnNormalVectorColorChanged(System.Windows.Media.Color value) + { + settingsService.RenderSettings.FaceSettings.NormalVectorColor = value; + UpdateNormalVectorColor(value); + } + + partial void OnExtrusionChanged(double value) + { + settingsService.RenderSettings.FaceSettings.Extrusion = value; + UpdateExtrusion(value); + } + + partial void OnTransparencyChanged(double value) + { + settingsService.RenderSettings.FaceSettings.Transparency = value; + UpdateTransparency(value); + } + + partial void OnShowSurfaceChanged(bool value) + { + settingsService.RenderSettings.FaceSettings.ShowSurface = value; + UpdateShowSurface(value); + } + + partial void OnShowMeshGridChanged(bool value) + { + settingsService.RenderSettings.FaceSettings.ShowMeshGrid = value; + UpdateShowMeshGrid(value); + } + + partial void OnShowNormalVectorChanged(bool value) + { + settingsService.RenderSettings.FaceSettings.ShowNormalVector = value; + UpdateShowNormalVector(value); + } + + private void UpdateSurfaceColor(System.Windows.Media.Color value) + { + _server.UpdateSurfaceColor(new Color(value.R, value.G, value.B)); + } + + private void UpdateMeshColor(System.Windows.Media.Color value) + { + _server.UpdateMeshGridColor(new Color(value.R, value.G, value.B)); + } + + private void UpdateNormalVectorColor(System.Windows.Media.Color value) + { + _server.UpdateNormalVectorColor(new Color(value.R, value.G, value.B)); + } + + private void UpdateExtrusion(double value) + { + _server.UpdateExtrusion(value / 12); + } + + private void UpdateTransparency(double value) + { + _server.UpdateTransparency(value / 100); + } + + private void UpdateShowSurface(bool value) + { + _server.UpdateSurfaceVisibility(value); + } + + private void UpdateShowMeshGrid(bool value) + { + _server.UpdateMeshGridVisibility(value); + } + + private void UpdateShowNormalVector(bool value) + { + _server.UpdateNormalVectorVisibility(value); + } +} \ No newline at end of file diff --git a/source/RevitLookup/ViewModels/Dialogs/Visualization/MeshVisualizationViewModel.cs b/source/RevitLookup/ViewModels/Dialogs/Visualization/MeshVisualizationViewModel.cs new file mode 100644 index 00000000..36393ae9 --- /dev/null +++ b/source/RevitLookup/ViewModels/Dialogs/Visualization/MeshVisualizationViewModel.cs @@ -0,0 +1,167 @@ +// Copyright 2003-2024 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using Microsoft.Extensions.Logging; +using RevitLookup.Core.Visualization; +using RevitLookup.Models.Render; +using RevitLookup.Services; +using RevitLookup.Services.Contracts; +using Color = Autodesk.Revit.DB.Color; + +namespace RevitLookup.ViewModels.Dialogs.Visualization; + +public sealed partial class MeshVisualizationViewModel( + NotificationService notificationService, + ISettingsService settingsService, + ILogger logger) + : ObservableObject +{ + private readonly MeshVisualizationServer _server = new(); + + [ObservableProperty] private double _extrusion = settingsService.RenderSettings.MeshSettings.Extrusion; + [ObservableProperty] private double _transparency = settingsService.RenderSettings.MeshSettings.Transparency; + + [ObservableProperty] private System.Windows.Media.Color _surfaceColor = settingsService.RenderSettings.MeshSettings.SurfaceColor; + [ObservableProperty] private System.Windows.Media.Color _meshColor = settingsService.RenderSettings.MeshSettings.MeshColor; + [ObservableProperty] private System.Windows.Media.Color _normalVectorColor = settingsService.RenderSettings.MeshSettings.NormalVectorColor; + + [ObservableProperty] private bool _showSurface = settingsService.RenderSettings.MeshSettings.ShowSurface; + [ObservableProperty] private bool _showMeshGrid = settingsService.RenderSettings.MeshSettings.ShowMeshGrid; + [ObservableProperty] private bool _showNormalVector = settingsService.RenderSettings.MeshSettings.ShowNormalVector; + + public double MinExtrusion => settingsService.RenderSettings.MeshSettings.MinExtrusion; + + public void RegisterServer(Mesh mesh) + { + UpdateShowSurface(ShowSurface); + UpdateShowMeshGrid(ShowMeshGrid); + UpdateShowNormalVector(ShowNormalVector); + + UpdateSurfaceColor(SurfaceColor); + UpdateMeshColor(MeshColor); + UpdateNormalVectorColor(NormalVectorColor); + + UpdateTransparency(Transparency); + UpdateExtrusion(Extrusion); + + _server.RenderFailed += HandleRenderFailure; + _server.Register(mesh); + } + + public void UnregisterServer() + { + _server.RenderFailed -= HandleRenderFailure; + _server.Unregister(); + } + + private void HandleRenderFailure(object sender, RenderFailedEventArgs args) + { + logger.LogError(args.Exception, "Render error"); + notificationService.ShowError("Render error", args.Exception); + } + + partial void OnSurfaceColorChanged(System.Windows.Media.Color value) + { + settingsService.RenderSettings.MeshSettings.SurfaceColor = value; + UpdateSurfaceColor(value); + } + + partial void OnMeshColorChanged(System.Windows.Media.Color value) + { + settingsService.RenderSettings.MeshSettings.MeshColor = value; + UpdateMeshColor(value); + } + + partial void OnNormalVectorColorChanged(System.Windows.Media.Color value) + { + settingsService.RenderSettings.MeshSettings.NormalVectorColor = value; + UpdateNormalVectorColor(value); + } + + partial void OnExtrusionChanged(double value) + { + settingsService.RenderSettings.MeshSettings.Extrusion = value; + UpdateExtrusion(value); + } + + partial void OnTransparencyChanged(double value) + { + settingsService.RenderSettings.MeshSettings.Transparency = value; + UpdateTransparency(value); + } + + partial void OnShowSurfaceChanged(bool value) + { + settingsService.RenderSettings.MeshSettings.ShowSurface = value; + UpdateShowSurface(value); + } + + partial void OnShowMeshGridChanged(bool value) + { + settingsService.RenderSettings.MeshSettings.ShowMeshGrid = value; + UpdateShowMeshGrid(value); + } + + partial void OnShowNormalVectorChanged(bool value) + { + settingsService.RenderSettings.MeshSettings.ShowNormalVector = value; + UpdateShowNormalVector(value); + } + + private void UpdateSurfaceColor(System.Windows.Media.Color value) + { + _server.UpdateSurfaceColor(new Color(value.R, value.G, value.B)); + } + + private void UpdateMeshColor(System.Windows.Media.Color value) + { + _server.UpdateMeshGridColor(new Color(value.R, value.G, value.B)); + } + + private void UpdateNormalVectorColor(System.Windows.Media.Color value) + { + _server.UpdateNormalVectorColor(new Color(value.R, value.G, value.B)); + } + + private void UpdateExtrusion(double value) + { + _server.UpdateExtrusion(value / 12); + } + + private void UpdateTransparency(double value) + { + _server.UpdateTransparency(value / 100); + } + + private void UpdateShowSurface(bool value) + { + _server.UpdateSurfaceVisibility(value); + } + + private void UpdateShowMeshGrid(bool value) + { + _server.UpdateMeshGridVisibility(value); + } + + private void UpdateShowNormalVector(bool value) + { + _server.UpdateNormalVectorVisibility(value); + } +} \ No newline at end of file diff --git a/source/RevitLookup/ViewModels/Dialogs/Visualization/PolylineVisualizationViewModel.cs b/source/RevitLookup/ViewModels/Dialogs/Visualization/PolylineVisualizationViewModel.cs new file mode 100644 index 00000000..03d9c04d --- /dev/null +++ b/source/RevitLookup/ViewModels/Dialogs/Visualization/PolylineVisualizationViewModel.cs @@ -0,0 +1,178 @@ +// Copyright 2003-2024 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using Microsoft.Extensions.Logging; +using RevitLookup.Core.Visualization; +using RevitLookup.Models.Render; +using RevitLookup.Services; +using RevitLookup.Services.Contracts; +using Color = Autodesk.Revit.DB.Color; + +namespace RevitLookup.ViewModels.Dialogs.Visualization; + +public sealed partial class PolylineVisualizationViewModel( + NotificationService notificationService, + ISettingsService settingsService, + ILogger logger) + : ObservableObject +{ + private readonly PolylineVisualizationServer _server = new(); + + [ObservableProperty] private double _diameter = settingsService.RenderSettings.PolylineSettings.Diameter; + [ObservableProperty] private double _transparency = settingsService.RenderSettings.PolylineSettings.Transparency; + + [ObservableProperty] private System.Windows.Media.Color _surfaceColor = settingsService.RenderSettings.PolylineSettings.SurfaceColor; + [ObservableProperty] private System.Windows.Media.Color _curveColor = settingsService.RenderSettings.PolylineSettings.CurveColor; + [ObservableProperty] private System.Windows.Media.Color _directionColor = settingsService.RenderSettings.PolylineSettings.DirectionColor; + + [ObservableProperty] private bool _showSurface = settingsService.RenderSettings.PolylineSettings.ShowSurface; + [ObservableProperty] private bool _showCurve = settingsService.RenderSettings.PolylineSettings.ShowCurve; + [ObservableProperty] private bool _showDirection = settingsService.RenderSettings.PolylineSettings.ShowDirection; + + public double MinThickness => settingsService.RenderSettings.PolylineSettings.MinThickness; + + public void RegisterServer(Curve curve) + { + Initialize(); + _server.RenderFailed += HandleRenderFailure; + _server.Register(curve.Tessellate()); + } + + public void RegisterServer(Edge edge) + { + Initialize(); + _server.RenderFailed += HandleRenderFailure; + _server.Register(edge.Tessellate()); + } + + private void Initialize() + { + UpdateShowSurface(ShowSurface); + UpdateShowCurve(ShowCurve); + UpdateShowDirection(ShowDirection); + + UpdateSurfaceColor(SurfaceColor); + UpdateCurveColor(CurveColor); + UpdateDirectionColor(DirectionColor); + + UpdateTransparency(Transparency); + UpdateDiameter(Diameter); + } + + public void UnregisterServer() + { + _server.RenderFailed -= HandleRenderFailure; + _server.Unregister(); + } + + private void HandleRenderFailure(object sender, RenderFailedEventArgs args) + { + logger.LogError(args.Exception, "Render error"); + notificationService.ShowError("Render error", args.Exception); + } + + partial void OnSurfaceColorChanged(System.Windows.Media.Color value) + { + settingsService.RenderSettings.PolylineSettings.SurfaceColor = value; + UpdateSurfaceColor(value); + } + + partial void OnCurveColorChanged(System.Windows.Media.Color value) + { + settingsService.RenderSettings.PolylineSettings.CurveColor = value; + UpdateCurveColor(value); + } + + partial void OnDirectionColorChanged(System.Windows.Media.Color value) + { + settingsService.RenderSettings.PolylineSettings.DirectionColor = value; + UpdateDirectionColor(value); + } + + partial void OnDiameterChanged(double value) + { + settingsService.RenderSettings.PolylineSettings.Diameter = value; + UpdateDiameter(value); + } + + partial void OnTransparencyChanged(double value) + { + settingsService.RenderSettings.PolylineSettings.Transparency = value; + UpdateTransparency(value); + } + + partial void OnShowSurfaceChanged(bool value) + { + settingsService.RenderSettings.PolylineSettings.ShowSurface = value; + UpdateShowSurface(value); + } + + partial void OnShowCurveChanged(bool value) + { + settingsService.RenderSettings.PolylineSettings.ShowCurve = value; + UpdateShowCurve(value); + } + + partial void OnShowDirectionChanged(bool value) + { + settingsService.RenderSettings.PolylineSettings.ShowDirection = value; + UpdateShowDirection(value); + } + + private void UpdateSurfaceColor(System.Windows.Media.Color value) + { + _server.UpdateSurfaceColor(new Color(value.R, value.G, value.B)); + } + + private void UpdateCurveColor(System.Windows.Media.Color value) + { + _server.UpdateCurveColor(new Color(value.R, value.G, value.B)); + } + + private void UpdateDirectionColor(System.Windows.Media.Color value) + { + _server.UpdateDirectionColor(new Color(value.R, value.G, value.B)); + } + + private void UpdateDiameter(double value) + { + _server.UpdateDiameter(value / 12); + } + + private void UpdateTransparency(double value) + { + _server.UpdateTransparency(value / 100); + } + + private void UpdateShowSurface(bool value) + { + _server.UpdateSurfaceVisibility(value); + } + + private void UpdateShowCurve(bool value) + { + _server.UpdateCurveVisibility(value); + } + + private void UpdateShowDirection(bool value) + { + _server.UpdateDirectionVisibility(value); + } +} \ No newline at end of file diff --git a/source/RevitLookup/ViewModels/Dialogs/Visualization/SolidVisualizationViewModel.cs b/source/RevitLookup/ViewModels/Dialogs/Visualization/SolidVisualizationViewModel.cs new file mode 100644 index 00000000..45a5594a --- /dev/null +++ b/source/RevitLookup/ViewModels/Dialogs/Visualization/SolidVisualizationViewModel.cs @@ -0,0 +1,191 @@ +// Copyright 2003-2024 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using Microsoft.Extensions.Logging; +using RevitLookup.Core.Visualization; +using RevitLookup.Models.Render; +using RevitLookup.Services; +using RevitLookup.Services.Contracts; +using Color = Autodesk.Revit.DB.Color; + +namespace RevitLookup.ViewModels.Dialogs.Visualization; + +public sealed partial class SolidVisualizationViewModel( + NotificationService notificationService, + ISettingsService settingsService, + ILogger logger) + : ObservableObject +{ + private readonly SolidVisualizationServer _server = new(); + + [ObservableProperty] private double _transparency = settingsService.RenderSettings.SolidSettings.Transparency; + [ObservableProperty] private double _cageTransparency = settingsService.RenderSettings.SolidSettings.CageTransparency; + [ObservableProperty] private double _cageSize = settingsService.RenderSettings.SolidSettings.CageSize; + + [ObservableProperty] private System.Windows.Media.Color _faceColor = settingsService.RenderSettings.SolidSettings.FaceColor; + [ObservableProperty] private System.Windows.Media.Color _edgeColor = settingsService.RenderSettings.SolidSettings.EdgeColor; + [ObservableProperty] private System.Windows.Media.Color _cageSurfaceColor = settingsService.RenderSettings.SolidSettings.CageSurfaceColor; + [ObservableProperty] private System.Windows.Media.Color _cageFrameColor = settingsService.RenderSettings.SolidSettings.CageFrameColor; + + [ObservableProperty] private bool _showFace = settingsService.RenderSettings.SolidSettings.ShowFace; + [ObservableProperty] private bool _showEdge = settingsService.RenderSettings.SolidSettings.ShowEdge; + [ObservableProperty] private bool _showCageSurface = settingsService.RenderSettings.SolidSettings.ShowCageSurface; + + public void RegisterServer(Solid solid) + { + UpdateShowFace(ShowFace); + UpdateShowEdge(ShowEdge); + UpdateShowCageSurface(ShowCageSurface); + + UpdateFaceColor(FaceColor); + UpdateEdgeColor(EdgeColor); + UpdateCageSurfaceColor(CageSurfaceColor); + UpdateCageFrameColor(CageFrameColor); + + UpdateTransparency(Transparency); + UpdateCageTransparency(CageTransparency); + UpdateCageSize(CageSize); + + _server.RenderFailed += HandleRenderFailure; + _server.Register(solid); + } + + public void UnregisterServer() + { + _server.RenderFailed -= HandleRenderFailure; + _server.Unregister(); + } + + private void HandleRenderFailure(object sender, RenderFailedEventArgs args) + { + logger.LogError(args.Exception, "Render error"); + notificationService.ShowError("Render error", args.Exception); + } + + partial void OnFaceColorChanged(System.Windows.Media.Color value) + { + settingsService.RenderSettings.SolidSettings.FaceColor = value; + UpdateFaceColor(value); + } + + partial void OnEdgeColorChanged(System.Windows.Media.Color value) + { + settingsService.RenderSettings.SolidSettings.EdgeColor = value; + UpdateEdgeColor(value); + } + + partial void OnCageSurfaceColorChanged(System.Windows.Media.Color value) + { + settingsService.RenderSettings.SolidSettings.CageSurfaceColor = value; + UpdateCageSurfaceColor(value); + } + + partial void OnCageFrameColorChanged(System.Windows.Media.Color value) + { + settingsService.RenderSettings.SolidSettings.CageFrameColor = value; + UpdateCageFrameColor(value); + } + + partial void OnTransparencyChanged(double value) + { + settingsService.RenderSettings.SolidSettings.Transparency = value; + UpdateTransparency(value); + } + + partial void OnCageTransparencyChanged(double value) + { + settingsService.RenderSettings.SolidSettings.CageTransparency = value; + UpdateCageTransparency(value); + } + + partial void OnCageSizeChanged(double value) + { + settingsService.RenderSettings.SolidSettings.CageSize = value; + UpdateCageSize(value); + } + + partial void OnShowFaceChanged(bool value) + { + settingsService.RenderSettings.SolidSettings.ShowFace = value; + UpdateShowFace(value); + } + + partial void OnShowEdgeChanged(bool value) + { + settingsService.RenderSettings.SolidSettings.ShowEdge = value; + UpdateShowEdge(value); + } + + partial void OnShowCageSurfaceChanged(bool value) + { + settingsService.RenderSettings.SolidSettings.ShowCageSurface = value; + UpdateShowCageSurface(value); + } + + private void UpdateFaceColor(System.Windows.Media.Color value) + { + _server.UpdateFaceColor(new Color(value.R, value.G, value.B)); + } + + private void UpdateEdgeColor(System.Windows.Media.Color value) + { + _server.UpdateEdgeColor(new Color(value.R, value.G, value.B)); + } + + private void UpdateCageSurfaceColor(System.Windows.Media.Color value) + { + _server.UpdateCageSurfaceColor(new Color(value.R, value.G, value.B)); + } + + private void UpdateCageFrameColor(System.Windows.Media.Color value) + { + _server.UpdateCageFrameColor(new Color(value.R, value.G, value.B)); + } + + private void UpdateTransparency(double value) + { + _server.UpdateTransparency(value / 100); + } + + private void UpdateCageTransparency(double value) + { + _server.UpdateCageTransparency(value / 100); + } + + private void UpdateCageSize(double value) + { + _server.UpdateCageSize(value / 12); + } + + private void UpdateShowFace(bool value) + { + _server.UpdateFaceVisibility(value); + } + + private void UpdateShowEdge(bool value) + { + _server.UpdateEdgeVisibility(value); + } + + private void UpdateShowCageSurface(bool value) + { + _server.UpdateCageSurfaceVisibility(value); + } +} \ No newline at end of file diff --git a/source/RevitLookup/ViewModels/Dialogs/Visualization/XyzVisualizationViewModel.cs b/source/RevitLookup/ViewModels/Dialogs/Visualization/XyzVisualizationViewModel.cs new file mode 100644 index 00000000..705b1cda --- /dev/null +++ b/source/RevitLookup/ViewModels/Dialogs/Visualization/XyzVisualizationViewModel.cs @@ -0,0 +1,180 @@ +// Copyright 2003-2024 by Autodesk, Inc. +// +// Permission to use, copy, modify, and distribute this software in +// object code form for any purpose and without fee is hereby granted, +// provided that the above copyright notice appears in all copies and +// that both that copyright notice and the limited warranty and +// restricted rights notice below appear in all supporting +// documentation. +// +// AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. +// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF +// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. +// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE +// UNINTERRUPTED OR ERROR FREE. +// +// Use, duplication, or disclosure by the U.S. Government is subject to +// restrictions set forth in FAR 52.227-19 (Commercial Computer +// Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) +// (Rights in Technical Data and Computer Software), as applicable. + +using Microsoft.Extensions.Logging; +using RevitLookup.Core.Visualization; +using RevitLookup.Models.Render; +using RevitLookup.Services; +using RevitLookup.Services.Contracts; +using Color = Autodesk.Revit.DB.Color; + +namespace RevitLookup.ViewModels.Dialogs.Visualization; + +public sealed partial class XyzVisualizationViewModel( + NotificationService notificationService, + ISettingsService settingsService, + ILogger logger) + : ObservableObject +{ + private readonly XyzVisualizationServer _server = new(); + + [ObservableProperty] private double _axisLength = settingsService.RenderSettings.XyzSettings.AxisLength; + [ObservableProperty] private double _transparency = settingsService.RenderSettings.XyzSettings.Transparency; + + [ObservableProperty] private System.Windows.Media.Color _xColor = settingsService.RenderSettings.XyzSettings.XColor; + [ObservableProperty] private System.Windows.Media.Color _yColor = settingsService.RenderSettings.XyzSettings.YColor; + [ObservableProperty] private System.Windows.Media.Color _zColor = settingsService.RenderSettings.XyzSettings.ZColor; + + [ObservableProperty] private bool _showPlane = settingsService.RenderSettings.XyzSettings.ShowPlane; + [ObservableProperty] private bool _showXAxis = settingsService.RenderSettings.XyzSettings.ShowXAxis; + [ObservableProperty] private bool _showYAxis = settingsService.RenderSettings.XyzSettings.ShowYAxis; + [ObservableProperty] private bool _showZAxis = settingsService.RenderSettings.XyzSettings.ShowZAxis; + + public double MinAxisLength => settingsService.RenderSettings.XyzSettings.MinAxisLength; + + public void RegisterServer(XYZ point) + { + UpdateShowPlane(ShowPlane); + UpdateShowXAxis(ShowXAxis); + UpdateShowYAxis(ShowYAxis); + UpdateShowZAxis(ShowZAxis); + + UpdateXColor(XColor); + UpdateYColor(YColor); + UpdateZColor(ZColor); + + UpdateAxisLength(AxisLength); + UpdateTransparency(Transparency); + + _server.RenderFailed += HandleRenderFailure; + _server.Register(point); + } + + public void UnregisterServer() + { + _server.RenderFailed -= HandleRenderFailure; + _server.Unregister(); + } + + private void HandleRenderFailure(object sender, RenderFailedEventArgs args) + { + logger.LogError(args.Exception, "Render error"); + notificationService.ShowError("Render error", args.Exception); + } + + partial void OnXColorChanged(System.Windows.Media.Color value) + { + settingsService.RenderSettings.XyzSettings.XColor = value; + UpdateXColor(value); + } + + partial void OnYColorChanged(System.Windows.Media.Color value) + { + settingsService.RenderSettings.XyzSettings.YColor = value; + UpdateYColor(value); + } + + partial void OnZColorChanged(System.Windows.Media.Color value) + { + settingsService.RenderSettings.XyzSettings.ZColor = value; + UpdateZColor(value); + } + + partial void OnAxisLengthChanged(double value) + { + settingsService.RenderSettings.XyzSettings.AxisLength = value; + UpdateAxisLength(value); + } + + partial void OnTransparencyChanged(double value) + { + settingsService.RenderSettings.XyzSettings.Transparency = value; + UpdateTransparency(value); + } + + partial void OnShowPlaneChanged(bool value) + { + settingsService.RenderSettings.XyzSettings.ShowPlane = value; + UpdateShowPlane(value); + } + + partial void OnShowXAxisChanged(bool value) + { + settingsService.RenderSettings.XyzSettings.ShowXAxis = value; + UpdateShowXAxis(value); + } + + partial void OnShowYAxisChanged(bool value) + { + settingsService.RenderSettings.XyzSettings.ShowYAxis = value; + UpdateShowYAxis(value); + } + + partial void OnShowZAxisChanged(bool value) + { + settingsService.RenderSettings.XyzSettings.ShowZAxis = value; + UpdateShowZAxis(value); + } + + private void UpdateXColor(System.Windows.Media.Color value) + { + _server.UpdateXColor(new Color(value.R, value.G, value.B)); + } + + private void UpdateYColor(System.Windows.Media.Color value) + { + _server.UpdateYColor(new Color(value.R, value.G, value.B)); + } + + private void UpdateZColor(System.Windows.Media.Color value) + { + _server.UpdateZColor(new Color(value.R, value.G, value.B)); + } + + private void UpdateAxisLength(double value) + { + _server.UpdateAxisLength(value / 12); + } + + private void UpdateTransparency(double value) + { + _server.UpdateTransparency(value / 100); + } + + private void UpdateShowPlane(bool value) + { + _server.UpdatePlaneVisibility(value); + } + + private void UpdateShowXAxis(bool value) + { + _server.UpdateXAxisVisibility(value); + } + + private void UpdateShowYAxis(bool value) + { + _server.UpdateYAxisVisibility(value); + } + + private void UpdateShowZAxis(bool value) + { + _server.UpdateZAxisVisibility(value); + } +} \ No newline at end of file diff --git a/source/RevitLookup/ViewModels/Pages/SettingsViewModel.cs b/source/RevitLookup/ViewModels/Pages/SettingsViewModel.cs index 1d09b93d..873d033a 100644 --- a/source/RevitLookup/ViewModels/Pages/SettingsViewModel.cs +++ b/source/RevitLookup/ViewModels/Pages/SettingsViewModel.cs @@ -18,7 +18,9 @@ // Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) // (Rights in Technical Data and Computer Software), as applicable. +using RevitLookup.Services; using RevitLookup.Services.Contracts; +using RevitLookup.Views.Dialogs; using Wpf.Ui; using Wpf.Ui.Appearance; using Wpf.Ui.Controls; @@ -28,24 +30,26 @@ namespace RevitLookup.ViewModels.Pages; public sealed partial class SettingsViewModel( ISettingsService settingsService, INavigationService navigationService, + IContentDialogService dialogService, + NotificationService notificationService, IWindow window) : ObservableObject { - [ObservableProperty] private ApplicationTheme _theme = settingsService.Theme; - [ObservableProperty] private WindowBackdropType _background = settingsService.Background; - - [ObservableProperty] private bool _useTransition = settingsService.TransitionDuration > 0; - [ObservableProperty] private bool _useHardwareRendering = settingsService.UseHardwareRendering; - [ObservableProperty] private bool _useSizeRestoring = settingsService.UseSizeRestoring; - [ObservableProperty] private bool _useModifyTab = settingsService.UseModifyTab; - + [ObservableProperty] private ApplicationTheme _theme = settingsService.GeneralSettings.Theme; + [ObservableProperty] private WindowBackdropType _background = settingsService.GeneralSettings.Background; + + [ObservableProperty] private bool _useTransition = settingsService.GeneralSettings.TransitionDuration > 0; + [ObservableProperty] private bool _useHardwareRendering = settingsService.GeneralSettings.UseHardwareRendering; + [ObservableProperty] private bool _useSizeRestoring = settingsService.GeneralSettings.UseSizeRestoring; + [ObservableProperty] private bool _useModifyTab = settingsService.GeneralSettings.UseModifyTab; + public List Themes { get; } = [ ApplicationTheme.Light, ApplicationTheme.Dark // ApplicationTheme.HighContrast ]; - + public List BackgroundEffects { get; } = [ WindowBackdropType.None, @@ -53,47 +57,69 @@ public sealed partial class SettingsViewModel( WindowBackdropType.Tabbed, WindowBackdropType.Mica ]; - + + [RelayCommand] + private async Task ResetSettings() + { + var dialog = new ResetSettingsDialog(dialogService, settingsService); + var result = await dialog.ShowAsync(); + if (!result) return; + + foreach (var settings in dialog.SelectedSettings) + { + try + { + settings.SetDefault(); + } + catch (Exception exception) + { + notificationService.ShowWarning("Reset settings error", exception.Message); + } + } + + notificationService.ShowSuccess("Reset was successful", "Some changes will be applied after closing the window"); + } + partial void OnThemeChanged(ApplicationTheme value) { - settingsService.Theme = value; - + settingsService.GeneralSettings.Theme = value; + foreach (var target in Wpf.Ui.Application.Windows) { Wpf.Ui.Application.MainWindow = target; - ApplicationThemeManager.Apply(settingsService.Theme, settingsService.Background); + ApplicationThemeManager.Apply(settingsService.GeneralSettings.Theme, settingsService.GeneralSettings.Background); } } - + partial void OnBackgroundChanged(WindowBackdropType value) { - settingsService.Background = value; - ApplicationThemeManager.Apply(settingsService.Theme, settingsService.Background); + settingsService.GeneralSettings.Background = value; + ApplicationThemeManager.Apply(settingsService.GeneralSettings.Theme, settingsService.GeneralSettings.Background); } - + partial void OnUseTransitionChanged(bool value) { - var transitionDuration = settingsService.ApplyTransition(value); + var transitionDuration = settingsService.GeneralSettings.ApplyTransition(value); navigationService.GetNavigationControl().TransitionDuration = transitionDuration; } - + partial void OnUseHardwareRenderingChanged(bool value) { - settingsService.UseHardwareRendering = value; - if (value) Application.EnableHardwareRendering(settingsService); - else Application.DisableHardwareRendering(settingsService); + settingsService.GeneralSettings.UseHardwareRendering = value; + if (value) Application.EnableHardwareRendering(settingsService.GeneralSettings); + else Application.DisableHardwareRendering(settingsService.GeneralSettings); } - + partial void OnUseSizeRestoringChanged(bool value) { - settingsService.UseSizeRestoring = value; + settingsService.GeneralSettings.UseSizeRestoring = value; if (value) window.EnableSizeTracking(); else window.DisableSizeTracking(); } - + partial void OnUseModifyTabChanged(bool value) { - settingsService.UseModifyTab = value; - RibbonController.ReloadPanels(settingsService); + settingsService.GeneralSettings.UseModifyTab = value; + RibbonController.ReloadPanels(settingsService.GeneralSettings); } } \ No newline at end of file diff --git a/source/RevitLookup/Views/Controls/ColorPicker/ColorPickerControl.xaml b/source/RevitLookup/Views/Controls/ColorPicker/ColorPickerControl.xaml new file mode 100644 index 00000000..7cc64e0b --- /dev/null +++ b/source/RevitLookup/Views/Controls/ColorPicker/ColorPickerControl.xaml @@ -0,0 +1,572 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/RevitLookup/Views/Controls/ColorPicker/ColorPickerControl.xaml.cs b/source/RevitLookup/Views/Controls/ColorPicker/ColorPickerControl.xaml.cs new file mode 100644 index 00000000..f680d37d --- /dev/null +++ b/source/RevitLookup/Views/Controls/ColorPicker/ColorPickerControl.xaml.cs @@ -0,0 +1,404 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Text.RegularExpressions; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Animation; +using RevitLookup.Utils; +using Wpf.Ui.Controls; +using Color = System.Windows.Media.Color; +using Point = System.Windows.Point; +using TextBox = System.Windows.Controls.TextBox; + +namespace RevitLookup.Views.Controls.ColorPicker; + +public partial class ColorPickerControl +{ + private double _currH = 360; + private double _currS = 1; + private double _currV = 1; + private bool _ignoreHexChanges; + private bool _ignoreRgbChanges; + private bool _ignoreGradientsChanges; + private bool _isCollapsed = true; + private Color _originalColor; + private Color _currentColor; + + public static readonly DependencyProperty SelectedColorProperty = DependencyProperty.Register(nameof(SelectedColor), + typeof(Color), + typeof(ColorPickerControl), + new FrameworkPropertyMetadata(Color.FromArgb(0, 0, 0, 0), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, SelectedColorPropertyChanged)); + + public ColorPickerControl() + { + InitializeComponent(); + UpdateHueGradient(1, 1); + } + + public Color SelectedColor + { + get => (Color) GetValue(SelectedColorProperty); + set => SetValue(SelectedColorProperty, value); + } + + private static void SelectedColorPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) + { + var control = (ColorPickerControl) dependencyObject; + var newColor = (Color) e.NewValue; + + control._originalColor = control._currentColor = newColor; + var newColorBackground = new SolidColorBrush(newColor); + control.CurrentColorButton.Background = newColorBackground; + + control._ignoreHexChanges = true; + control._ignoreRgbChanges = true; + + control.HexCode.Text = ColorToHex(newColor); + control.RNumberBox.Value = newColor.R; + control.GNumberBox.Value = newColor.G; + control.BNumberBox.Value = newColor.B; + control.SetColorFromTextBoxes(System.Drawing.Color.FromArgb(newColor.R, newColor.G, newColor.B)); + + control._ignoreRgbChanges = false; + control._ignoreHexChanges = false; + + var hsv = ColorFormatUtils.ConvertToHsvColor(System.Drawing.Color.FromArgb(newColor.R, newColor.G, newColor.B)); + SetColorVariationsForCurrentColor(dependencyObject, hsv); + } + + private void UpdateHueGradient(double saturation, double value) + { + var g6 = HsvColor.HueSpectrum(saturation, value); + + var gradientBrush = new LinearGradientBrush + { + StartPoint = new Point(0, 0), + EndPoint = new Point(1, 0) + }; + + for (var i = 0; i < g6.Length; i++) + { + var stop = new GradientStop(g6[i], i * 0.16); + gradientBrush.GradientStops.Add(stop); + } + + HueGradientSlider.Background = gradientBrush; + } + + private static void SetColorVariationsForCurrentColor(DependencyObject d, (double Hue, double Saturation, double Value) hsv) + { + var hueCoefficient = 0; + var hueCoefficient2 = 0; + if (1 - hsv.Value < 0.15) + { + hueCoefficient = 1; + } + + if (hsv.Value - 0.3 < 0) + { + hueCoefficient2 = 1; + } + + var s = hsv.Saturation; + var control = (ColorPickerControl) d; + + control.ColorVariation1Button.Background = new SolidColorBrush(HsvColor.RgbFromHsv(Math.Min(hsv.Hue + (hueCoefficient * 8), 360), s, Math.Min(hsv.Value + 0.3, 1))); + control.ColorVariation2Button.Background = new SolidColorBrush(HsvColor.RgbFromHsv(Math.Min(hsv.Hue + (hueCoefficient * 4), 360), s, Math.Min(hsv.Value + 0.15, 1))); + + control.ColorVariation3Button.Background = new SolidColorBrush(HsvColor.RgbFromHsv(Math.Max(hsv.Hue - (hueCoefficient2 * 4), 0), s, Math.Max(hsv.Value - 0.2, 0))); + control.ColorVariation4Button.Background = new SolidColorBrush(HsvColor.RgbFromHsv(Math.Max(hsv.Hue - (hueCoefficient2 * 8), 0), s, Math.Max(hsv.Value - 0.3, 0))); + } + + private void UpdateValueColorGradient(double posX) + { + ValueGradientSlider.Value = posX; + + _currV = posX / ValueGradientSlider.Maximum; + + UpdateHueGradient(_currS, _currV); + + SaturationStartColor.Color = HsvColor.RgbFromHsv(_currH, 0f, _currV); + SaturationStopColor.Color = HsvColor.RgbFromHsv(_currH, 1f, _currV); + } + + private void UpdateSaturationColorGradient(double posX) + { + SaturationGradientSlider.Value = posX; + + _currS = posX / HueGradientSlider.Maximum; + + UpdateHueGradient(_currS, _currV); + + ValueStartColor.Color = HsvColor.RgbFromHsv(_currH, _currS, 0f); + ValueStopColor.Color = HsvColor.RgbFromHsv(_currH, _currS, 1f); + } + + private void UpdateHueColorGradient(double posX) + { + HueGradientSlider.Value = posX; + _currH = posX / HueGradientSlider.Maximum * 360; + + SaturationStartColor.Color = HsvColor.RgbFromHsv(_currH, 0f, _currV); + SaturationStopColor.Color = HsvColor.RgbFromHsv(_currH, 1f, _currV); + + ValueStartColor.Color = HsvColor.RgbFromHsv(_currH, _currS, 0f); + ValueStopColor.Color = HsvColor.RgbFromHsv(_currH, _currS, 1f); + } + + private void UpdateTextBoxesAndCurrentColor(Color currentColor) + { + if (!_ignoreHexChanges) + { + // Second parameter is set to keep the hashtag if typed by the user before + HexCode.Text = ColorToHex(currentColor, HexCode.Text); + } + + if (!_ignoreRgbChanges) + { + RNumberBox.Value = currentColor.R; + GNumberBox.Value = currentColor.G; + BNumberBox.Value = currentColor.B; + } + + _currentColor = currentColor; + CurrentColorButton.Background = new SolidColorBrush(currentColor); + } + + private void OnCurrentColorButtonClicked(object sender, RoutedEventArgs e) + { + ShowDetails(); + } + + private void ShowDetails() + { + if (_isCollapsed) + { + _isCollapsed = false; + + var resizeColor = new DoubleAnimation(256, new Duration(TimeSpan.FromMilliseconds(250))) + { + EasingFunction = new ExponentialEase {EasingMode = EasingMode.EaseInOut} + }; + + var moveColor = new ThicknessAnimation(new Thickness(0), new Duration(TimeSpan.FromMilliseconds(250))) + { + EasingFunction = new ExponentialEase {EasingMode = EasingMode.EaseInOut} + }; + + CurrentColorButton.BeginAnimation(WidthProperty, resizeColor); + CurrentColorButton.BeginAnimation(MarginProperty, moveColor); + CurrentColorButton.IsEnabled = false; + DetailsFlyout.IsOpen = true; + } + } + + private void HideDetails() + { + if (_isCollapsed) return; + + _isCollapsed = true; + + var resizeColor = new DoubleAnimation(165, new Duration(TimeSpan.FromMilliseconds(150))) + { + EasingFunction = new ExponentialEase {EasingMode = EasingMode.EaseInOut} + }; + + var moveColor = new ThicknessAnimation(new Thickness(72, 0, 72, 0), new Duration(TimeSpan.FromMilliseconds(150))) + { + EasingFunction = new ExponentialEase {EasingMode = EasingMode.EaseInOut} + }; + + CurrentColorButton.BeginAnimation(WidthProperty, resizeColor); + CurrentColorButton.BeginAnimation(MarginProperty, moveColor); + CurrentColorButton.IsEnabled = true; + } + + private void OnOkButtonClicked(object sender, RoutedEventArgs e) + { + SelectedColor = _currentColor; + DetailsFlyout.Hide(); + } + + private void OnDetailsFlyoutClosed(object sender, object e) + { + HideDetails(); + + // Revert to original color + var originalColorBackground = new SolidColorBrush(_originalColor); + CurrentColorButton.Background = originalColorBackground; + + HexCode.Text = ColorToHex(_originalColor); + } + + private void OnColorVariationButtonClicked(object sender, RoutedEventArgs e) + { + var selectedColor = ((SolidColorBrush) ((System.Windows.Controls.Button) sender).Background).Color; + SelectedColor = selectedColor; + } + + private void OnSaturationGradientSliderValueChanged(object sender, RoutedPropertyChangedEventArgs e) + { + UpdateSaturationColorGradient(((Slider) sender).Value); + _ignoreGradientsChanges = true; + UpdateTextBoxesAndCurrentColor(HsvColor.RgbFromHsv(_currH, _currS, _currV)); + _ignoreGradientsChanges = false; + } + + private void OnHueGradientSliderValueChanged(object sender, RoutedPropertyChangedEventArgs e) + { + UpdateHueColorGradient(((Slider) sender).Value); + _ignoreGradientsChanges = true; + UpdateTextBoxesAndCurrentColor(HsvColor.RgbFromHsv(_currH, _currS, _currV)); + _ignoreGradientsChanges = false; + } + + private void OnValueGradientSliderValueChanged(object sender, RoutedPropertyChangedEventArgs e) + { + UpdateValueColorGradient(((Slider) sender).Value); + _ignoreGradientsChanges = true; + UpdateTextBoxesAndCurrentColor(HsvColor.RgbFromHsv(_currH, _currS, _currV)); + _ignoreGradientsChanges = false; + } + + private void OnHexCodeTextChanged(object sender, TextChangedEventArgs e) + { + var newValue = ((TextBox) sender).Text; + + // support hex with 3 and 6 characters and optional with hashtag + var reg = new Regex("^#?([0-9A-Fa-f]{3}){1,2}$"); + + if (!reg.IsMatch(newValue)) + { + return; + } + + if (_ignoreHexChanges) return; + + var converter = new System.Drawing.ColorConverter(); + + // "FormatHexColorString()" is needed to add hashtag if missing and to convert the hex code from three to six characters. Without this we get format exceptions and incorrect color values. + var color = (System.Drawing.Color) converter.ConvertFromString(FormatHexColorString(HexCode.Text))!; + + _ignoreHexChanges = true; + SetColorFromTextBoxes(color); + _ignoreHexChanges = false; + } + + private void SetColorFromTextBoxes(System.Drawing.Color color) + { + if (!_ignoreGradientsChanges) + { + var hsv = ColorFormatUtils.ConvertToHsvColor(color); + + var huePosition = (hsv.Hue / 360) * HueGradientSlider.Maximum; + var saturationPosition = hsv.Saturation * SaturationGradientSlider.Maximum; + var valuePosition = hsv.Value * ValueGradientSlider.Maximum; + UpdateHueColorGradient(huePosition); + UpdateSaturationColorGradient(saturationPosition); + UpdateValueColorGradient(valuePosition); + } + + UpdateTextBoxesAndCurrentColor(Color.FromRgb(color.R, color.G, color.B)); + } + + private static string ColorToHex(Color color, string oldValue = "") + { +#if NETCOREAPP + var newHexString = BitConverter.ToString([color.R, color.G, color.B]).Replace("-", string.Empty, StringComparison.InvariantCulture); +#else + var newHexString = BitConverter.ToString([color.R, color.G, color.B]).Replace("-", string.Empty); +#endif + newHexString = newHexString.ToLowerInvariant(); + + // Return only with hashtag if user typed it before +#if NETCOREAPP + var addHashtag = oldValue.StartsWith('#'); +#else + var addHashtag = oldValue.StartsWith("#"); +#endif + return addHashtag ? "#" + newHexString : newHexString; + } + + /// + /// Formats the hex code string to be accepted by . We are adding hashtag at the beginning if needed and convert from three characters to six characters code. + /// + /// The string we read from the hex text box. + /// Formatted string with hashtag and six characters of hex code. + private static string FormatHexColorString(string hexCodeText) + { + if (hexCodeText.Length is 3 or 4) + { + // Hex with or without hashTag and three characters + return Regex.Replace(hexCodeText, "^#?([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$", "#$1$1$2$2$3$3"); + } + + // Hex with or without hashTag and six characters +#if NETCOREAPP + return hexCodeText.StartsWith('#') ? hexCodeText : "#" + hexCodeText; +#else + return hexCodeText.StartsWith("#") ? hexCodeText : "#" + hexCodeText; +#endif + } + + private void OnHexCodeGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) + { + ((TextBox) sender).SelectAll(); + } + + private void OnRgbNumberBoxTextChanged(object sender, TextChangedEventArgs e) + { + if (_ignoreRgbChanges) return; + + var numberBox = (NumberBox) sender; + + if (!RNumberBox.Value.HasValue) return; + if (!GNumberBox.Value.HasValue) return; + if (!BNumberBox.Value.HasValue) return; + + var r = numberBox.Name == "RNumberBox" ? GetValueFromNumberBox(numberBox) : (byte) RNumberBox.Value; + var g = numberBox.Name == "GNumberBox" ? GetValueFromNumberBox(numberBox) : (byte) GNumberBox.Value; + var b = numberBox.Name == "BNumberBox" ? GetValueFromNumberBox(numberBox) : (byte) BNumberBox.Value; + + _ignoreRgbChanges = true; + SetColorFromTextBoxes(System.Drawing.Color.FromArgb(r, g, b)); + _ignoreRgbChanges = false; + } + + /// + /// NumberBox provides value only after it has been validated - happens after pressing enter or leaving this control. + /// However, we need to get value immediately after the underlying textbox value changes + /// + /// numberBox control which value we want to get + /// Validated value as per numberbox conditions, if content is invalid it returns previous value + private static byte GetValueFromNumberBox(NumberBox numberBox) + { + if (!numberBox.Value.HasValue) return byte.MinValue; + + var parsedValue = ParseDouble(numberBox.Text); + if (!parsedValue.HasValue) return (byte) numberBox.Value; + + var parsedValueByte = (byte) parsedValue; + + if (parsedValueByte >= numberBox.Minimum && parsedValueByte <= numberBox.Maximum) + { + return parsedValueByte; + } + + // not valid input, return previous value + return (byte) numberBox.Value; + } + + public static double? ParseDouble(string text) + { + if (double.TryParse(text, out var result)) + { + return result; + } + + return null; + } +} \ No newline at end of file diff --git a/source/RevitLookup/Views/Controls/ColorPicker/HSVColor.cs b/source/RevitLookup/Views/Controls/ColorPicker/HSVColor.cs new file mode 100644 index 00000000..919607b7 --- /dev/null +++ b/source/RevitLookup/Views/Controls/ColorPicker/HSVColor.cs @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Color = System.Windows.Media.Color; + +namespace RevitLookup.Views.Controls.ColorPicker; + +public static class HsvColor +{ + public static Color[] GetSpectrum() + { + var rgbs = new Color[360]; + + for (var h = 0; h < 360; h++) + { + rgbs[h] = RgbFromHsv(h, 1f, 1f); + } + + return rgbs; + } + + public static Color[] HueSpectrum(double saturation, double value) + { + var rgbs = new Color[7]; + + for (var h = 0; h < 7; h++) + { + rgbs[h] = RgbFromHsv(h * 60, saturation, value); + } + + return rgbs; + } + + public static Color RgbFromHsv(double h, double s, double v) + { + if (h > 360 || h < 0 || s > 1 || s < 0 || v > 1 || v < 0) + { + return Color.FromRgb(0, 0, 0); + } + + var c = v * s; + var x = c * (1 - Math.Abs(((h / 60) % 2) - 1)); + var m = v - c; + + double r = 0, g = 0, b = 0; + + if (h < 60) + { + r = c; + g = x; + } + else if (h < 120) + { + r = x; + g = c; + } + else if (h < 180) + { + g = c; + b = x; + } + else if (h < 240) + { + g = x; + b = c; + } + else if (h < 300) + { + r = x; + b = c; + } + else if (h <= 360) + { + r = c; + b = x; + } + + return Color.FromRgb((byte)((r + m) * 255), (byte)((g + m) * 255), (byte)((b + m) * 255)); + } +} \ No newline at end of file diff --git a/source/RevitLookup/Views/Dialogs/OpenSourceDialog.xaml b/source/RevitLookup/Views/Dialogs/OpenSourceDialog.xaml index dd7f18db..ec02182d 100644 --- a/source/RevitLookup/Views/Dialogs/OpenSourceDialog.xaml +++ b/source/RevitLookup/Views/Dialogs/OpenSourceDialog.xaml @@ -50,9 +50,11 @@ +