/// <summary> /// Returns an integer to indicate if two vectors are parallel, antiparallel or not. /// </summary> /// <param name="a">The one vector.</param> /// <param name="b">The other vector.</param> /// <returns>1 parallel, -1 antiparallel, 0 not parallel.</returns> public static int VectorsAreParallel2(XYZ a, XYZ b) { if (a == null || b == null) { return(0); } double aa, bb, ab; double epsSq = Eps() * Eps(); double angleEps = Math.PI / 1800.0; aa = a.DotProduct(a); bb = b.DotProduct(b); if (aa < epsSq || bb < epsSq) { return(0); } ab = a.DotProduct(b); double cosAngleSq = (ab / aa) * (ab / bb); if (cosAngleSq < 1.0 - angleEps * angleEps) { return(0); } return(ab > 0 ? 1 : -1); }
public static PlanarFace LastFace(IList <PlanarFace> listFace, XYZ vector, Transform transform) { PlanarFace result = null; IList <PlanarFace> list = Facelibry.PerpendicularFace(listFace, vector, transform); double num = 0.0; foreach (PlanarFace planarFace in list) { XYZ xyz = planarFace.Origin; xyz = transform.OfPoint(xyz); bool flag = MathLib.IsEqual(num, 0.0, 0.0001); if (flag) { num = vector.DotProduct(xyz); result = planarFace; } else { double num2 = vector.DotProduct(xyz); bool flag2 = num2 > num; if (flag2) { result = planarFace; num = num2; } } } return(result); }
public static PlanarFace FirstFace(IList <PlanarFace> listFace, XYZ vector) { PlanarFace result = null; IList <PlanarFace> list = Facelibry.PerpendicularFace(listFace, vector); double num = 0.0; foreach (PlanarFace planarFace in list) { XYZ source = Facelibry.CenterPoint(planarFace); bool flag = MathLib.IsEqual(num, 0.0, 0.0001); if (flag) { num = vector.DotProduct(source); result = planarFace; } else { double num2 = vector.DotProduct(source); bool flag2 = num2 < num; if (flag2) { result = planarFace; num = num2; } } } return(result); }
/// <summary> /// Project given 3D XYZ point into plane, /// returning the UV coordinates of the result /// in the local 2D plane coordinate system. /// </summary> public static UV ProjectInto(this Plane plane, XYZ p) { XYZ q = plane.ProjectOnto(p); XYZ o = plane.Origin; XYZ d = q - o; double u = d.DotProduct(plane.XVec); double v = d.DotProduct(plane.YVec); return(new UV(u, v)); }
//From Jeremy Tammik - The Building Coder - Util.cs public static UV ProjectInto(this Plane plane, XYZ p) { XYZ _q = ProjectOnto(plane, p); XYZ _o = plane.Origin; XYZ _d = _q - _o; double _u = _d.DotProduct(plane.XVec); double _v = _d.DotProduct(plane.YVec); return(new UV(_u, _v)); }
/// <summary> /// Align the given grid horizontally or vertically /// if it is very slightly off axis, by Fair59 in /// https://forums.autodesk.com/t5/revit-api-forum/grids-off-axis/m-p/7129065 /// </summary> void AlignOffAxisGrid( Grid grid) { //Grid grid = doc.GetElement( // sel.GetElementIds().FirstOrDefault() ) as Grid; Document doc = grid.Document; XYZ direction = grid.Curve .GetEndPoint(1) .Subtract(grid.Curve.GetEndPoint(0)) .Normalize(); double distance2hor = direction.DotProduct(XYZ.BasisY); double distance2vert = direction.DotProduct(XYZ.BasisX); double angle = 0; // Maybe use another criterium then <0.0001 double max_distance = 0.0001; if (Math.Abs(distance2hor) < max_distance) { XYZ vector = direction.X < 0 ? direction.Negate() : direction; angle = Math.Asin(-vector.Y); } if (Math.Abs(distance2vert) < max_distance) { XYZ vector = direction.Y < 0 ? direction.Negate() : direction; angle = Math.Asin(vector.X); } if (angle.CompareTo(0) != 0) { using (Transaction t = new Transaction(doc)) { t.Start("correctGrid"); ElementTransformUtils.RotateElement(doc, grid.Id, Line.CreateBound(grid.Curve.GetEndPoint(0), grid.Curve.GetEndPoint(0).Add(XYZ.BasisZ)), angle); t.Commit(); } } }
//++++++++++++++++++++++++++++++++ // BinAdd // xuzhaobin //++++++++++++++++++++++++++++++++ /// <summary> /// 点在面上的投影点 /// </summary> /// <param name="po"></param> /// <param name="p"></param> /// <returns></returns> public static XYZ ProjectToPlane(this XYZ po, Plane p) { XYZ vec_po_to_planeOrigin = p.Origin - po; if (!IsEqual(vec_po_to_planeOrigin.DotProduct(p.Normal), 0)) { return(po + p.Normal * vec_po_to_planeOrigin.DotProduct(p.Normal)); } else { return(po); } }
/// <summary> /// Transform all 3D points to the 2D points. /// </summary> private void transform22D() { if (_Points.Count > 2) { var zdist = _Points.Select(s => s.Z).Distinct(); if ((_Points.Max(s => s.Z) - _Points.Min(s => s.Z)) < 1.0e-6) { _vPoints2d = _Points.Select(s => new UV(s.X, s.Y)).ToList(); UV move = new UV(_vPoints2d.Min(s => s.U), _vPoints2d.Min(s => s.V)); _vPoints2d = _vPoints2d.Select(s => (new UV(s.U - move.U, s.V - move.V))).ToList(); } else { int indLast = _Points.Count - 1; XYZ vX = null; for (int i = 0; i <= indLast; i++) { vX = (_Points[i] - _Points[0]).Normalize(); if (!vX.IsZeroLength()) { break; } } XYZ vY = null; for (int i = indLast; i >= 0; i--) { XYZ v2 = _Points[0] - _Points[i], vZ = vX.CrossProduct(v2).Normalize(); if (!vZ.IsZeroLength()) { vY = vZ.CrossProduct(vX); break; } } { XYZ p0 = _Points[0]; List <XYZ> vSrc = _vPointsTransformed; List <UV> vTar = _vPoints2d; foreach (XYZ pt in vSrc) { XYZ u = pt - p0; vTar.Add(new UV(u.DotProduct(vX), u.DotProduct(vY))); } } } } }
/// <summary> /// This method constructs the winder corner and two straight runs. /// Please be sure the input properties being set properly before calling this method. /// </summary> private void Construct() { // // Construct the winder corner. // XYZ dir1 = (ControlPoints[1] - ControlPoints[0]).Normalize(); XYZ dir2 = (ControlPoints[2] - ControlPoints[1]).Normalize(); m_corner = new WinderSinglePoint(ControlPoints[1], dir1, dir2, NumStepsInCorner); m_corner.Construct(RunWidth, CenterOffsetE, CenterOffsetF); // // Construct two straight runs to connect to the winder corner. // XYZ startPnt = m_corner.StartPoint - TreadDepth * NumStepsAtStart * dir1; XYZ endPnt = m_corner.EndPoint + TreadDepth * NumStepsAtEnd * dir2; XYZ bisectDir = (dir2 - dir1).Normalize(); XYZ perpendicularDir1 = new XYZ(-dir1.Y, dir1.X, 0); XYZ perpendicularDir2 = new XYZ(-dir2.Y, dir2.X, 0); if (bisectDir.DotProduct(perpendicularDir1) < 0) { perpendicularDir1 = perpendicularDir1.Negate(); perpendicularDir2 = perpendicularDir2.Negate(); } m_straightAtStart = new WinderStraight( startPnt, m_corner.StartPoint, perpendicularDir1, NumStepsAtStart); m_straightAtEnd = new WinderStraight( m_corner.EndPoint, endPnt, perpendicularDir2, NumStepsAtEnd); }
static bool ModifiedProfile(Wall wall) { Solid solid = GetUppermostSolid(wall); XYZ orientation = wall.Orientation; Face face = null; foreach (Face f in solid.Faces) { if (orientation .DotProduct(f.ComputeNormal(new UV(0, 0))) > 0) { face = f; break; } } if (face == null) { throw new ArgumentNullException("Something has gone wrong!"); } if (face.GetEdgesAsCurveLoops().Count > 1) { return(true); } else { return(false); } }
static public XYZ Project(this XYZ vector, XYZ vectorV) { double magnitude = Math.Abs(vectorV.GetLength()); XYZ projection = vectorV.Multiply(vector.DotProduct(vectorV) / (magnitude * magnitude)); return(projection); }
public static bool IsPerpendicular(XYZ v, XYZ w) { double length = v.GetLength(); double length2 = v.GetLength(); double num = Math.Abs(v.DotProduct(w)); return 1E-09 < length && 1E-09 < length2 && 1E-09 > num; }
/// <summary> /// Transform all 3D points to the plane. /// </summary> private void transform2Plane() { if (_Points.Count > 2) { if ((_Points.Max(s => s.Z) - _Points.Min(s => s.Z)) < 1.0e-6) { _vPointsTransformed = new List <XYZ>(_Points); } else { XYZ v1 = _Points[1] - _Points[0], v2 = _Points[2] - _Points[1], n = v1.CrossProduct(v2).Normalize(), p0 = _Points[0]; for (int i = 0; i < 2; i++) { bool bContour = i == 0; List <XYZ> vSrc = _Points, vTar = _vPointsTransformed; foreach (XYZ pt in vSrc) { XYZ u = pt - p0, u1 = u - n.Multiply(u.DotProduct(n)); vTar.Add(p0 + u1); } } } } }
public override FScheme.Value Evaluate(FSharpList <FScheme.Value> args) { XYZ a = (XYZ)((FScheme.Value.Container)args[0]).Item; XYZ b = (XYZ)((FScheme.Value.Container)args[1]).Item; return(FScheme.Value.NewContainer(a.DotProduct(b))); }
/// <summary> /// Calculate the CenterPoint base on Australia single-point layout algorithm. /// </summary> /// <param name="runWidth">Stairs Runwidth</param> /// <param name="offset1">CenterPoint Offset from the first inner boundary</param> /// <param name="offset2">CenterPoint Offset from the second inner boundary</param> public void Construct(double runWidth, double offset1, double offset2) { XYZ bisectDir = (Direction2 - Direction1).Normalize(); XYZ perpendicularDir1 = new XYZ(-Direction1.Y, Direction1.X, 0); XYZ perpendicularDir2 = new XYZ(-Direction2.Y, Direction2.X, 0); if (bisectDir.DotProduct(perpendicularDir1) < 0) { perpendicularDir1 = perpendicularDir1.Negate(); perpendicularDir2 = perpendicularDir2.Negate(); } Line line1Offset = Line.CreateUnbound( CornerPoint + perpendicularDir1 * (runWidth + offset1), Direction1); Line line2Offset = Line.CreateUnbound( CornerPoint + perpendicularDir2 * (runWidth + offset2), Direction2); IntersectionResultArray xsect; line1Offset.Intersect(line2Offset, out xsect); CenterPoint = xsect.get_Item(0).XYZPoint; Line line1 = Line.CreateUnbound(CornerPoint, Direction1.Negate()); Line line2 = Line.CreateUnbound(CornerPoint, Direction2); var proj1 = line1.Project(CenterPoint); var proj2 = line2.Project(CenterPoint); Distance1 = proj1.Parameter; Distance2 = proj2.Parameter; }
private XYZ ProjectionOnPlane(XYZ q, Plane plane) { XYZ origin = plane.get_Origin(); XYZ xyz = plane.get_Normal().Normalize(); return(XYZ.op_Subtraction(q, xyz.Multiply(xyz.DotProduct(XYZ.op_Subtraction(q, origin))))); }
/// <summary> /// Offsets an arc along the offset direction from the point on the arc. /// </summary> /// <param name="arc">The arc.</param> /// <param name="offsetPntOnArc">The point on the arc.</param> /// <param name="offset">The offset vector.</param> /// <returns>The offset Arc.</returns> private static Arc OffsetArc(Arc arc, XYZ offsetPntOnArc, XYZ offset) { if (arc == null || offset == null) { throw new ArgumentNullException(); } if (offset.IsZeroLength()) { return(arc); } XYZ axis = arc.Normal.Normalize(); XYZ offsetAlongAxis = axis.Multiply(offset.DotProduct(axis)); XYZ offsetOrthAxis = offset - offsetAlongAxis; XYZ offsetPntToCenter = (arc.Center - offsetPntOnArc).Normalize(); double signedOffsetLengthTowardCenter = offsetOrthAxis.DotProduct(offsetPntToCenter); double newRadius = arc.Radius - signedOffsetLengthTowardCenter; // signedOffsetLengthTowardCenter > 0, minus, < 0, add Arc offsetArc = Arc.Create(arc.Center, newRadius, arc.GetEndParameter(0), arc.GetEndParameter(1), arc.XDirection, arc.YDirection); offsetArc = GeometryUtil.MoveCurve(offsetArc, offsetAlongAxis) as Arc; return(offsetArc); }
public List <FamilyInstance> GetFamilyInstances2(Document doc, View view, FamilyInstance firstInstance, FamilyInstance secondInstance, XYZ dimDirection) { List <FamilyInstance> list = new List <FamilyInstance>(); bool flag = firstInstance.Id == secondInstance.Id; List <FamilyInstance> result; if (flag) { list.Add(firstInstance); result = list; } else { LocationPoint locationPoint = firstInstance.Location as LocationPoint; LocationPoint locationPoint2 = secondInstance.Location as LocationPoint; bool flag2 = locationPoint == null || locationPoint2 == null; if (flag2) { result = list; } else { list.Add(firstInstance); list.Add(secondInstance); PLane3D pLane3D = new PLane3D(doc.ActiveView.Origin, doc.ActiveView.ViewDirection); XYZ point1 = pLane3D.ProjectPointOnPlane(locationPoint.Point); XYZ point2 = pLane3D.ProjectPointOnPlane(locationPoint2.Point); //XYZ tranlator = Timdiemthu3(point1,point2); //XYZ direction = (tranlator - point2); XYZ direction = (point1 - point2); FilteredElementCollector filteredElementCollector = new FilteredElementCollector(doc, view.Id); IList <FamilyInstance> list2 = (from x in filteredElementCollector.OfClass(typeof(FamilyInstance)).OfCategory(firstInstance.Category.ToBuiltinCategory()).Cast <FamilyInstance>() where x.Symbol.Name.Contains(firstInstance.Symbol.Name) select x).ToList(); foreach (var item in list2) { if (item.Id != firstInstance.Id && item.Id != secondInstance.Id) { XYZ locationinstance = item.Getlocationofinstacne(); //XYZ tranlatoritem = Timdiemthu3(locationinstance, locationPoint2.Point); //XYZ vector = (pLane3D.ProjectPointOnPlane(tranlatoritem) - point2); XYZ vector = (pLane3D.ProjectPointOnPlane(locationinstance) - point2); if (vector.IsParallel(direction)) { if (vector.GetLength() <= direction.GetLength()) { bool flag3 = vector.DotProduct(direction) != 0; if (flag3) { list.Add(item); } } } } } result = list; } } return(result); }
public PlanarFace Facexanhat(XYZ point, XYZ direction, IList <PlanarFace> listFaces, Transform transform) { PlanarFace planarFace = null; double num = double.MinValue; PLane3D plane3D = new PLane3D(point, direction); foreach (PlanarFace i in listFaces) { XYZ xyz = transform.OfVector(i.FaceNormal); XYZ xyz2 = direction.CrossProduct(xyz); bool check = xyz2.GetLength() > 0.001 || xyz.DotProduct(direction) < 0.0; if (!check) { double num2 = plane3D.DistancepointtoPlane(transform.OfPoint(i.Origin)); bool flag2 = num2 < 0.001; if (!flag2) { bool flag3 = num2 > num; if (flag3) { planarFace = i; num = num2; } } } } return(planarFace); }
List <XYZ> intersectPoints(List <XYZ> polygons, XYZ lineParas) { if (polygons == null && polygons.Count == 0) { return(null); } XYZ axis = new XYZ(lineParas.Y, lineParas.X, 0); List <XYZ> intersectpoints = new List <XYZ>(); for (int i = 0; i < polygons.Count - 1; i++) { //计算交点时,只计算与水平方向和垂直方向接近垂直的两个点组成的直线;即两个向量的夹角大于60小于120 XYZ vector1 = (polygons[i] - polygons[i + 1]).Normalize(); double cosValue = vector1.DotProduct(axis); if (cosValue > -0.5f && cosValue < 0.5f) { XYZ temppoint = Intersect(polygons[i], polygons[i + 1], lineParas); if (temppoint != null) { intersectpoints.Add(temppoint); } } } return(intersectpoints); }
public static PlanarFace FurthestFace(XYZ point, XYZ direction, IList <PlanarFace> listFaces, Transform transform) { PlanarFace result = null; double num = double.MinValue; PLane3D plane3DLib = new PLane3D(point, direction); foreach (PlanarFace planarFace in listFaces) { XYZ xyz = transform.OfVector(planarFace.FaceNormal); XYZ xyz2 = direction.CrossProduct(xyz); bool flag = xyz2.GetLength() > 0.001 || xyz.DotProduct(direction) < 0.0; if (!flag) { double num2 = plane3DLib.DistanceFromPointToPlane(transform.OfPoint(planarFace.Origin)); bool flag2 = num2 < 0.001; if (!flag2) { bool flag3 = num2 > num; if (flag3) { result = planarFace; num = num2; } } } } return(result); }
static bool ModifiedProfile(Wall wall) { Solid solid = RvtGeometryUtils.GetSolid(wall); XYZ orientation = wall.Orientation; Face face = null; foreach (Face f in solid.Faces) { if (orientation .DotProduct(f.ComputeNormal(new UV(0, 0))) > 0) { face = f; break; } } if (face == null) { Trace.WriteIf(generalSwitch.TraceError, "Unable to extract a face from the wall."); throw new ArgumentNullException("Something has gone wrong!"); } if (face.GetEdgesAsCurveLoops().Count > 1) { return(true); } else { return(false); } }
static Face GetClosestFace(Element e, XYZ p, XYZ normal, Options opt) { Face face = null; double minDistance = double.MaxValue; GeometryElement geo = e.get_Geometry(opt); foreach (GeometryObject obj in geo) { Solid solid = obj as Solid; if (solid != null) { FaceArray fa = solid.Faces; foreach (Face f in fa) { PlanarFace pf = f as PlanarFace; if (null != pf && IsParallel(normal, pf.FaceNormal)) { XYZ v = p - pf.Origin; double d = v.DotProduct(-pf.FaceNormal); if (d < minDistance) { face = f; minDistance = d; } } } } } return(face); }
public static double PointToFace(XYZ point, PlanarFace face) { XYZ faceNormal = face.FaceNormal; XYZ xyz = point - face.Origin; return(Math.Abs(xyz.DotProduct(faceNormal) / faceNormal.GetLength())); }
public override void Action() { XYZ u = XYZ.BasisX; XYZ v = XYZ.BasisY; XYZ w = XYZ.BasisZ; //dot product : Angle Between Vector double dotProduct = u.DotProduct(v); // cos90 = 0 MessageBox.Show($"Angle : {dotProduct}"); //cross product : matrix //https://en.wikipedia.org/wiki/Cross_product XYZ crossProduct = u.CrossProduct(v); //Cross Same Direction with Z crossProduct.ShowMessageBox(nameof(crossProduct)); //This article is about ternary operations on vectors. For the identity in number theory, see Jacobi triple product. // https://en.wikipedia.org/wiki/Triple_product //(u,v,w)=u⋅(v×w)=v⋅(w×u)=w⋅(u×v) //(u,v,w)=(w,u,v)=(v,w,u)=−(v,u,w)=−(w,v,u)=−(u,w,v) // ku⋅(v×w)=k(u,v,w) //(u,v,w)=u⋅(v×w) double tripleProduct = u.TripleProduct(v, w); System.Windows.Forms.MessageBox.Show($"{nameof(tripleProduct)}: {tripleProduct}"); // Write By Step By Step //Dot Product DotProduct(u, v).ShowMessageBox(); //Cross Product CrossProduct(u, v).ShowMessageBox(); }
/// <summary> /// 对8个面基于平行关系 分成列表 /// </summary> /// <param name="twoFaces"></param> /// <returns></returns> internal List <List <Face> > FindParallelPlane(List <Face> eightFaces) { List <Face> faces01 = new List <Face>(); List <Face> faces02 = new List <Face>(); Face face01 = eightFaces.First(); XYZ normalXyz = (face01 as PlanarFace).FaceNormal; foreach (Face face in eightFaces) { XYZ _normalXyz = (face as PlanarFace).FaceNormal; double dotResult = normalXyz.DotProduct(_normalXyz); if (EqualPrecision(dotResult, 1) || EqualPrecision(dotResult, -1)) { faces01.Add(face); } else { faces02.Add(face); } } // 求平行且距离最短的两个对面faces return(new List <List <Face> >() { faces01, faces02 }); }
// test to see if one Point has collinearity private bool hasCollinearity(int index, double angle) { if (index == 0 || index == this.processedPolygon.Count - 1) { return(false); } int after = (index == this.processedPolygon.Count - 1) ? 0 : index + 1; int before = (index == 0) ? this.processedPolygon.Count - 1 : index - 1; XYZ bfr = this.processedPolygon[before]; XYZ pnt = this.processedPolygon[index]; XYZ aftr = this.processedPolygon[after]; XYZ direction1 = (pnt - bfr); XYZ direction2 = (aftr - pnt); if (direction1 == XYZ.Zero || direction2 == XYZ.Zero) { return(true); } direction1 = direction1.Normalize(); direction2 = direction2.Normalize(); double cosine = Math.Abs(direction1.DotProduct(direction2)); double cosineAngle = Math.Cos(angle); bool trueOrFalse = (cosine >= cosineAngle); return(trueOrFalse); }
public void Test_XYZTests_DotProduct() { XYZ bottom = new XYZ(0, 0, 0); XYZ top = new XYZ(20, 30, 0); XYZ right = new XYZ(45, 70, 0); Assert.Equal(3000, XYZ.DotProduct(bottom, top, right)); }
public double Gochopboi2vector(XYZ u1, XYZ u2) { double a = u1.DotProduct(u2); double y1 = Math.Sqrt(Math.Pow(u1.X, 2) + Math.Pow(u1.Y, 2) + Math.Pow(u1.Z, 2)); double y2 = Math.Sqrt(Math.Pow(u2.X, 2) + Math.Pow(u2.Y, 2) + Math.Pow(u2.Z, 2)); return(Math.Abs(a / (y1 * y2))); }
/***************************************************/ /**** Public methods ****/ /***************************************************/ public static Plane ArbitraryPlane(this Curve curve) { XYZ origin = curve.GetEndPoint(0); XYZ x = (curve.GetEndPoint(1) - origin).Normalize(); XYZ helper = 1 - Math.Abs(x.DotProduct(XYZ.BasisZ)) > BH.oM.Geometry.Tolerance.Angle ? XYZ.BasisZ : XYZ.BasisX; XYZ y = x.CrossProduct(helper).Normalize(); return(Plane.CreateByOriginAndBasis(origin, x, y)); }
/// <summary> /// Create the opening associated to an already created door or window. /// </summary> /// <param name="exporterIFC">The exporter class.</param> /// <param name="doc">The document.</param> /// <param name="hostObjHnd">The host object IFC handle.</param> /// <param name="hostId">The host object element id.</param> /// <param name="insertId">The insert element id.</param> /// <param name="openingGUID">The GUID for the IfcOpeningElement.</param> /// <param name="cutLoop">The 2D outline representing the opening geometry.</param> /// <param name="cutDir">The direction of the extrusion representing the opening geometry.</param> /// <param name="origUnscaledDepth">The width of the host object that the opening is cutting.</param> /// <param name="posHingeSide">True if the 2D outline is on the plane containing the hinge.</param> /// <param name="isRecess">True if the IfcOpeningElement should represent a recess.</param> /// <returns>The class containing information about the opening.</returns> static public DoorWindowOpeningInfo CreateOpeningForDoorWindow(ExporterIFC exporterIFC, Document doc, IFCAnyHandle hostObjHnd, ElementId hostId, ElementId insertId, string openingGUID, CurveLoop cutLoop, XYZ cutDir, double origUnscaledDepth, bool posHingeSide, bool isRecess) { // calculate some values. double openingHeight = -1.0; double openingWidth = -1.0; Element wallElement = doc.GetElement(hostId); Wall wall = (wallElement != null) ? wallElement as Wall : null; Curve curve = WallExporter.GetWallAxis(wall); if (curve == null) return null; // Don't export opening if we are exporting parts on a wall, as the parts will already have the openings cut out. if (PartExporter.CanExportParts(wall)) return null; ElementId catId = CategoryUtil.GetSafeCategoryId(wall); double unScaledDepth = origUnscaledDepth; IFCAnyHandle hostObjPlacementHnd = IFCAnyHandleUtil.GetObjectPlacement(hostObjHnd); IFCAnyHandle ownerHistory = ExporterCacheManager.OwnerHistoryHandle; XYZ relOrig = XYZ.Zero; XYZ relZ = XYZ.BasisZ; XYZ relX = XYZ.BasisX; double openingZNonScaled = -1.0; Plane plane = new Plane(cutDir, XYZ.Zero); // get height, width before transform BoundingBoxXYZ cutLoopBBox = ComputeApproximateCurveLoopBBoxForOpening(cutLoop, null); if (cutLoopBBox != null) { XYZ dist = cutLoopBBox.Max - cutLoopBBox.Min; openingZNonScaled = cutLoopBBox.Min.Z; openingHeight = Math.Abs(dist.Z); openingWidth = Math.Sqrt(dist.X * dist.X + dist.Y * dist.Y); } Transform openingTrf = ExporterIFCUtils.GetUnscaledTransform(exporterIFC, hostObjPlacementHnd); XYZ hostObjYDir = openingTrf.BasisY; XYZ hostObjOrig = openingTrf.Origin; openingTrf = openingTrf.Inverse; // move to wall axis CurveLoop tmpCutLoop = GeometryUtil.TransformCurveLoop(cutLoop, openingTrf); cutDir = openingTrf.OfVector(cutDir); if (curve is Line) { Plane cutLoopPlane = null; try { cutLoopPlane = tmpCutLoop.GetPlane(); } catch { return null; } XYZ clOrig = cutLoopPlane.Origin; double wantOriginAtY = posHingeSide ? (-unScaledDepth / 2.0) : (unScaledDepth / 2.0); if (!MathUtil.IsAlmostEqual(wantOriginAtY, clOrig[1])) { XYZ moveVec = new XYZ(0, wantOriginAtY - clOrig[1], 0); tmpCutLoop = GeometryUtil.MoveCurveLoop(tmpCutLoop, moveVec); } bool cutDirRelToHostObjY = (cutDir[1] > 0.0); // true = same sense, false = opp. sense if (posHingeSide != cutDirRelToHostObjY) { cutDir = cutDir * -1.0; cutDirRelToHostObjY = !cutDirRelToHostObjY; // not used beyond this point. } } else if ((cutLoopBBox != null) && (curve is Arc)) { Arc arc = curve as Arc; double radius = arc.Radius; XYZ curveCtr = arc.Center; // check orientation to cutDir, make sure it points to center of arc. XYZ origLL = new XYZ(cutLoopBBox.Min.X, cutLoopBBox.Min.Y, curveCtr.Z); XYZ origUR = new XYZ(cutLoopBBox.Max.X, cutLoopBBox.Max.Y, curveCtr.Z); XYZ origCtr = (origLL + origUR) / 2.0; double centerDist = origCtr.DistanceTo(curveCtr); XYZ approxMoveDir = (origCtr - curveCtr).Normalize(); bool cutDirPointingIn = (cutDir.DotProduct(approxMoveDir) < 0.0); bool centerInsideArc = (centerDist < radius); if (centerInsideArc == cutDirPointingIn) { XYZ moveVec = cutDir * -unScaledDepth; origCtr += moveVec; tmpCutLoop = GeometryUtil.MoveCurveLoop(tmpCutLoop, moveVec); } // not for windows that are too big ... forget about it. Very rare case. double depthFactor = openingWidth / (2.0 * radius); double eps = MathUtil.Eps(); if (depthFactor < 1.0 - eps) { double depthFactorSq = depthFactor * depthFactor * 4; double extraDepth = radius * (1.0 - Math.Sqrt(1.0 - depthFactorSq)); if (extraDepth > eps) { XYZ moveVec = cutDir * -extraDepth; tmpCutLoop = GeometryUtil.MoveCurveLoop(tmpCutLoop, moveVec); unScaledDepth += extraDepth; } } // extra fudge on the other side of the window opening. depthFactor = origUnscaledDepth / (2.0 * radius); if (depthFactor < 1.0 - eps) { double extraDepth = radius * (1.0 - Math.Sqrt(1.0 - depthFactor)); if (extraDepth > eps) unScaledDepth += extraDepth; } } XYZ cutXDir = XYZ.BasisZ; XYZ cutOrig = XYZ.Zero; XYZ cutYDir = cutDir.CrossProduct(cutXDir); plane = new Plane(cutXDir, cutYDir, cutOrig); // now move to origin in this coordinate system. // todo: update openingtrf if we are to use it again! BoundingBoxXYZ tmpBBox = ComputeApproximateCurveLoopBBoxForOpening(tmpCutLoop, plane); if (tmpBBox != null) { relOrig = tmpBBox.Min; XYZ moveVec = relOrig * -1.0; tmpCutLoop = GeometryUtil.MoveCurveLoop(tmpCutLoop, moveVec); } IList<CurveLoop> oCutLoopList = new List<CurveLoop>(); oCutLoopList.Add(tmpCutLoop); double depth = UnitUtil.ScaleLength(unScaledDepth); Element doorWindowElement = doc.GetElement(insertId); IFCAnyHandle openingRepHnd = RepresentationUtil.CreateExtrudedProductDefShape(exporterIFC, doorWindowElement, catId, oCutLoopList, plane, cutDir, depth); if (IFCAnyHandleUtil.IsNullOrHasNoValue(openingRepHnd)) return null; // care only about first loop. IFCFile file = exporterIFC.GetFile(); XYZ scaledOrig = UnitUtil.ScaleLength(relOrig); IFCAnyHandle openingPlacement = ExporterUtil.CreateLocalPlacement(file, hostObjPlacementHnd, scaledOrig, relZ, relX); string openingObjectType = isRecess ? "Recess": "Opening"; string origOpeningName = NamingUtil.GetIFCNamePlusIndex(doorWindowElement, 1); string openingName = NamingUtil.GetNameOverride(doorWindowElement, origOpeningName); IFCAnyHandle openingHnd = IFCInstanceExporter.CreateOpeningElement(file, openingGUID, ownerHistory, openingName, null, openingObjectType, openingPlacement, openingRepHnd, null); string openingVoidsGUID = GUIDUtil.CreateSubElementGUID(doorWindowElement, (int)IFCDoorSubElements.DoorOpeningRelVoid); IFCInstanceExporter.CreateRelVoidsElement(file, openingVoidsGUID, ownerHistory, null, null, hostObjHnd, openingHnd); if (ExporterCacheManager.ExportOptionsCache.ExportBaseQuantities) { using (IFCExtrusionCreationData extraParams = new IFCExtrusionCreationData()) { double height = 0.0, width = 0.0; if (ExtrusionExporter.ComputeHeightWidthOfCurveLoop(tmpCutLoop, plane, out height, out width)) { extraParams.ScaledHeight = UnitUtil.ScaleLength(height); extraParams.ScaledWidth = UnitUtil.ScaleLength(width); } IList<CurveLoop> curveLoops = new List<CurveLoop>(); curveLoops.Add(tmpCutLoop); double area = ExporterIFCUtils.ComputeAreaOfCurveLoops(curveLoops); if (area > 0.0) extraParams.ScaledArea = UnitUtil.ScaleArea(area); extraParams.ScaledLength = depth; PropertyUtil.CreateOpeningQuantities(exporterIFC, openingHnd, extraParams); } } return DoorWindowOpeningInfo.Create(openingHnd, openingHeight, openingWidth); }
/// <summary> /// Generates the UV value of a point projected to a plane, given an extrusion direction. /// </summary> /// <param name="plane">The plane.</param> /// <param name="projDir">The projection direction.</param> /// <param name="point">The point.</param> /// <returns>The UV value.</returns> public static UV ProjectPointToPlane(Plane plane, XYZ projDir, XYZ point) { XYZ zDir = plane.Normal; double denom = projDir.DotProduct(zDir); if (MathUtil.IsAlmostZero(denom)) return null; XYZ xDir = plane.XVec; XYZ yDir = plane.YVec; XYZ orig = plane.Origin; double distToPlane = ((orig - point).DotProduct(zDir)) / denom; XYZ pointProj = distToPlane * projDir + point; XYZ pointProjOffset = pointProj - orig; UV pointProjUV = new UV(pointProjOffset.DotProduct(xDir), pointProjOffset.DotProduct(yDir)); return pointProjUV; }
/// <summary> /// Main implementation to export walls. /// </summary> /// <param name="exporterIFC"> /// The ExporterIFC object. /// </param> /// <param name="element"> /// The element. /// </param> /// <param name="geometryElement"> /// The geometry element. /// </param> /// <param name="origWrapper"> /// The IFCProductWrapper. /// </param> /// <param name="overrideLevelId"> /// The level id. /// </param> /// <param name="range"> /// The range to be exported for the element. /// </param> /// <returns> /// The exported wall handle. /// </returns> public static IFCAnyHandle ExportWallBase(ExporterIFC exporterIFC, Element element, GeometryElement geometryElement, IFCProductWrapper origWrapper, ElementId overrideLevelId, IFCRange range) { using (IFCProductWrapper localWrapper = IFCProductWrapper.Create(origWrapper)) { ElementId catId = CategoryUtil.GetSafeCategoryId(element); Wall wallElement = element as Wall; FamilyInstance famInstWallElem = element as FamilyInstance; if (wallElement == null && famInstWallElem == null) return null; if (wallElement != null && IsWallCompletelyClipped(wallElement, exporterIFC, range)) return null; // get global values. Document doc = element.Document; double scale = exporterIFC.LinearScale; IFCFile file = exporterIFC.GetFile(); IFCAnyHandle ownerHistory = exporterIFC.GetOwnerHistoryHandle(); IFCAnyHandle contextOfItemsAxis = exporterIFC.Get3DContextHandle("Axis"); IFCAnyHandle contextOfItemsBody = exporterIFC.Get3DContextHandle("Body"); IFCRange zSpan = new IFCRange(); double depth = 0.0; bool validRange = (range != null && !MathUtil.IsAlmostZero(range.Start - range.End)); bool exportParts = PartExporter.CanExportParts(wallElement); if (exportParts && !PartExporter.CanExportElementInPartExport(wallElement, validRange? overrideLevelId : wallElement.Level.Id, validRange)) return null; // get bounding box height so that we can subtract out pieces properly. // only for Wall, not FamilyInstance. if (wallElement != null && geometryElement != null) { BoundingBoxXYZ boundingBox = element.get_BoundingBox(null); if (boundingBox == null) return null; zSpan = new IFCRange(boundingBox.Min.Z, boundingBox.Max.Z); // if we have a top clipping plane, modify depth accordingly. double bottomHeight = validRange ? Math.Max(zSpan.Start, range.Start) : zSpan.Start; double topHeight = validRange ? Math.Min(zSpan.End, range.End) : zSpan.End; depth = topHeight - bottomHeight; if (MathUtil.IsAlmostZero(depth)) return null; depth *= scale; } IFCAnyHandle axisRep = null; IFCAnyHandle bodyRep = null; bool exportingAxis = false; Curve curve = null; bool exportedAsWallWithAxis = false; bool exportedBodyDirectly = false; bool exportingInplaceOpenings = false; Curve centerCurve = GetWallAxis(wallElement); XYZ localXDir = new XYZ(1, 0, 0); XYZ localYDir = new XYZ(0, 1, 0); XYZ localZDir = new XYZ(0, 0, 1); XYZ localOrig = new XYZ(0, 0, 0); double eps = MathUtil.Eps(); if (centerCurve != null) { Curve baseCurve = GetWallAxisAtBaseHeight(wallElement); curve = GetWallTrimmedCurve(wallElement, baseCurve); IFCRange curveBounds; XYZ oldOrig; GeometryUtil.GetAxisAndRangeFromCurve(curve, out curveBounds, out localXDir, out oldOrig); localOrig = oldOrig; if (baseCurve != null) { if (!validRange || (MathUtil.IsAlmostEqual(range.Start, zSpan.Start))) { XYZ newOrig = baseCurve.Evaluate(curveBounds.Start, false); if (!validRange && (zSpan.Start < newOrig[2] - eps)) localOrig = new XYZ(localOrig.X, localOrig.Y, zSpan.Start); else localOrig = new XYZ(localOrig.X, localOrig.Y, newOrig[2]); } else { localOrig = new XYZ(localOrig.X, localOrig.Y, range.Start); } } double dist = localOrig[2] - oldOrig[2]; if (!MathUtil.IsAlmostZero(dist)) { XYZ moveVec = new XYZ(0, 0, dist); curve = GeometryUtil.MoveCurve(curve, moveVec); } localYDir = localZDir.CrossProduct(localXDir); // ensure that X and Z axes are orthogonal. double xzDot = localZDir.DotProduct(localXDir); if (!MathUtil.IsAlmostZero(xzDot)) localXDir = localYDir.CrossProduct(localZDir); } Transform orientationTrf = Transform.Identity; orientationTrf.BasisX = localXDir; orientationTrf.BasisY = localYDir; orientationTrf.BasisZ = localZDir; orientationTrf.Origin = localOrig; using (IFCPlacementSetter setter = IFCPlacementSetter.Create(exporterIFC, element, null, orientationTrf, overrideLevelId)) { IFCAnyHandle localPlacement = setter.GetPlacement(); Plane plane = new Plane(localXDir, localYDir, localOrig); // project curve to XY plane. XYZ projDir = XYZ.BasisZ; // two representations: axis, body. { if ((centerCurve != null) && (GeometryUtil.CurveIsLineOrArc(centerCurve))) { exportingAxis = true; string identifierOpt = "Axis"; // IFC2x2 convention string representationTypeOpt = "Curve2D"; // IFC2x2 convention IFCGeometryInfo info = IFCGeometryInfo.CreateCurveGeometryInfo(exporterIFC, plane, projDir, false); ExporterIFCUtils.CollectGeometryInfo(exporterIFC, info, curve, XYZ.Zero, true); IList<IFCAnyHandle> axisItems = info.GetCurves(); if (axisItems.Count == 0) { exportingAxis = false; } else { HashSet<IFCAnyHandle> axisItemSet = new HashSet<IFCAnyHandle>(); foreach (IFCAnyHandle axisItem in axisItems) axisItemSet.Add(axisItem); axisRep = RepresentationUtil.CreateShapeRepresentation(exporterIFC, element, catId, contextOfItemsAxis, identifierOpt, representationTypeOpt, axisItemSet); } } } IList<IFCExtrusionData> cutPairOpenings = new List<IFCExtrusionData>(); Document document = element.Document; if (wallElement != null && exportingAxis && curve != null) { SolidMeshGeometryInfo solidMeshInfo = (range == null) ? GeometryUtil.GetSolidMeshGeometry(geometryElement, Transform.Identity) : GeometryUtil.GetClippedSolidMeshGeometry(geometryElement, range); IList<Solid> solids = solidMeshInfo.GetSolids(); IList<Mesh> meshes = solidMeshInfo.GetMeshes(); if (solids.Count == 0 && meshes.Count == 0) return null; bool useNewCode = false; if (useNewCode && solids.Count == 1 && meshes.Count == 0) { bool completelyClipped; bodyRep = ExtrusionExporter.CreateExtrusionWithClipping(exporterIFC, wallElement, catId, solids[0], plane, projDir, range, out completelyClipped); if (completelyClipped) return null; if (!IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) { exportedAsWallWithAxis = true; exportedBodyDirectly = true; } else { exportedAsWallWithAxis = false; exportedBodyDirectly = false; } } if (!exportedAsWallWithAxis) { // Fallback - use native routines to try to export wall. bool isCompletelyClipped; bodyRep = FallbackTryToCreateAsExtrusion(exporterIFC, wallElement, solidMeshInfo, catId, curve, plane, depth, zSpan, range, setter, out cutPairOpenings, out isCompletelyClipped); if (isCompletelyClipped) return null; if (!IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) exportedAsWallWithAxis = true; } } using (IFCExtrusionCreationData extraParams = new IFCExtrusionCreationData()) { ElementId matId = ElementId.InvalidElementId; if (!exportedAsWallWithAxis) { SolidMeshGeometryInfo solidMeshCapsule = null; if (wallElement != null) { if (validRange) { solidMeshCapsule = GeometryUtil.GetClippedSolidMeshGeometry(geometryElement, range); } else { solidMeshCapsule = GeometryUtil.GetSplitSolidMeshGeometry(geometryElement); } if (solidMeshCapsule.SolidsCount() == 0 && solidMeshCapsule.MeshesCount() == 0) { return null; } } else { GeometryElement geomElemToUse = GetGeometryFromInplaceWall(famInstWallElem); if (geomElemToUse != null) { exportingInplaceOpenings = true; } else { exportingInplaceOpenings = false; geomElemToUse = geometryElement; } Transform trf = Transform.Identity; if (geomElemToUse != geometryElement) trf = famInstWallElem.GetTransform(); solidMeshCapsule = GeometryUtil.GetSolidMeshGeometry(geomElemToUse, trf); } IList<Solid> solids = solidMeshCapsule.GetSolids(); IList<Mesh> meshes = solidMeshCapsule.GetMeshes(); extraParams.PossibleExtrusionAxes = IFCExtrusionAxes.TryZ; // only allow vertical extrusions! extraParams.AreInnerRegionsOpenings = true; BodyExporterOptions bodyExporterOptions = new BodyExporterOptions(true); if ((solids.Count > 0) || (meshes.Count > 0)) { matId = BodyExporter.GetBestMaterialIdForGeometry(solids, meshes); bodyRep = BodyExporter.ExportBody(element.Document.Application, exporterIFC, element, catId, solids, meshes, bodyExporterOptions, extraParams).RepresentationHnd; } else { IList<GeometryObject> geomElemList = new List<GeometryObject>(); geomElemList.Add(geometryElement); BodyData bodyData = BodyExporter.ExportBody(element.Document.Application, exporterIFC, element, catId, geomElemList, bodyExporterOptions, extraParams); bodyRep = bodyData.RepresentationHnd; } if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) { extraParams.ClearOpenings(); return null; } // We will be able to export as a IfcWallStandardCase as long as we have an axis curve. XYZ extrDirUsed = XYZ.Zero; if (extraParams.HasExtrusionDirection) { extrDirUsed = extraParams.ExtrusionDirection; if (MathUtil.IsAlmostEqual(Math.Abs(extrDirUsed[2]), 1.0)) { if ((solids.Count == 1) && (meshes.Count == 0)) exportedAsWallWithAxis = exportingAxis; exportedBodyDirectly = true; } } } IFCAnyHandle prodRep = null; IList<IFCAnyHandle> representations = new List<IFCAnyHandle>(); if (exportingAxis) representations.Add(axisRep); representations.Add(bodyRep); prodRep = IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, representations); string objectType = NamingUtil.CreateIFCObjectName(exporterIFC, element); IFCAnyHandle wallHnd = null; string elemGUID = (validRange) ? ExporterIFCUtils.CreateGUID() : ExporterIFCUtils.CreateGUID(element); string elemName = NamingUtil.GetNameOverride(element, exporterIFC.GetName()); string elemDesc = NamingUtil.GetDescriptionOverride(element, null); string elemObjectType = NamingUtil.GetObjectTypeOverride(element, objectType); string elemId = NamingUtil.CreateIFCElementId(element); if (exportedAsWallWithAxis) { wallHnd = IFCInstanceExporter.CreateWallStandardCase(file, elemGUID, ownerHistory, elemName, elemDesc, elemObjectType, localPlacement, exportParts ? null : prodRep, elemId); if (exportParts) PartExporter.ExportHostPart(exporterIFC, wallElement, wallHnd, localWrapper, setter, localPlacement, overrideLevelId); localWrapper.AddElement(wallHnd, setter, extraParams, true); OpeningUtil.CreateOpeningsIfNecessary(wallHnd, element, cutPairOpenings, exporterIFC, localPlacement, setter, localWrapper); if (exportedBodyDirectly) { OpeningUtil.CreateOpeningsIfNecessary(wallHnd, element, extraParams, exporterIFC, localPlacement, setter, localWrapper); } else { double scaledWidth = wallElement.Width * scale; ExporterIFCUtils.AddOpeningsToElement(exporterIFC, wallHnd, wallElement, scaledWidth, range, setter, localPlacement, localWrapper); } // export Base Quantities if (ExporterCacheManager.ExportOptionsCache.ExportBaseQuantities) { CreateWallBaseQuantities(exporterIFC, wallElement, wallHnd, depth); } } else { wallHnd = IFCInstanceExporter.CreateWall(file, elemGUID, ownerHistory, elemName, elemDesc, elemObjectType, localPlacement, exportParts ? null : prodRep, elemId); if (exportParts) PartExporter.ExportHostPart(exporterIFC, wallElement, wallHnd, localWrapper, setter, localPlacement, overrideLevelId); localWrapper.AddElement(wallHnd, setter, extraParams, true); // Only export one material for 2x2; for future versions, export the whole list. if (exporterIFC.ExportAs2x2 && (matId != ElementId.InvalidElementId) && !exportParts) { CategoryUtil.CreateMaterialAssociation(doc, exporterIFC, wallHnd, matId); } if (exportingInplaceOpenings) { ExporterIFCUtils.AddOpeningsToElement(exporterIFC, wallHnd, famInstWallElem, 0.0, range, setter, localPlacement, localWrapper); } if (exportedBodyDirectly) OpeningUtil.CreateOpeningsIfNecessary(wallHnd, element, extraParams, exporterIFC, localPlacement, setter, localWrapper); } PropertyUtil.CreateInternalRevitPropertySets(exporterIFC, element, localWrapper); ElementId wallLevelId = (validRange) ? setter.LevelId : ElementId.InvalidElementId; if (wallElement != null && !exportParts) { if (!exporterIFC.ExportAs2x2 || exportedAsWallWithAxis) //will move this check into ExportHostObject HostObjectExporter.ExportHostObjectMaterials(exporterIFC, wallElement, localWrapper.GetAnElement(), geometryElement, localWrapper, wallLevelId, Toolkit.IFCLayerSetDirection.Axis2); } exporterIFC.RegisterSpaceBoundingElementHandle(wallHnd, element.Id, wallLevelId); return wallHnd; } } } }
/// <summary> /// Main implementation to export walls. /// </summary> /// <param name="exporterIFC"> /// The ExporterIFC object. /// </param> /// <param name="element"> /// The element. /// </param> /// <param name="geometryElement"> /// The geometry element. /// </param> /// <param name="productWrapper"> /// The IFCProductWrapper. /// </param> /// <param name="overrideLevelId"> /// The level id. /// </param> /// <param name="range"> /// The range to be exported for the element. /// </param> /// <returns> /// The exported wall handle. /// </returns> public static IFCAnyHandle ExportWallBase(ExporterIFC exporterIFC, Element element, GeometryElement geometryElement, IFCProductWrapper productWrapper, ElementId overrideLevelId, UV range) { using (IFCProductWrapper localWrapper = IFCProductWrapper.Create(productWrapper)) { ElementId catId = CategoryUtil.GetSafeCategoryId(element); Wall wallElement = element as Wall; FamilyInstance famInstWallElem = element as FamilyInstance; if (wallElement == null && famInstWallElem == null) return IFCAnyHandle.Create(); if (wallElement != null && IsWallCompletelyClipped(wallElement, exporterIFC, range)) return IFCAnyHandle.Create(); // get global values. Document doc = element.Document; double scale = exporterIFC.LinearScale; IFCFile file = exporterIFC.GetFile(); IFCAnyHandle ownerHistory = exporterIFC.GetOwnerHistoryHandle(); IFCAnyHandle contextOfItems = exporterIFC.Get3DContextHandle(); UV zSpan = UV.Zero; double depth = 0.0; bool validRange = (!MathUtil.IsAlmostZero(range.V - range.U)); // get bounding box height so that we can subtract out pieces properly. // only for Wall, not FamilyInstance. if (wallElement != null && geometryElement != null) { BoundingBoxXYZ boundingBox = element.get_BoundingBox(null); zSpan = new UV(boundingBox.Min.Z, boundingBox.Max.Z); // if we have a top clipping plane, modify depth accordingly. double bottomHeight = validRange ? Math.Max(zSpan[0], range[0]) : zSpan[0]; double topHeight = validRange ? Math.Min(zSpan[1], range[1]) : zSpan[1]; depth = topHeight - bottomHeight; if (MathUtil.IsAlmostZero(depth)) return IFCAnyHandle.Create(); depth *= scale; } IFCAnyHandle axisRep = IFCAnyHandle.Create(); IFCAnyHandle bodyRep = IFCAnyHandle.Create(); bool exportingAxis = false; Curve curve = null; bool exportedAsWallWithAxis = false; bool exportedBodyDirectly = false; bool exportingInplaceOpenings = false; Curve centerCurve = GetWallAxis(wallElement); XYZ localXDir = new XYZ(1, 0, 0); XYZ localYDir = new XYZ(0, 1, 0); XYZ localZDir = new XYZ(0, 0, 1); XYZ localOrig = new XYZ(0, 0, 0); double eps = MathUtil.Eps(); if (centerCurve != null) { Curve baseCurve = GetWallAxisAtBaseHeight(wallElement); curve = GetWallTrimmedCurve(wallElement, baseCurve); UV curveBounds; XYZ oldOrig; GeometryUtil.GetAxisAndRangeFromCurve(curve, out curveBounds, out localXDir, out oldOrig); localOrig = oldOrig; if (baseCurve != null) { if (!validRange || (MathUtil.IsAlmostEqual(range[0], zSpan[0]))) { XYZ newOrig = baseCurve.Evaluate(curveBounds.U, false); if (validRange && (zSpan[0] < newOrig[2] - eps)) localOrig = new XYZ(localOrig.X, localOrig.Y, zSpan[0]); else localOrig = new XYZ(localOrig.X, localOrig.Y, newOrig[2]); } else { localOrig = new XYZ(localOrig.X, localOrig.Y, range[0]); } } double dist = localOrig[2] - oldOrig[2]; if (!MathUtil.IsAlmostZero(dist)) { XYZ moveVec = new XYZ(0, 0, dist); curve = GeometryUtil.MoveCurve(curve, moveVec); } localYDir = localZDir.CrossProduct(localXDir); // ensure that X and Z axes are orthogonal. double xzDot = localZDir.DotProduct(localXDir); if (!MathUtil.IsAlmostZero(xzDot)) localXDir = localYDir.CrossProduct(localZDir); } Transform orientationTrf = Transform.Identity; orientationTrf.BasisX = localXDir; orientationTrf.BasisY = localYDir; orientationTrf.BasisZ = localZDir; orientationTrf.Origin = localOrig; using (IFCPlacementSetter setter = IFCPlacementSetter.Create(exporterIFC, element, null, orientationTrf, overrideLevelId)) { IFCAnyHandle localPlacement = setter.GetPlacement(); Plane plane = new Plane(localXDir, localYDir, localOrig); // project curve to XY plane. XYZ projDir = new XYZ(0, 0, 1); // two representations: axis, body. { if ((centerCurve != null) && (GeometryUtil.CurveIsLineOrArc(centerCurve))) { exportingAxis = true; IFCLabel identifierOpt = IFCLabel.Create("Axis"); // IFC2x2 convention IFCLabel representationTypeOpt = IFCLabel.Create("Curve2D"); // IFC2x2 convention IFCGeometryInfo info = IFCGeometryInfo.CreateCurveGeometryInfo(exporterIFC, plane, projDir, false); ExporterIFCUtils.CollectGeometryInfo(exporterIFC, info, curve, XYZ.Zero, true); IList<IFCAnyHandle> axisItems = info.GetCurves(); if (axisItems.Count == 0) { exportingAxis = false; } else { HashSet<IFCAnyHandle> axisItemSet = new HashSet<IFCAnyHandle>(); foreach (IFCAnyHandle axisItem in axisItems) axisItemSet.Add(axisItem); axisRep = RepresentationUtil.CreateShapeRepresentation(exporterIFC, catId, contextOfItems, identifierOpt, representationTypeOpt, axisItemSet); } } } IList<IFCExtrusionData> cutPairOpenings = new List<IFCExtrusionData>(); do { if (wallElement == null || !exportingAxis || curve == null) break; bool hasExtrusion = HasElevationProfile(wallElement); if (hasExtrusion) { IList<CurveLoop> loops = GetElevationProfile(wallElement); if (loops.Count == 0) hasExtrusion = false; else { IList<IList<CurveLoop>> sortedLoops = ExporterIFCUtils.SortCurveLoops(loops); if (sortedLoops.Count == 0) break; // Current limitation: can't handle wall split into multiple disjointed pieces. int numSortedLoops = sortedLoops.Count; if (numSortedLoops > 1) break; bool ignoreExtrusion = true; bool cantHandle = false; bool hasGeometry = false; for (int ii = 0; (ii < numSortedLoops) && !cantHandle; ii++) { int sortedLoopSize = sortedLoops[ii].Count; if (sortedLoopSize == 0) continue; if (!ExporterIFCUtils.IsCurveLoopConvexWithOpenings(sortedLoops[ii][0], range, wallElement, out ignoreExtrusion)) { if (ignoreExtrusion) { // we need more information. Is there something to export? If so, we'll // ignore the extrusion. Otherwise, we will fail. IFCSolidMeshGeometryInfo solidMeshInfo = ExporterIFCUtils.GetClippedSolidMeshGeometry(exporterIFC, range, geometryElement); if (solidMeshInfo.GetSolids().Count == 0 && solidMeshInfo.GetMeshes().Count == 0) continue; hasExtrusion = false; } else { cantHandle = true; } hasGeometry = true; } else { hasGeometry = true; } } if (!hasGeometry) return IFCAnyHandle.Create(); if (cantHandle) break; } } if (!CanExportWallGeometryAsExtrusion(element, range)) break; // extrusion direction. XYZ extrusionDir = GetWallHeightDirection(wallElement); // create extrusion boundary. IList<CurveLoop> boundaryLoops = new List<CurveLoop>(); bool alwaysThickenCurve = IsWallBaseRectangular(wallElement, curve); if (!alwaysThickenCurve) { boundaryLoops = GetLoopsFromTopBottomFace(wallElement, exporterIFC); if (boundaryLoops.Count == 0) continue; } else { CurveLoop newLoop = CurveLoop.CreateViaThicken(curve, wallElement.Width, new XYZ(0, 0, 1)); if (newLoop == null) break; if (!GeometryUtil.IsIFCLoopCCW(newLoop, new XYZ(0, 0, 1))) newLoop = GeometryUtil.ReverseOrientation(newLoop); boundaryLoops.Add(newLoop); } // origin gets scaled later. XYZ setterOffset = new XYZ(0, 0, setter.Offset + (localOrig[2] - setter.BaseOffset)); IFCAnyHandle baseBodyItemHnd = file.CreateExtrudedSolidFromCurveLoop(exporterIFC, catId, boundaryLoops, plane, extrusionDir, depth); if (!baseBodyItemHnd.HasValue) break; IFCAnyHandle bodyItemHnd = AddClippingsToBaseExtrusion(exporterIFC, wallElement, setterOffset, range, zSpan, baseBodyItemHnd, out cutPairOpenings); if (!bodyItemHnd.HasValue) break; HashSet<IFCAnyHandle> bodyItems = new HashSet<IFCAnyHandle>(); bodyItems.Add(bodyItemHnd); if (baseBodyItemHnd.Id == bodyItemHnd.Id) { bodyRep = RepresentationUtil.CreateSweptSolidRep(exporterIFC, catId, contextOfItems, bodyItems, IFCAnyHandle.Create()); } else { bodyRep = RepresentationUtil.CreateClippingRep(exporterIFC, catId, contextOfItems, bodyItems); } if (bodyRep.HasValue) exportedAsWallWithAxis = true; } while (false); IFCExtrusionCreationData extraParams = new IFCExtrusionCreationData(); ElementId matId = ElementId.InvalidElementId; if (!exportedAsWallWithAxis) { IFCSolidMeshGeometryInfo solidMeshInfo; if (wallElement != null) { if (validRange) { solidMeshInfo = ExporterIFCUtils.GetClippedSolidMeshGeometry(exporterIFC, range, geometryElement); if (solidMeshInfo.GetSolids().Count == 0 && solidMeshInfo.GetMeshes().Count == 0) return IFCAnyHandle.Create(); } else { solidMeshInfo = ExporterIFCUtils.GetSplitSolidMeshGeometry(exporterIFC, geometryElement); } } else { GeometryElement geomElemToUse = GetGeometryFromInplaceWall(famInstWallElem); if (geomElemToUse != null) { exportingInplaceOpenings = true; } else { exportingInplaceOpenings = false; geomElemToUse = geometryElement; } Transform trf = Transform.Identity; if (geomElemToUse != geometryElement) trf = famInstWallElem.GetTransform(); solidMeshInfo = ExporterIFCUtils.GetSolidMeshGeometry(exporterIFC, geomElemToUse, trf); } IList<Solid> solids = solidMeshInfo.GetSolids(); IList<Mesh> meshes = solidMeshInfo.GetMeshes(); extraParams.PossibleExtrusionAxes = IFCExtrusionAxes.TryZ; // only allow vertical extrusions! extraParams.AreInnerRegionsOpenings = true; if ((solids.Count > 0) || (meshes.Count > 0)) { matId = BodyExporter.GetBestMaterialIdForGeometry(solids, meshes); bodyRep = BodyExporter.ExportBody(element.Document.Application, exporterIFC, catId, solids, meshes, true, extraParams); } else { IList<GeometryObject> geomElemList = new List<GeometryObject>(); geomElemList.Add(geometryElement); bodyRep = BodyExporter.ExportBody(element.Document.Application, exporterIFC, catId, geomElemList, true, extraParams); } if (!bodyRep.HasValue) return IFCAnyHandle.Create(); // We will be able to export as a IfcWallStandardCase as long as we have an axis curve. XYZ extrDirUsed = XYZ.Zero; if (extraParams.HasCustomAxis) { extrDirUsed = extraParams.CustomAxis; if (MathUtil.IsAlmostEqual(Math.Abs(extrDirUsed[2]), 1.0)) { if ((solids.Count == 1) && (meshes.Count == 0)) exportedAsWallWithAxis = exportingAxis; exportedBodyDirectly = true; } } } IFCAnyHandle prodRep = IFCAnyHandle.Create(); IList<IFCAnyHandle> representations = new List<IFCAnyHandle>(); if (exportingAxis) representations.Add(axisRep); representations.Add(bodyRep); prodRep = file.CreateProductDefinitionShape(IFCLabel.Create(), IFCLabel.Create(), representations); IFCLabel objectType = NamingUtil.CreateIFCObjectName(exporterIFC, element); IFCAnyHandle wallHnd = IFCAnyHandle.Create(); IFCLabel elemGUID = (validRange) ? IFCLabel.CreateGUID() : IFCLabel.CreateGUID(element); IFCLabel elemName = NamingUtil.GetNameOverride(element, NamingUtil.CreateIFCName(exporterIFC, -1)); IFCLabel elemDesc = NamingUtil.GetDescriptionOverride(element, IFCLabel.Create()); IFCLabel elemObjectType = NamingUtil.GetObjectTypeOverride(element, objectType); IFCLabel elemId = NamingUtil.CreateIFCElementId(element); if (exportedAsWallWithAxis) { wallHnd = file.CreateWallStandardCase(elemGUID, ownerHistory, elemName, elemDesc, elemObjectType, localPlacement, elemId, prodRep); localWrapper.AddElement(wallHnd, setter, extraParams, true); OpeningUtil.CreateOpeningsIfNecessary(wallHnd, element, cutPairOpenings, exporterIFC, localPlacement, setter, localWrapper); if (exportedBodyDirectly) { OpeningUtil.CreateOpeningsIfNecessary(wallHnd, element, extraParams, exporterIFC, localPlacement, setter, localWrapper); } else { double scaledWidth = wallElement.Width * scale; ExporterIFCUtils.AddOpeningsToWall(exporterIFC, wallHnd, wallElement, scaledWidth, range, setter, localPlacement, localWrapper); } // export Base Quantities if (exporterIFC.ExportBaseQuantities) { CreateWallBaseQuantities(exporterIFC, wallElement, wallHnd, depth); } } else { wallHnd = file.CreateWall(elemGUID, ownerHistory, elemName, elemDesc, elemObjectType, localPlacement, elemId, prodRep); localWrapper.AddElement(wallHnd, setter, extraParams, true); // Only export one material for 2x2; for future versions, export the whole list. if (exporterIFC.ExportAs2x2 && (matId != ElementId.InvalidElementId)) { CategoryUtil.CreateMaterialAssociation(doc, exporterIFC, wallHnd, matId); } if (exportingInplaceOpenings) { double scaledWidth = wallElement.Width * scale; ExporterIFCUtils.AddOpeningsToWall(exporterIFC, wallHnd, wallElement, scaledWidth, range, setter, localPlacement, localWrapper); } if (exportedBodyDirectly) OpeningUtil.CreateOpeningsIfNecessary(wallHnd, element, extraParams, exporterIFC, localPlacement, setter, localWrapper); } ExporterIFCUtils.CreateGenericElementPropertySet(exporterIFC, element, localWrapper); ElementId wallLevelId = (validRange) ? setter.LevelId : ElementId.InvalidElementId; if (wallElement != null) ExporterIFCUtils.ExportHostObject(exporterIFC, wallElement, geometryElement, localWrapper); exporterIFC.RegisterSpaceBoundingElementHandle(wallHnd, element.Id, wallLevelId); return wallHnd; } } }
/// <summary> /// Creates or updates the IfcLocalPlacement associated with the current origin offset. /// </summary> /// <param name="exporterIFC">The exporter.</param> /// <param name="bbox">The bounding box.</param> /// <param name="ecData">The extrusion creation data which contains the local placement.</param> /// <param name="lpOrig">The local placement origin.</param> /// <param name="unscaledTrfOrig">The unscaled local placement origin.</param> public void CreateLocalPlacementFromOffset(ExporterIFC exporterIFC, BoundingBoxXYZ bbox, IFCExtrusionCreationData ecData, XYZ lpOrig, XYZ unscaledTrfOrig) { if (ecData == null) return; IFCAnyHandle localPlacement = ecData.GetLocalPlacement(); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(localPlacement)) { IFCFile file = exporterIFC.GetFile(); // If the BBox passes through (0,0, 0), or no bbox, do nothing. if (bbox == null || ((bbox.Min.X < MathUtil.Eps() && bbox.Max.X > -MathUtil.Eps()) && (bbox.Min.Y < MathUtil.Eps() && bbox.Max.Y > -MathUtil.Eps()) && (bbox.Min.Z < MathUtil.Eps() && bbox.Max.Z > -MathUtil.Eps()))) { if (!ecData.ReuseLocalPlacement) ecData.SetLocalPlacement(ExporterUtil.CopyLocalPlacement(file, localPlacement)); return; } if (!MathUtil.IsAlmostZero(unscaledTrfOrig.DotProduct(unscaledTrfOrig))) { if (!ecData.ReuseLocalPlacement) ecData.SetLocalPlacement(ExporterUtil.CreateLocalPlacement(file, localPlacement, lpOrig, null, null)); else { IFCAnyHandle relativePlacement = GeometryUtil.GetRelativePlacementFromLocalPlacement(localPlacement); if (IFCAnyHandleUtil.IsNullOrHasNoValue(relativePlacement)) { IFCAnyHandle newRelativePlacement = ExporterUtil.CreateAxis(file, lpOrig, null, null); GeometryUtil.SetRelativePlacement(localPlacement, newRelativePlacement); } else { IFCAnyHandle newOriginHnd = ExporterUtil.CreateCartesianPoint(file, lpOrig); IFCAnyHandleUtil.SetAttribute(relativePlacement, "Location", newOriginHnd); } } } else if (ecData.ForceOffset) ecData.SetLocalPlacement(ExporterUtil.CreateLocalPlacement(file, localPlacement, null)); else if (!ecData.ReuseLocalPlacement) ecData.SetLocalPlacement(ExporterUtil.CopyLocalPlacement(file, localPlacement)); } }
private static bool GetPolygonPlane(List<XYZ> polygon, out XYZ normal, out double dist, out double area) { normal = XYZ.Zero; dist = area = 0.0; int n = (null == polygon) ? 0 : polygon.Count; bool rc = (2 < n); if (3 == n) { XYZ a = polygon[0]; XYZ b = polygon[1]; XYZ c = polygon[2]; XYZ v = b - a; normal = v.CrossProduct(c - a); dist = normal.DotProduct(a); } else if (4 == n) { XYZ a = polygon[0]; XYZ b = polygon[1]; XYZ c = polygon[2]; XYZ d = polygon[3]; normal = new XYZ( (c.Y - a.Y) * (d.Z - b.Z) + (c.Z - a.Z) * (b.Y - d.Y), (c.Z - a.Z) * (d.X - b.X) + (c.X - a.X) * (b.Z - d.Z), (c.X - a.X) * (d.Y - b.Y) + (c.Y - a.Y) * (b.X - d.X)); dist = 0.25 * (normal.X * (a.X + b.X + c.X + d.X) + normal.Y * (a.Y + b.Y + c.Y + d.Y) + normal.Z * (a.Z + b.Z + c.Z + d.Z)); } else if (4 < n) { XYZ a; XYZ b = polygon[n - 2]; XYZ c = polygon[n - 1]; XYZ s = XYZ.Zero; for (int i = 0; i < n; ++i) { a = b; b = c; c = polygon[i]; normal = new XYZ( b.Y * (c.Z - a.Z), b.Z * (c.X - a.X), b.X * (c.Y - a.Y)); s += c; } dist = s.DotProduct(normal) / n; } if (rc) { double length = normal.GetLength(); rc = !normal.IsZeroLength(); if (rc) { normal /= length; dist /= length; area = 0.5 * length; } } return rc; }
/// <summary> /// Creates an extruded solid from a collection of curve loops and a thickness. /// </summary> /// <param name="exporterIFC">The ExporterIFC object.</param> /// <param name="profileName">The name of the extrusion profile.</param> /// <param name="origCurveLoops">The profile boundary curves.</param> /// <param name="plane">The plane of the boundary curves.</param> /// <param name="extrDirVec">The direction of the extrusion.</param> /// <param name="scaledExtrusionSize">The thickness of the extrusion, perpendicular to the plane.</param> /// <returns>The IfcExtrudedAreaSolid handle.</returns> /// <remarks>If the curveLoop plane normal is not the same as the plane direction, only tesellated boundaries are supported.</remarks> public static IFCAnyHandle CreateExtrudedSolidFromCurveLoop(ExporterIFC exporterIFC, string profileName, IList<CurveLoop> origCurveLoops, Plane plane, XYZ extrDirVec, double scaledExtrusionSize) { IFCAnyHandle extrudedSolidHnd = null; if (scaledExtrusionSize < MathUtil.Eps()) return extrudedSolidHnd; IFCFile file = exporterIFC.GetFile(); // we need to figure out the plane of the curve loops and modify the extrusion direction appropriately. // assumption: first curve loop defines the plane. int sz = origCurveLoops.Count; if (sz == 0) return extrudedSolidHnd; XYZ planeXDir = plane.XVec; XYZ planeYDir = plane.YVec; XYZ planeZDir = plane.Normal; XYZ planeOrig = plane.Origin; double slantFactor = Math.Abs(planeZDir.DotProduct(extrDirVec)); if (MathUtil.IsAlmostZero(slantFactor)) return extrudedSolidHnd; // Check that curve loops are valid. IList<CurveLoop> curveLoops = ExporterIFCUtils.ValidateCurveLoops(origCurveLoops, extrDirVec); if (curveLoops.Count == 0) return extrudedSolidHnd; scaledExtrusionSize /= slantFactor; IFCAnyHandle sweptArea = null; if (curveLoops.Count == 1) { sweptArea = CreateRectangleProfileDefIfPossible(exporterIFC, profileName, curveLoops[0], plane, extrDirVec); if (sweptArea == null) sweptArea = CreateCircleProfileDefIfPossible(exporterIFC, profileName, curveLoops[0], plane, extrDirVec); if (sweptArea == null) sweptArea = CreateIShapeProfileDefIfPossible(exporterIFC, profileName, curveLoops[0], plane, extrDirVec); } if (sweptArea == null) { IFCAnyHandle profileCurve = null; HashSet<IFCAnyHandle> innerCurves = new HashSet<IFCAnyHandle>(); // reorient curves if necessary: outer CCW, inners CW. foreach (CurveLoop curveLoop in curveLoops) { bool isCCW = false; try { isCCW = curveLoop.IsCounterclockwise(planeZDir); } catch { if (profileCurve == null) return null; else continue; } if (profileCurve == null) { if (!isCCW) curveLoop.Flip(); profileCurve = ExporterIFCUtils.CreateCurveFromCurveLoop(exporterIFC, curveLoop, plane, extrDirVec); if (IFCAnyHandleUtil.IsNullOrHasNoValue(profileCurve)) return extrudedSolidHnd; } else { if (isCCW) curveLoop.Flip(); IFCAnyHandle innerCurve = ExporterIFCUtils.CreateCurveFromCurveLoop(exporterIFC, curveLoop, plane, extrDirVec); if (!IFCAnyHandleUtil.IsNullOrHasNoValue(innerCurve)) innerCurves.Add(innerCurve); } } if (innerCurves.Count > 0) sweptArea = IFCInstanceExporter.CreateArbitraryProfileDefWithVoids(file, IFCProfileType.Area, profileName, profileCurve, innerCurves); else sweptArea = IFCInstanceExporter.CreateArbitraryClosedProfileDef(file, IFCProfileType.Area, profileName, profileCurve); } IList<double> relExtrusionDirList = new List<double>(); relExtrusionDirList.Add(extrDirVec.DotProduct(planeXDir)); relExtrusionDirList.Add(extrDirVec.DotProduct(planeYDir)); relExtrusionDirList.Add(extrDirVec.DotProduct(planeZDir)); XYZ scaledXDir = ExporterIFCUtils.TransformAndScaleVector(exporterIFC, planeXDir); XYZ scaledZDir = ExporterIFCUtils.TransformAndScaleVector(exporterIFC, planeZDir); XYZ scaledOrig = ExporterIFCUtils.TransformAndScalePoint(exporterIFC, planeOrig); IFCAnyHandle solidAxis = ExporterUtil.CreateAxis(file, scaledOrig, scaledZDir, scaledXDir); IFCAnyHandle extrusionDirection = ExporterUtil.CreateDirection(file, relExtrusionDirList); extrudedSolidHnd = IFCInstanceExporter.CreateExtrudedAreaSolid(file, sweptArea, solidAxis, extrusionDirection, scaledExtrusionSize); return extrudedSolidHnd; }
/// <summary> /// Main implementation to export walls. /// </summary> /// <param name="exporterIFC">The ExporterIFC object.</param> /// <param name="element">The element.</param> /// <param name="connectedWalls">Information about walls joined to this wall.</param> /// <param name="geometryElement">The geometry element.</param> /// <param name="origWrapper">The ProductWrapper.</param> /// <param name="overrideLevelId">The level id.</param> /// <param name="range">The range to be exported for the element.</param> /// <returns>The exported wall handle.</returns> public static IFCAnyHandle ExportWallBase(ExporterIFC exporterIFC, Element element, IList<IList<IFCConnectedWallData>> connectedWalls, GeometryElement geometryElement, ProductWrapper origWrapper, ElementId overrideLevelId, IFCRange range) { // Check cases where we choose not to export early. ElementId catId = CategoryUtil.GetSafeCategoryId(element); Wall wallElement = element as Wall; FamilyInstance famInstWallElem = element as FamilyInstance; FaceWall faceWall = element as FaceWall; bool exportingWallElement = (wallElement != null); bool exportingFamilyInstance = (famInstWallElem != null); bool exportingFaceWall = (faceWall != null); if (!exportingWallElement && !exportingFamilyInstance && !exportingFaceWall) return null; if (exportingWallElement && IsWallCompletelyClipped(wallElement, exporterIFC, range)) return null; IFCRange zSpan = null; double depth = 0.0; bool validRange = (range != null && !MathUtil.IsAlmostZero(range.Start - range.End)); bool exportParts = PartExporter.CanExportParts(element); if (exportParts && !PartExporter.CanExportElementInPartExport(element, validRange ? overrideLevelId : element.LevelId, validRange)) return null; IList<Solid> solids = new List<Solid>(); IList<Mesh> meshes = new List<Mesh>(); bool exportingInplaceOpenings = false; if (!exportParts) { if (exportingWallElement || exportingFaceWall) { GetSolidsAndMeshes(geometryElement, range, ref solids, ref meshes); if (solids.Count == 0 && meshes.Count == 0) return null; } else { GeometryElement geomElemToUse = GetGeometryFromInplaceWall(famInstWallElem); if (geomElemToUse != null) { exportingInplaceOpenings = true; } else { exportingInplaceOpenings = false; geomElemToUse = geometryElement; } Transform trf = Transform.Identity; if (geomElemToUse != geometryElement) trf = famInstWallElem.GetTransform(); SolidMeshGeometryInfo solidMeshCapsule = GeometryUtil.GetSplitSolidMeshGeometry(geomElemToUse, trf); solids = solidMeshCapsule.GetSolids(); meshes = solidMeshCapsule.GetMeshes(); } } IFCFile file = exporterIFC.GetFile(); using (IFCTransaction tr = new IFCTransaction(file)) { using (ProductWrapper localWrapper = ProductWrapper.Create(origWrapper)) { // get bounding box height so that we can subtract out pieces properly. // only for Wall, not FamilyInstance. if (exportingWallElement && geometryElement != null) { // There is a problem in the API where some walls with vertical structures are overreporting their height, // making it appear as if there are clipping problems on export. We will work around this by getting the // height directly from the solid(s). if (solids.Count > 0 && meshes.Count == 0) { zSpan = GetBoundingBoxOfSolids(solids); } else { BoundingBoxXYZ boundingBox = wallElement.get_BoundingBox(null); if (boundingBox != null) zSpan = GetBoundingBoxZRange(boundingBox); } if (zSpan == null) return null; // if we have a top clipping plane, modify depth accordingly. double bottomHeight = validRange ? Math.Max(zSpan.Start, range.Start) : zSpan.Start; double topHeight = validRange ? Math.Min(zSpan.End, range.End) : zSpan.End; depth = topHeight - bottomHeight; if (MathUtil.IsAlmostZero(depth)) return null; depth = UnitUtil.ScaleLength(depth); } else { zSpan = new IFCRange(); } Document doc = element.Document; double baseWallElevation = 0.0; ElementId baseLevelId = PlacementSetter.GetBaseLevelIdForElement(element); if (baseLevelId != ElementId.InvalidElementId) { Element baseLevel = doc.GetElement(baseLevelId); if (baseLevel is Level) baseWallElevation = (baseLevel as Level).Elevation; } IFCAnyHandle axisRep = null; IFCAnyHandle bodyRep = null; bool exportingAxis = false; Curve trimmedCurve = null; bool exportedAsWallWithAxis = false; bool exportedBodyDirectly = false; Curve centerCurve = GetWallAxis(wallElement); XYZ localXDir = new XYZ(1, 0, 0); XYZ localYDir = new XYZ(0, 1, 0); XYZ localZDir = new XYZ(0, 0, 1); XYZ localOrig = new XYZ(0, 0, 0); double eps = MathUtil.Eps(); if (centerCurve != null) { Curve baseCurve = GetWallAxisAtBaseHeight(wallElement); trimmedCurve = GetWallTrimmedCurve(wallElement, baseCurve); IFCRange curveBounds; XYZ oldOrig; GeometryUtil.GetAxisAndRangeFromCurve(trimmedCurve, out curveBounds, out localXDir, out oldOrig); // Move the curve to the bottom of the geometry or the bottom of the range, which is higher. if (baseCurve != null) localOrig = new XYZ(oldOrig.X, oldOrig.Y, validRange ? Math.Max(range.Start, zSpan.Start) : zSpan.Start); else localOrig = oldOrig; double dist = localOrig[2] - oldOrig[2]; if (!MathUtil.IsAlmostZero(dist)) { XYZ moveVec = new XYZ(0, 0, dist); trimmedCurve = GeometryUtil.MoveCurve(trimmedCurve, moveVec); } localYDir = localZDir.CrossProduct(localXDir); // ensure that X and Z axes are orthogonal. double xzDot = localZDir.DotProduct(localXDir); if (!MathUtil.IsAlmostZero(xzDot)) localXDir = localYDir.CrossProduct(localZDir); } else { BoundingBoxXYZ boundingBox = element.get_BoundingBox(null); if (boundingBox != null) { XYZ bBoxMin = boundingBox.Min; XYZ bBoxMax = boundingBox.Max; if (validRange) localOrig = new XYZ(bBoxMin.X, bBoxMin.Y, range.Start); else localOrig = boundingBox.Min; XYZ localXDirMax = null; Transform bTrf = boundingBox.Transform; XYZ localXDirMax1 = new XYZ(bBoxMax.X, localOrig.Y, localOrig.Z); localXDirMax1 = bTrf.OfPoint(localXDirMax1); XYZ localXDirMax2 = new XYZ(localOrig.X, bBoxMax.Y, localOrig.Z); localXDirMax2 = bTrf.OfPoint(localXDirMax2); if (localXDirMax1.DistanceTo(localOrig) >= localXDirMax2.DistanceTo(localOrig)) localXDirMax = localXDirMax1; else localXDirMax = localXDirMax2; localXDir = localXDirMax.Subtract(localOrig); localXDir = localXDir.Normalize(); localYDir = localZDir.CrossProduct(localXDir); // ensure that X and Z axes are orthogonal. double xzDot = localZDir.DotProduct(localXDir); if (!MathUtil.IsAlmostZero(xzDot)) localXDir = localYDir.CrossProduct(localZDir); } } IFCAnyHandle ownerHistory = exporterIFC.GetOwnerHistoryHandle(); Transform orientationTrf = Transform.Identity; orientationTrf.BasisX = localXDir; orientationTrf.BasisY = localYDir; orientationTrf.BasisZ = localZDir; orientationTrf.Origin = localOrig; double scaledFootprintArea = 0; double scaledLength = 0; using (PlacementSetter setter = PlacementSetter.Create(exporterIFC, element, null, orientationTrf, overrideLevelId)) { IFCAnyHandle localPlacement = setter.LocalPlacement; // The local coordinate system of the wall as defined by IFC for IfcWallStandardCase. Plane wallLCS = new Plane(localXDir, localYDir, localOrig); // project curve to XY plane. XYZ projDir = XYZ.BasisZ; // two representations: axis, body. { if (!exportParts && (centerCurve != null) && (GeometryUtil.CurveIsLineOrArc(centerCurve))) { exportingAxis = true; string identifierOpt = "Axis"; // IFC2x2 convention string representationTypeOpt = "Curve2D"; // IFC2x2 convention IFCGeometryInfo info = IFCGeometryInfo.CreateCurveGeometryInfo(exporterIFC, wallLCS, projDir, false); ExporterIFCUtils.CollectGeometryInfo(exporterIFC, info, trimmedCurve, XYZ.Zero, true); IList<IFCAnyHandle> axisItems = info.GetCurves(); if (axisItems.Count == 0) { exportingAxis = false; } else { HashSet<IFCAnyHandle> axisItemSet = new HashSet<IFCAnyHandle>(); foreach (IFCAnyHandle axisItem in axisItems) axisItemSet.Add(axisItem); IFCAnyHandle contextOfItemsAxis = exporterIFC.Get3DContextHandle("Axis"); axisRep = RepresentationUtil.CreateShapeRepresentation(exporterIFC, element, catId, contextOfItemsAxis, identifierOpt, representationTypeOpt, axisItemSet); } } } IList<IFCExtrusionData> cutPairOpenings = new List<IFCExtrusionData>(); if (!exportParts && exportingWallElement && exportingAxis && trimmedCurve != null) { bool isCompletelyClipped; bodyRep = TryToCreateAsExtrusion(exporterIFC, wallElement, connectedWalls, solids, meshes, baseWallElevation, catId, centerCurve, trimmedCurve, wallLCS, depth, zSpan, range, setter, out cutPairOpenings, out isCompletelyClipped, out scaledFootprintArea, out scaledLength); if (isCompletelyClipped) return null; if (!IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) exportedAsWallWithAxis = true; } using (IFCExtrusionCreationData extraParams = new IFCExtrusionCreationData()) { BodyData bodyData = null; if (!exportedAsWallWithAxis) { extraParams.PossibleExtrusionAxes = IFCExtrusionAxes.TryZ; // only allow vertical extrusions! extraParams.AreInnerRegionsOpenings = true; BodyExporterOptions bodyExporterOptions = new BodyExporterOptions(true); // Swept solids are not natively exported as part of CV2.0. // We have removed the UI toggle for this, so that it is by default false, but keep for possible future use. if (ExporterCacheManager.ExportOptionsCache.ExportAdvancedSweptSolids) bodyExporterOptions.TryToExportAsSweptSolid = true; ElementId overrideMaterialId = ElementId.InvalidElementId; if (exportingWallElement) overrideMaterialId = HostObjectExporter.GetFirstLayerMaterialId(wallElement); if (!exportParts) { if ((solids.Count > 0) || (meshes.Count > 0)) { bodyRep = BodyExporter.ExportBody(exporterIFC, element, catId, overrideMaterialId, solids, meshes, bodyExporterOptions, extraParams).RepresentationHnd; } else { IList<GeometryObject> geomElemList = new List<GeometryObject>(); geomElemList.Add(geometryElement); bodyData = BodyExporter.ExportBody(exporterIFC, element, catId, overrideMaterialId, geomElemList, bodyExporterOptions, extraParams); bodyRep = bodyData.RepresentationHnd; } if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) { extraParams.ClearOpenings(); return null; } } // We will be able to export as a IfcWallStandardCase as long as we have an axis curve. XYZ extrDirUsed = XYZ.Zero; if (extraParams.HasExtrusionDirection) { extrDirUsed = extraParams.ExtrusionDirection; if (MathUtil.IsAlmostEqual(Math.Abs(extrDirUsed[2]), 1.0)) { if ((solids.Count == 1) && (meshes.Count == 0)) exportedAsWallWithAxis = exportingAxis; exportedBodyDirectly = true; } } } IFCAnyHandle prodRep = null; if (!exportParts) { IList<IFCAnyHandle> representations = new List<IFCAnyHandle>(); if (exportingAxis) representations.Add(axisRep); representations.Add(bodyRep); IFCAnyHandle boundingBoxRep = null; if ((solids.Count > 0) || (meshes.Count > 0)) boundingBoxRep = BoundingBoxExporter.ExportBoundingBox(exporterIFC, solids, meshes, Transform.Identity); else boundingBoxRep = BoundingBoxExporter.ExportBoundingBox(exporterIFC, geometryElement, Transform.Identity); if (boundingBoxRep != null) representations.Add(boundingBoxRep); prodRep = IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, representations); } ElementId matId = ElementId.InvalidElementId; string objectType = NamingUtil.CreateIFCObjectName(exporterIFC, element); IFCAnyHandle wallHnd = null; string elemGUID = null; int subElementIndex = ExporterStateManager.GetCurrentRangeIndex(); if (subElementIndex == 0) elemGUID = GUIDUtil.CreateGUID(element); else if (subElementIndex <= ExporterStateManager.RangeIndexSetter.GetMaxStableGUIDs()) elemGUID = GUIDUtil.CreateSubElementGUID(element, subElementIndex + (int)IFCGenericSubElements.SplitInstanceStart - 1); else elemGUID = GUIDUtil.CreateGUID(); string elemName = NamingUtil.GetNameOverride(element, NamingUtil.GetIFCName(element)); string elemDesc = NamingUtil.GetDescriptionOverride(element, null); string elemObjectType = NamingUtil.GetObjectTypeOverride(element, objectType); string elemTag = NamingUtil.GetTagOverride(element, NamingUtil.CreateIFCElementId(element)); string ifcType = IFCValidateEntry.GetValidIFCType(element, null); // For Foundation and Retaining walls, allow exporting as IfcFooting instead. bool exportAsFooting = false; if (exportingWallElement) { WallType wallType = wallElement.WallType; if (wallType != null) { int wallFunction; if (ParameterUtil.GetIntValueFromElement(wallType, BuiltInParameter.FUNCTION_PARAM, out wallFunction) != null) { if (wallFunction == (int)WallFunction.Retaining || wallFunction == (int)WallFunction.Foundation) { // In this case, allow potential to export foundation and retaining walls as footing. string enumTypeValue = null; IFCExportType exportType = ExporterUtil.GetExportType(exporterIFC, wallElement, out enumTypeValue); if (exportType == IFCExportType.IfcFooting) exportAsFooting = true; } } } } if (exportedAsWallWithAxis) { if (exportAsFooting) { wallHnd = IFCInstanceExporter.CreateFooting(file, elemGUID, ownerHistory, elemName, elemDesc, elemObjectType, localPlacement, exportParts ? null : prodRep, elemTag, ifcType); } else { bool exportAsWall = exportParts; if (!exportAsWall) { // (For Reference View export) If the representation returned earlier is of type Tessellation, create IfcWall instead. foreach (IFCAnyHandle pRep in IFCAnyHandleUtil.GetRepresentations(prodRep)) { if (String.Compare(IFCAnyHandleUtil.GetRepresentationType(pRep), "Tessellation") == 0) { exportAsWall = true; break; } } } if (exportAsWall) { wallHnd = IFCInstanceExporter.CreateWall(file, elemGUID, ownerHistory, elemName, elemDesc, elemObjectType, localPlacement, null, elemTag, ifcType); } else { wallHnd = IFCInstanceExporter.CreateWallStandardCase(file, elemGUID, ownerHistory, elemName, elemDesc, elemObjectType, localPlacement, prodRep, elemTag, ifcType); } } if (exportParts) PartExporter.ExportHostPart(exporterIFC, element, wallHnd, localWrapper, setter, localPlacement, overrideLevelId); localWrapper.AddElement(element, wallHnd, setter, extraParams, true); if (!exportParts) { OpeningUtil.CreateOpeningsIfNecessary(wallHnd, element, cutPairOpenings, null, exporterIFC, localPlacement, setter, localWrapper); if (exportedBodyDirectly) { Transform offsetTransform = (bodyData != null) ? bodyData.OffsetTransform : Transform.Identity; OpeningUtil.CreateOpeningsIfNecessary(wallHnd, element, extraParams, offsetTransform, exporterIFC, localPlacement, setter, localWrapper); } else { double scaledWidth = UnitUtil.ScaleLength(wallElement.Width); OpeningUtil.AddOpeningsToElement(exporterIFC, wallHnd, wallElement, null, scaledWidth, range, setter, localPlacement, localWrapper); } } // export Base Quantities if (ExporterCacheManager.ExportOptionsCache.ExportBaseQuantities) { scaledFootprintArea = MathUtil.AreaIsAlmostZero(scaledFootprintArea) ? extraParams.ScaledArea : scaledFootprintArea; scaledLength = MathUtil.IsAlmostZero(scaledLength) ? extraParams.ScaledLength : scaledLength; PropertyUtil.CreateWallBaseQuantities(exporterIFC, wallElement, solids, meshes, wallHnd, scaledLength, depth, scaledFootprintArea); } } else { if (exportAsFooting) { wallHnd = IFCInstanceExporter.CreateFooting(file, elemGUID, ownerHistory, elemName, elemDesc, elemObjectType, localPlacement, exportParts ? null : prodRep, elemTag, ifcType); } else { wallHnd = IFCInstanceExporter.CreateWall(file, elemGUID, ownerHistory, elemName, elemDesc, elemObjectType, localPlacement, exportParts ? null : prodRep, elemTag, ifcType); } if (exportParts) PartExporter.ExportHostPart(exporterIFC, element, wallHnd, localWrapper, setter, localPlacement, overrideLevelId); localWrapper.AddElement(element, wallHnd, setter, extraParams, true); if (!exportParts) { // Only export one material for 2x2; for future versions, export the whole list. if (ExporterCacheManager.ExportOptionsCache.ExportAs2x2 || exportingFamilyInstance) { matId = BodyExporter.GetBestMaterialIdFromGeometryOrParameter(solids, meshes, element); if (matId != ElementId.InvalidElementId) CategoryUtil.CreateMaterialAssociation(exporterIFC, wallHnd, matId); } if (exportingInplaceOpenings) { OpeningUtil.AddOpeningsToElement(exporterIFC, wallHnd, famInstWallElem, null, 0.0, range, setter, localPlacement, localWrapper); } if (exportedBodyDirectly) { Transform offsetTransform = (bodyData != null) ? bodyData.OffsetTransform : Transform.Identity; OpeningUtil.CreateOpeningsIfNecessary(wallHnd, element, extraParams, offsetTransform, exporterIFC, localPlacement, setter, localWrapper); } } } ElementId wallLevelId = (validRange) ? setter.LevelId : ElementId.InvalidElementId; if ((exportingWallElement || exportingFaceWall) && !exportParts) { HostObject hostObject = null; if (exportingWallElement) hostObject = wallElement; else hostObject = faceWall; if (!ExporterCacheManager.ExportOptionsCache.ExportAs2x2 || exportedAsWallWithAxis) HostObjectExporter.ExportHostObjectMaterials(exporterIFC, hostObject, localWrapper.GetAnElement(), geometryElement, localWrapper, wallLevelId, Toolkit.IFCLayerSetDirection.Axis2, !exportedAsWallWithAxis); } ExportWallType(exporterIFC, localWrapper, wallHnd, element, matId, exportedAsWallWithAxis, exportAsFooting); SpaceBoundingElementUtil.RegisterSpaceBoundingElementHandle(exporterIFC, wallHnd, element.Id, wallLevelId); tr.Commit(); return wallHnd; } } } } }
public override Value Evaluate(FSharpList<Value> args) { var symbol = (FamilySymbol)((Value.Container)args[0]).Item; var curves = ((Value.List) args[1]).Item; IEnumerable<Tuple<Curve, XYZ>> data; if (args[2].IsList) { var targets = ((Value.List)args[2]).Item; if (curves.Count() != targets.Count()) throw new Exception("The number of curves and the number of up vectors must be the same."); //if we get a list of up vectors, then pair each //curve with a corresponding up vector data = curves.Zip(targets, (first, second) => new Tuple<Curve, XYZ>((Curve) ((Value.Container) first).Item, (XYZ) ((Value.Container) second).Item)); } else { //if we get a single up vector, then pair each //curve with that up vector data = curves.Select(x=>new Tuple<Curve, XYZ>((Curve)((Value.Container)x).Item, (XYZ)((Value.Container)args[2]).Item)); } var instData = new List<FamilyInstanceCreationData>(); int count = 0; foreach (var pair in data) { var curve = pair.Item1; var target = pair.Item2; //calculate the desired rotation //we do this by finding the angle between the z axis //and vector between the start of the beam and the target point //both projected onto the start plane of the beam. XYZ zAxis = new XYZ(0, 0, 1); XYZ yAxis = new XYZ(0, 1, 0); //flatten the beam line onto the XZ plane //using the start's z coordinate XYZ start = curve.get_EndPoint(0); XYZ end = curve.get_EndPoint(1); XYZ newEnd = new XYZ(end.X, end.Y, start.Z); //drop end point to plane ////use the x axis of the curve's transform ////as the normal of the start plane //XYZ planeNormal = (curve.get_EndPoint(0) - curve.get_EndPoint(1)).Normalize(); //catch the case where the end is directly above //the start, creating a normal with zero length //in that case, use the Z axis XYZ planeNormal = newEnd.IsAlmostEqualTo(start) ? zAxis : (newEnd - start).Normalize(); XYZ target_project = target - target.DotProduct(planeNormal)*planeNormal; XYZ z_project = zAxis - zAxis.DotProduct(planeNormal)*planeNormal; //double gamma = target_project.AngleTo(z_project); double gamma = target.AngleOnPlaneTo(zAxis.IsAlmostEqualTo(planeNormal) ? yAxis : zAxis, planeNormal); FamilyInstance instance = null; if (this.Elements.Count > count) { if (dynUtils.TryGetElement(this.Elements[count], out instance)) { if (instance.Symbol != symbol) instance.Symbol = symbol; //update the curve var locCurve = instance.Location as LocationCurve; locCurve.Curve = curve; } else { var beamData = new FamilyInstanceCreationData(curve, symbol, dynRevitSettings.DefaultLevel, StructuralType.Beam) { RotateAngle = gamma }; instData.Add(beamData); } } else { var beamData = new FamilyInstanceCreationData(curve, symbol, dynRevitSettings.DefaultLevel, StructuralType.Beam) { RotateAngle = gamma }; instData.Add(beamData); } count++; } //trim the elements collection foreach (var e in this.Elements.Skip(count)) { this.DeleteElement(e); } FSharpList<Value> results = FSharpList<Value>.Empty; if (instData.Any()) { var ids = dynRevitSettings.Doc.Document.Create.NewFamilyInstances2(instData); //add our batch-created instances ids' //to the elements collection ids.ToList().ForEach(x=>Elements.Add(x)); } //add all of the instances results = Elements.Aggregate(results, (current, id) => FSharpList<Value>.Cons(Value.NewContainer(dynRevitSettings.Doc.Document.GetElement(id)), current)); results.Reverse(); return Value.NewList(results); }
/// <summary> /// Main implementation to export walls. /// </summary> /// <param name="exporterIFC"> /// The ExporterIFC object. /// </param> /// <param name="element"> /// The element. /// </param> /// <param name="geometryElement"> /// The geometry element. /// </param> /// <param name="origWrapper"> /// The ProductWrapper. /// </param> /// <param name="overrideLevelId"> /// The level id. /// </param> /// <param name="range"> /// The range to be exported for the element. /// </param> /// <returns> /// The exported wall handle. /// </returns> public static IFCAnyHandle ExportWallBase(ExporterIFC exporterIFC, Element element, GeometryElement geometryElement, ProductWrapper origWrapper, ElementId overrideLevelId, IFCRange range) { IFCFile file = exporterIFC.GetFile(); using (IFCTransaction tr = new IFCTransaction(file)) { using (ProductWrapper localWrapper = ProductWrapper.Create(origWrapper)) { ElementId catId = CategoryUtil.GetSafeCategoryId(element); Wall wallElement = element as Wall; FamilyInstance famInstWallElem = element as FamilyInstance; FaceWall faceWall = element as FaceWall; if (wallElement == null && famInstWallElem == null && faceWall == null) return null; if (wallElement != null && IsWallCompletelyClipped(wallElement, exporterIFC, range)) return null; Document doc = element.Document; double scale = exporterIFC.LinearScale; double baseWallElevation = 0.0; ElementId baseLevelId = ExporterUtil.GetBaseLevelIdForElement(element); if (baseLevelId != ElementId.InvalidElementId) { Element baseLevel = doc.GetElement(baseLevelId); if (baseLevel is Level) baseWallElevation = (baseLevel as Level).Elevation; } IFCAnyHandle ownerHistory = exporterIFC.GetOwnerHistoryHandle(); IFCAnyHandle contextOfItemsAxis = exporterIFC.Get3DContextHandle("Axis"); IFCAnyHandle contextOfItemsBody = exporterIFC.Get3DContextHandle("Body"); IFCRange zSpan = new IFCRange(); double depth = 0.0; bool validRange = (range != null && !MathUtil.IsAlmostZero(range.Start - range.End)); bool exportParts = PartExporter.CanExportParts(element); if (exportParts && !PartExporter.CanExportElementInPartExport(element, validRange ? overrideLevelId : element.Level.Id, validRange)) return null; // get bounding box height so that we can subtract out pieces properly. // only for Wall, not FamilyInstance. if (wallElement != null && geometryElement != null) { BoundingBoxXYZ boundingBox = element.get_BoundingBox(null); if (boundingBox == null) return null; zSpan = new IFCRange(boundingBox.Min.Z, boundingBox.Max.Z); // if we have a top clipping plane, modify depth accordingly. double bottomHeight = validRange ? Math.Max(zSpan.Start, range.Start) : zSpan.Start; double topHeight = validRange ? Math.Min(zSpan.End, range.End) : zSpan.End; depth = topHeight - bottomHeight; if (MathUtil.IsAlmostZero(depth)) return null; depth *= scale; } IFCAnyHandle axisRep = null; IFCAnyHandle bodyRep = null; bool exportingAxis = false; Curve curve = null; bool exportedAsWallWithAxis = false; bool exportedBodyDirectly = false; bool exportingInplaceOpenings = false; Curve centerCurve = GetWallAxis(wallElement); XYZ localXDir = new XYZ(1, 0, 0); XYZ localYDir = new XYZ(0, 1, 0); XYZ localZDir = new XYZ(0, 0, 1); XYZ localOrig = new XYZ(0, 0, 0); double eps = MathUtil.Eps(); if (centerCurve != null) { Curve baseCurve = GetWallAxisAtBaseHeight(wallElement); curve = GetWallTrimmedCurve(wallElement, baseCurve); IFCRange curveBounds; XYZ oldOrig; GeometryUtil.GetAxisAndRangeFromCurve(curve, out curveBounds, out localXDir, out oldOrig); localOrig = oldOrig; if (baseCurve != null) { if (!validRange || (MathUtil.IsAlmostEqual(range.Start, zSpan.Start))) { XYZ newOrig = baseCurve.Evaluate(curveBounds.Start, false); if (!validRange && (zSpan.Start < newOrig[2] - eps)) localOrig = new XYZ(localOrig.X, localOrig.Y, zSpan.Start); else localOrig = new XYZ(localOrig.X, localOrig.Y, newOrig[2]); } else { localOrig = new XYZ(localOrig.X, localOrig.Y, range.Start); } } double dist = localOrig[2] - oldOrig[2]; if (!MathUtil.IsAlmostZero(dist)) { XYZ moveVec = new XYZ(0, 0, dist); curve = GeometryUtil.MoveCurve(curve, moveVec); } localYDir = localZDir.CrossProduct(localXDir); // ensure that X and Z axes are orthogonal. double xzDot = localZDir.DotProduct(localXDir); if (!MathUtil.IsAlmostZero(xzDot)) localXDir = localYDir.CrossProduct(localZDir); } else { BoundingBoxXYZ boundingBox = element.get_BoundingBox(null); if (boundingBox != null) { XYZ bBoxMin = boundingBox.Min; XYZ bBoxMax = boundingBox.Max; if (validRange) localOrig = new XYZ(bBoxMin.X, bBoxMin.Y, range.Start); else localOrig = boundingBox.Min; XYZ localXDirMax = null; Transform bTrf = boundingBox.Transform; XYZ localXDirMax1 = new XYZ(bBoxMax.X, localOrig.Y, localOrig.Z); localXDirMax1 = bTrf.OfPoint(localXDirMax1); XYZ localXDirMax2 = new XYZ(localOrig.X, bBoxMax.Y, localOrig.Z); localXDirMax2 = bTrf.OfPoint(localXDirMax2); if (localXDirMax1.DistanceTo(localOrig) >= localXDirMax2.DistanceTo(localOrig)) localXDirMax = localXDirMax1; else localXDirMax = localXDirMax2; localXDir = localXDirMax.Subtract(localOrig); localXDir = localXDir.Normalize(); localYDir = localZDir.CrossProduct(localXDir); // ensure that X and Z axes are orthogonal. double xzDot = localZDir.DotProduct(localXDir); if (!MathUtil.IsAlmostZero(xzDot)) localXDir = localYDir.CrossProduct(localZDir); } } Transform orientationTrf = Transform.Identity; orientationTrf.BasisX = localXDir; orientationTrf.BasisY = localYDir; orientationTrf.BasisZ = localZDir; orientationTrf.Origin = localOrig; if (overrideLevelId == ElementId.InvalidElementId) overrideLevelId = ExporterUtil.GetBaseLevelIdForElement(element); using (IFCPlacementSetter setter = IFCPlacementSetter.Create(exporterIFC, element, null, orientationTrf, overrideLevelId)) { IFCAnyHandle localPlacement = setter.GetPlacement(); Plane plane = new Plane(localXDir, localYDir, localOrig); // project curve to XY plane. XYZ projDir = XYZ.BasisZ; // two representations: axis, body. { if (!exportParts && (centerCurve != null) && (GeometryUtil.CurveIsLineOrArc(centerCurve))) { exportingAxis = true; string identifierOpt = "Axis"; // IFC2x2 convention string representationTypeOpt = "Curve2D"; // IFC2x2 convention IFCGeometryInfo info = IFCGeometryInfo.CreateCurveGeometryInfo(exporterIFC, plane, projDir, false); ExporterIFCUtils.CollectGeometryInfo(exporterIFC, info, curve, XYZ.Zero, true); IList<IFCAnyHandle> axisItems = info.GetCurves(); if (axisItems.Count == 0) { exportingAxis = false; } else { HashSet<IFCAnyHandle> axisItemSet = new HashSet<IFCAnyHandle>(); foreach (IFCAnyHandle axisItem in axisItems) axisItemSet.Add(axisItem); axisRep = RepresentationUtil.CreateShapeRepresentation(exporterIFC, element, catId, contextOfItemsAxis, identifierOpt, representationTypeOpt, axisItemSet); } } } IList<IFCExtrusionData> cutPairOpenings = new List<IFCExtrusionData>(); Document document = element.Document; IList<Solid> solids = new List<Solid>(); IList<Mesh> meshes = new List<Mesh>(); if (!exportParts && wallElement != null && exportingAxis && curve != null) { SolidMeshGeometryInfo solidMeshInfo = (range == null) ? GeometryUtil.GetSplitSolidMeshGeometry(geometryElement) : GeometryUtil.GetSplitClippedSolidMeshGeometry(geometryElement, range); solids = solidMeshInfo.GetSolids(); meshes = solidMeshInfo.GetMeshes(); if (solids.Count == 0 && meshes.Count == 0) return null; bool useNewCode = false; if (useNewCode && solids.Count == 1 && meshes.Count == 0) { bool completelyClipped; bodyRep = ExtrusionExporter.CreateExtrusionWithClipping(exporterIFC, wallElement, catId, solids[0], plane, projDir, range, out completelyClipped); if (completelyClipped) return null; if (!IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) { exportedAsWallWithAxis = true; exportedBodyDirectly = true; } else { exportedAsWallWithAxis = false; exportedBodyDirectly = false; } } if (!exportedAsWallWithAxis) { // Fallback - use native routines to try to export wall. bool isCompletelyClipped; bodyRep = FallbackTryToCreateAsExtrusion(exporterIFC, wallElement, solidMeshInfo, baseWallElevation, catId, curve, plane, depth, zSpan, range, setter, out cutPairOpenings, out isCompletelyClipped); if (isCompletelyClipped) return null; if (!IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) exportedAsWallWithAxis = true; } } using (IFCExtrusionCreationData extraParams = new IFCExtrusionCreationData()) { BodyData bodyData = null; if (!exportedAsWallWithAxis) { SolidMeshGeometryInfo solidMeshCapsule = null; if (wallElement != null || faceWall != null) { if (validRange) { solidMeshCapsule = GeometryUtil.GetSplitClippedSolidMeshGeometry(geometryElement, range); } else { solidMeshCapsule = GeometryUtil.GetSplitSolidMeshGeometry(geometryElement); } if (solidMeshCapsule.SolidsCount() == 0 && solidMeshCapsule.MeshesCount() == 0) { return null; } } else { GeometryElement geomElemToUse = GetGeometryFromInplaceWall(famInstWallElem); if (geomElemToUse != null) { exportingInplaceOpenings = true; } else { exportingInplaceOpenings = false; geomElemToUse = geometryElement; } Transform trf = Transform.Identity; if (geomElemToUse != geometryElement) trf = famInstWallElem.GetTransform(); solidMeshCapsule = GeometryUtil.GetSplitSolidMeshGeometry(geomElemToUse, trf); } solids = solidMeshCapsule.GetSolids(); meshes = solidMeshCapsule.GetMeshes(); extraParams.PossibleExtrusionAxes = IFCExtrusionAxes.TryZ; // only allow vertical extrusions! extraParams.AreInnerRegionsOpenings = true; BodyExporterOptions bodyExporterOptions = new BodyExporterOptions(true); // Swept solids are not natively exported as part of CV2.0. // We have removed the UI toggle for this, so that it is by default false, but keep for possible future use. if (ExporterCacheManager.ExportOptionsCache.ExportAdvancedSweptSolids) bodyExporterOptions.TryToExportAsSweptSolid = true; ElementId overrideMaterialId = ElementId.InvalidElementId; if (wallElement != null) overrideMaterialId = HostObjectExporter.GetFirstLayerMaterialId(wallElement); if (!exportParts) { if ((solids.Count > 0) || (meshes.Count > 0)) { bodyRep = BodyExporter.ExportBody(exporterIFC, element, catId, overrideMaterialId, solids, meshes, bodyExporterOptions, extraParams).RepresentationHnd; } else { IList<GeometryObject> geomElemList = new List<GeometryObject>(); geomElemList.Add(geometryElement); bodyData = BodyExporter.ExportBody(exporterIFC, element, catId, overrideMaterialId, geomElemList, bodyExporterOptions, extraParams); bodyRep = bodyData.RepresentationHnd; } if (IFCAnyHandleUtil.IsNullOrHasNoValue(bodyRep)) { extraParams.ClearOpenings(); return null; } } // We will be able to export as a IfcWallStandardCase as long as we have an axis curve. XYZ extrDirUsed = XYZ.Zero; if (extraParams.HasExtrusionDirection) { extrDirUsed = extraParams.ExtrusionDirection; if (MathUtil.IsAlmostEqual(Math.Abs(extrDirUsed[2]), 1.0)) { if ((solids.Count == 1) && (meshes.Count == 0)) exportedAsWallWithAxis = exportingAxis; exportedBodyDirectly = true; } } } IFCAnyHandle prodRep = null; if (!exportParts) { IList<IFCAnyHandle> representations = new List<IFCAnyHandle>(); if (exportingAxis) representations.Add(axisRep); representations.Add(bodyRep); IFCAnyHandle boundingBoxRep = null; if ((solids.Count > 0) || (meshes.Count > 0)) boundingBoxRep = BoundingBoxExporter.ExportBoundingBox(exporterIFC, solids, meshes, Transform.Identity); else boundingBoxRep = BoundingBoxExporter.ExportBoundingBox(exporterIFC, geometryElement, Transform.Identity); if (boundingBoxRep != null) representations.Add(boundingBoxRep); prodRep = IFCInstanceExporter.CreateProductDefinitionShape(file, null, null, representations); } ElementId matId = ElementId.InvalidElementId; string objectType = NamingUtil.CreateIFCObjectName(exporterIFC, element); IFCAnyHandle wallHnd = null; string elemGUID = (validRange) ? GUIDUtil.CreateGUID() : GUIDUtil.CreateGUID(element); string elemName = NamingUtil.GetNameOverride(element, NamingUtil.GetIFCName(element)); string elemDesc = NamingUtil.GetDescriptionOverride(element, null); string elemObjectType = NamingUtil.GetObjectTypeOverride(element, objectType); string elemTag = NamingUtil.GetTagOverride(element, NamingUtil.CreateIFCElementId(element)); // For Foundation and Retaining walls, allow exporting as IfcFooting instead. bool exportAsFooting = false; string enumTypeValue = null; if (wallElement != null) { WallType wallType = wallElement.WallType; if (wallType != null) { int wallFunction; if (ParameterUtil.GetIntValueFromElement(wallType, BuiltInParameter.FUNCTION_PARAM, out wallFunction) != null) { if (wallFunction == (int)WallFunction.Retaining || wallFunction == (int)WallFunction.Foundation) { // In this case, allow potential to export foundation and retaining walls as footing. IFCExportType exportType = ExporterUtil.GetExportType(exporterIFC, wallElement, out enumTypeValue); if (exportType == IFCExportType.ExportFooting) exportAsFooting = true; } } } } if (exportedAsWallWithAxis) { if (exportAsFooting) { Toolkit.IFCFootingType footingType = FootingExporter.GetIFCFootingType(element, enumTypeValue); wallHnd = IFCInstanceExporter.CreateFooting(file, elemGUID, ownerHistory, elemName, elemDesc, elemObjectType, localPlacement, exportParts ? null : prodRep, elemTag, footingType); } else if (exportParts) { wallHnd = IFCInstanceExporter.CreateWall(file, elemGUID, ownerHistory, elemName, elemDesc, elemObjectType, localPlacement, null, elemTag); } else { wallHnd = IFCInstanceExporter.CreateWallStandardCase(file, elemGUID, ownerHistory, elemName, elemDesc, elemObjectType, localPlacement, prodRep, elemTag); } if (exportParts) PartExporter.ExportHostPart(exporterIFC, element, wallHnd, localWrapper, setter, localPlacement, overrideLevelId); localWrapper.AddElement(element, wallHnd, setter, extraParams, true); if (!exportParts) { OpeningUtil.CreateOpeningsIfNecessary(wallHnd, element, cutPairOpenings, null, exporterIFC, localPlacement, setter, localWrapper); if (exportedBodyDirectly) { Transform offsetTransform = (bodyData != null) ? bodyData.OffsetTransform : Transform.Identity; OpeningUtil.CreateOpeningsIfNecessary(wallHnd, element, extraParams, offsetTransform, exporterIFC, localPlacement, setter, localWrapper); } else { ICollection<IFCAnyHandle> beforeOpenings = localWrapper.GetAllObjects(); double scaledWidth = wallElement.Width * scale; ExporterIFCUtils.AddOpeningsToElement(exporterIFC, wallHnd, wallElement, scaledWidth, range, setter, localPlacement, localWrapper.ToNative()); ICollection<IFCAnyHandle> afterOpenings = localWrapper.GetAllObjects(); if (beforeOpenings.Count != afterOpenings.Count) { foreach (IFCAnyHandle before in beforeOpenings) afterOpenings.Remove(before); foreach (IFCAnyHandle potentiallyBadOpening in afterOpenings) { PotentiallyCorrectOpeningOrientationAndOpeningType(potentiallyBadOpening, localPlacement, scaledWidth); } } } } // export Base Quantities if (ExporterCacheManager.ExportOptionsCache.ExportBaseQuantities) { CreateWallBaseQuantities(exporterIFC, wallElement, wallHnd, depth); } } else { if (exportAsFooting) { Toolkit.IFCFootingType footingType = FootingExporter.GetIFCFootingType(element, enumTypeValue); wallHnd = IFCInstanceExporter.CreateFooting(file, elemGUID, ownerHistory, elemName, elemDesc, elemObjectType, localPlacement, exportParts ? null : prodRep, elemTag, footingType); } else { wallHnd = IFCInstanceExporter.CreateWall(file, elemGUID, ownerHistory, elemName, elemDesc, elemObjectType, localPlacement, exportParts ? null : prodRep, elemTag); } if (exportParts) PartExporter.ExportHostPart(exporterIFC, element, wallHnd, localWrapper, setter, localPlacement, overrideLevelId); localWrapper.AddElement(element, wallHnd, setter, extraParams, true); if (!exportParts) { // Only export one material for 2x2; for future versions, export the whole list. if (exporterIFC.ExportAs2x2 || famInstWallElem != null) { matId = BodyExporter.GetBestMaterialIdFromGeometryOrParameter(solids, meshes, element); if (matId != ElementId.InvalidElementId) CategoryUtil.CreateMaterialAssociation(exporterIFC, wallHnd, matId); } if (exportingInplaceOpenings) { ExporterIFCUtils.AddOpeningsToElement(exporterIFC, wallHnd, famInstWallElem, 0.0, range, setter, localPlacement, localWrapper.ToNative()); } if (exportedBodyDirectly) { Transform offsetTransform = (bodyData != null) ? bodyData.OffsetTransform : Transform.Identity; OpeningUtil.CreateOpeningsIfNecessary(wallHnd, element, extraParams, offsetTransform, exporterIFC, localPlacement, setter, localWrapper); } } } ElementId wallLevelId = (validRange) ? setter.LevelId : ElementId.InvalidElementId; if ((wallElement != null || faceWall != null) && !exportParts) { HostObject hostObject = null; if (wallElement != null) hostObject = wallElement; else hostObject = faceWall; if (!exporterIFC.ExportAs2x2 || exportedAsWallWithAxis) HostObjectExporter.ExportHostObjectMaterials(exporterIFC, hostObject, localWrapper.GetAnElement(), geometryElement, localWrapper, wallLevelId, Toolkit.IFCLayerSetDirection.Axis2, !exportedAsWallWithAxis); } ExportWallType(exporterIFC, localWrapper, wallHnd, element, matId, exportedAsWallWithAxis, exportAsFooting); exporterIFC.RegisterSpaceBoundingElementHandle(wallHnd, element.Id, wallLevelId); tr.Commit(); return wallHnd; } } } } }
/* /// <summary> /// Return the average of a list of values. /// Prerequisite: the underlying class T must supply /// operator*(double) and operator+(const T &). /// </summary> T Average<T>( List<T> a ) { T result; bool first = true; foreach( T x in a ) { if( first ) { result = x; } else { result += x; } } return result * ( 1.0 / a.Count ); } XYZ Sum( List<XYZ> a ) { XYZ sum = XYZ.Zero; foreach( XYZ x in a ) { sum += x; } return sum; } XYZ Average( List<XYZ> a ) { return Sum( a ) * (1.0 / a.Count); } XYZ TriangleCenter( List<XYZ> pts ) { Debug.Assert( 3 == pts.Count, "expected three points in triangle" ); return Average( pts ); } */ /// <summary> /// Return the plane properties of a given polygon, /// i.e. the plane normal, area, and its distance /// from the origin. Cf. also GetSignedPolygonArea. /// </summary> internal static bool GetPolygonPlane( List<XYZ> polygon, out XYZ normal, out double dist, out double area) { normal = XYZ.Zero; dist = area = 0.0; int n = ( null == polygon ) ? 0 : polygon.Count; bool rc = ( 2 < n ); if( 3 == n ) { // the general case returns a wrong result for the triangle // ((-1 -1 -1) (1 -1 -1) (-1 -1 1)), so implement specific // code for triangle: XYZ a = polygon[0]; XYZ b = polygon[1]; XYZ c = polygon[2]; XYZ v = b - a; normal = v.CrossProduct( c - a ); dist = normal.DotProduct( a ); } else if( 4 == n ) { // more efficient code for 4-sided polygons XYZ a = polygon[0]; XYZ b = polygon[1]; XYZ c = polygon[2]; XYZ d = polygon[3]; normal = new XYZ( ( c.Y - a.Y ) * ( d.Z - b.Z ) + ( c.Z - a.Z ) * ( b.Y - d.Y ), ( c.Z - a.Z ) * ( d.X - b.X ) + ( c.X - a.X ) * ( b.Z - d.Z ), ( c.X - a.X ) * ( d.Y - b.Y ) + ( c.Y - a.Y ) * ( b.X - d.X ) ); dist = 0.25 * ( normal.X * ( a.X + b.X + c.X + d.X ) + normal.Y * ( a.Y + b.Y + c.Y + d.Y ) + normal.Z * ( a.Z + b.Z + c.Z + d.Z ) ); } else if( 4 < n ) { // general case for n-sided polygons XYZ a; XYZ b = polygon[n - 2]; XYZ c = polygon[n - 1]; XYZ s = XYZ.Zero; for( int i = 0; i < n; ++i ) { a = b; b = c; c = polygon[i]; normal = new XYZ( normal.X + b.Y * ( c.Z - a.Z ), normal.Y + b.Z * ( c.X - a.X ), normal.Z + b.X * ( c.Y - a.Y ) ); s += c; } dist = s.DotProduct( normal ) / n; } if( rc ) { // the polygon area is half of the length // of the non-normalized normal vector of the plane: double length = normal.GetLength(); rc = !Util.IsZero( length ); Debug.Assert( rc ); if( rc ) { normal /= length; dist /= length; area = 0.5 * length; } } return rc; }
/// <summary> /// Offsets an arc along the offset direction from the point on the arc. /// </summary> /// <param name="arc">The arc.</param> /// <param name="offsetPntOnArc">The point on the arc.</param> /// <param name="offset">The offset vector.</param> /// <returns>The offset Arc.</returns> private static Arc OffsetArc(Arc arc, XYZ offsetPntOnArc, XYZ offset) { if (arc == null || offset == null) throw new ArgumentNullException(); if (offset.IsZeroLength()) return arc; XYZ axis = arc.Normal.Normalize(); XYZ offsetAlongAxis = axis.Multiply(offset.DotProduct(axis)); XYZ offsetOrthAxis = offset - offsetAlongAxis; XYZ offsetPntToCenter = (arc.Center - offsetPntOnArc).Normalize(); double signedOffsetLengthTowardCenter = offsetOrthAxis.DotProduct(offsetPntToCenter); double newRadius = arc.Radius - signedOffsetLengthTowardCenter; // signedOffsetLengthTowardCenter > 0, minus, < 0, add Arc offsetArc = Arc.Create(arc.Center, newRadius, arc.GetEndParameter(0), arc.GetEndParameter(1), arc.XDirection, arc.YDirection); offsetArc = GeometryUtil.MoveCurve(offsetArc, offsetAlongAxis) as Arc; return offsetArc; }
/// <summary> /// Creates an extruded solid from a collection of curve loops and a thickness. /// </summary> /// <param name="exporterIFC">The ExporterIFC object.</param> /// <param name="profileName">The name of the extrusion profile.</param> /// <param name="origCurveLoops">The profile boundary curves.</param> /// <param name="plane">The plane of the boundary curves.</param> /// <param name="extrDirVec">The direction of the extrusion.</param> /// <param name="scaledExtrusionSize">The thickness of the extrusion, perpendicular to the plane.</param> /// <returns>The IfcExtrudedAreaSolid handle.</returns> /// <remarks>If the curveLoop plane normal is not the same as the plane direction, only tesellated boundaries are supported.</remarks> public static IFCAnyHandle CreateExtrudedSolidFromCurveLoop(ExporterIFC exporterIFC, string profileName, IList<CurveLoop> origCurveLoops, Plane plane, XYZ extrDirVec, double scaledExtrusionSize) { IFCAnyHandle extrudedSolidHnd = null; if (scaledExtrusionSize < MathUtil.Eps()) return extrudedSolidHnd; IFCFile file = exporterIFC.GetFile(); // we need to figure out the plane of the curve loops and modify the extrusion direction appropriately. // assumption: first curve loop defines the plane. int sz = origCurveLoops.Count; if (sz == 0) return extrudedSolidHnd; XYZ planeXDir = plane.XVec; XYZ planeYDir = plane.YVec; XYZ planeZDir = plane.Normal; XYZ planeOrig = plane.Origin; double slantFactor = Math.Abs(planeZDir.DotProduct(extrDirVec)); if (MathUtil.IsAlmostZero(slantFactor)) return extrudedSolidHnd; // Reduce the number of line segments in the curveloops from highly tessellated polylines, if applicable. IList<CurveLoop> curveLoops = CoarsenCurveLoops(origCurveLoops); // Check that curve loops are valid. curveLoops = ExporterIFCUtils.ValidateCurveLoops(curveLoops, extrDirVec); if (curveLoops.Count == 0) return extrudedSolidHnd; scaledExtrusionSize /= slantFactor; IFCAnyHandle sweptArea = CreateSweptArea(exporterIFC, profileName, curveLoops, plane, extrDirVec); if (IFCAnyHandleUtil.IsNullOrHasNoValue(sweptArea)) return extrudedSolidHnd; IList<double> relExtrusionDirList = new List<double>(); relExtrusionDirList.Add(extrDirVec.DotProduct(planeXDir)); relExtrusionDirList.Add(extrDirVec.DotProduct(planeYDir)); relExtrusionDirList.Add(extrDirVec.DotProduct(planeZDir)); XYZ scaledXDir = ExporterIFCUtils.TransformAndScaleVector(exporterIFC, planeXDir); XYZ scaledZDir = ExporterIFCUtils.TransformAndScaleVector(exporterIFC, planeZDir); XYZ scaledOrig = ExporterIFCUtils.TransformAndScalePoint(exporterIFC, planeOrig); IFCAnyHandle solidAxis = ExporterUtil.CreateAxis(file, scaledOrig, scaledZDir, scaledXDir); IFCAnyHandle extrusionDirection = ExporterUtil.CreateDirection(file, relExtrusionDirList); extrudedSolidHnd = IFCInstanceExporter.CreateExtrudedAreaSolid(file, sweptArea, solidAxis, extrusionDirection, scaledExtrusionSize); return extrudedSolidHnd; }
/// <summary> /// Determine the elevation boundary profile /// polygons of the exterior vertical planar /// face of the given wall solid. /// </summary> /// <param name="polygons">Return polygonal boundary /// loops of exterior vertical planar face, i.e. /// profile of wall elevation incl. holes</param> /// <param name="solid">Input solid</param> /// <param name="w">Vector pointing along /// wall centre line</param> /// <param name="w">Vector pointing towards /// exterior wall face</param> /// <returns>False if no exterior vertical /// planar face was found, else true</returns> static bool GetProfile( List<List<XYZ>> polygons, Solid solid, XYZ v, XYZ w) { double d, dmax = 0; PlanarFace outermost = null; FaceArray faces = solid.Faces; foreach( Face f in faces ) { PlanarFace pf = f as PlanarFace; if( null != pf && Util.IsVertical( pf ) && Util.IsZero( v.DotProduct( pf.Normal ) ) ) { d = pf.Origin.DotProduct( w ); if( ( null == outermost ) || ( dmax < d ) ) { outermost = pf; dmax = d; } } } if( null != outermost ) { XYZ voffset = _offset * w; XYZ p, q = XYZ.Zero; bool first; int i, n; EdgeArrayArray loops = outermost.EdgeLoops; foreach( EdgeArray loop in loops ) { List<XYZ> vertices = new List<XYZ>(); first = true; foreach( Edge e in loop ) { IList<XYZ> points = e.Tessellate(); p = points[0]; if( !first ) { Debug.Assert( p.IsAlmostEqualTo( q ), "expected subsequent start point" + " to equal previous end point" ); } n = points.Count; q = points[n - 1]; for( i = 0; i < n - 1; ++i ) { XYZ a = points[i]; a += voffset; vertices.Add( a ); } } q += voffset; Debug.Assert( q.IsAlmostEqualTo( vertices[0] ), "expected last end point to equal" + " first start point" ); polygons.Add( vertices ); } } return null != outermost; }
/// <summary> /// Returns an integer to indicate if two vectors are parallel, antiparallel or not. /// </summary> /// <param name="a">The one vector.</param> /// <param name="b">The other vector.</param> /// <returns>1 parallel, -1 antiparallel, 0 not parallel.</returns> public static int VectorsAreParallel2(XYZ a, XYZ b) { if (a == null || b == null) return 0; double aa, bb, ab; double epsSq = Eps() * Eps(); aa = a.DotProduct(a); bb = b.DotProduct(b); if (aa < epsSq || bb < epsSq) return 0; ab = a.DotProduct(b); double cosAngleSq = (ab / aa) * (ab / bb); if (cosAngleSq < 1.0 - AngleEps() * AngleEps()) return 0; return ab > 0 ? 1 : -1; }
/// <summary> /// Checks if two vectors are orthogonal or not. /// </summary> /// <param name="a">The one vector.</param> /// <param name="b">The other vector.</param> /// <returns>True if they are orthogonal, false if not.</returns> public static bool VectorsAreOrthogonal(XYZ a, XYZ b) { if (a == null || b == null) return false; if (a.IsAlmostEqualTo(XYZ.Zero) || b.IsAlmostEqualTo(XYZ.Zero)) return true; double ab = a.DotProduct(b); double aa = a.DotProduct(a); double bb = b.DotProduct(b); return (ab * ab < aa * AngleEps() * bb * AngleEps()) ? true : false; }