Skip to content

Commit

Permalink
Merge remote-tracking branch '@speckle/dev' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
jsdbroughton committed Apr 22, 2023
2 parents 5111b24 + 29950d8 commit 5ed066c
Show file tree
Hide file tree
Showing 6 changed files with 217 additions and 117 deletions.
40 changes: 22 additions & 18 deletions Core/Core/Logging/SpeckleLog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,26 +92,30 @@ public SpeckleLogConfiguration(
/// </summary>
public static class SpeckleLog
{
private static ILogger? _logger;
private static bool _initialized;
private static ILogger? _logger;

public static ILogger Logger =>
_logger
?? throw new SpeckleException(
$"The logger has not been initialized. Please call {typeof(SpeckleLog).FullName}.{nameof(Initialize)}"
);
public static ILogger Logger
{
get
{
if(_logger == null) Initialize("Core", "unknown");
return _logger;
}
}

/// <summary>
/// Initialize logger configuration for a global Serilog.Log logger.
/// </summary>
public static void Initialize(
string hostApplicationName,
string? hostApplicationVersion,
SpeckleLogConfiguration? logConfiguration = null
)
{
if (_initialized)
return;
private static bool _initialized = false;

/// <summary>
/// Initialize logger configuration for a global Serilog.Log logger.
/// </summary>
public static void Initialize(
string hostApplicationName,
string? hostApplicationVersion,
SpeckleLogConfiguration? logConfiguration = null
)
{
if (_initialized)
return;

logConfiguration ??= new SpeckleLogConfiguration();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ private bool ShouldConvertHostedElement(DB.Element element, DB.Element host, ref
{
// there are certain elements in Revit that can be a host to another element
// yet not know it.
var hostedElementIds = GetDependentElementIds(host);
var hostedElementIds = GetHostedElementIds(host);
var elementId = element.Id;
if (!hostedElementIds.Where(b => b.IntegerValue == elementId.IntegerValue).Any())
{
Expand All @@ -82,7 +82,7 @@ private bool ShouldConvertHostedElement(DB.Element element, DB.Element host, ref
public void GetHostedElements(Base @base, Element host, out List<string> notes)
{
notes = new List<string>();
var hostedElementIds = GetDependentElementIds(host);
var hostedElementIds = GetHostedElementIds(host);

if (!hostedElementIds.Any())
return;
Expand Down Expand Up @@ -139,12 +139,13 @@ public void GetHostedElementsFromIds(Base @base, Element host, IList<ElementId>
(@base["elements"] as List<Base>).AddRange(convertedHostedElements);
}
}

public IList<ElementId> GetDependentElementIds(Element host)
public IList<ElementId> GetHostedElementIds(Element host)
{
IList<ElementId> ids = null;
if (host is HostObject hostObject)
{
ids = hostObject.FindInserts(true, false, false, false);
}
else
{
var typeFilter = new ElementIsElementTypeFilter(true);
Expand Down Expand Up @@ -1325,49 +1326,68 @@ private string RemoveProhibitedCharacters(string s)
return Regex.Replace(s, "[\\[\\]{}|;<>?`~]", "");
}

// MEGA HACK to get the slope arrow of a roof which is technically not accessable by the api
// https://forums.autodesk.com/t5/revit-api-forum/access-parameters-of-slope-arrow/td-p/8134470
private void GetSlopeArrowHack(ElementId elementId, Document doc, out Point tail, out Point head, out double tailOffset, out double headOffset, out double slope)
private static ModelLine GetSlopeArrow(Element element)
{
List<ElementId> deleted = null;
tail = null;
head = null;
tailOffset = 0;
headOffset = 0;
slope = 0;
using (var t = new Transaction(doc, "TTT"))
IList<ElementId> elementIds = null;
#if !REVIT2020 && !REVIT2021
if (element is DB.Floor floor)
{
t.Start();
deleted = doc.Delete(elementId).ToList();
t.RollBack();
elementIds = ((Sketch)floor.Document.GetElement(floor.SketchId)).GetAllElements();
}
foreach (ElementId id in deleted)
#endif
if (elementIds == null)
{
ModelLine l = doc.GetElement(id) as ModelLine;
if (l == null) continue;
if (!l.Name.Equals("Slope Arrow")) continue; // TODO: does this work with other languages of Revit?

tail = PointToSpeckle(((LocationCurve)l.Location).Curve.GetEndPoint(0), doc);
head = PointToSpeckle(((LocationCurve)l.Location).Curve.GetEndPoint(1), doc);
tailOffset = GetParamValue<double>(l, BuiltInParameter.SLOPE_START_HEIGHT);
using var modelLineFilter = new ElementCategoryFilter(BuiltInCategory.OST_SketchLines);
elementIds = element.GetDependentElements(modelLineFilter);
}

var specifyOffset = GetParamValue<int>(l, BuiltInParameter.SPECIFY_SLOPE_OR_OFFSET);
var lineLength = GetParamValue<double>(l, BuiltInParameter.CURVE_ELEM_LENGTH);
foreach (var elementId in elementIds)
{
if (element.Document.GetElement(elementId) is not ModelLine line) continue;

// 1 corrosponds to the "slope" option
if (specifyOffset == 1)
{
// in this scenario, slope is returned as a percentage. Divide by 100 to get the unitless form
slope = GetParamValue<double>(l, BuiltInParameter.ROOF_SLOPE) / 100;
headOffset = tailOffset + lineLength * Math.Sin(Math.Atan(slope));
}
else if (specifyOffset == 0) // 0 corrospondes to the "height at tail" option
var offsetAtTailParameter = line.get_Parameter(BuiltInParameter.SLOPE_START_HEIGHT);
if (offsetAtTailParameter != null)
{
headOffset = GetParamValue<double>(l, BuiltInParameter.SLOPE_END_HEIGHT);
slope = (headOffset - tailOffset) / lineLength;
return line;
}
break;
}
return null;
}
private Point GetSlopeArrowHead(ModelLine slopeArrow, Document doc)
{
if (slopeArrow == null) return null;
return PointToSpeckle(((LocationCurve)slopeArrow.Location).Curve.GetEndPoint(1), doc);
}
private Point GetSlopeArrowTail(ModelLine slopeArrow, Document doc)
{
if (slopeArrow == null) return null;
return PointToSpeckle(((LocationCurve)slopeArrow.Location).Curve.GetEndPoint(0), doc);
}
public double GetSlopeArrowTailOffset(ModelLine slopeArrow, Document doc)
{
return GetParamValue<double>(slopeArrow, BuiltInParameter.SLOPE_START_HEIGHT);
}
public double GetSlopeArrowHeadOffset(ModelLine slopeArrow, Document doc, double tailOffset, out double slope)
{
var specifyOffset = GetParamValue<int>(slopeArrow, BuiltInParameter.SPECIFY_SLOPE_OR_OFFSET);
var lineLength = GetParamValue<double>(slopeArrow, BuiltInParameter.CURVE_ELEM_LENGTH);

slope = 0;
double headOffset = 0;
// 1 corrosponds to the "slope" option
if (specifyOffset == 1)
{
// in this scenario, slope is returned as a percentage. Divide by 100 to get the unitless form
slope = GetParamValue<double>(slopeArrow, BuiltInParameter.ROOF_SLOPE) / 100;
headOffset = tailOffset + lineLength * Math.Sin(Math.Atan(slope));
}
else if (specifyOffset == 0) // 0 corrospondes to the "height at tail" option
{
headOffset = GetParamValue<double>(slopeArrow, BuiltInParameter.SLOPE_END_HEIGHT);
slope = (headOffset - tailOffset) / lineLength;
}

return headOffset;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.DB;
Expand Down Expand Up @@ -160,30 +160,21 @@ private RevitFloor FloorToSpeckle(DB.Floor revitFloor, out List<string> notes)
new List<string> { "LEVEL_PARAM", "FLOOR_PARAM_IS_STRUCTURAL", "ROOF_SLOPE" }
);

GetSlopeArrowHack(
revitFloor.Id,
revitFloor.Document,
out var tail,
out var head,
out double tailOffset,
out double headOffset,
out double slope
);
var slopeArrow = GetSlopeArrow(revitFloor);
if (slopeArrow != null)
{
var tail = GetSlopeArrowTail(slopeArrow, Doc);
var head = GetSlopeArrowHead(slopeArrow, Doc);
var tailOffset = GetSlopeArrowTailOffset(slopeArrow, Doc);
_ = GetSlopeArrowHeadOffset(slopeArrow, Doc, tailOffset, out var slope);

slopeParam ??= slope;
speckleFloor.slope = (double)slopeParam;
slopeParam ??= slope;
speckleFloor.slope = (double)slopeParam;

if (tail != null && head != null)
{
speckleFloor.slopeDirection = new Geometry.Line(tail, head);
if (
speckleFloor["parameters"] is Base parameters
&& parameters["FLOOR_HEIGHTABOVELEVEL_PARAM"] is BuiltElements.Revit.Parameter offsetParam
&& offsetParam.value is double offset
)
if (speckleFloor["parameters"] is Base parameters && parameters["FLOOR_HEIGHTABOVELEVEL_PARAM"] is BuiltElements.Revit.Parameter offsetParam && offsetParam.value is double offset)
{
offsetParam.value = offset + tailOffset;
parameters["FLOOR_HEIGHTABOVELEVEL_PARAM"] = offsetParam;
}
}

Expand Down Expand Up @@ -231,23 +222,6 @@ private ICurve GetFlattenedCurve(ICurve curve, double z)

switch (curve)
{
case OG.Line line:
return new OG.Line(
new OG.Point(
line.start.x,
line.start.y,
z * Speckle.Core.Kits.Units.GetConversionFactor(ModelUnits, line.start.units),
line.start.units
),
new OG.Point(
line.end.x,
line.end.y,
z * Speckle.Core.Kits.Units.GetConversionFactor(ModelUnits, line.end.units),
line.end.units
),
line.units
);

case OG.Arc arc:
var normalUnit = arc.plane.normal.Unit();
var normalAsPoint = new OG.Point(normalUnit.x, normalUnit.y, normalUnit.z);
Expand Down Expand Up @@ -325,9 +299,47 @@ private ICurve GetFlattenedCurve(ICurve curve, double z)
return newArcCurve;
}

//case OG.Circle circle:
//case OG.Ellipse ellipse:
//case OG.Spiral spiral:
// Note: this method is untested. It seems Revit doesn't send circles... it sends two arcs instead.
// Other applications may send circles though... needs more testing
case OG.Circle circle:
if (!(circle.radius is double radius && radius > 0))
{
throw new Exception($"Circle with id, {circle.id}, does not have a valid radius");
}
var circleNormalUnit = circle.plane.normal.Unit();
var circleNormalAsPoint = new OG.Point(circleNormalUnit.x, circleNormalUnit.y, circleNormalUnit.z);
var circleConversionFactor = Speckle.Core.Kits.Units.GetConversionFactor(ModelUnits, circle.units);

var flattenTransformCircle = new OO.Transform(
new Vector(1, 0, 0),
new Vector(0, 1, 0),
new Vector(0, 0, 0),
new Vector(0, 0, z * circleConversionFactor, units: circle.plane.units)
);

_ = circle.plane.TransformTo(flattenTransformCircle, out OG.Plane newCirclePlane);

if (circleNormalAsPoint.DistanceTo(new OG.Point(0, 0, 1)) < TOLERANCE)
{
return new OG.Circle(newCirclePlane, radius, units: circle.units);
}

newCirclePlane.xdir.Normalize();
newCirclePlane.ydir.Normalize();
newCirclePlane.normal = Vector.CrossProduct(newCirclePlane.xdir, newCirclePlane.ydir);

// this is the formula for an angle between two vectors
// cos T = a . b / (|a| * |b|)
var rad1ScaleCircle = Vector.DotProduct(circle.plane.xdir, newCirclePlane.xdir) / (circle.plane.xdir.Length * newCirclePlane.xdir.Length);

var rad2ScaleCircle = Vector.DotProduct(circle.plane.ydir, newCirclePlane.ydir) / (circle.plane.ydir.Length * newCirclePlane.ydir.Length);

return new OG.Ellipse(
newCirclePlane,
(circle.radius ?? 0) * rad1ScaleCircle,
(circle.radius ?? 0) * rad2ScaleCircle,
units: circle.units
);

case OG.Curve nurbs:
var curvePoints = new List<double>();
Expand All @@ -353,6 +365,57 @@ private ICurve GetFlattenedCurve(ICurve curve, double z)
};
return newCurve;

case OG.Ellipse ellipse:
if (!(ellipse.firstRadius is double firstRadius && firstRadius > 0))
{
throw new Exception($"Ellipse with id, {ellipse.id}, does not have a valid first radius");
}
if (!(ellipse.secondRadius is double secondRadius && secondRadius > 0))
{
throw new Exception($"Ellipse with id, {ellipse.id}, does not have a valid second radius");
}
var ellipseConversionFactor = Speckle.Core.Kits.Units.GetConversionFactor(ModelUnits, ellipse.units);
var flattenTransform = new OO.Transform(
new Vector(1, 0, 0),
new Vector(0, 1, 0),
new Vector(0, 0, 0),
new Vector(0, 0, z * ellipseConversionFactor, units: ellipse.plane.units)
);

_ = ellipse.plane.TransformTo(flattenTransform, out OG.Plane newEllipsePlane);

newEllipsePlane.xdir.Normalize();
newEllipsePlane.ydir.Normalize();
newEllipsePlane.normal = Vector.CrossProduct(newEllipsePlane.xdir, newEllipsePlane.ydir);

// this is the formula for an angle between two vectors
// cos T = a . b / (|a| * |b|)
var rad1Scale = Vector.DotProduct(ellipse.plane.xdir, newEllipsePlane.xdir) / (ellipse.plane.xdir.Length * newEllipsePlane.xdir.Length);

var rad2Scale = Vector.DotProduct(ellipse.plane.ydir, newEllipsePlane.ydir) / (ellipse.plane.ydir.Length * newEllipsePlane.ydir.Length);

return new OG.Ellipse(
newEllipsePlane,
firstRadius * rad1Scale,
secondRadius * rad2Scale,
ellipse.domain, ellipse.trimDomain, units: ellipse.units
);

case OG.Line line:
return new OG.Line(
new OG.Point(
line.start.x,
line.start.y,
z * Speckle.Core.Kits.Units.GetConversionFactor(ModelUnits, line.start.units),
line.start.units),
new OG.Point(
line.end.x,
line.end.y,
z * Speckle.Core.Kits.Units.GetConversionFactor(ModelUnits, line.end.units),
line.end.units),
line.units
);

case OG.Polyline poly:
var polylinePonts = new List<double>();
var originalPolylinePoints = poly.GetPoints();
Expand All @@ -372,6 +435,8 @@ private ICurve GetFlattenedCurve(ICurve curve, double z)
foreach (var seg in plc.segments)
newPolycurve.segments.Add(GetFlattenedCurve(seg, z));
return newPolycurve;

//case OG.Spiral spiral:
}
throw new NotSupportedException($"Trying to flatten unsupported curve type, {curve.GetType()}");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public partial class ConverterRevit
{
public Base GroupToSpeckle(Group revitGroup)
{
var elIdsToConvert = GetDependentElementIds(revitGroup);
var elIdsToConvert = GetHostedElementIds(revitGroup);
if (!elIdsToConvert.Any())
return null;

Expand Down
Loading

0 comments on commit 5ed066c

Please sign in to comment.