From 1edc546f1badb26a35e87a4d2605a3e66bed2590 Mon Sep 17 00:00:00 2001 From: izzy lyseggen Date: Tue, 19 Jul 2022 10:26:53 +0100 Subject: [PATCH] feat(objects/revit): organization classes (#1402) * feat(objects): container and model classes * setting proposal (#1323) * fix(objects): cleanup on the model objs * feat(objects): add another transform constructor * feat(revit): use `Model` and `ModelInfo` on send Co-authored-by: Claire Kuang Co-authored-by: Alan Rynne --- .../ConnectorBindingsRevit2.Send.cs | 2 +- .../ConverterRevitShared/ConverterRevit.cs | 14 +- .../ConverterRevitShared.projitems | 1 + .../Partial Classes/ConvertModel.cs | 68 ++++++++++ .../BuiltElements/Revit/ProjectInfo.cs | 9 +- Objects/Objects/Organization/Container.cs | 50 ++++++++ Objects/Objects/Organization/Model.cs | 121 ++++++++++++++++++ Objects/Objects/Other/Transform.cs | 48 +++++-- 8 files changed, 293 insertions(+), 20 deletions(-) create mode 100644 Objects/Converters/ConverterRevit/ConverterRevitShared/Partial Classes/ConvertModel.cs create mode 100644 Objects/Objects/Organization/Container.cs create mode 100644 Objects/Objects/Organization/Model.cs diff --git a/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit2/ConnectorBindingsRevit2.Send.cs b/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit2/ConnectorBindingsRevit2.Send.cs index 03501cd51b..89f5069493 100644 --- a/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit2/ConnectorBindingsRevit2.Send.cs +++ b/ConnectorRevit/ConnectorRevit/UI/ConnectorBindingsRevit2/ConnectorBindingsRevit2.Send.cs @@ -50,7 +50,7 @@ public override async Task SendStream(StreamState state, ProgressViewMod converter.SetContextObjects(selectedObjects.Select(x => new ApplicationPlaceholderObject { applicationId = x.UniqueId }).ToList()); - var commitObject = new Base(); + var commitObject = converter.ConvertToSpeckle(CurrentDoc.Document) ?? new Base(); var conversionProgressDict = new ConcurrentDictionary(); conversionProgressDict["Conversion"] = 0; diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevit.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevit.cs index 731c7eab11..1c1cd35265 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevit.cs +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevit.cs @@ -104,6 +104,9 @@ public Base ConvertToSpeckle(object @object) Base returnObject = null; switch (@object) { + case DB.Document o: + returnObject = ModelToSpeckle(o); + break; case DB.DetailCurve o: returnObject = DetailCurveToSpeckle(o); break; @@ -270,8 +273,15 @@ public Base ConvertToSpeckle(object @object) && returnObject["renderMaterial"] == null && returnObject["displayValue"] == null) { - var material = GetElementRenderMaterial(@object as DB.Element); - returnObject["renderMaterial"] = material; + try + { + var material = GetElementRenderMaterial(@object as DB.Element); + returnObject["renderMaterial"] = material; + } + catch ( Exception e ) + { + // passing for stuff without a material (eg converting the current document to get the `Model` and `Info` objects) + } } //NOTE: adds the quantities of all materials to an element diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevitShared.projitems b/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevitShared.projitems index 9e55deba68..b6c22c57b1 100644 --- a/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevitShared.projitems +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/ConverterRevitShared.projitems @@ -23,6 +23,7 @@ + diff --git a/Objects/Converters/ConverterRevit/ConverterRevitShared/Partial Classes/ConvertModel.cs b/Objects/Converters/ConverterRevit/ConverterRevitShared/Partial Classes/ConvertModel.cs new file mode 100644 index 0000000000..fdefcf4607 --- /dev/null +++ b/Objects/Converters/ConverterRevit/ConverterRevitShared/Partial Classes/ConvertModel.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using DB = Autodesk.Revit.DB; +using Objects.Organization; +using Objects.Other; +using Speckle.Core.Models; + +namespace Objects.Converter.Revit +{ + public partial class ConverterRevit + { + /// + /// Returns a object containing and location + /// information. This is intended to be used as the root commit object when sending to Speckle. + /// + /// the currently active document + /// true if project info should be added + /// + public Base ModelToSpeckle(DB.Document doc, bool sendProjectInfo = true) + { + var model = new Model(); + // TODO: setting for whether or not to include project info + if ( sendProjectInfo ) + { + var info = ProjectInfoToSpeckle(doc.ProjectInformation); + info.latitude = doc.SiteLocation.Latitude; + info.longitude = doc.SiteLocation.Longitude; + info.siteName = doc.SiteLocation.PlaceName; + info.locations = ProjectLocationsToSpeckle(doc.ProjectLocations); + model.info = info; + } + + Report.Log($"Created Model Object"); + + return model; + } + + /// + /// Converts the Revit document's into a list of bases including the , + /// name, and true north angle (in radians) + /// + /// + /// + public List ProjectLocationsToSpeckle(DB.ProjectLocationSet locations) + { + // TODO: do we need a location obj? + var spcklLocations = new List(); + foreach ( DB.ProjectLocation location in locations ) + { + var position = location.GetProjectPosition(DB.XYZ.Zero); + var revitTransform = DB.Transform.CreateRotation(DB.XYZ.BasisZ, position.Angle); + + var spcklLoc = new Base() { applicationId = location.UniqueId }; + spcklLoc[ "transform" ] = new Transform( + new[ ] { revitTransform.BasisX[ 0 ], revitTransform.BasisX[ 1 ], revitTransform.BasisX[ 2 ] }, + new[ ] { revitTransform.BasisY[ 0 ], revitTransform.BasisY[ 1 ], revitTransform.BasisY[ 2 ] }, + new[ ] { revitTransform.BasisZ[ 0 ], revitTransform.BasisZ[ 1 ], revitTransform.BasisZ[ 2 ] }, + new[ ] { ScaleToSpeckle(position.EastWest), ScaleToSpeckle(position.NorthSouth), ScaleToSpeckle(position.Elevation) }, + ModelUnits); + spcklLoc[ "name" ] = location.Name; + spcklLoc[ "trueNorth" ] = position.Angle; + spcklLocations.Add(spcklLoc); + } + + return spcklLocations; + } + } +} \ No newline at end of file diff --git a/Objects/Objects/BuiltElements/Revit/ProjectInfo.cs b/Objects/Objects/BuiltElements/Revit/ProjectInfo.cs index af472f346e..2ad78ea5d6 100644 --- a/Objects/Objects/BuiltElements/Revit/ProjectInfo.cs +++ b/Objects/Objects/BuiltElements/Revit/ProjectInfo.cs @@ -1,21 +1,16 @@ using Speckle.Core.Kits; using Speckle.Core.Models; using System.Collections.Generic; +using Objects.Organization; namespace Objects.BuiltElements.Revit { - public class ProjectInfo : Base + public class ProjectInfo : BIMModelInfo { - public string address { get; set; } public string author { get; set; } - public string buildingName { get; set; } - public string clientName { get; set; } public string issueDate { get; set; } - public string name { get; set; } - public string number { get; set; } public string organizationDescription { get; set; } public string organizationName { get; set; } - public string status { get; set; } } diff --git a/Objects/Objects/Organization/Container.cs b/Objects/Objects/Organization/Container.cs new file mode 100644 index 0000000000..6ecf5335c9 --- /dev/null +++ b/Objects/Objects/Organization/Container.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using Speckle.Core.Models; + +namespace Objects.Organization +{ + /// + /// A simple container for organising objects within a model and preserving object hierarchy. + /// A container is defined by a unique and its list of contained . + /// The can include an unrestricted number of objects including additional nested s. + /// + /// + /// A correlates to eg Rhino and AutoCad Layers or Blender Collections. + /// While it is possible for an object to belong to multiple containers, most applications will not support this and + /// on receive the object will be assigned the the first container it is encountered in. + /// + public class Container : Base + { + /// + /// The name of the container. + /// This should be unique within the commit. + /// + /// TODO: standardise this behaviour across connectors + /// On receive, this will be prepended with the id of the stream as to not overwrite existing containers in the file. + public string name { get; set; } + + /// + /// The elements contained in this . This can include any object including + /// additional nested s. + /// + /// + /// Most applications will expect all contained elements to have displayable geometry or to be another . + /// This means that purely data objects may be lost on receive. + /// + [DetachProperty] + public List elements { get; set; } + + public Container() {} + + /// + /// Constructor for a basic container. + /// + /// The unique name of this container + /// Any contained objects + public Container(string name, List elements = null) + { + this.name = name; + this.elements = elements; + } + } +} \ No newline at end of file diff --git a/Objects/Objects/Organization/Model.cs b/Objects/Objects/Organization/Model.cs new file mode 100644 index 0000000000..50b49ff83a --- /dev/null +++ b/Objects/Objects/Organization/Model.cs @@ -0,0 +1,121 @@ +using System.Collections.Generic; +using Objects.Other; +using Speckle.Core.Models; + +namespace Objects.Organization +{ + /// + /// Represents a model from an authoring application and can be used as the root commit object when sending. + /// It contains and objects + /// + public class Model : Base + { + /// + /// General model-wide information stored in a object. + /// This may include anything from simply a project / file name to specific location information (eg with ) + /// + public ModelInfo info { get; set; } + + + public List settings { get; set; } + + public Model() + { + } + + public Model(ModelInfo info, List settings = null) + { + this.info = info; + this.settings = settings; + } + } + + /// + /// Basic model info class to be attached to the field on a object. + /// It contains general information about the model and can be extended or subclassed to include more application-specific + /// information. + /// + public class ModelInfo : Base + { + /// + /// The name of the model. + /// + public string name { get; set; } + + /// + /// The identifying number of the model. + /// + public string number { get; set; } + + public ModelInfo() + { + } + + // TODO: not sure about adding a typed `elements` list here? prob should let ppl add whatever named categories here? + } + + // TODO: not quite sure about this name? + /// + /// Extended to be attached to the field on a object. + /// This contains additional properties applicable to AEC projects. + /// + public class BIMModelInfo : ModelInfo + { + /// + /// The name of the client + /// + public string clientName { get; set; } + + /// + /// The name of the building + /// + public string buildingName { get; set; } + + /// + /// The status or phase of the model. + /// + public string status { get; set; } + + /// + /// The address of the model. + /// + public string address { get; set; } + + /// + /// The name of the site location as a string. + /// + public string siteName { get; set; } + + /// + /// The latitude of the site location in radians. + /// + public double latitude { get; set; } + + /// + /// The longitude of the site location in radians. + /// + public double longitude { get; set; } + + /// + /// A list of origin locations within this model as a list of s + /// + public List locations { get; set; } + + public BIMModelInfo() + { + } + } + + public class Setting : Base + { + /// + /// The name of the setting + /// + public string name { get; set; } + + /// + /// The objects selected in the setting + /// + public List selection { get; set; } + } +} \ No newline at end of file diff --git a/Objects/Objects/Other/Transform.cs b/Objects/Objects/Other/Transform.cs index 0641cf64b5..f66a48aa7a 100644 --- a/Objects/Objects/Other/Transform.cs +++ b/Objects/Objects/Other/Transform.cs @@ -18,7 +18,10 @@ namespace Objects.Other /// public class Transform : Base { - public double[ ] value { get; set; } = {1d, 0d, 0d, 0d, 0d, 1d, 0d, 0d, 0d, 0d, 1d, 0d, 0d, 0d, 0d, 1d}; + public double[ ] value { get; set; } = { 1d, 0d, 0d, 0d, + 0d, 1d, 0d, 0d, + 0d, 0d, 1d, 0d, + 0d, 0d, 0d, 1d }; public string units { get; set; } @@ -45,6 +48,26 @@ public Transform(double[ ] value, string units = null) this.units = units; } + /// + /// Construct a transform given the x, y, and z bases and the translation vector + /// + /// + /// + /// + /// + /// + public Transform(double[ ] x, double[ ] y, double[ ] z, double[ ] translation, string units = null) + { + this.units = units; + value = new[ ] + { + x[ 0 ], y[ 0 ], z[ 0 ], translation[ 0 ], + x[ 1 ], y[ 1 ], z[ 1 ], translation[ 1 ], + x[ 2 ], y[ 2 ], z[ 2 ], translation[ 2 ], + 0d, 0d, 0d, 1d + }; + } + /// /// Get the translation, scaling, and units out of the /// @@ -68,7 +91,7 @@ public List ApplyToPoints(List points) $"Cannot apply transform as the points list is malformed: expected length to be multiple of 3"); var transformed = new List(points.Count); for ( var i = 0; i < points.Count; i += 3 ) - transformed.AddRange(ApplyToPoint(new List(3) {points[ i ], points[ i + 1 ], points[ i + 2 ]})); + transformed.AddRange(ApplyToPoint(new List(3) { points[ i ], points[ i + 1 ], points[ i + 2 ] })); return transformed; } @@ -92,8 +115,8 @@ public List ApplyToPoints(List points) public Point ApplyToPoint(Point point) { var (x, y, z, units) = point; - var newCoords = ApplyToPoint(new List {x, y, z}); - return new Point(newCoords[0], newCoords[1], newCoords[2], point.units, point.applicationId); + var newCoords = ApplyToPoint(new List { x, y, z }); + return new Point(newCoords[ 0 ], newCoords[ 1 ], newCoords[ 2 ], point.units, point.applicationId); } /// @@ -103,10 +126,13 @@ public List ApplyToPoint(List point) { var transformed = new List(); for ( var i = 0; i < 16; i += 4 ) - transformed.Add(point[ 0 ] * value[ i ] + point[ 1 ] * value[ i + 1 ] + point[ 2 ] * value[ i + 2 ] + value[ i + 3 ]); + transformed.Add(point[ 0 ] * value[ i ] + point[ 1 ] * value[ i + 1 ] + point[ 2 ] * value[ i + 2 ] + + value[ i + 3 ]); return new List(3) - {transformed[ 0 ] / transformed[ 3 ], transformed[ 1 ] / transformed[ 3 ], transformed[ 2 ] / transformed[ 3 ]}; + { + transformed[ 0 ] / transformed[ 3 ], transformed[ 1 ] / transformed[ 3 ], transformed[ 2 ] / transformed[ 3 ] + }; } /// @@ -114,9 +140,9 @@ public List ApplyToPoint(List point) /// public Vector ApplyToVector(Vector vector) { - var newCoords = ApplyToVector(new List {vector.x, vector.y, vector.z}); + var newCoords = ApplyToVector(new List { vector.x, vector.y, vector.z }); - return new Vector(newCoords[0], newCoords[1], newCoords[2], vector.units, vector.applicationId); + return new Geometry.Vector(newCoords[ 0 ], newCoords[ 1 ], newCoords[ 2 ], vector.units, vector.applicationId); } /// @@ -125,12 +151,13 @@ public Vector ApplyToVector(Vector vector) public List ApplyToVector(List vector) { var newPoint = new List(4) {vector[ 0 ], vector[ 1 ], vector[ 2 ], 1}; + for ( var i = 0; i < 16; i += 4 ) newPoint[ i / 4 ] = newPoint[ 0 ] * value[ i ] + newPoint[ 1 ] * value[ i + 1 ] + newPoint[ 2 ] * value[ i + 2 ]; return new List(3) - {newPoint[ 0 ], newPoint[ 1 ], newPoint[ 2 ]}; + { newPoint[ 0 ], newPoint[ 1 ], newPoint[ 2 ] }; } /// @@ -146,11 +173,12 @@ public List ApplyToCurves(List curves, out bool success) if ( curve is ITransformable c ) { c.TransformTo(this, out ITransformable tc); - transformed.Add(( ICurve ) tc); + transformed.Add(( ICurve )tc); } else success = false; } + return transformed; }