Skip to content

Commit

Permalink
Merge branch 'master' into dmuravskyi/polycurve-transform-at
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewheumann authored Oct 31, 2023
2 parents cbe03f9 + eb7aef0 commit f8d87cb
Show file tree
Hide file tree
Showing 11 changed files with 219 additions and 79 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
- `Profile.ThickenedEdgePolygons`
- `Elements.MEP`
- `GeometricElement.RepresentationInstances`
- `ContentRepresentation`

### Fixed

Expand All @@ -59,6 +60,7 @@
- `Polygon.Frames` now works correctly with `startSetbackDistance` and `endSetbackDistance` parameters.
- `Polyline.TransformAt` returns correct transformations when parameter on domain is provided.
- `IndexedPolycurve` constructor that takes list of `BoundedCurve` now produces `CurveIndices` that share vertices and are withing index range. This means `IndexedPolyline.TransformedPolyline` preserves `CurveIndicies` on new `IndexedPolyline`.
- `BoundedCurve.ToPolyline` now works correctly for `EllipticalArc` class.

### Changed
- `GltfExtensions.UseReferencedContentExtension` is now true by default.
Expand Down
11 changes: 8 additions & 3 deletions Elements/src/CoreModels/ElementRepresentation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using Elements;
using Elements.Geometry;
using glTFLoader.Schema;
using System;
using Elements.Serialization.glTF;

/// <summary>
/// The element's representation
Expand All @@ -13,13 +13,18 @@ public abstract class ElementRepresentation : SharedObject
/// Get graphics buffers and other metadata required to modify a GLB.
/// </summary>
/// <param name="element">The element with this representation.</param>
/// <param name="graphicsBuffers">The list of graphc buffers.</param>
/// <param name="graphicsBuffers">The list of graphic buffers.</param>
/// <param name="id">The buffer id. It will be used as a primitive name.</param>
/// <param name="mode">The gltf primitive mode</param>
/// <returns>
/// True if there is graphicsbuffers data applicable to add, false otherwise.
/// True if there is graphics buffers data applicable to add, false otherwise.
/// Out variables should be ignored if the return value is false.
/// </returns>
public abstract bool TryToGraphicsBuffers(GeometricElement element, out List<GraphicsBuffers> graphicsBuffers,
out string id, out MeshPrimitive.ModeEnum? mode);

internal virtual List<NodeExtension> GetNodeExtensions(GeometricElement element)
{
return new List<NodeExtension>();
}
}
68 changes: 33 additions & 35 deletions Elements/src/GeometricElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,6 @@ public bool Intersects(Plane plane,
var graphVertices = new List<Vector3>();
var graphEdges = new List<List<(int from, int to, int? tag)>>();

var intersectionPoints = new List<Vector3>();
var beyondPolygonsList = new List<Polygon>();

if (Representation != null && _csg != null)
Expand Down Expand Up @@ -252,7 +251,7 @@ public bool Intersects(Plane plane,

var d = csgNormal.Cross(plane.Normal).Unitized();
edgeResults.Sort(new DirectionComparer(d));
intersectionPoints.AddRange(edgeResults);
AddToGraph(edgeResults, graphVertices, graphEdges);
}
}

Expand All @@ -268,43 +267,15 @@ public bool Intersects(Plane plane,

if (instance.Representation is SolidRepresentation solidRepresentation)
{
intersectionPoints.AddRange(solidRepresentation.CalculateIntersectionPoints(this, plane,
out var beyondPolygonsLocal));
beyondPolygonsList.AddRange(beyondPolygonsLocal);
foreach (var intersection in solidRepresentation.CalculateIntersectionPoints(this, plane,
out var beyondPolygonsLocal))
{
AddToGraph(intersection, graphVertices, graphEdges);
}
}
}
}

if (!intersectionPoints.Any())
{
return false;
}

// Draw segments through the results and add to the
// half edge graph.
for (var j = 0; j < intersectionPoints.Count - 1; j += 2)
{
// Don't create zero-length edges.
if (intersectionPoints[j].IsAlmostEqualTo(intersectionPoints[j + 1]))
{
continue;
}

var a = Solid.FindOrCreateGraphVertex(intersectionPoints[j], graphVertices, graphEdges);
var b = Solid.FindOrCreateGraphVertex(intersectionPoints[j + 1], graphVertices, graphEdges);
var e1 = (a, b, 0);
var e2 = (b, a, 0);
if (graphEdges[a].Contains(e1) || graphEdges[b].Contains(e2))
{
continue;
}
else
{
graphEdges[a].Add(e1);
}
}
// }

var heg = new HalfEdgeGraph2d()
{
Vertices = graphVertices,
Expand Down Expand Up @@ -364,6 +335,33 @@ public bool Intersects(Plane plane,
}
}

private static void AddToGraph(List<Vector3> intersectionPoints, List<Vector3> graphVertices, List<List<(int from, int to, int? tag)>> graphEdges)
{
// Draw segments through the results and add to the
// half edge graph.
for (var j = 0; j < intersectionPoints.Count - 1; j += 2)
{
// Don't create zero-length edges.
if (intersectionPoints[j].IsAlmostEqualTo(intersectionPoints[j + 1]))
{
continue;
}

var a = Solid.FindOrCreateGraphVertex(intersectionPoints[j], graphVertices, graphEdges);
var b = Solid.FindOrCreateGraphVertex(intersectionPoints[j + 1], graphVertices, graphEdges);
var e1 = (a, b, 0);
var e2 = (b, a, 0);
if (graphEdges[a].Contains(e1) || graphEdges[b].Contains(e2))
{
continue;
}
else
{
graphEdges[a].Add(e1);
}
}
}

/// <summary>
/// Get the computed csg solid.
/// The csg is centered on the origin by default.
Expand Down
23 changes: 0 additions & 23 deletions Elements/src/Geometry/Arc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -484,29 +484,6 @@ public override Transform TransformAt(double u)
return this.BasisCurve.TransformAt(u);
}

/// <summary>
/// Create a polyline through a set of points along the curve.
/// </summary>
/// <param name="divisions">The number of divisions of the curve.</param>
/// <returns>A polyline.</returns>
public override Polyline ToPolyline(int divisions = 10)
{
var pts = new List<Vector3>(divisions + 1);
var step = this.Domain.Length / divisions;
for (var t = this.Domain.Min; t < this.Domain.Max; t += step)
{
pts.Add(PointAt(t));
}

// We don't go all the way to the end parameter, and
// add it here explicitly because rounding errors can
// cause small imprecision which accumulates to make
// the final parameter slightly more/less than the actual
// end parameter.
pts.Add(PointAt(this.Domain.Max));
return new Polyline(pts);
}

/// <summary>
/// Get the parameter at a distance from the start parameter along the curve.
/// </summary>
Expand Down
11 changes: 9 additions & 2 deletions Elements/src/Geometry/BoundedCurve.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,17 @@ public virtual Polyline ToPolyline(int divisions = 10)
{
var pts = new List<Vector3>(divisions + 1);
var step = this.Domain.Length / divisions;
for (var t = 0; t <= divisions; t++)
for (var t = this.Domain.Min; t < this.Domain.Max; t += step)
{
pts.Add(PointAt(t * step));
pts.Add(PointAt(t));
}

// We don't go all the way to the end parameter, and
// add it here explicitly because rounding errors can
// cause small imprecision which accumulates to make
// the final parameter slightly more/less than the actual
// end parameter.
pts.Add(PointAt(this.Domain.Max));
return new Polyline(pts);
}

Expand Down
87 changes: 87 additions & 0 deletions Elements/src/Representations/ContentRepresentation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using System.Collections.Generic;
using Elements.Geometry;
using Elements.Geometry.Solids;
using Elements.Serialization.glTF;
using Elements.Utilities;
using glTFLoader.Schema;

namespace Elements
{
/// <summary>
/// Represents an element as a reference to a GLB file location within the content catalog.
/// </summary>
public class ContentRepresentation : ElementRepresentation
{
/// <summary>The URI of the glb for this element.</summary>
public string GlbLocation { get; set; }

/// <summary>The bounding box of the content.</summary>
public BBox3 BoundingBox { get; set; }

/// <summary>
/// Initializes a new instance of ContentRepresentation class
/// </summary>
/// <param name="glbLocation">The URI of the glb for this element.</param>
/// <param name="boundingBox">The bounding box of the content.</param>
public ContentRepresentation(string glbLocation, BBox3 boundingBox)
{
GlbLocation = glbLocation;
BoundingBox = boundingBox;
}

/// <summary>
/// Initializes a new instance of ContentRepresentation class
/// </summary>
/// <param name="glbLocation">The URI of the glb for this element.</param>
public ContentRepresentation(string glbLocation) : this(glbLocation, default) { }

/// <inheritdoc/>
public override bool TryToGraphicsBuffers(GeometricElement element, out List<GraphicsBuffers> graphicsBuffers, out string id, out MeshPrimitive.ModeEnum? mode)
{
id = element.Id + "_mesh";

graphicsBuffers = new List<GraphicsBuffers>();
mode = MeshPrimitive.ModeEnum.TRIANGLES;

if (!BoundingBox.IsValid() || BoundingBox.IsDegenerate())
{
return true;
}

var bottomProfile = new Geometry.Polygon(new List<Vector3>{
new Vector3(BoundingBox.Min.X, BoundingBox.Min.Y, BoundingBox.Min.Z),
new Vector3(BoundingBox.Min.X, BoundingBox.Max.Y, BoundingBox.Min.Z),
new Vector3(BoundingBox.Max.X, BoundingBox.Max.Y, BoundingBox.Min.Z),
new Vector3(BoundingBox.Max.X, BoundingBox.Min.Y, BoundingBox.Min.Z),
});

var height = BoundingBox.Max.Z - BoundingBox.Min.Z;
var boxSolid = new Extrude(bottomProfile, height, Vector3.ZAxis, false);

var csg = SolidOperationUtils.GetFinalCsgFromSolids(new List<SolidOperation>() { boxSolid }, element, false);

if (csg == null)
{
return false;
}

GraphicsBuffers buffers = null;
buffers = csg.Tessellate();

if (buffers.Vertices.Count == 0)
{
return false;
}

graphicsBuffers.Add(buffers);
return true;
}

internal override List<NodeExtension> GetNodeExtensions(GeometricElement element)
{
var extensions = base.GetNodeExtensions(element);
extensions.Add(new NodeExtension("HYPAR_referenced_content", "contentUrl", GlbLocation));
return extensions;
}
}
}
6 changes: 3 additions & 3 deletions Elements/src/Representations/SolidRepresentation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,9 @@ public BBox3 ComputeBounds(GeometricElement element)
/// <param name="plane">The intersecting plane.</param>
/// <param name="beyondPolygons">The output collection of the polygons beyond the input plane.</param>
/// <returns>Returns the collection of intersection points.</returns>
public List<Vector3> CalculateIntersectionPoints(GeometricElement element, Plane plane, out List<Polygon> beyondPolygons)
public List<List<Vector3>> CalculateIntersectionPoints(GeometricElement element, Plane plane, out List<Polygon> beyondPolygons)
{
var intersectionPoints = new List<Vector3>();
var intersectionPoints = new List<List<Vector3>>();
beyondPolygons = new List<Polygon>();

var csg = SolidOperationUtils.GetFinalCsgFromSolids(SolidOperations, element, true);
Expand Down Expand Up @@ -186,7 +186,7 @@ public List<Vector3> CalculateIntersectionPoints(GeometricElement element, Plane

var d = csgNormal.Cross(plane.Normal).Unitized();
edgeResults.Sort(new DirectionComparer(d));
intersectionPoints.AddRange(edgeResults);
intersectionPoints.Add(edgeResults);
}

return intersectionPoints;
Expand Down
47 changes: 34 additions & 13 deletions Elements/src/Serialization/glTF/GltfExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1080,7 +1080,7 @@ internal static Gltf InitializeGlTF(Model model,
errors.Add(new ElementError(e.Id, ex));
}
}
if (allBuffers.Sum(b => b.Count()) + buffer.Count == 0 && lights.Count == 0)
if (allBuffers.Sum(b => b.Count()) + buffer.Count == 0 && lights.Count == 0 && nodes.Count < 1)
{
return null;
}
Expand Down Expand Up @@ -1292,7 +1292,7 @@ private static void GetRenderDataForElement(Element e,
var elementNodeId = NodeUtilities.AddInstanceNode(nodes, element.Transform, element.Id);
foreach (var representation in element.RepresentationInstances)
{
// get the unique id that contains representation Id and opening Ids
// get the unique id that contains representation Id and opening Ids
int combinedId = representation.GetHashCode(element);

if (representationsMap.TryGetValue(combinedId, out var mesh))
Expand All @@ -1302,31 +1302,52 @@ private static void GetRenderDataForElement(Element e,
{
NodeUtilities.SetRepresentationInfo(nodes[index], representation);
NodeUtilities.SetElementInfo(nodes[index], element.Id);
foreach (var nodeExtension in representation.Representation.GetNodeExtensions(element))
{
AddExtension(gltf, nodes[index], nodeExtension.Name, nodeExtension.Attributes);
}
}
}
else if (representation.Representation.TryToGraphicsBuffers(geometricElement, out var graphicsBuffers,
out var bufferId, out var mode))
{
meshId = AddMesh(bufferId,
buffer,
bufferViews,
accessors,
materialIndexMap[representation.Material.Id.ToString()],
graphicsBuffers,
(MeshPrimitive.ModeEnum)mode,
meshes);
var addedNodes = new List<int>();
if (graphicsBuffers.Any())
{
meshId = AddMesh(bufferId,
buffer,
bufferViews,
accessors,
materialIndexMap[representation.Material.Id.ToString()],
graphicsBuffers,
(MeshPrimitive.ModeEnum)mode,
meshes);

if (meshId != -1)
{
var meshIdList = new List<int> { meshId };
representationsMap.Add(combinedId, meshIdList);
addedNodes.AddRange(NodeUtilities.AddNodes(nodes, meshIdList, elementNodeId));
}
}
else
{
meshId = NodeUtilities.AddEmptyNode(nodes, elementNodeId);
addedNodes.Add(meshId);
}

// If the id == -1, the mesh is malformed.
// It may have no geometry.
if (meshId != -1)
{
var meshIdList = new List<int> { meshId };
representationsMap.Add(combinedId, meshIdList);
var addedNodes = NodeUtilities.AddNodes(nodes, meshIdList, elementNodeId);
foreach (var index in addedNodes)
{
NodeUtilities.SetRepresentationInfo(nodes[index], representation);
NodeUtilities.SetElementInfo(nodes[index], element.Id);
foreach (var nodeExtension in representation.Representation.GetNodeExtensions(element))
{
AddExtension(gltf, nodes[index], nodeExtension.Name, nodeExtension.Attributes);
}
}
}
}
Expand Down
Loading

0 comments on commit f8d87cb

Please sign in to comment.