/// <summary> /// Creates a facetation of a simple swept solid from a list of curve loops. /// </summary> /// <param name="exporterIFC">The exporter.</param> /// <param name="profileName">The profile name.</param> /// <param name="profileCurveLoops">The profile curve loops.</param> /// <param name="normal">The normal of the plane that the path lies on.</param> /// <param name="directrix">The path curve.</param> /// <returns>The list of facet handles.</returns> public static HashSet <IFCAnyHandle> CreateSimpleSweptSolidAsBRep(ExporterIFC exporterIFC, string profileName, IList <CurveLoop> profileCurveLoops, XYZ normal, Curve directrix) { // see definition of IfcSurfaceCurveSweptAreaSolid from // http://www.buildingsmart-tech.org/ifc/IFC2x4/rc4/html/schema/ifcgeometricmodelresource/lexical/ifcsurfacecurvesweptareasolid.htm HashSet <IFCAnyHandle> facetHnds = null; if (!CanCreateSimpleSweptSolid(profileCurveLoops, normal, directrix)) { return(facetHnds); } // An extra requirement, as we can't tessellate an unbound curve. if (!directrix.IsBound) { return(facetHnds); } double originalStartParam = directrix.GetEndParameter(0); Plane axisPlane, profilePlane; CreateAxisAndProfileCurvePlanes(directrix, originalStartParam, out axisPlane, out profilePlane); IList <CurveLoop> curveLoops = null; try { // Check that curve loops are valid. curveLoops = ExporterIFCUtils.ValidateCurveLoops(profileCurveLoops, profilePlane.Normal); } catch (Exception) { return(null); } if (curveLoops == null || curveLoops.Count == 0) { return(facetHnds); } // Tessellate the curve loops. We don't add the last point, as these should all be closed curves. IList <IList <XYZ> > tessellatedOutline = new List <IList <XYZ> >(); foreach (CurveLoop curveLoop in curveLoops) { List <XYZ> tessellatedCurve = new List <XYZ>(); foreach (Curve curve in curveLoop) { if (curve is Line) { AddScaledPointToList(exporterIFC, tessellatedCurve, curve.GetEndPoint(0)); } else { IList <XYZ> curveTessellation = CreateRoughTessellation(exporterIFC, curve); tessellatedCurve.AddRange(curveTessellation); } } if (tessellatedCurve.Count != 0) { tessellatedOutline.Add(tessellatedCurve); } } IFCFile file = exporterIFC.GetFile(); IList <IList <IList <IFCAnyHandle> > > facetVertexHandles = new List <IList <IList <IFCAnyHandle> > >(); IList <IList <IFCAnyHandle> > tessellatedOutlineHandles = new List <IList <IFCAnyHandle> >(); foreach (IList <XYZ> tessellatedOutlinePolygon in tessellatedOutline) { IList <IFCAnyHandle> tessellatedOutlinePolygonHandles = new List <IFCAnyHandle>(); foreach (XYZ tessellatedOutlineXYZ in tessellatedOutlinePolygon) { tessellatedOutlinePolygonHandles.Add(ExporterUtil.CreateCartesianPoint(file, tessellatedOutlineXYZ)); } tessellatedOutlineHandles.Add(tessellatedOutlinePolygonHandles); } facetVertexHandles.Add(tessellatedOutlineHandles); // Tessellate the Directrix. This only works for bound Directrix curves. Unfortunately, we get XYZ values, which we will have to convert // back to parameter values to get the local transform. IList <double> tessellatedDirectrixParameters = CreateRoughParametricTessellation(directrix); // Create all of the other outlines by transformng the first tessellated outline to the current transform. Transform profilePlaneTrf = Transform.CreateTranslation(ExporterIFCUtils.TransformAndScalePoint(exporterIFC, profilePlane.Origin)); profilePlaneTrf.BasisX = ExporterIFCUtils.TransformAndScaleVector(exporterIFC, profilePlane.XVec); profilePlaneTrf.BasisY = ExporterIFCUtils.TransformAndScaleVector(exporterIFC, profilePlane.YVec); profilePlaneTrf.BasisZ = ExporterIFCUtils.TransformAndScaleVector(exporterIFC, profilePlane.Normal); // The inverse transform will be applied to generate the delta transform for the profile curves from the start of the directrix // to the current location. This could be optimized in the case of a Line, but current usage is really only for a single arc. // If that changes, we should revisit optimization possibilities. Transform profilePlaneTrfInverse = profilePlaneTrf.Inverse; // Create the delta transforms and the offset tessellated profiles. foreach (double parameter in tessellatedDirectrixParameters) { Transform directrixDirs = CreateProfileCurveTransform(exporterIFC, directrix, parameter); Transform deltaTransform = directrixDirs.Multiply(profilePlaneTrfInverse); IList <IList <IFCAnyHandle> > currTessellatedOutline = new List <IList <IFCAnyHandle> >(); foreach (IList <XYZ> pointLoop in tessellatedOutline) { IList <IFCAnyHandle> currTessellatedPoinLoop = new List <IFCAnyHandle>(); foreach (XYZ point in pointLoop) { XYZ transformedPoint = deltaTransform.OfPoint(point); IFCAnyHandle transformedPointHandle = ExporterUtil.CreateCartesianPoint(file, transformedPoint); currTessellatedPoinLoop.Add(transformedPointHandle); } currTessellatedOutline.Add(currTessellatedPoinLoop); } facetVertexHandles.Add(currTessellatedOutline); } // Create the side facets. facetHnds = new HashSet <IFCAnyHandle>(); int numFacets = facetVertexHandles.Count - 1; for (int ii = 0; ii < numFacets; ii++) { IList <IList <IFCAnyHandle> > firstOutline = facetVertexHandles[ii]; IList <IList <IFCAnyHandle> > secondOutline = facetVertexHandles[ii + 1]; int numLoops = firstOutline.Count; for (int jj = 0; jj < numLoops; jj++) { IList <IFCAnyHandle> firstLoop = firstOutline[jj]; IList <IFCAnyHandle> secondLoop = secondOutline[jj]; int numVertices = firstLoop.Count; for (int kk = 0; kk < numVertices; kk++) { IList <IFCAnyHandle> polyLoopHandles = new List <IFCAnyHandle>(4); polyLoopHandles.Add(secondLoop[kk]); polyLoopHandles.Add(secondLoop[(kk + 1) % numVertices]); polyLoopHandles.Add(firstLoop[(kk + 1) % numVertices]); polyLoopHandles.Add(firstLoop[kk]); IFCAnyHandle face = BodyExporter.CreateFaceFromVertexList(file, polyLoopHandles); facetHnds.Add(face); } } } // Create the end facets. for (int ii = 0; ii < 2; ii++) { int faceIndex = (ii == 0) ? 0 : facetVertexHandles.Count - 1; int numLoops = facetVertexHandles[faceIndex].Count; HashSet <IFCAnyHandle> faceBounds = new HashSet <IFCAnyHandle>(); for (int jj = 0; jj < numLoops; jj++) { IList <IFCAnyHandle> polyLoopHandles = null; if (ii == 0) { polyLoopHandles = facetVertexHandles[faceIndex][jj]; } else { int numHandles = facetVertexHandles[faceIndex][jj].Count; polyLoopHandles = new List <IFCAnyHandle>(numHandles); for (int kk = numHandles - 1; kk >= 0; kk--) { polyLoopHandles.Add(facetVertexHandles[faceIndex][jj][kk]); } } IFCAnyHandle polyLoop = IFCInstanceExporter.CreatePolyLoop(file, polyLoopHandles); IFCAnyHandle faceBound = (jj == 0) ? IFCInstanceExporter.CreateFaceOuterBound(file, polyLoop, true) : IFCInstanceExporter.CreateFaceBound(file, polyLoop, true); faceBounds.Add(faceBound); } IFCAnyHandle face = IFCInstanceExporter.CreateFace(file, faceBounds); facetHnds.Add(face); } return(facetHnds); }
/// <summary> /// Creates a simple swept solid from a list of curve loops. /// </summary> /// <param name="exporterIFC">The exporter.</param> /// <param name="profileName">The profile name.</param> /// <param name="profileCurveLoops">The profile curve loops.</param> /// <param name="normal">The normal of the plane that the path lies on.</param> /// <param name="directrix">The path curve.</param> /// <returns>The swept solid handle.</returns> public static IFCAnyHandle CreateSimpleSweptSolid(ExporterIFC exporterIFC, string profileName, IList <CurveLoop> profileCurveLoops, XYZ normal, Curve directrix) { // see definition of IfcSurfaceCurveSweptAreaSolid from // http://www.buildingsmart-tech.org/ifc/IFC2x4/rc4/html/schema/ifcgeometricmodelresource/lexical/ifcsurfacecurvesweptareasolid.htm IFCAnyHandle simpleSweptSolidHnd = null; if (!CanCreateSimpleSweptSolid(profileCurveLoops, normal, directrix)) { return(simpleSweptSolidHnd); } bool isBound = directrix.IsBound; double originalStartParam = isBound ? directrix.GetEndParameter(0) : 0.0; Plane axisPlane, profilePlane; CreateAxisAndProfileCurvePlanes(directrix, originalStartParam, out axisPlane, out profilePlane); IList <CurveLoop> curveLoops = null; try { // Check that curve loops are valid. curveLoops = ExporterIFCUtils.ValidateCurveLoops(profileCurveLoops, profilePlane.Normal); } catch (Exception) { return(null); } if (curveLoops == null || curveLoops.Count == 0) { return(simpleSweptSolidHnd); } double startParam = 0.0, endParam = 1.0; if (directrix is Arc) { // This effectively resets the start parameter to 0.0, and end parameter = length of curve. if (isBound) { endParam = UnitUtil.ScaleAngle(MathUtil.PutInRange(directrix.GetEndParameter(1), Math.PI, 2 * Math.PI) - MathUtil.PutInRange(originalStartParam, Math.PI, 2 * Math.PI)); } else { endParam = 2.0 * Math.PI; } } // Start creating IFC entities. IFCAnyHandle sweptArea = ExtrusionExporter.CreateSweptArea(exporterIFC, profileName, curveLoops, profilePlane, profilePlane.Normal); if (IFCAnyHandleUtil.IsNullOrHasNoValue(sweptArea)) { return(simpleSweptSolidHnd); } IFCAnyHandle curveHandle = null; IFCAnyHandle referenceSurfaceHandle = ExtrusionExporter.CreateSurfaceOfLinearExtrusionFromCurve(exporterIFC, directrix, axisPlane, 1.0, 1.0, out curveHandle); // Should this be moved up? Check. Plane scaledAxisPlane = GeometryUtil.GetScaledPlane(exporterIFC, axisPlane); IFCFile file = exporterIFC.GetFile(); IFCAnyHandle solidAxis = ExporterUtil.CreateAxis(file, scaledAxisPlane.Origin, scaledAxisPlane.Normal, scaledAxisPlane.XVec); simpleSweptSolidHnd = IFCInstanceExporter.CreateSurfaceCurveSweptAreaSolid(file, sweptArea, solidAxis, curveHandle, startParam, endParam, referenceSurfaceHandle); return(simpleSweptSolidHnd); }
/// <summary> /// Creates a simple swept solid from a list of curve loops. /// </summary> /// <param name="exporterIFC">The exporter.</param> /// <param name="profileName">The profile name.</param> /// <param name="profileCurveLoops">The profile curve loops.</param> /// <param name="normal">The normal of the plane that the path lies on.</param> /// <param name="directrix">The path curve.</param> /// <returns>The swept solid handle.</returns> public static IFCAnyHandle CreateSimpleSweptSolid(ExporterIFC exporterIFC, string profileName, IList <CurveLoop> profileCurveLoops, XYZ normal, Curve directrix) { // see definition of IfcSurfaceCurveSweptAreaSolid from // http://www.buildingsmart-tech.org/ifc/IFC2x4/rc4/html/schema/ifcgeometricmodelresource/lexical/ifcsurfacecurvesweptareasolid.htm IFCAnyHandle simpleSweptSolidHnd = null; if (!CanCreateSimpleSweptSolid(profileCurveLoops, normal, directrix)) { return(simpleSweptSolidHnd); } bool isBound = directrix.IsBound; double originalStartParam = isBound ? directrix.GetEndParameter(0) : 0.0; Transform axisLCS, profileLCS; CreateAxisAndProfileCurveLCS(directrix, originalStartParam, out axisLCS, out profileLCS); IList <CurveLoop> curveLoops = null; try { // Check that curve loops are valid. curveLoops = ExporterIFCUtils.ValidateCurveLoops(profileCurveLoops, profileLCS.BasisZ); } catch (Exception) { return(null); } if (curveLoops == null || curveLoops.Count == 0) { return(simpleSweptSolidHnd); } double startParam = 0.0, endParam = 1.0; if (directrix is Arc) { // This effectively resets the start parameter to 0.0, and end parameter = length of curve. if (isBound) { // Put the parameters in range of [0, 2*Pi] double inRangeStarParam = (directrix.GetEndParameter(0) % (2 * Math.PI)); double inRangeEndParam = (directrix.GetEndParameter(1) % (2 * Math.PI)); // We want the angle direction is anti-clockwise (+ direction), therefore we will always start with the smaller one if (inRangeEndParam < inRangeStarParam) { double tmp = inRangeStarParam; inRangeStarParam = inRangeEndParam; inRangeEndParam = tmp; } // If start param is negative, we will reset it to 0 and shift the end accordingly if (inRangeStarParam < 0) { double parRange = inRangeEndParam - inRangeStarParam; inRangeStarParam = 0.0; inRangeEndParam = parRange; } endParam = UnitUtil.ScaleAngle(inRangeEndParam); //endParam = UnitUtil.ScaleAngle(MathUtil.PutInRange(directrix.GetEndParameter(1), Math.PI, 2 * Math.PI) - // MathUtil.PutInRange(originalStartParam, Math.PI, 2 * Math.PI)); } else { endParam = 2.0 * Math.PI; } } // Start creating IFC entities. IFCAnyHandle sweptArea = ExtrusionExporter.CreateSweptArea(exporterIFC, profileName, curveLoops, profileLCS, profileLCS.BasisZ); if (IFCAnyHandleUtil.IsNullOrHasNoValue(sweptArea)) { return(simpleSweptSolidHnd); } IFCAnyHandle curveHandle = null; IFCAnyHandle referenceSurfaceHandle = ExtrusionExporter.CreateSurfaceOfLinearExtrusionFromCurve(exporterIFC, directrix, axisLCS, 1.0, 1.0, out curveHandle); // Should this be moved up? Check. XYZ scaledOrigin = ExporterIFCUtils.TransformAndScalePoint(exporterIFC, axisLCS.Origin); XYZ scaledXDir = ExporterIFCUtils.TransformAndScaleVector(exporterIFC, axisLCS.BasisX).Normalize(); XYZ scaledNormal = ExporterIFCUtils.TransformAndScaleVector(exporterIFC, axisLCS.BasisZ).Normalize(); IFCFile file = exporterIFC.GetFile(); IFCAnyHandle solidAxis = ExporterUtil.CreateAxis(file, scaledOrigin, scaledNormal, scaledXDir); simpleSweptSolidHnd = IFCInstanceExporter.CreateSurfaceCurveSweptAreaSolid(file, sweptArea, solidAxis, curveHandle, startParam, endParam, referenceSurfaceHandle); return(simpleSweptSolidHnd); }
/// <summary> /// 对曲线循环列表执行有效性检查,以确保它们全部共面,闭合且方向正确 /// more info :https://thebuildingcoder.typepad.com/blog/2015/01/exporterifcutils-curve-loop-sort-and-validate.html /// </summary> /// <param name="curveLoops">要检查的循环</param> /// <param name="extrDirVec">法向量</param> /// <returns> /// 如果可能,返回正确定向的曲线环。 /// <!!经测试,该方法不可靠,经常返回空!!> /// </returns> public static IList <CurveLoop> ValidateCurveLoops(this IList <CurveLoop> curveLoops, XYZ extrDirVec) { return(ExporterIFCUtils.ValidateCurveLoops(curveLoops, extrDirVec)); }
/// <summary> /// Creates a simple swept solid from a list of curve loops. /// </summary> /// <param name="exporterIFC">The exporter.</param> /// <param name="profileName">The profile name.</param> /// <param name="profileCurveLoops">The profile curve loops.</param> /// <param name="normal">The normal of the plane that the path lies on.</param> /// <param name="directrix">The path curve.</param> /// <returns>The swept solid handle.</returns> public static IFCAnyHandle CreateSimpleSweptSolid(ExporterIFC exporterIFC, string profileName, IList <CurveLoop> profileCurveLoops, XYZ normal, Curve directrix) { // see definition of IfcSurfaceCurveSweptAreaSolid from // http://www.buildingsmart-tech.org/ifc/IFC2x4/rc4/html/schema/ifcgeometricmodelresource/lexical/ifcsurfacecurvesweptareasolid.htm IFCAnyHandle simpleSweptSolidHnd = null; if (profileCurveLoops.Count == 0) { return(simpleSweptSolidHnd); } IFCFile file = exporterIFC.GetFile(); XYZ startPoint = directrix.get_EndPoint(0); XYZ profilePlaneNormal = null; XYZ profilePlaneXDir = null; XYZ profilePlaneYDir = null; IFCAnyHandle curveHandle = null; double startParam = 0, endParam = 1; Plane scaledReferencePlane = null; if (directrix is Line) { Line line = directrix as Line; startParam = 0.0; endParam = 1.0; profilePlaneNormal = line.Direction; profilePlaneYDir = normal; profilePlaneXDir = profilePlaneNormal.CrossProduct(profilePlaneYDir); XYZ linePlaneNormal = profilePlaneYDir; XYZ linePlaneXDir = profilePlaneXDir; XYZ linePlaneYDir = linePlaneNormal.CrossProduct(linePlaneXDir); XYZ linePlaneOrig = startPoint; scaledReferencePlane = GeometryUtil.CreateScaledPlane(exporterIFC, linePlaneXDir, linePlaneYDir, linePlaneOrig); curveHandle = GeometryUtil.CreateLine(exporterIFC, line, scaledReferencePlane); } else if (directrix is Arc) { Arc arc = directrix as Arc; // profilePlaneXDir is set relative to the startPoint of the directrix. This effectively resets the start parameter to 0.0, and end parameter = length of curve. startParam = 0.0; endParam = (MathUtil.PutInRange(arc.get_EndParameter(1), Math.PI, 2 * Math.PI) - MathUtil.PutInRange(arc.get_EndParameter(0), Math.PI, 2 * Math.PI)) * (180 / Math.PI); profilePlaneNormal = directrix.ComputeDerivatives(0, true).BasisX; profilePlaneXDir = (arc.Center - startPoint).Normalize(); profilePlaneYDir = profilePlaneNormal.CrossProduct(profilePlaneXDir).Normalize(); XYZ arcPlaneNormal = arc.Normal; XYZ arcPlaneXDir = profilePlaneXDir; XYZ arcPlaneYDir = arcPlaneNormal.CrossProduct(arcPlaneXDir); XYZ arcPlaneOrig = startPoint; scaledReferencePlane = GeometryUtil.CreateScaledPlane(exporterIFC, arcPlaneXDir, arcPlaneYDir, arcPlaneOrig); curveHandle = GeometryUtil.CreateArc(exporterIFC, arc, scaledReferencePlane); } else { return(simpleSweptSolidHnd); } IFCAnyHandle referencePlaneAxisHandle = ExporterUtil.CreateAxis(file, scaledReferencePlane.Origin, scaledReferencePlane.Normal, scaledReferencePlane.XVec); IFCAnyHandle referencePlaneHandle = IFCInstanceExporter.CreatePlane(file, referencePlaneAxisHandle); Plane profilePlane = new Plane(profilePlaneXDir, profilePlaneYDir, startPoint); IList <CurveLoop> curveLoops = null; try { // Check that curve loops are valid. curveLoops = ExporterIFCUtils.ValidateCurveLoops(profileCurveLoops, profilePlaneNormal); } catch (Exception) { return(null); } if (curveLoops == null || curveLoops.Count == 0) { return(simpleSweptSolidHnd); } IFCAnyHandle sweptArea = ExtrusionExporter.CreateSweptArea(exporterIFC, profileName, curveLoops, profilePlane, profilePlaneNormal); if (IFCAnyHandleUtil.IsNullOrHasNoValue(sweptArea)) { return(simpleSweptSolidHnd); } profilePlane = GeometryUtil.GetScaledPlane(exporterIFC, profilePlane); IFCAnyHandle solidAxis = ExporterUtil.CreateAxis(file, profilePlane.Origin, profilePlane.Normal, profilePlane.XVec); simpleSweptSolidHnd = IFCInstanceExporter.CreateSurfaceCurveSweptAreaSolid(file, sweptArea, solidAxis, curveHandle, startParam, endParam, referencePlaneHandle); return(simpleSweptSolidHnd); }