protected override void Process(IFCAnyHandle ifcCurve) { base.Process(ifcCurve); IList <IFCAnyHandle> points = IFCAnyHandleUtil.GetAggregateInstanceAttribute <List <IFCAnyHandle> >(ifcCurve, "Points"); int numPoints = points.Count; if (numPoints < 2) { string msg = "IfcPolyLine had " + numPoints + ", expected at least 2, ignoring"; Importer.TheLog.LogError(Id, msg, false); return; } IList <XYZ> pointXYZs = new List <XYZ>(); foreach (IFCAnyHandle point in points) { XYZ pointXYZ = IFCPoint.ProcessScaledLengthIFCCartesianPoint(point); pointXYZs.Add(pointXYZ); } if (pointXYZs.Count != numPoints) { Importer.TheLog.LogError(Id, "Some of the IFC points cannot be converted to Revit points", true); } CurveLoop = IFCGeometryUtil.CreatePolyCurveLoop(pointXYZs, points, Id, false); Curve = IFCGeometryUtil.CreateCurveFromPolyCurveLoop(CurveLoop, pointXYZs); }
private void CreateBoxShape(IFCImportShapeEditScope shapeEditScope, Transform scaledLcs) { using (IFCImportShapeEditScope.IFCContainingRepresentationSetter repSetter = new IFCImportShapeEditScope.IFCContainingRepresentationSetter(shapeEditScope, this)) { // Get the material and graphics style based in the "Box" sub-category of Generic Models. // We will create the sub-category if this is our first time trying to use it. // Note that all bounding boxes are controlled by a sub-category of Generic Models. We may revisit that decision later. // Note that we hard-wire the identifier to "Box" because older files may have bounding box items in an obsolete representation. SolidOptions solidOptions = null; Category bboxCategory = IFCCategoryUtil.GetSubCategoryForRepresentation(shapeEditScope.Document, Id, IFCRepresentationIdentifier.Box); if (bboxCategory != null) { ElementId materialId = (bboxCategory.Material == null) ? ElementId.InvalidElementId : bboxCategory.Material.Id; GraphicsStyle graphicsStyle = bboxCategory.GetGraphicsStyle(GraphicsStyleType.Projection); ElementId gstyleId = (graphicsStyle == null) ? ElementId.InvalidElementId : graphicsStyle.Id; solidOptions = new SolidOptions(materialId, gstyleId); } Solid bboxSolid = IFCGeometryUtil.CreateSolidFromBoundingBox(scaledLcs, BoundingBox, solidOptions); if (bboxSolid != null) { IFCSolidInfo bboxSolidInfo = IFCSolidInfo.Create(Id, bboxSolid); shapeEditScope.AddGeometry(bboxSolidInfo); } } return; }
private IList <CurveLoop> GetTransformedCurveLoopsFromSimpleProfile(IFCSimpleProfile simpleSweptArea, Transform unscaledLcs, Transform scaledLcs) { IList <CurveLoop> loops = new List <CurveLoop>(); // It is legal for simpleSweptArea.Position to be null, for example for IfcArbitraryClosedProfileDef. Transform unscaledSweptAreaPosition = (simpleSweptArea.Position == null) ? unscaledLcs : unscaledLcs.Multiply(simpleSweptArea.Position); Transform scaledSweptAreaPosition = (simpleSweptArea.Position == null) ? scaledLcs : scaledLcs.Multiply(simpleSweptArea.Position); CurveLoop currLoop = simpleSweptArea.OuterCurve; if (currLoop == null || currLoop.Count() == 0) { Importer.TheLog.LogError(simpleSweptArea.Id, "No outer curve loop for profile, ignoring.", false); return(null); } loops.Add(IFCGeometryUtil.CreateTransformed(currLoop, Id, unscaledSweptAreaPosition, scaledSweptAreaPosition)); if (simpleSweptArea.InnerCurves != null) { foreach (CurveLoop innerCurveLoop in simpleSweptArea.InnerCurves) { loops.Add(IFCGeometryUtil.CreateTransformed(innerCurveLoop, Id, unscaledSweptAreaPosition, scaledSweptAreaPosition)); } } return(loops); }
protected override void Process(IFCAnyHandle ifcCurve) { base.Process(ifcCurve); KnotMultiplicities = IFCAnyHandleUtil.GetAggregateIntAttribute <List <int> >(ifcCurve, "KnotMultiplicities"); Knots = IFCAnyHandleUtil.GetAggregateDoubleAttribute <List <double> >(ifcCurve, "Knots"); if (KnotMultiplicities == null || Knots == null) { Importer.TheLog.LogError(ifcCurve.StepId, "Cannot find the KnotMultiplicities or Knots attribute of this IfcBSplineCurveWithKnots", true); } if (KnotMultiplicities.Count != Knots.Count) { Importer.TheLog.LogError(ifcCurve.StepId, "The number of knots and knot multiplicities are not the same", true); } IList <double> revitKnots = IFCGeometryUtil.ConvertIFCKnotsToRevitKnots(KnotMultiplicities, Knots); Curve nurbsSpline = NurbSpline.CreateCurve(Degree, revitKnots, ControlPointsList); SetCurve(nurbsSpline); if (nurbsSpline == null) { Importer.TheLog.LogWarning(ifcCurve.StepId, "Cannot get the curve representation of this IfcCurve", false); } }
/// <summary> /// Return geometry for a particular representation item. /// </summary> /// <param name="shapeEditScope">The geometry creation scope.</param> /// <param name="unscaledLcs">Local coordinate system for the geometry, without scale.</param> /// <param name="scaledLcs">Local coordinate system for the geometry, including scale, potentially non-uniform.</param> /// <param name="guid">The guid of an element for which represntation is being created.</param> /// <returns>Zero or more created geometries.</returns> protected override IList <GeometryObject> CreateGeometryInternal( IFCImportShapeEditScope shapeEditScope, Transform unscaledLcs, Transform scaledLcs, string guid) { Transform unscaledSweptDiskPosition = (unscaledLcs == null) ? Transform.Identity : unscaledLcs; Transform scaledSweptDiskPosition = (scaledLcs == null) ? Transform.Identity : scaledLcs; CurveLoop trimmedDirectrix = IFCGeometryUtil.TrimCurveLoop(Id, Directrix, StartParameter, EndParameter); if (trimmedDirectrix == null) { return(null); } CurveLoop trimmedDirectrixInWCS = IFCGeometryUtil.CreateTransformed(trimmedDirectrix, Id, unscaledSweptDiskPosition, scaledSweptDiskPosition); // Create the disk. Curve firstCurve = null; foreach (Curve curve in trimmedDirectrixInWCS) { firstCurve = curve; break; } double startParam = 0.0; IList <CurveLoop> profileCurveLoops = CreateProfileCurveLoopsForDirectrix(firstCurve, out startParam); if (profileCurveLoops == null) { return(null); } SolidOptions solidOptions = new SolidOptions(GetMaterialElementId(shapeEditScope), shapeEditScope.GraphicsStyleId); IList <GeometryObject> myObjs = new List <GeometryObject>(); try { Solid sweptDiskSolid = GeometryCreationUtilities.CreateSweptGeometry(trimmedDirectrixInWCS, 0, startParam, profileCurveLoops, solidOptions); if (sweptDiskSolid != null) { myObjs.Add(sweptDiskSolid); } } catch (Exception ex) { // If we can't create a solid, we will attempt to split the Solid into valid pieces (that will likely have some overlap). if (ex.Message.Contains("self-intersections")) { Importer.TheLog.LogWarning(Id, "The IfcSweptDiskSolid definition does not define a valid solid, likely due to self-intersections or other such problems; the profile probably extends too far toward the inner curvature of the sweep path. Creating the minimum number of solids possible to represent the geometry.", false); myObjs = SplitSweptDiskIntoValidPieces(trimmedDirectrixInWCS, profileCurveLoops, solidOptions); } else { throw ex; } } return(myObjs); }
private void CreateLineSegments(CurveLoop curveLoop, IList <XYZ> currentSegments) { if (currentSegments.Count > 0) { IFCGeometryUtil.AppendPolyCurveToCurveLoop(curveLoop, currentSegments, null, Id, false); currentSegments.Clear(); } }
/// <summary> /// Set the representation of the curve based on one CurveLoop. /// </summary> /// <param name="curveLoop">The one CurveLoop.</param> /// <param name="pointXYZs">The point list that created this CurveLoop.</param> /// <remarks>The point list is used to potentially collapse a series of /// line segments into one.</remarks> public void SetCurveLoop(CurveLoop curveLoop, IList <XYZ> pointXYZs) { SetCurveLoop(curveLoop); if (Curve == null) { Curve = IFCGeometryUtil.CreateCurveFromPolyCurveLoop(GetTheCurveLoop(), pointXYZs); } }
protected List <GeometryObject> CreateConformalGeometryIfPossible( IFCImportShapeEditScope shapeEditScope, Transform unscaledLcs) { Transform unscaledSweptDiskPosition = (unscaledLcs == null) ? Transform.Identity : unscaledLcs; IList <CurveLoop> trimmedDirectrices = IFCGeometryUtil.TrimCurveLoops(Id, Directrix, StartParameter, EndParameter); if (trimmedDirectrices == null) { return(null); } List <GeometryObject> myObjs = null; bool isIdentity = unscaledSweptDiskPosition.IsIdentity; foreach (CurveLoop trimmedDirectrix in trimmedDirectrices) { // Create the disk. Curve firstCurve = null; foreach (Curve curve in trimmedDirectrix) { firstCurve = curve; break; } double startParam = 0.0; IList <CurveLoop> profileCurveLoops = CreateProfileCurveLoopsForDirectrix(firstCurve, out startParam); if (profileCurveLoops == null) { return(null); } SolidOptions solidOptions = new SolidOptions(GetMaterialElementId(shapeEditScope), shapeEditScope.GraphicsStyleId); myObjs = new List <GeometryObject>(); try { Solid sweptDiskSolid = GeometryCreationUtilities.CreateSweptGeometry(trimmedDirectrix, 0, startParam, profileCurveLoops, solidOptions); if (!isIdentity) { sweptDiskSolid = SolidUtils.CreateTransformed(sweptDiskSolid, unscaledSweptDiskPosition); } if (sweptDiskSolid != null) { myObjs.Add(sweptDiskSolid); } } catch { return(null); } } return(myObjs); }
protected override void Process(IFCAnyHandle ifcCurve) { base.Process(ifcCurve); IList <IFCAnyHandle> points = IFCAnyHandleUtil.GetAggregateInstanceAttribute <List <IFCAnyHandle> >(ifcCurve, "Points"); int numPoints = points.Count; if (numPoints < 2) { string msg = "IfcPolyLine had " + numPoints + ", expected at least 2, ignoring"; Importer.TheLog.LogError(Id, msg, false); return; } IList <XYZ> pointXYZs = new List <XYZ>(); foreach (IFCAnyHandle point in points) { XYZ pointXYZ = IFCPoint.ProcessScaledLengthIFCCartesianPoint(point); pointXYZs.Add(pointXYZ); } if (pointXYZs.Count != numPoints) { Importer.TheLog.LogError(Id, "Some of the IFC points cannot be converted to Revit points", true); } CurveLoop = IFCGeometryUtil.CreatePolyCurveLoop(pointXYZs, points, Id, false); if (pointXYZs.Count == 2) { Curve = Line.CreateBound(pointXYZs[0], pointXYZs[1]); } else { // if we go here we are sure that the number of point must be at least 3 XYZ firstPoint = pointXYZs[0]; XYZ secondPoint = pointXYZs[1]; XYZ vectorToTest = (secondPoint - firstPoint).Normalize(); bool allAreCollinear = true; for (int ii = 2; ii < numPoints; ii++) { XYZ vectorTmp = (pointXYZs[ii] - firstPoint).Normalize(); if (!vectorTmp.IsAlmostEqualTo(vectorToTest)) { allAreCollinear = false; break; } } if (allAreCollinear) { Curve = Line.CreateBound(firstPoint, pointXYZs[numPoints - 1]); } } }
private BRepBuilderSurfaceGeometry StartCollectingNURBSFace(IFCBSplineSurfaceWithKnots bSplineSurfaceWithKnots, Transform localTransform) { if (bSplineSurfaceWithKnots == null) { return(null); } IFCRationalBSplineSurfaceWithKnots rationalBSplineSurfaceWithKnots = (bSplineSurfaceWithKnots as IFCRationalBSplineSurfaceWithKnots); IList <double> knotsU = IFCGeometryUtil.ConvertIFCKnotsToRevitKnots(bSplineSurfaceWithKnots.UMultiplicities, bSplineSurfaceWithKnots.UKnots); if (knotsU == null || knotsU.Count == 0) { throw new InvalidOperationException("No knots in u-direction"); } IList <double> knotsV = IFCGeometryUtil.ConvertIFCKnotsToRevitKnots(bSplineSurfaceWithKnots.VMultiplicities, bSplineSurfaceWithKnots.VKnots); if (knotsV == null || knotsV.Count == 0) { throw new InvalidOperationException("No knots in v-direction"); } IList <double> weights = (rationalBSplineSurfaceWithKnots != null) ? rationalBSplineSurfaceWithKnots.WeightsList : null; IList <XYZ> controlPoints = new List <XYZ>(); foreach (XYZ point in bSplineSurfaceWithKnots.ControlPointsList) { controlPoints.Add(localTransform.OfPoint(point)); } int uDegree = bSplineSurfaceWithKnots.UDegree; int vDegree = bSplineSurfaceWithKnots.VDegree; BRepBuilderSurfaceGeometry surfaceGeometry = null; if (weights == null) { surfaceGeometry = BRepBuilderSurfaceGeometry.CreateNURBSSurface(uDegree, vDegree, knotsU, knotsV, controlPoints, false, null); } else { surfaceGeometry = BRepBuilderSurfaceGeometry.CreateNURBSSurface(uDegree, vDegree, knotsU, knotsV, controlPoints, weights, false, null); } return(surfaceGeometry); }
override protected CurveLoop GenerateLoop() { IList <XYZ> polygon = Polygon; if (polygon == null) { throw new InvalidOperationException("#" + Id + ": missing polygon, ignoring."); } int numVertices = Polygon.Count; if (numVertices < 3) { throw new InvalidOperationException("#" + Id + ": Polygon attribute has only " + numVertices + " vertices, 3 expected."); } return(IFCGeometryUtil.CreatePolyCurveLoop(polygon, null, Id, true)); }
protected override void Process(IFCAnyHandle ifcCurve) { base.Process(ifcCurve); IFCAnyHandle points = IFCAnyHandleUtil.GetInstanceAttribute(ifcCurve, "Points"); if (IFCAnyHandleUtil.IsNullOrHasNoValue(points)) { Importer.TheLog.LogMissingRequiredAttributeError(ifcCurve, "Points", true); return; } IFCCartesianPointList pointList = IFCCartesianPointList.ProcessIFCCartesianPointList(points); IList <XYZ> pointXYZs = pointList.CoordList; int numPoints = pointXYZs.Count; SetCurveLoop(IFCGeometryUtil.CreatePolyCurveLoop(pointXYZs, null, Id, false), pointXYZs); }
/// <summary> /// Cut a IFCSolidInfo by the voids in this IFCProduct, if any. /// </summary> /// <param name="solidInfo">The solid information.</param> /// <returns>False if the return solid is empty; true otherwise.</returns> protected override bool CutSolidByVoids(IFCSolidInfo solidInfo) { int numVoids = Voids.Count; if (numVoids == 0) { return(true); } // We only cut body representation items. if (solidInfo.RepresentationType != IFCRepresentationIdentifier.Body) { return(true); } if (!(solidInfo.GeometryObject is Solid)) { string typeName = (solidInfo.GeometryObject is Mesh) ? "mesh" : "instance"; Importer.TheLog.LogError(Id, "Can't cut " + typeName + " geometry, ignoring " + numVoids + " void(s).", false); return(true); } for (int voidIdx = 0; voidIdx < numVoids; voidIdx++) { Solid voidObject = Voids[voidIdx].GeometryObject as Solid; if (voidObject == null) { Importer.TheLog.LogError(Id, "Can't cut Solid geometry with a Mesh (# " + Voids[voidIdx].Id + "), ignoring.", false); return(true); } solidInfo.GeometryObject = IFCGeometryUtil.ExecuteSafeBooleanOperation(solidInfo.Id, Voids[voidIdx].Id, (solidInfo.GeometryObject as Solid), voidObject, BooleanOperationsType.Difference, null); if (solidInfo.GeometryObject == null || (solidInfo.GeometryObject as Solid).Faces.IsEmpty) { return(false); } } return(true); }
override protected CurveLoop GenerateLoop() { IList <XYZ> polygon = Polygon; if (polygon == null) { // This used to throw an error. However, we found files that threw this error // thousands of times, causing incredibly slow links. Instead, null out the // data and log an error. Importer.TheLog.LogError(Id, "missing polygon, ignoring.", false); } int numVertices = Polygon.Count; if (numVertices < 3) { throw new InvalidOperationException("#" + Id + ": Polygon attribute has only " + numVertices + " vertices, 3 expected."); } return(IFCGeometryUtil.CreatePolyCurveLoop(polygon, null, Id, true)); }
private void ProcessIFCPolyline(IFCAnyHandle ifcCurve) { IList <IFCAnyHandle> points = IFCAnyHandleUtil.GetAggregateInstanceAttribute <List <IFCAnyHandle> >(ifcCurve, "Points"); int numPoints = points.Count; if (numPoints < 2) { string msg = "IfcPolyLine had " + numPoints + ", expected at least 2, ignoring"; IFCImportFile.TheLog.LogError(Id, msg, false); return; } IList <XYZ> pointXYZs = new List <XYZ>(); foreach (IFCAnyHandle point in points) { XYZ pointXYZ = IFCPoint.ProcessScaledLengthIFCCartesianPoint(point); pointXYZs.Add(pointXYZ); } CurveLoop = IFCGeometryUtil.CreatePolyCurveLoop(pointXYZs, points, Id, false); }
protected override void Process(IFCAnyHandle ifcCurve) { base.Process(ifcCurve); bool found = false; double radius = IFCImportHandleUtil.GetRequiredScaledLengthAttribute(ifcCurve, "Radius", out found); if (!found) { Importer.TheLog.LogError(ifcCurve.StepId, "Cannot find the radius of this circle", false); return; } if (!IFCGeometryUtil.IsValidRadius(radius)) { Importer.TheLog.LogError(ifcCurve.StepId, "Invalid radius for this circle: " + radius, false); return; } try { SetCurve(Arc.Create(Position.Origin, radius, 0, 2.0 * Math.PI, Position.BasisX, Position.BasisY)); } catch (Exception ex) { if (ex.Message.Contains("too small")) { string lengthAsString = IFCUnitUtil.FormatLengthAsString(radius); Importer.TheLog.LogError(Id, "Found a circle with radius of " + lengthAsString + ", ignoring.", false); } else { Importer.TheLog.LogError(Id, ex.Message, false); } SetCurve(null); } }
/// <summary> /// Return geometry for a particular representation item. /// </summary> /// <param name="shapeEditScope">The geometry creation scope.</param> /// <param name="unscaledLcs">The unscaled local coordinate system for the geometry, if the scaled version isn't supported downstream.</param> /// <param name="scaledLcs">The scaled (true) local coordinate system for the geometry.</param> /// <param name="guid">The guid of an element for which represntation is being created.</param> /// <returns>The created geometry.</returns> protected override IList <GeometryObject> CreateGeometryInternal( IFCImportShapeEditScope shapeEditScope, Transform unscaledLcs, Transform scaledLcs, string guid) { Transform unscaledObjectPosition = (unscaledLcs == null) ? Position : unscaledLcs.Multiply(Position); Transform scaledObjectPosition = (scaledLcs == null) ? Position : scaledLcs.Multiply(Position); CurveLoop baseProfileCurve = Directrix.GetCurveLoop(); if (baseProfileCurve == null) { return(null); } CurveLoop trimmedDirectrix = IFCGeometryUtil.TrimCurveLoop(Id, baseProfileCurve, StartParameter, EndParameter); if (trimmedDirectrix == null) { return(null); } double startParam = 0.0; // If the directrix isn't bound, this arbitrary parameter will do. Transform originTrf0 = null; Curve firstCurve0 = trimmedDirectrix.First(); if (firstCurve0.IsBound) { startParam = firstCurve0.GetEndParameter(0); } originTrf0 = firstCurve0.ComputeDerivatives(startParam, false); if (originTrf0 == null) { return(null); } // Note: the computation of the reference Surface Local Transform must be done before the directrix is transform to LCS (because the ref surface isn't) // and therefore the origin is at the start of the curve should be the start of the directrix that lies on the surface. // This is needed to transform the swept area that must be perpendicular to the start of the directrix curve Transform referenceSurfaceLocalTransform = ReferenceSurface.GetTransformAtPoint(originTrf0.Origin); CurveLoop trimmedDirectrixInLCS = IFCGeometryUtil.CreateTransformed(trimmedDirectrix, Id, unscaledObjectPosition, scaledObjectPosition); // Create the sweep. Transform originTrf = null; Curve firstCurve = trimmedDirectrixInLCS.First(); //if (firstCurve.IsBound) // startParam = firstCurve.GetEndParameter(0); originTrf = firstCurve.ComputeDerivatives(startParam, false); Transform unscaledReferenceSurfaceTransform = unscaledObjectPosition.Multiply(referenceSurfaceLocalTransform); Transform scaledReferenceSurfaceTransform = scaledObjectPosition.Multiply(referenceSurfaceLocalTransform); Transform profileCurveLoopsTransform = Transform.CreateTranslation(originTrf.Origin); profileCurveLoopsTransform.BasisX = scaledReferenceSurfaceTransform.BasisZ; profileCurveLoopsTransform.BasisZ = originTrf.BasisX.Normalize(); profileCurveLoopsTransform.BasisY = profileCurveLoopsTransform.BasisZ.CrossProduct(profileCurveLoopsTransform.BasisX); ISet <IList <CurveLoop> > profileCurveLoops = GetTransformedCurveLoops(profileCurveLoopsTransform, profileCurveLoopsTransform); if (profileCurveLoops == null || profileCurveLoops.Count == 0) { return(null); } SolidOptions solidOptions = new SolidOptions(GetMaterialElementId(shapeEditScope), shapeEditScope.GraphicsStyleId); IList <GeometryObject> myObjs = new List <GeometryObject>(); foreach (IList <CurveLoop> loops in profileCurveLoops) { GeometryObject myObj = GeometryCreationUtilities.CreateSweptGeometry(trimmedDirectrixInLCS, 0, startParam, loops, solidOptions); if (myObj != null) { myObjs.Add(myObj); } } return(myObjs); }
/// <summary> /// Add curves to represent the plan view of the created object. /// </summary> /// <param name="curves">The list of curves, to be validated.</param> /// <param name="id">The id of the object being created, for error logging.</param> /// <returns>True if any curves were added to the plan view representation.</returns> public bool AddPlanViewCurves(IList <Curve> curves, int id) { ViewShapeBuilder = null; int numCurves = curves.Count; if (numCurves > 0) { ViewShapeBuilder = new ViewShapeBuilder(DirectShapeTargetViewType.Plan); IFCGeometryUtil.SplitUnboundCyclicCurves(curves); // Ideally we'd form these curves into a CurveLoop and get the Plane of the CurveLoop. However, there is no requirement // that the plan view curves form one contiguous loop. foreach (Curve curve in curves) { if (ViewShapeBuilder.ValidateCurve(curve)) { ViewShapeBuilder.AddCurve(curve); } else { // We will move the origin to Z=0 if necessary, since the VSB requires all curves to be in the Z=0 plane. // This only works if the curves are in a plane parallel to the Z=0 plane. // NOTE: We could instead project the curves to the Z=0 plane, which could have the effect of changing their geometry. // Until we see such cases, we will take the easier route here. try { // If the end points aren't equal in Z, then the curve isn't parallel to Z. bool isBound = curve.IsBound; XYZ startPoint = isBound ? curve.GetEndPoint(0) : curve.Evaluate(0, false); XYZ endPoint = isBound ? curve.GetEndPoint(1) : startPoint; if (!MathUtil.IsAlmostEqual(startPoint.Z, endPoint.Z)) { throw new InvalidOperationException("Non-planar curve in footprint representation."); } // Lines won't have a non-zero BasisZ value, so don't bother computing. if (!(curve is Line)) { Transform coordinatePlane = curve.ComputeDerivatives(0, true); if (coordinatePlane != null && coordinatePlane.BasisZ != null && !coordinatePlane.BasisZ.IsZeroLength()) { XYZ normalizedZ = coordinatePlane.BasisZ.Normalize(); if (!MathUtil.IsAlmostEqual(Math.Abs(normalizedZ.Z), 1.0)) { throw new InvalidOperationException("Non-planar curve in footprint representation."); } } } // We expect startPoint.Z to be non-zero, otherwise ValidateCurve would have accepted the curve in the first place. Transform offsetTransform = Transform.CreateTranslation(-startPoint.Z * XYZ.BasisZ); Curve projectedCurve = curve.CreateTransformed(offsetTransform); // We may have missed a case above - for example, a curve whose end points have the same Z value, and whose normal at the // start point is in +/-Z, but is regardless non-planar. ValidateCurve has a final chance to reject such curves here. if (projectedCurve == null || !ViewShapeBuilder.ValidateCurve(projectedCurve)) { throw new InvalidOperationException("Invalid curve in footprint representation."); } ViewShapeBuilder.AddCurve(projectedCurve); continue; } catch { } Importer.TheLog.LogError(id, "Invalid curve in FootPrint representation, ignoring.", false); numCurves--; } } if (numCurves == 0) { ViewShapeBuilder = null; } } return(ViewShapeBuilder != null); }
/// <summary> /// Return geometry for a particular representation item. /// </summary> /// <param name="shapeEditScope">The geometry creation scope.</param> /// <param name="lcs">Local coordinate system for the geometry, without scale.</param> /// <param name="scaledLcs">Local coordinate system for the geometry, including scale, potentially non-uniform.</param> /// <param name="guid">The guid of an element for which represntation is being created.</param> /// <returns>Zero or more created geometries.</returns> protected override IList <GeometryObject> CreateGeometryInternal( IFCImportShapeEditScope shapeEditScope, Transform lcs, Transform scaledLcs, string guid) { Transform sweptDiskPosition = (lcs == null) ? Transform.Identity : lcs; CurveLoop baseProfileCurve = Directrix.GetCurveLoop(); if (baseProfileCurve == null) { return(null); } CurveLoop trimmedDirectrix = IFCGeometryUtil.TrimCurveLoop(baseProfileCurve, StartParameter, EndParameter); if (trimmedDirectrix == null) { return(null); } CurveLoop trimmedDirectrixInLCS = IFCGeometryUtil.CreateTransformed(trimmedDirectrix, sweptDiskPosition); // Create the disk. Transform originTrf = null; double startParam = 0.0; // If the directrix isn't bound, this arbitrary parameter will do. foreach (Curve curve in trimmedDirectrixInLCS) { if (curve.IsBound) { startParam = curve.GetEndParameter(0); } originTrf = curve.ComputeDerivatives(startParam, false); break; } if (originTrf == null) { return(null); } // The X-dir of the transform of the start of the directrix will form the normal of the disk. Plane diskPlane = new Plane(originTrf.BasisX, originTrf.Origin); IList <CurveLoop> profileCurveLoops = new List <CurveLoop>(); CurveLoop diskOuterCurveLoop = new CurveLoop(); diskOuterCurveLoop.Append(Arc.Create(diskPlane, Radius, 0, Math.PI)); diskOuterCurveLoop.Append(Arc.Create(diskPlane, Radius, Math.PI, 2.0 * Math.PI)); profileCurveLoops.Add(diskOuterCurveLoop); if (InnerRadius.HasValue) { CurveLoop diskInnerCurveLoop = new CurveLoop(); diskInnerCurveLoop.Append(Arc.Create(diskPlane, InnerRadius.Value, 0, Math.PI)); diskInnerCurveLoop.Append(Arc.Create(diskPlane, InnerRadius.Value, Math.PI, 2.0 * Math.PI)); profileCurveLoops.Add(diskInnerCurveLoop); } SolidOptions solidOptions = new SolidOptions(GetMaterialElementId(shapeEditScope), shapeEditScope.GraphicsStyleId); Solid sweptDiskSolid = GeometryCreationUtilities.CreateSweptGeometry(trimmedDirectrixInLCS, 0, startParam, profileCurveLoops, solidOptions); IList <GeometryObject> myObjs = new List <GeometryObject>(); if (sweptDiskSolid != null) { myObjs.Add(sweptDiskSolid); } return(myObjs); }
/// <summary> /// Return geometry for a particular representation item. /// </summary> /// <param name="shapeEditScope">The geometry creation scope.</param> /// <param name="lcs">Local coordinate system for the geometry.</param> /// <param name="guid">The guid of an element for which represntation is being created.</param> /// <returns>The created geometry.</returns> protected override IList <GeometryObject> CreateGeometryInternal( IFCImportShapeEditScope shapeEditScope, Transform lcs, Transform scaledLcs, string guid) { Transform objectPosition = (lcs == null) ? Position : lcs.Multiply(Position); CurveLoop baseProfileCurve = Directrix.GetCurveLoop(); if (baseProfileCurve == null) { return(null); } CurveLoop trimmedDirectrix = IFCGeometryUtil.TrimCurveLoop(baseProfileCurve, StartParameter, EndParameter); if (trimmedDirectrix == null) { return(null); } CurveLoop trimmedDirectrixInLCS = IFCGeometryUtil.CreateTransformed(trimmedDirectrix, objectPosition); // Create the sweep. double startParam = 0.0; // If the directrix isn't bound, this arbitrary parameter will do. Transform originTrf = null; Curve firstCurve = trimmedDirectrixInLCS.First(); if (firstCurve.IsBound) { startParam = firstCurve.GetEndParameter(0); } originTrf = firstCurve.ComputeDerivatives(startParam, false); if (originTrf == null) { return(null); } Transform referenceSurfaceLocalTransform = ReferenceSurface.GetTransformAtPoint(originTrf.Origin); Transform referenceSurfaceTransform = objectPosition.Multiply(referenceSurfaceLocalTransform); Transform profileCurveLoopsTransform = Transform.CreateTranslation(originTrf.Origin); profileCurveLoopsTransform.BasisX = referenceSurfaceTransform.BasisZ; profileCurveLoopsTransform.BasisZ = originTrf.BasisX.Normalize(); profileCurveLoopsTransform.BasisY = profileCurveLoopsTransform.BasisZ.CrossProduct(profileCurveLoopsTransform.BasisX); ISet <IList <CurveLoop> > profileCurveLoops = GetTransformedCurveLoops(profileCurveLoopsTransform); if (profileCurveLoops == null || profileCurveLoops.Count == 0) { return(null); } SolidOptions solidOptions = new SolidOptions(GetMaterialElementId(shapeEditScope), shapeEditScope.GraphicsStyleId); IList <GeometryObject> myObjs = new List <GeometryObject>(); foreach (IList <CurveLoop> loops in profileCurveLoops) { GeometryObject myObj = GeometryCreationUtilities.CreateSweptGeometry(trimmedDirectrixInLCS, 0, startParam, loops, solidOptions); if (myObj != null) { myObjs.Add(myObj); } } return(myObjs); }
private void CreateTessellatedShapeInternal(IFCImportShapeEditScope shapeEditScope, Transform scaledLcs) { TessellatedShapeBuilderScope tsBuilderScope = shapeEditScope.BuilderScope as TessellatedShapeBuilderScope; if (tsBuilderScope == null) { throw new InvalidOperationException("Expect a TessellatedShapeBuilderScope, but get a BrepBuilderScope instead"); } IList <XYZ> loopVertices = Bound.LoopVertices; int count = 0; if (loopVertices == null || ((count = loopVertices.Count) == 0)) { throw new InvalidOperationException("#" + Id + ": missing loop vertices, ignoring."); } if (count < 3) { throw new InvalidOperationException("#" + Id + ": too few loop vertices (" + count + "), ignoring."); } if (!Orientation) { loopVertices.Reverse(); } // Apply the transform IList <XYZ> transformedVertices = new List <XYZ>(); foreach (XYZ vertex in loopVertices) { transformedVertices.Add(scaledLcs.OfPoint(vertex)); } // Check that the loop vertices don't contain points that are very close to one another; // if so, throw the point away and hope that the TessellatedShapeBuilder can repair the result. // Warn in this case. If the entire boundary is bad, report an error and don't add the loop vertices. IList <XYZ> validVertices; IFCGeometryUtil.CheckAnyDistanceVerticesWithinTolerance(Id, shapeEditScope, transformedVertices, out validVertices); // We are going to catch any exceptions if the loop is invalid. // We are going to hope that we can heal the parent object in the TessellatedShapeBuilder. bool bPotentiallyAbortFace = false; count = validVertices.Count; if (validVertices.Count < 3) { Importer.TheLog.LogComment(Id, "Too few distinct loop vertices (" + count + "), ignoring.", false); bPotentiallyAbortFace = true; } else { if (!tsBuilderScope.AddLoopVertices(Id, validVertices)) { bPotentiallyAbortFace = true; } } if (bPotentiallyAbortFace && IsOuter) { tsBuilderScope.AbortCurrentFace(); } }
/// <summary> /// Creates or populates Revit elements based on the information contained in this class. /// </summary> /// <param name="doc">The document.</param> protected override void Create(Document doc) { Transform lcs = (ObjectLocation != null) ? ObjectLocation.TotalTransform : Transform.Identity; if (ProductRepresentation != null) { if (ProductRepresentation.IsValid()) { using (IFCImportShapeEditScope shapeEditScope = IFCImportShapeEditScope.Create(doc, this)) { shapeEditScope.GraphicsStyleId = m_GraphicsStyleId; shapeEditScope.CategoryId = CategoryId; // The name can be added as well. but it is usually less useful than 'oid' string myId = GlobalId; // + "(" + Name + ")"; ProductRepresentation.CreateProductRepresentation(shapeEditScope, lcs, lcs, myId); int numSolids = Solids.Count; int numVoids = Voids.Count; if ((numSolids > 0) && (numVoids > 0)) { // We may have some GeometryInstances. These need to be "exploded" to do the cutting. for (int solidIdx = 0; solidIdx < numSolids; solidIdx++) { if (Solids[solidIdx].GeometryObject is GeometryInstance) { //// This code currently doesn't work, so commented out. //GeometryInstance geomInst = Solids[solidIdx].GeometryObject as GeometryInstance; //GeometryElement geomElem = geomInst.GetInstanceGeometry(); //foreach (GeometryObject geomObj in geomElem) //{ //if (geomObj is Solid) //Solids.Add(IFCSolidInfo.Create(Solids[solidIdx].Id, geomObj as Solid)); //else if (geomObj is Mesh) //Solids.Add(IFCSolidInfo.Create(Solids[solidIdx].Id, geomObj as Mesh)); //else if (geomObj is GeometryInstance) //IFCImportFile.TheLog.LogError(Solids[solidIdx].Id, "Can't cut nested mapped items, ignoring " + numVoids + " void(s).", false); // Other items are irrelevant here. //} //Solids.RemoveAt(solidIdx); //solidIdx--; //numSolids--; } } // This may be different than before, with the addition of solids from FamilyInstances. numSolids = Solids.Count; // Attempt to cut each solid with each void. for (int solidIdx = 0; solidIdx < numSolids; solidIdx++) { if (!(Solids[solidIdx].GeometryObject is Solid)) { // Assume that we only deal with solids, meshes and instances. string typeName = (Solids[solidIdx].GeometryObject is Mesh) ? "mesh" : "instance"; IFCImportFile.TheLog.LogError(Id, "Can't cut " + typeName + " geometry, ignoring " + numVoids + " void(s).", false); continue; } for (int voidIdx = 0; voidIdx < numVoids; voidIdx++) { if (!(Voids[voidIdx].GeometryObject is Solid)) { IFCImportFile.TheLog.LogError(Id, "Can't cut Solid geometry with a Mesh (# " + Voids[voidIdx].Id + "), ignoring.", false); continue; } Solids[solidIdx].GeometryObject = IFCGeometryUtil.ExecuteSafeBooleanOperation(Solids[solidIdx].Id, Voids[voidIdx].Id, Solids[solidIdx].GeometryObject as Solid, Voids[voidIdx].GeometryObject as Solid, BooleanOperationsType.Difference); if ((Solids[solidIdx].GeometryObject as Solid).Faces.IsEmpty) { Solids.RemoveAt(solidIdx); solidIdx--; numSolids--; break; } } } } bool addedCurves = shapeEditScope.AddPlanViewCurves(FootprintCurves, Id); if ((numSolids > 0 || addedCurves)) { if (GlobalId != null) { // If the GlobalId is null, this is a fake IfcProduct that we don't want to create into a DirectShape, or // add to the caches in any way. We only wanted to gather its geometry. DirectShape shape = Importer.TheCache.UseElementByGUID <DirectShape>(doc, GlobalId); if (shape == null) { shape = IFCElementUtil.CreateElement(doc, CategoryId, Importer.ImportAppGUID(), GlobalId); } List <GeometryObject> directShapeGeometries = new List <GeometryObject>(); foreach (IFCSolidInfo geometryObject in Solids) { // We need to check if the solid created is good enough for DirectShape. If not, warn and use a fallback Mesh. GeometryObject currObject = geometryObject.GeometryObject; if (currObject is Solid) { Solid solid = currObject as Solid; if (!shape.IsValidGeometry(solid)) { IFCImportFile.TheLog.LogWarning(Id, "Couldn't create valid solid, reverting to mesh.", false); directShapeGeometries.AddRange(IFCGeometryUtil.CreateMeshesFromSolid(solid)); currObject = null; } } if (currObject != null) { directShapeGeometries.Add(currObject); } } // We will use the first IfcTypeObject id, if it exists. In general, there should be 0 or 1. ElementId typeId = ElementId.InvalidElementId; foreach (IFCTypeObject typeObject in TypeObjects) { if (typeObject.IsValidForCreation && typeObject.CreatedElementId != ElementId.InvalidElementId) { typeId = typeObject.CreatedElementId; break; } } shape.SetShape(directShapeGeometries); shapeEditScope.SetPlanViewRep(shape); if (typeId != ElementId.InvalidElementId) { shape.SetTypeId(typeId); } PresentationLayerNames.UnionWith(shapeEditScope.PresentationLayerNames); m_CreatedElementId = shape.Id; } } } } else { IFCImportFile.TheLog.LogWarning(Id, "There is no valid geometry for this " + EntityType.ToString() + "; entity will not be built.", false); } } base.Create(doc); }
/// <summary> /// Cut a IFCSolidInfo by the voids in this IFCProduct, if any. /// </summary> /// <param name="solidInfo">The solid information.</param> /// <returns>False if the return solid is empty; true otherwise.</returns> protected override bool CutSolidByVoids(IFCSolidInfo solidInfo) { // We only cut "Body" representation items. if (solidInfo.RepresentationType != IFCRepresentationIdentifier.Body) { return(true); } IList <IFCVoidInfo> voidsToUse = null; List <IFCVoidInfo> partVoids = null; IList <IFCVoidInfo> parentVoids = (Decomposes as IFCProduct)?.Voids; if (parentVoids != null) { partVoids = new List <IFCVoidInfo>(); partVoids.AddRange(Voids); partVoids.AddRange(parentVoids); voidsToUse = partVoids; } else { voidsToUse = Voids; } int numVoids = voidsToUse.Count; if (numVoids == 0) { return(true); } if (!(solidInfo.GeometryObject is Solid)) { string typeName = (solidInfo.GeometryObject is Mesh) ? "mesh" : "instance"; Importer.TheLog.LogError(Id, "Can't cut " + typeName + " geometry, ignoring " + numVoids + " void(s).", false); return(true); } foreach (IFCVoidInfo voidInfo in voidsToUse) { Solid voidObject = voidInfo.GeometryObject as Solid; if (voidObject == null) { Importer.TheLog.LogError(Id, "Can't cut Solid geometry with a Mesh (# " + voidInfo.Id + "), ignoring.", false); continue; } var voidTransform = voidInfo.TotalTransform; if (voidTransform != null && voidTransform.IsIdentity == false) { // Transform the void into the space of the solid. var t = ObjectLocation.TotalTransform.Inverse.Multiply(voidTransform); voidObject = SolidUtils.CreateTransformed(voidObject, t); } solidInfo.GeometryObject = IFCGeometryUtil.ExecuteSafeBooleanOperation(solidInfo.Id, voidInfo.Id, (solidInfo.GeometryObject as Solid), voidObject, BooleanOperationsType.Difference, null); if (solidInfo.GeometryObject == null || (solidInfo.GeometryObject as Solid).Faces.IsEmpty) { return(false); } } return(true); }
/// <summary> /// Create geometry for an IfcHalfSpaceSolid. /// </summary> /// <param name="shapeEditScope">The shape edit scope.</param> /// <param name="lcs">Local coordinate system for the geometry, without scale.</param> /// <param name="scaledLcs">Local coordinate system for the geometry, including scale, potentially non-uniform.</param> /// <param name="guid">The guid of an element for which represntation is being created.</param> /// <returns>A list containing one geometry for the IfcHalfSpaceSolid.</returns> protected virtual IList <GeometryObject> CreateGeometryInternal( IFCImportShapeEditScope shapeEditScope, Transform unscaledLcs, Transform scaledLcs, string guid) { IFCPlane ifcPlane = BaseSurface as IFCPlane; Plane plane = ifcPlane.Plane; XYZ origin = plane.Origin; XYZ xVec = plane.XVec; XYZ yVec = plane.YVec; // Set some huge boundaries for now. const double largeCoordinateValue = 100000; XYZ[] corners = new XYZ[4] { unscaledLcs.OfPoint((xVec * -largeCoordinateValue) + (yVec * -largeCoordinateValue) + origin), unscaledLcs.OfPoint((xVec * largeCoordinateValue) + (yVec * -largeCoordinateValue) + origin), unscaledLcs.OfPoint((xVec * largeCoordinateValue) + (yVec * largeCoordinateValue) + origin), unscaledLcs.OfPoint((xVec * -largeCoordinateValue) + (yVec * largeCoordinateValue) + origin) }; IList <CurveLoop> loops = new List <CurveLoop>(); CurveLoop loop = new CurveLoop(); for (int ii = 0; ii < 4; ii++) { if (AgreementFlag) { loop.Append(Line.CreateBound(corners[(5 - ii) % 4], corners[(4 - ii) % 4])); } else { loop.Append(Line.CreateBound(corners[ii], corners[(ii + 1) % 4])); } } loops.Add(loop); XYZ normal = unscaledLcs.OfVector(AgreementFlag ? -plane.Normal : plane.Normal); SolidOptions solidOptions = new SolidOptions(GetMaterialElementId(shapeEditScope), shapeEditScope.GraphicsStyleId); Solid baseSolid = GeometryCreationUtilities.CreateExtrusionGeometry(loops, normal, largeCoordinateValue, solidOptions); if (BaseBoundingCurve != null) { CurveLoop polygonalBoundary = BaseBoundingCurve.CurveLoop; Transform unscaledTotalTransform = unscaledLcs.Multiply(BaseBoundingCurveTransform); Transform scaledTotalTransform = scaledLcs.Multiply(BaseBoundingCurveTransform); // Make sure this bounding polygon extends below base of half-space soild. Transform moveBaseTransform = Transform.Identity; moveBaseTransform.Origin = new XYZ(0, 0, -largeCoordinateValue); unscaledTotalTransform = unscaledTotalTransform.Multiply(moveBaseTransform); scaledTotalTransform = scaledTotalTransform.Multiply(moveBaseTransform); CurveLoop transformedPolygonalBoundary = IFCGeometryUtil.CreateTransformed(polygonalBoundary, Id, unscaledTotalTransform, scaledTotalTransform); IList <CurveLoop> boundingLoops = new List <CurveLoop>(); boundingLoops.Add(transformedPolygonalBoundary); Solid boundingSolid = GeometryCreationUtilities.CreateExtrusionGeometry(boundingLoops, unscaledTotalTransform.BasisZ, 2.0 * largeCoordinateValue, solidOptions); baseSolid = IFCGeometryUtil.ExecuteSafeBooleanOperation(Id, BaseBoundingCurve.Id, baseSolid, boundingSolid, BooleanOperationsType.Intersect, null); } IList <GeometryObject> returnList = new List <GeometryObject>(); returnList.Add(baseSolid); return(returnList); }
/// <summary> /// Creates or populates Revit elements based on the information contained in this class. /// </summary> /// <param name="doc">The document.</param> protected override void Create(Document doc) { bool preventInstances = false; IFCElement element = this as IFCElement; if (element != null) { preventInstances = this is IFCOpeningElement; foreach (IFCFeatureElement opening in element.Openings) { try { preventInstances = true; // Create the actual Revit element based on the IFCFeatureElement here. ElementId openingId = CreateElement(doc, opening); // This gets around the issue that the Boolean operation between the void(s) in the IFCFeatureElement and // the solid(s) in the IFCElement may use the Graphics Style of the voids in the resulting Solid(s), meaning // that some faces may disappear when we turn off the visibility of IfcOpeningElements. IList <IFCSolidInfo> voids = IFCElement.CloneElementGeometry(doc, opening, this, true); if (voids != null) { foreach (IFCSolidInfo voidGeom in voids) { IFCVoidInfo voidInfo = new IFCVoidInfo(voidGeom); if (!Importer.TheProcessor.ApplyTransforms) { // If we aren't applying transforms, then the Voids and Solids will be // in different coordinate spaces, so we need the transform of the // void, so we can transform it into the Solid coordinate space voidInfo.TotalTransform = opening?.ObjectLocation?.TotalTransform; } Voids.Add(voidInfo); } } } catch (Exception ex) { Importer.TheLog.LogError(opening.Id, ex.Message, false); } } } if (HasValidTopLevelGeometry()) { using (IFCImportShapeEditScope shapeEditScope = IFCImportShapeEditScope.Create(doc, this)) { shapeEditScope.GraphicsStyleId = GraphicsStyleId; shapeEditScope.CategoryId = CategoryId; shapeEditScope.PreventInstances = preventInstances; // The name can be added as well. but it is usually less useful than 'oid' string myId = GlobalId; // + "(" + Name + ")"; Transform lcs = IFCImportFile.TheFile.IFCProject.WorldCoordinateSystem; if (lcs == null) { lcs = (ObjectLocation != null) ? ObjectLocation.TotalTransform : Transform.Identity; } else if (ObjectLocation != null) { lcs = lcs.Multiply(ObjectLocation.TotalTransform); } // If we are not applying transforms to the geometry, then pass in the identity matrix. // Lower down this method we then pass lcs to the consumer element, so that it can apply // the transform as required. Transform transformToUse = Importer.TheProcessor.ApplyTransforms ? lcs : Transform.Identity; ProductRepresentation.CreateProductRepresentation(shapeEditScope, transformToUse, transformToUse, myId); int numSolids = Solids.Count; // Attempt to cut each solid with each void. for (int solidIdx = 0; solidIdx < numSolids; solidIdx++) { if (!CutSolidByVoids(Solids[solidIdx])) { Solids.RemoveAt(solidIdx); solidIdx--; numSolids--; } } bool addedCurves = shapeEditScope.AddPlanViewCurves(FootprintCurves, Id); if ((numSolids > 0 || addedCurves)) { if (GlobalId != null) { // If the GlobalId is null, this is a fake IfcProduct that we don't want to create into a DirectShape, or // add to the caches in any way. We only wanted to gather its geometry. DirectShape shape = Importer.TheCache.UseElementByGUID <DirectShape>(doc, GlobalId); if (shape == null) { shape = IFCElementUtil.CreateElement(doc, CategoryId, GlobalId, null, Id, EntityType); } List <GeometryObject> directShapeGeometries = new List <GeometryObject>(); foreach (IFCSolidInfo geometryObject in Solids) { // We need to check if the solid created is good enough for DirectShape. If not, warn and use a fallback Mesh. GeometryObject currObject = geometryObject.GeometryObject; if (currObject is Solid) { Solid solid = currObject as Solid; if (!shape.IsValidGeometry(solid)) { Importer.TheLog.LogWarning(Id, "Couldn't create valid solid, reverting to mesh.", false); directShapeGeometries.AddRange(IFCGeometryUtil.CreateMeshesFromSolid(solid)); currObject = null; } } if (currObject != null) { directShapeGeometries.Add(currObject); } } // We will use the first IfcTypeObject id, if it exists. In general, there should be 0 or 1. IFCTypeObject typeObjectToUse = null; foreach (IFCTypeObject typeObject in TypeObjects) { if (typeObject.IsValidForCreation && typeObject.CreatedElementId != ElementId.InvalidElementId) { typeObjectToUse = typeObject; break; } } if (!Importer.TheProcessor.PostProcessProduct(Id, typeObjectToUse?.Id, lcs, directShapeGeometries)) { if (shape != null) { shape.SetShape(directShapeGeometries); shapeEditScope.SetPlanViewRep(shape); if (typeObjectToUse != null && typeObjectToUse.CreatedElementId != ElementId.InvalidElementId) { shape.SetTypeId(typeObjectToUse.CreatedElementId); } } } PresentationLayerNames.UnionWith(shapeEditScope.PresentationLayerNames); CreatedElementId = shape.Id; CreatedGeometry = directShapeGeometries; } } } } else { if (this is IFCElement || this is IFCGrid) { IList <IFCEntity> visitedEntities = new List <IFCEntity>(); visitedEntities.Add(this); if (!HasValidSubElementGeometry(visitedEntities)) { // We will not warn if this is an IfcSpatialStructureElement; those aren't expected to have their own geometry. Importer.TheLog.LogWarning(Id, "There is no valid geometry for this " + EntityType.ToString() + "; entity will not be built.", false); } } } base.Create(doc); }
/// <summary> /// Return geometry for a particular representation item. /// </summary> /// <param name="shapeEditScope">The geometry creation scope.</param> /// <param name="lcs">Local coordinate system for the geometry, without scale.</param> /// <param name="scaledLcs">Local coordinate system for the geometry, including scale, potentially non-uniform.</param> /// <param name="guid">The guid of an element for which represntation is being created.</param> /// <returns>The created geometry.</returns> public IList <GeometryObject> CreateGeometry( IFCImportShapeEditScope shapeEditScope, Transform lcs, Transform scaledLcs, string guid) { IList <GeometryObject> firstSolids = FirstOperand.CreateGeometry(shapeEditScope, lcs, scaledLcs, guid); if (firstSolids != null) { foreach (GeometryObject potentialSolid in firstSolids) { if (!(potentialSolid is Solid)) { IFCImportFile.TheLog.LogError((FirstOperand as IFCRepresentationItem).Id, "Can't perform Boolean operation on a Mesh.", false); return(firstSolids); } } } IList <GeometryObject> secondSolids = null; if (SecondOperand != null) { try { using (IFCImportShapeEditScope.IFCTargetSetter setter = new IFCImportShapeEditScope.IFCTargetSetter(shapeEditScope, TessellatedShapeBuilderTarget.Solid, TessellatedShapeBuilderFallback.Abort)) { secondSolids = SecondOperand.CreateGeometry(shapeEditScope, lcs, scaledLcs, guid); } } catch (Exception ex) { // We will allow something to be imported, in the case where the second operand is invalid. // If the first (base) operand is invalid, we will still fail the import of this solid. if (SecondOperand is IFCRepresentationItem) { IFCImportFile.TheLog.LogError((SecondOperand as IFCRepresentationItem).Id, ex.Message, false); } else { throw ex; } secondSolids = null; } } IList <GeometryObject> resultSolids = null; if (firstSolids == null) { resultSolids = secondSolids; } else if (secondSolids == null) { resultSolids = firstSolids; } else { BooleanOperationsType booleanOperationsType = BooleanOperationsType.Difference; switch (BooleanOperator) { case IFCBooleanOperator.Difference: booleanOperationsType = BooleanOperationsType.Difference; break; case IFCBooleanOperator.Intersection: booleanOperationsType = BooleanOperationsType.Intersect; break; case IFCBooleanOperator.Union: booleanOperationsType = BooleanOperationsType.Union; break; default: IFCImportFile.TheLog.LogError(Id, "Invalid BooleanOperationsType.", true); break; } resultSolids = new List <GeometryObject>(); foreach (GeometryObject firstSolid in firstSolids) { Solid resultSolid = (firstSolid as Solid); int secondId = (SecondOperand == null) ? -1 : (SecondOperand as IFCRepresentationItem).Id; foreach (GeometryObject secondSolid in secondSolids) { resultSolid = IFCGeometryUtil.ExecuteSafeBooleanOperation(Id, secondId, resultSolid, secondSolid as Solid, booleanOperationsType); if (resultSolid == null) { break; } } if (resultSolid != null) { resultSolids.Add(resultSolid); } } } return(resultSolids); }
/// <summary> /// Return geometry for a particular representation item. /// </summary> /// <param name="shapeEditScope">The geometry creation scope.</param> /// <param name="lcs">Local coordinate system for the geometry, without scale.</param> /// <param name="scaledLcs">Local coordinate system for the geometry, including scale, potentially non-uniform.</param> /// <param name="guid">The guid of an element for which represntation is being created.</param> /// <returns>The created geometry.</returns> public IList <GeometryObject> CreateGeometry( IFCImportShapeEditScope shapeEditScope, Transform lcs, Transform scaledLcs, string guid) { IList <GeometryObject> firstSolids = FirstOperand.CreateGeometry(shapeEditScope, lcs, scaledLcs, guid); if (firstSolids != null) { foreach (GeometryObject potentialSolid in firstSolids) { if (!(potentialSolid is Solid)) { Importer.TheLog.LogError((FirstOperand as IFCRepresentationItem).Id, "Can't perform Boolean operation on a Mesh.", false); return(firstSolids); } } } IList <GeometryObject> secondSolids = null; if (SecondOperand != null) { try { using (IFCImportShapeEditScope.BuildPreferenceSetter setter = new IFCImportShapeEditScope.BuildPreferenceSetter(shapeEditScope, IFCImportShapeEditScope.BuildPreferenceType.ForceSolid)) { // Before we process the second operand, we are going to see if there is a uniform material set for the first operand // (corresponding to the solid in the Boolean operation). We will try to suggest the same material for the voids to avoid arbitrary // setting of material information for the cut faces. IFCStyledItem firstOperandStyledItem = GetStyledItemFromOperand(FirstOperand as IFCRepresentationItem); using (IFCImportShapeEditScope.IFCMaterialStack stack = new IFCImportShapeEditScope.IFCMaterialStack(shapeEditScope, firstOperandStyledItem, null)) { secondSolids = SecondOperand.CreateGeometry(shapeEditScope, lcs, scaledLcs, guid); } } } catch (Exception ex) { // We will allow something to be imported, in the case where the second operand is invalid. // If the first (base) operand is invalid, we will still fail the import of this solid. if (SecondOperand is IFCRepresentationItem) { Importer.TheLog.LogError((SecondOperand as IFCRepresentationItem).Id, ex.Message, false); } else { throw ex; } secondSolids = null; } } IList <GeometryObject> resultSolids = null; if (firstSolids == null) { resultSolids = secondSolids; } else if (secondSolids == null || BooleanOperator == null) { if (BooleanOperator == null) { Importer.TheLog.LogError(Id, "Invalid BooleanOperationsType.", false); } resultSolids = firstSolids; } else { BooleanOperationsType booleanOperationsType = BooleanOperationsType.Difference; switch (BooleanOperator) { case IFCBooleanOperator.Difference: booleanOperationsType = BooleanOperationsType.Difference; break; case IFCBooleanOperator.Intersection: booleanOperationsType = BooleanOperationsType.Intersect; break; case IFCBooleanOperator.Union: booleanOperationsType = BooleanOperationsType.Union; break; default: Importer.TheLog.LogError(Id, "Invalid BooleanOperationsType.", true); break; } resultSolids = new List <GeometryObject>(); foreach (GeometryObject firstSolid in firstSolids) { Solid resultSolid = (firstSolid as Solid); int secondId = (SecondOperand == null) ? -1 : (SecondOperand as IFCRepresentationItem).Id; XYZ suggestedShiftDirection = (SecondOperand == null) ? null : SecondOperand.GetSuggestedShiftDirection(lcs); foreach (GeometryObject secondSolid in secondSolids) { resultSolid = IFCGeometryUtil.ExecuteSafeBooleanOperation(Id, secondId, resultSolid, secondSolid as Solid, booleanOperationsType, suggestedShiftDirection); if (resultSolid == null) { break; } } if (resultSolid != null) { resultSolids.Add(resultSolid); } } } return(resultSolids); }
protected override void CreateShapeInternal(IFCImportShapeEditScope shapeEditScope, Transform lcs, Transform scaledLcs, string guid) { using (BuilderScope bs = shapeEditScope.InitializeBuilder(IFCShapeBuilderType.TessellatedShapeBuilder)) { base.CreateShapeInternal(shapeEditScope, lcs, scaledLcs, guid); TessellatedShapeBuilderScope tsBuilderScope = bs as TessellatedShapeBuilderScope; tsBuilderScope.StartCollectingFaceSet(); // Create the face set from IFCIndexedPolygonalFace foreach (IFCIndexedPolygonalFace face in Faces) { // TODO: Consider adding ability to triangulate here. tsBuilderScope.StartCollectingFace(GetMaterialElementId(shapeEditScope), false); IList <XYZ> loopVertices = new List <XYZ>(); foreach (int vertInd in face.CoordIndex) { int actualVIdx = vertInd - 1; // IFC starts the list position at 1 if (PnIndex != null) { actualVIdx = PnIndex[actualVIdx] - 1; } XYZ vertex = Coordinates.CoordList[actualVIdx]; loopVertices.Add(scaledLcs.OfPoint(vertex)); } List <XYZ> validVertices; IFCGeometryUtil.CheckAnyDistanceVerticesWithinTolerance(Id, shapeEditScope, loopVertices, out validVertices); bool bPotentiallyAbortFace = false; if (!tsBuilderScope.AddLoopVertices(Id, validVertices)) { bPotentiallyAbortFace = true; } // Handle holes if (face.InnerCoordIndices != null) { foreach (IList <int> innerLoop in face.InnerCoordIndices) { IList <XYZ> innerLoopVertices = new List <XYZ>(); foreach (int innerVerIdx in innerLoop) { int actualVIdx = innerVerIdx - 1; if (PnIndex != null) { actualVIdx = PnIndex[actualVIdx] - 1; } XYZ vertex = Coordinates.CoordList[actualVIdx]; // add vertex to the loop innerLoopVertices.Add(scaledLcs.OfPoint(vertex)); } List <XYZ> validInnerV; IFCGeometryUtil.CheckAnyDistanceVerticesWithinTolerance(Id, shapeEditScope, innerLoopVertices, out validInnerV); if (!tsBuilderScope.AddLoopVertices(Id, validInnerV)) { bPotentiallyAbortFace = true; } } } tsBuilderScope.StopCollectingFace(!bPotentiallyAbortFace, false); } IList <GeometryObject> createdGeometries = tsBuilderScope.CreateGeometry(guid); if (createdGeometries != null) { foreach (GeometryObject createdGeometry in createdGeometries) { shapeEditScope.AddGeometry(IFCSolidInfo.Create(Id, createdGeometry)); } } } }
/// <summary> /// Perform end of import/link cleanup. /// </summary> /// <param name="doc">The document.</param> /// <param name="fileName">The full path of the original IFC file.</param> public void EndImport(Document doc, string fileName) { // Remove an unupdated Elements as a result of a reload operation. try { // We are working around a limitation in deleting unused DirectShapeTypes. IList <ElementId> otherElementsToDelete = new List <ElementId>(); IList <ElementId> typesToDelete = new List <ElementId>(); foreach (ElementId elementId in Importer.TheCache.GUIDToElementMap.Values) { if (DontDeleteSpecialElement(elementId)) { continue; } Element element = doc.GetElement(elementId); if (element == null) { continue; } if (element is DirectShapeType) { typesToDelete.Add(elementId); } else { otherElementsToDelete.Add(elementId); } } foreach (ElementId elementId in Importer.TheCache.GridNameToElementMap.Values) { Element element = doc.GetElement(elementId); if (element == null) { continue; } otherElementsToDelete.Add(elementId); } // Don't expect this to fail. try { if (otherElementsToDelete.Count > 0) { doc.Delete(otherElementsToDelete); } } catch (Exception ex) { Importer.TheLog.LogError(-1, ex.Message, false); } // Delete the temporary element we used for validation purposes. IFCGeometryUtil.DeleteSolidValidator(); // This might fail. if (typesToDelete.Count > 0) { doc.Delete(typesToDelete); } UpdateDocumentFileMetrics(doc, fileName); } catch // (Exception ex) { // Catch, but don't report, since this is an internal limitation in the API. //TheLog.LogError(-1, ex.Message, false); } if (m_Transaction != null) { m_Transaction.Commit(); } }
protected override void CreateShapeInternal(IFCImportShapeEditScope shapeEditScope, Transform lcs, Transform scaledLcs, string guid) { using (BuilderScope bs = shapeEditScope.InitializeBuilder(IFCShapeBuilderType.TessellatedShapeBuilder)) { base.CreateShapeInternal(shapeEditScope, lcs, scaledLcs, guid); TessellatedShapeBuilderScope tsBuilderScope = bs as TessellatedShapeBuilderScope; tsBuilderScope.StartCollectingFaceSet(); // Create triangle face set from CoordIndex. We do not support the Normals yet at this point foreach (List <int> triIndex in CoordIndex) { // This is a defensive check in an unlikely situation that the index is larger than the data if (triIndex[0] > Coordinates.CoordList.Count || triIndex[1] > Coordinates.CoordList.Count || triIndex[2] > Coordinates.CoordList.Count) { continue; } tsBuilderScope.StartCollectingFace(GetMaterialElementId(shapeEditScope)); IList <XYZ> loopVertices = new List <XYZ>(); for (int ii = 0; ii < 3; ++ii) { int actualVIdx = triIndex[ii] - 1; if (PnIndex != null) { actualVIdx = PnIndex[actualVIdx] - 1; } IList <double> v = Coordinates.CoordList[actualVIdx]; loopVertices.Add(new XYZ(v[0], v[1], v[2])); } IList <XYZ> transformedVertices = new List <XYZ>(); foreach (XYZ vertex in loopVertices) { // Need to apply the project unit scaling here XYZ scaledVertex = applyProjectUnitScaleVertex(vertex); transformedVertices.Add(scaledLcs.OfPoint(scaledVertex)); } // Check triangle that is too narrow (2 vertices are within the tolerance IList <XYZ> validVertices; IFCGeometryUtil.CheckAnyDistanceVerticesWithinTolerance(Id, shapeEditScope, transformedVertices, out validVertices); // We are going to catch any exceptions if the loop is invalid. // We are going to hope that we can heal the parent object in the TessellatedShapeBuilder. bool bPotentiallyAbortFace = false; int count = validVertices.Count; if (validVertices.Count < 3) { Importer.TheLog.LogComment(Id, "Too few distinct loop vertices (" + count + "), ignoring.", false); bPotentiallyAbortFace = true; } else { if (!tsBuilderScope.AddLoopVertices(Id, validVertices)) { bPotentiallyAbortFace = true; } } if (bPotentiallyAbortFace) { tsBuilderScope.AbortCurrentFace(); } else { tsBuilderScope.StopCollectingFace(); } } IList <GeometryObject> createdGeometries = tsBuilderScope.CreateGeometry(guid); if (createdGeometries != null) { foreach (GeometryObject createdGeometry in createdGeometries) { shapeEditScope.AddGeometry(IFCSolidInfo.Create(Id, createdGeometry)); } } } }