//
// (C) Copyright 2003-2019 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;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Structure;
namespace Revit.SDK.Samples.MultiplanarRebar.CS
{
///
/// This class is to parse the geometry information of given Corbel FamilyInstance,
/// and finally construct a CorbelFrame according to the parsed geometry information.
///
class GeometryUtil
{
///
/// This method parses geometry information of given Corbel to construct the CorbelFrame.
///
/// Given corbel family instance to parse
/// CorbelFrame object
public static CorbelFrame ParseCorbelGeometry(FamilyInstance corbel)
{
// Get Corbel Host information.
Element corbelHost = corbel.Host;
Reference corbelHostFace = corbel.HostFace;
PlanarFace hostPlane = corbelHost.GetGeometryObjectFromReference(corbelHostFace) as PlanarFace;
XYZ hostNormal = GetNormalOutside(hostPlane);
// Extract the faces in Corbel parallel with Corbel host face.
Solid corbelSolid = GetElementSolid(corbel);
PlanarFace corbelTopFace = null;
PlanarFace corbelBottomFace = null;
foreach (Face face in corbelSolid.Faces)
{
PlanarFace planarFace = face as PlanarFace;
XYZ normal = GetNormalOutside(planarFace);
if (normal.IsAlmostEqualTo(hostNormal))
{
corbelTopFace = planarFace;
}
else if (normal.IsAlmostEqualTo(-hostNormal))
{
corbelBottomFace = planarFace;
}
}
// Extract the faces in Corbel Host parallel with Corbel host face.
Solid hostSolid = GetElementSolid(corbelHost);
PlanarFace hostTopFace = null;
PlanarFace hostBottomFace = hostPlane;
foreach (Face face in hostSolid.Faces)
{
PlanarFace planarFace = face as PlanarFace;
XYZ normal = GetNormalOutside(planarFace);
if (normal.IsAlmostEqualTo(-hostNormal))
{
hostTopFace = planarFace;
}
}
// Parse the side faces to find out the Trapezoid face.
Edge topEdge = null;
Edge leftEdge = null;
Edge bottomEdge = null;
Edge rightEdge = null;
PlanarFace trapezoidFace = null;
int foundEdgeIndex = -1;
bool foundTrapezoid = false;
EdgeArray bottomEdges = corbelBottomFace.EdgeLoops.get_Item(0);
foreach (Edge edge in bottomEdges)
{
bottomEdge = edge;
foundEdgeIndex++;
foundTrapezoid = IsTrapezoid(hostNormal, corbelBottomFace, bottomEdge,
out trapezoidFace, out topEdge, out leftEdge, out rightEdge);
if (foundTrapezoid)
{
break;
}
}
// Check to see if the Trapezoid faces was found.
if (!foundTrapezoid)
{
// Throw if no any trapezoid face in corbel.
throw new Exception("Didn't find the trapezoid face in corbel [Id:" + corbel.Id + "].");
}
Edge depthEdge = bottomEdges.get_Item((foundEdgeIndex + 1) % bottomEdges.Size);
double hostDepth = GetDistance(hostTopFace, hostBottomFace);
// Compute the host face cover distance.
RebarHostData corbelHostData = RebarHostData.GetRebarHostData(corbelHost);
// Get CoverType of the given host face
RebarCoverType coverType = corbelHostData.GetCoverType(hostTopFace.Reference);
// if the host face don't have a CoverType, then try to get the common CoverType.
if (coverType == null)
coverType = corbelHostData.GetCommonCoverType();
// Get the Cover Distance
double coverDistance = coverType.CoverDistance;
// Construct the CorbelFrame from the given parsed trapezoid information.
return ConstructCorbelFrame(
corbel, depthEdge,
leftEdge, bottomEdge, rightEdge, topEdge,
corbel.Document, trapezoidFace,
hostDepth, coverDistance);
}
///
/// Check if the given bottom edge was shared by a trapezoid face with left edge vertical.
///
/// Corbel Host face Normal
/// Bottom Face of Corbel
/// Given bottom edge to test
/// Output the trapezoid Face
/// Output trapezoid top edge
/// Output trapezoid left edge
/// Output trapezoid right edge
/// True if there is a trapezoid face share the given bottom edge, otherwise false.
private static bool IsTrapezoid(
XYZ hostNormal, PlanarFace corbelBottomFace, Edge bottomEdge,
out PlanarFace trapezoidFace, out Edge topEdge,
out Edge leftEdge, out Edge rightEdge)
{
PlanarFace face1 = bottomEdge.GetFace(0) as PlanarFace;
PlanarFace face2 = bottomEdge.GetFace(1) as PlanarFace;
trapezoidFace = face1 == corbelBottomFace ? face2 : face1;
EdgeArray trapezoidFaceEdges = trapezoidFace.EdgeLoops.get_Item(0);
XYZ bottomEdgeDir = (bottomEdge.Evaluate(1.0) - bottomEdge.Evaluate(0.0)).Normalize();
int bottomEdgeIndex = -1;
topEdge = null;
for (int i = 0; i < trapezoidFaceEdges.Size; i++)
{
Edge edge = trapezoidFaceEdges.get_Item(i);
XYZ edgeDir = (edge.Evaluate(1.0) - edge.Evaluate(0.0)).Normalize();
if (edgeDir.IsAlmostEqualTo(bottomEdgeDir) ||
edgeDir.IsAlmostEqualTo(-bottomEdgeDir))
{
if (edge.Evaluate(0.0).IsAlmostEqualTo(bottomEdge.Evaluate(0.0)))
{
bottomEdge = edge;
bottomEdgeIndex = i;
}
else
{
topEdge = edge;
}
}
}
leftEdge = trapezoidFaceEdges.get_Item((trapezoidFaceEdges.Size + bottomEdgeIndex - 1) % trapezoidFaceEdges.Size);
rightEdge = trapezoidFaceEdges.get_Item((bottomEdgeIndex + 1) % trapezoidFaceEdges.Size);
XYZ leftEdgeDir = (leftEdge.Evaluate(1.0) - leftEdge.Evaluate(0.0)).Normalize();
bool isLeftEdgeVertical = false;
if (leftEdgeDir.IsAlmostEqualTo(hostNormal) ||
leftEdgeDir.IsAlmostEqualTo(-hostNormal))
{
isLeftEdgeVertical = true;
}
XYZ rightEdgeDir = (rightEdge.Evaluate(1.0) - rightEdge.Evaluate(0.0)).Normalize();
bool rightEdgeIsVertical = false;
if (rightEdgeDir.IsAlmostEqualTo(hostNormal) ||
rightEdgeDir.IsAlmostEqualTo(-hostNormal))
{
rightEdgeIsVertical = true;
}
return isLeftEdgeVertical && !rightEdgeIsVertical;
}
///
/// Create the CorbelFrame object with the given trapezoid face, corbel and its host information.
///
/// Corbel instance
/// Depth Edge which is vertical with trapezoid face
/// Left edge of trapezoid
/// Bottom edge of trapezoid
/// Right edge of trapezoid
/// Top edge of trapezoid
/// Revit Document
/// Trapezoid Face
/// Corbel Host depth
/// Corbel Host Top face cover distance
/// CorbelFrame object
private static CorbelFrame ConstructCorbelFrame(
FamilyInstance corbel,
Edge depthEdge, Edge leftEdge, Edge bottomEdge, Edge rightEdge, Edge topEdge,
Document revitDoc, PlanarFace trapezoidFace,
double hostDepth, double hostTopCoverDistance)
{
XYZ leftEdgeDir = (leftEdge.Evaluate(1.0) - leftEdge.Evaluate(0.0)).Normalize();
XYZ leftEdgeV0 = leftEdge.Evaluate(0.0);
Line leftEdgeLine = Line.CreateUnbound(leftEdgeV0, leftEdgeDir);
XYZ rightEdgeDir = (rightEdge.Evaluate(1.0) - rightEdge.Evaluate(0.0)).Normalize();
XYZ rightEdgeV0 = rightEdge.Evaluate(0.0);
Line rightEdgeLine = Line.CreateUnbound(rightEdgeV0, rightEdgeDir);
XYZ topEdgeDir = (topEdge.Evaluate(1.0) - topEdge.Evaluate(0.0)).Normalize();
XYZ topEdgeV0 = topEdge.Evaluate(0.0);
Line topEdgeLine = Line.CreateUnbound(topEdgeV0, topEdgeDir);
IntersectionResultArray intersections;
topEdgeLine.Intersect(leftEdgeLine, out intersections);
XYZ prevX = intersections.get_Item(0).XYZPoint;
topEdgeLine.Intersect(rightEdgeLine, out intersections);
XYZ nextX = intersections.get_Item(0).XYZPoint;
XYZ edgeV0 = GetCommonVertex(bottomEdge, leftEdge);
XYZ edgeV1 = GetCommonVertex(bottomEdge, rightEdge);
Line topBoundLine = Line.CreateBound(nextX, prevX);
Line leftBoundLine = Line.CreateBound(prevX, edgeV0);
Line bottomBoundLine = Line.CreateBound(edgeV0, edgeV1);
Line rightBoundLine = Line.CreateBound(edgeV1, nextX);
Trapezoid profile = new Trapezoid(topBoundLine, leftBoundLine, bottomBoundLine, rightBoundLine);
XYZ depthEdgeV0 = depthEdge.Evaluate(0.0);
XYZ depthEdgeV1 = depthEdge.Evaluate(1.0);
Line depthLine = null;
if (depthEdgeV0.IsAlmostEqualTo(edgeV0))
{
depthLine = Line.CreateBound(depthEdgeV0, depthEdgeV1);
}
else if (depthEdgeV1.IsAlmostEqualTo(edgeV0))
{
depthLine = Line.CreateBound(depthEdgeV1, depthEdgeV0);
}
CorbelFrame frame = new CorbelFrame(corbel, profile, depthLine, hostDepth, hostTopCoverDistance);
return frame;
}
///
/// Get the common vertex XYZ of two edges.
///
/// Edge 1
/// Edge 2
/// Common vertex XYZ
private static XYZ GetCommonVertex(Edge edge1, Edge edge2)
{
XYZ edge1V0 = edge1.Evaluate(0.0);
XYZ edge1V1 = edge1.Evaluate(1.0);
XYZ edge2V0 = edge2.Evaluate(0.0);
XYZ edge2V1 = edge2.Evaluate(1.0);
if (edge1V0.IsAlmostEqualTo(edge2V0) ||
edge1V0.IsAlmostEqualTo(edge2V1))
{
return edge1V0;
}
else if (edge1V1.IsAlmostEqualTo(edge2V0) ||
edge1V1.IsAlmostEqualTo(edge2V1))
{
return edge1V1;
}
return null;
}
///
/// Extract the Solid of given element.
///
/// Given Element to get its Solid
/// Solid of given element
private static Solid GetElementSolid(Element element)
{
Options goption = new Options();
goption.ComputeReferences = true;
GeometryElement gelem = element.get_Geometry(goption);
Solid resultSolid = null;
//foreach (GeometryObject gobj in gelem.Objects)
IEnumerator Objects = gelem.GetEnumerator();
while (Objects.MoveNext())
{
GeometryObject gobj = Objects.Current;
GeometryInstance gIns = gobj as GeometryInstance;
if (gIns != null)
{
GeometryElement finalGeom = gIns.GetInstanceGeometry();
//foreach (GeometryObject gobj2 in finalGeom.Objects)
IEnumerator Objects1 = finalGeom.GetEnumerator();
while (Objects1.MoveNext())
{
GeometryObject gobj2 = Objects1.Current;
Solid tSolid = gobj2 as Solid;
if (tSolid != null && tSolid.Faces.Size > 0 && tSolid.Volume > 0)
{
resultSolid = tSolid;
break;
}
}
}
if (resultSolid == null)
{
Solid tSolid2 = gobj as Solid;
if (tSolid2 != null && tSolid2.Faces.Size > 0 && tSolid2.Volume > 0)
{
resultSolid = tSolid2;
break;
}
}
}
return resultSolid;
}
///
/// Compute the outside normal of given face.
///
/// Given face to get its outside normal
/// Outside normal of given face
private static XYZ GetNormalOutside(Face face)
{
Edge edge = face.EdgeLoops.get_Item(0).get_Item(0);
UV pt = edge.EvaluateOnFace(0.5, face);
XYZ faceNormal = face.ComputeNormal(pt);
return faceNormal;
}
///
/// Compute the distance between two planar faces.
///
/// Face 1
/// Face 2
/// Distance of the two planar faces
private static double GetDistance(PlanarFace face1, PlanarFace face2)
{
BoundingBoxUV boxUV = face2.GetBoundingBox();
UV center = (boxUV.Max + boxUV.Min) * 0.5;
XYZ centerPt = face2.Evaluate(center);
IntersectionResult result = face1.Project(centerPt);
return face1.Project(centerPt).Distance;
}
}
}