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); }
/// <summary> /// 获得 CurveLoop 的所有点 /// </summary> /// <param name="loop"></param> /// <returns></returns> public static IList <XYZ> ToPointsList(this CurveLoop loop) { int n = loop.Count(); List <XYZ> polygon = new List <XYZ>(); foreach (Curve e in loop) { IList <XYZ> pts = e.Tessellate(); n = polygon.Count; if (0 < n) { //Debug.Assert(pts[0].IsAlmostEqualTo(polygon[n - 1]), // "expected last edge end point to equal next edge start point"); polygon.RemoveAt(n - 1); } polygon.AddRange(pts); } n = polygon.Count; //Debug.Assert(polygon[0].IsAlmostEqualTo(polygon[n - 1]), // "expected first edge start point to equal last edge end point"); polygon.RemoveAt(n - 1); return(polygon); }
/// <summary> /// Converts the CurveArray to the CurveLoop. /// </summary> /// <param name="curves"></param> /// <returns></returns> public static CurveLoop ToCurveLoop <T>(this IEnumerable <T> curves) where T : Curve { var newCurves = curves.ToList(); if (curves == null || newCurves.Count == 0) { throw new NullReferenceException(nameof(newCurves)); } var results = new CurveLoop(); var endPt = newCurves[0]?.GetEndPoint(1); results.Append(newCurves[0]); newCurves[0] = null; // If computing count equals curveLoop count, it should break. // Because, the curveLoop cannot find valid curve to append. var count = 0; while (count < newCurves.Count && results.Count() < newCurves.Count) { for (var i = 0; i < newCurves.Count; i++) { if (newCurves[i] == null) { continue; } var p0 = newCurves[i].GetEndPoint(0); var p1 = newCurves[i].GetEndPoint(1); if (p0.IsAlmostEqualTo(endPt)) { endPt = p1; results.Append(newCurves[i]); newCurves[i] = null; } // The curve should be reversed. else if (p1.IsAlmostEqualTo(endPt)) { endPt = p0; results.Append(newCurves[i].CreateReversed()); newCurves[i] = null; } } count++; } return(results); }
/// <summary> /// Attempt to create a single curve from a curve loop composed of linear segments. /// </summary> /// <param name="curveLoop">The curve loop.</param> /// <param name="pointXYZs">The original points from which the curve loop was created.</param> /// <returns>The curve, if the curve loop is linear, or null.</returns> /// <remarks>Note that the routine does not actually check that the curveLoop is composed /// of line segments, or that the point array matches the curve loop in any way.</remarks> public static Curve CreateCurveFromPolyCurveLoop(CurveLoop curveLoop, IList <XYZ> pointXYZs) { if (curveLoop == null) { return(null); } int numCurves = curveLoop.Count(); if (numCurves == 0) { return(null); } if (numCurves == 1) { Curve originalCurve = curveLoop.First(); if (originalCurve != null) { return(originalCurve.Clone()); } return(null); } if (pointXYZs == null) { return(null); } int numPoints = pointXYZs.Count; // If we are here, we are sure that the number of points 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) { return(Line.CreateBound(firstPoint, pointXYZs[numPoints - 1])); } return(null); }
private static bool CurveLoopIsARectangle(CurveLoop curveLoop, out IList<int> cornerIndices) { cornerIndices = new List<int>(4); // looking for four orthogonal lines in one curve loop. int sz = curveLoop.Count(); if (sz < 4) return false; IList<Line> lines = new List<Line>(); foreach (Curve curve in curveLoop) { if (!(curve is Line)) return false; lines.Add(curve as Line); } sz = lines.Count; int numAngles = 0; // Must have 4 right angles found, and all other lines collinear -- if not, not a rectangle. for (int ii = 0; ii < sz; ii++) { double dot = lines[ii].Direction.DotProduct(lines[(ii + 1) % sz].Direction); if (MathUtil.IsAlmostZero(dot)) { if (numAngles > 3) return false; cornerIndices.Add(ii); numAngles++; } else if (MathUtil.IsAlmostEqual(dot, 1.0)) { XYZ line0End1 = lines[ii].GetEndPoint(1); XYZ line1End0 = lines[(ii + 1) % sz].GetEndPoint(0); if (!line0End1.IsAlmostEqualTo(line1End0)) return false; } else return false; } return (numAngles == 4); }
/// <summary> /// Returns the surface which defines the internal shape of the face /// </summary> /// <param name="lcs">The local coordinate system for the surface. Can be null.</param> /// <returns>The surface which defines the internal shape of the face</returns> public override Surface GetSurface(Transform lcs) { if (SweptCurve == null) { Importer.TheLog.LogError(Id, "Cannot find the profile curve of this revolved face.", true); } IFCSimpleProfile simpleProfile = SweptCurve as IFCSimpleProfile; if (simpleProfile == null) { Importer.TheLog.LogError(Id, "Can't handle profile curve of type " + SweptCurve.GetType() + ".", true); } CurveLoop outerCurve = simpleProfile.OuterCurve; Curve profileCurve = (outerCurve != null) ? outerCurve.First <Curve>() : null; if (profileCurve == null) { Importer.TheLog.LogError(Id, "Cannot create the profile curve of this revolved surface.", true); } if (outerCurve.Count() > 1) { Importer.TheLog.LogError(Id, "Revolved surface has multiple profile curves, ignoring all but first.", false); } Curve revolvedSurfaceProfileCurve = profileCurve.CreateTransformed(Position); if (!RevolvedSurface.IsValidProfileCurve(AxisPosition.Origin, AxisPosition.BasisZ, revolvedSurfaceProfileCurve)) { Importer.TheLog.LogError(Id, "Profile curve is invalid for this revolved surface.", true); } if (lcs == null) { return(RevolvedSurface.Create(AxisPosition.Origin, AxisPosition.BasisZ, revolvedSurfaceProfileCurve)); } Curve transformedRevolvedSurfaceProfileCurve = revolvedSurfaceProfileCurve.CreateTransformed(lcs); return(RevolvedSurface.Create(lcs.OfPoint(AxisPosition.Origin), lcs.OfVector(AxisPosition.BasisZ), transformedRevolvedSurfaceProfileCurve)); }
public bool FirstPointIsInsideFace( CurveLoop CL, PlanarFace PFace) { Transform Trans = PFace.ComputeDerivatives( new UV(0, 0)); if (CL.Count() == 0) { return(false); } XYZ Pt = Trans.Inverse.OfPoint( CL.ToList()[0].GetEndPoint(0)); IntersectionResult Res = null; bool outval = PFace.IsInside( new UV(Pt.X, Pt.Y), out Res); return(outval); }
/// <summary> /// Get the local surface transform at a given point on the surface. /// </summary> /// <param name="pointOnSurface">The point.</param> /// <returns>The transform.</returns> /// <remarks>This does not include the translation component.</remarks> public override Transform GetTransformAtPoint(XYZ pointOnSurface) { if (!(SweptCurve is IFCSimpleProfile)) { // LOG: ERROR: warn that we only support simple profiles. return(null); } CurveLoop outerCurveLoop = (SweptCurve as IFCSimpleProfile).OuterCurve; if (outerCurveLoop == null || outerCurveLoop.Count() != 1) { // LOG: ERROR return(null); } Curve outerCurve = outerCurveLoop.First(); if (outerCurve == null) { // LOG: ERROR return(null); } IntersectionResult result = outerCurve.Project(pointOnSurface); if (result == null) { // LOG: ERROR return(null); } double parameter = result.Parameter; Transform atPoint = outerCurve.ComputeDerivatives(parameter, false); atPoint.set_Basis(0, atPoint.BasisX.Normalize()); atPoint.set_Basis(1, atPoint.BasisY.Normalize()); atPoint.set_Basis(2, atPoint.BasisZ.Normalize()); atPoint.Origin = pointOnSurface; return(atPoint); }
/// <returns>true if the curve loop is clockwise, false otherwise.</returns> private static bool SafeIsCurveLoopClockwise(CurveLoop curveLoop, XYZ dir) { if (curveLoop == null) return false; if (curveLoop.IsOpen()) return false; if ((curveLoop.Count() == 1) && !(curveLoop.First().IsBound)) return false; return !curveLoop.IsCounterclockwise(dir); }
private IList<GeometryObject> SplitSweptDiskIntoValidPieces(CurveLoop trimmedDirectrixInWCS, IList<CurveLoop> profileCurveLoops, SolidOptions solidOptions) { // If we have 0 or 1 curves, there is nothing we can do here. int numCurves = trimmedDirectrixInWCS.Count(); if (numCurves < 2) return null; // We will attempt to represent the original description in as few pieces as possible. IList<Curve> directrixCurves = new List<Curve>(); foreach (Curve directrixCurve in trimmedDirectrixInWCS) { if (directrixCurve == null) { numCurves--; if (numCurves < 2) return null; continue; } directrixCurves.Add(directrixCurve); } IList<GeometryObject> sweptDiskPieces = new List<GeometryObject>(); // We will march along the directrix one curve at a time, trying to build a bigger piece of the sweep. At the point that we throw an exception, // we will take the last biggest piece and start over. CurveLoop currentCurveLoop = new CurveLoop(); Solid bestSolidSoFar = null; double pathAttachmentParam = directrixCurves[0].GetEndParameter(0); for (int ii = 0; ii < numCurves; ii++) { currentCurveLoop.Append(directrixCurves[ii]); try { Solid currentSolid = GeometryCreationUtilities.CreateSweptGeometry(currentCurveLoop, 0, pathAttachmentParam, profileCurveLoops, solidOptions); bestSolidSoFar = currentSolid; } catch { if (bestSolidSoFar != null) { sweptDiskPieces.Add(bestSolidSoFar); bestSolidSoFar = null; } } // This should only happen as a result of the catch loop above. We want to protect against the case where one or more pieces of the sweep // are completely invalid. while (bestSolidSoFar == null && (ii < numCurves)) { try { currentCurveLoop = new CurveLoop(); currentCurveLoop.Append(directrixCurves[ii]); profileCurveLoops = CreateProfileCurveLoopsForDirectrix(directrixCurves[ii], out pathAttachmentParam); Solid currentSolid = GeometryCreationUtilities.CreateSweptGeometry(currentCurveLoop, 0, pathAttachmentParam, profileCurveLoops, solidOptions); bestSolidSoFar = currentSolid; break; } catch { ii++; } } } return sweptDiskPieces; }
/// <summary> /// Get the curve from the Axis representation of the given IfcProduct, transformed to the current local coordinate system. /// </summary> /// <param name="creator">The IfcProduct that may or may not contain a valid axis curve.</param> /// <param name="lcs">The local coordinate system.</param> /// <returns>The axis curve, if found, and valid.</returns> /// <remarks>In this case, we only allow bounded lines and arcs to be valid axis curves, as per IFC2x3 convention. /// The Curve may be contained as either a single Curve in the IFCCurve representation item, or it could be an /// open CurveLoop with one item.</remarks> private Curve GetAxisCurve(IFCProduct creator, Transform lcs) { // We need an axis curve to clip the extrusion profiles; if we can't get one, fail IFCProductRepresentation productRepresentation = creator.ProductRepresentation; if (productRepresentation == null) { return(null); } IList <IFCRepresentation> representations = productRepresentation.Representations; if (representations == null) { return(null); } foreach (IFCRepresentation representation in representations) { // Go through the list of representations for this product, to find the Axis representation. if (representation == null || representation.Identifier != IFCRepresentationIdentifier.Axis) { continue; } IList <IFCRepresentationItem> items = representation.RepresentationItems; if (items == null) { continue; } // Go through the list of representation items in the Axis representation, to look for the IfcCurve. foreach (IFCRepresentationItem item in items) { if (item is IFCCurve) { // We will accept either a bounded Curve of type Line or Arc, // or an open CurveLoop with one curve that satisfies the same condition. IFCCurve ifcCurve = item as IFCCurve; Curve axisCurve = ifcCurve.Curve; if (axisCurve == null) { CurveLoop axisCurveLoop = ifcCurve.CurveLoop; if (axisCurveLoop != null && axisCurveLoop.IsOpen() && axisCurveLoop.Count() == 1) { axisCurve = axisCurveLoop.First(); if (!(axisCurve is Line || axisCurve is Arc)) { axisCurve = null; } } } if (axisCurve != null) { return(axisCurve.CreateTransformed(lcs)); } } } } return(null); }
public Result Execute( ExternalCommandData commandData, ref string message, ElementSet elements) { UIApplication uiapp = commandData.Application; UIDocument uidoc = uiapp.ActiveUIDocument; Application app = uiapp.Application; Document doc = uidoc.Document; ICollection <ElementId> selectedElementsId = uidoc.Selection.GetElementIds(); List <View> selectedViews = new List <View>(); foreach (ElementId eid in selectedElementsId) { View current = doc.GetElement(eid) as View; selectedViews.Add(current); } int count = 0; string error = ""; var form = new Forms.FormPickFromDropDown(); using (Transaction t = new Transaction(doc, "Resize viewport")) { if (selectedViews.Count > 1) { form.ViewSectionList = new ObservableCollection <View>(selectedViews.OrderBy(x => x.Name)); form.ShowDialog(); } else { TaskDialog.Show("Error", "Please select some Section Views first."); return(Result.Cancelled); } if (form.DialogResult == false) { return(Result.Cancelled); } View sourceView = form.SelectedViewSection; BoundingBoxXYZ sourceViewBB = sourceView.CropBox; ViewCropRegionShapeManager vcrSource = sourceView.GetCropRegionShapeManager(); CurveLoop sourceLoop = vcrSource.GetCropShape()[0]; t.Start(); foreach (ElementId eid in selectedElementsId) { View destinationView = doc.GetElement(eid) as View; try { //The origin of the cropbox (from Transform) is not the same for different Sections. Use CropRegionShapeManager instead //destinationView.CropBox.Transform.Origin = sourceViewBB.Transform.Origin; //destinationView.CropBox = sourceViewBB; ViewCropRegionShapeManager destinationVcr = destinationView.GetCropRegionShapeManager(); destinationVcr.SetCropShape(sourceLoop); //if the crop is rectangular if (sourceLoop.Count() == 4) { destinationVcr.RemoveCropRegionShape(); //Reset Crop after it has been set } count++; } catch { error += $"Error processing view: {destinationView.Name}\n"; } } t.Commit(); } TaskDialog.Show("Result", $"{count}/{selectedElementsId.Count} viewport updated. \n{error}"); return(Result.Succeeded); }
private IList <GeometryObject> SplitSweptDiskIntoValidPieces(CurveLoop trimmedDirectrixInWCS, IList <CurveLoop> profileCurveLoops, SolidOptions solidOptions) { // If we have 0 or 1 curves, there is nothing we can do here. int numCurves = trimmedDirectrixInWCS.Count(); if (numCurves < 2) { return(null); } // We will attempt to represent the original description in as few pieces as possible. IList <Curve> directrixCurves = new List <Curve>(); foreach (Curve directrixCurve in trimmedDirectrixInWCS) { if (directrixCurve == null) { numCurves--; if (numCurves < 2) { return(null); } continue; } directrixCurves.Add(directrixCurve); } IList <GeometryObject> sweptDiskPieces = new List <GeometryObject>(); // We will march along the directrix one curve at a time, trying to build a bigger piece of the sweep. At the point that we throw an exception, // we will take the last biggest piece and start over. CurveLoop currentCurveLoop = new CurveLoop(); Solid bestSolidSoFar = null; double pathAttachmentParam = directrixCurves[0].GetEndParameter(0); for (int ii = 0; ii < numCurves; ii++) { currentCurveLoop.Append(directrixCurves[ii]); try { Solid currentSolid = GeometryCreationUtilities.CreateSweptGeometry(currentCurveLoop, 0, pathAttachmentParam, profileCurveLoops, solidOptions); bestSolidSoFar = currentSolid; } catch { if (bestSolidSoFar != null) { sweptDiskPieces.Add(bestSolidSoFar); bestSolidSoFar = null; } } // This should only happen as a result of the catch loop above. We want to protect against the case where one or more pieces of the sweep // are completely invalid. while (bestSolidSoFar == null && (ii < numCurves)) { try { currentCurveLoop = new CurveLoop(); currentCurveLoop.Append(directrixCurves[ii]); profileCurveLoops = CreateProfileCurveLoopsForDirectrix(directrixCurves[ii], out pathAttachmentParam); Solid currentSolid = GeometryCreationUtilities.CreateSweptGeometry(currentCurveLoop, 0, pathAttachmentParam, profileCurveLoops, solidOptions); bestSolidSoFar = currentSolid; break; } catch { ii++; } } } return(sweptDiskPieces); }
public static int NumberOfCurves(this CurveLoop curveLoop) { return(curveLoop.Count()); }
CurveLoop GetOuterLoopOfRoomFromCreateViaOffset( View view, IList <IList <BoundarySegment> > sloops) { Document doc = view.Document; CurveLoop loop = null; IList <double> wallthicknessList = new List <double>(); foreach (IList <BoundarySegment> sloop in sloops) { loop = new CurveLoop(); foreach (BoundarySegment s in sloop) { loop.Append(s.GetCurve()); ElementType type = doc.GetElement( s.ElementId) as ElementType; Element e = doc.GetElement(s.ElementId); double thickness = (e is Wall) ? (e as Wall).Width : 0; // Room separator; any other exceptions need including?? wallthicknessList.Add(thickness * _wall_width_factor); } // Skip out after first sloop - ignore // rooms with holes and disjunct parts break; } int n = loop.Count(); string slength = string.Join(",", loop.Select <Curve, string>( c => c.Length.ToString("#.##"))); int m = wallthicknessList.Count(); string sthickness = string.Join(",", wallthicknessList.Select <double, string>( d => d.ToString("#.##"))); Debug.Print( "{0} curves with lengths {1} and {2} thicknesses {3}", n, slength, m, sthickness); CreateModelCurves(view, loop); bool flip_normal = true; XYZ normal = flip_normal ? -XYZ.BasisZ : XYZ.BasisZ; CurveLoop room_outer_loop = CurveLoop.CreateViaOffset( loop, wallthicknessList, normal); CreateModelCurves(view, room_outer_loop); //CurveLoop newloop = new CurveLoop(); //foreach( Curve curve in loop2 ) //{ // IList<XYZ> points = curve.Tessellate(); // for( int ip = 0; ip < points.Count - 1; ip++ ) // { // Line l = Line.CreateBound( // points[ ip ], points[ ip + 1 ] ); // newloop.Append( l ); // } //} return(room_outer_loop); }
private static bool GetCalloutCornerPoints(Document doc, ElementId calloutId, out XYZ firstPoint, out XYZ secondPoint) { bool result = false; firstPoint = new XYZ(0, 0, 0); secondPoint = new XYZ(0, 0, 0); try { double minX = double.MaxValue; double minY = double.MaxValue; double minZ = double.MaxValue; double maxX = double.MinValue; double maxY = double.MinValue; double maxZ = double.MinValue; ViewCropRegionShapeManager cropRegion = View.GetCropRegionShapeManagerForReferenceCallout(doc, calloutId); #if RELEASE2016 || RELEASE2017 IList <CurveLoop> curveLoops = cropRegion.GetCropShape(); foreach (CurveLoop cLoop in curveLoops) { foreach (Curve curve in cLoop) { XYZ point = curve.GetEndPoint(0); if (point.X < minX) { minX = point.X; } if (point.Y < minY) { minY = point.Y; } if (point.Z < minZ) { minZ = point.Z; } if (point.X > maxX) { maxX = point.X; } if (point.Y > maxY) { maxY = point.Y; } if (point.Z > maxZ) { maxZ = point.Z; } } } if (curveLoops.Count() > 0) { firstPoint = new XYZ(minX, minY, minZ); secondPoint = new XYZ(maxX, maxY, maxZ); result = true; } #else CurveLoop curveLoop = cropRegion.GetCropRegionShape(); foreach (Curve curve in curveLoop) { XYZ point = curve.GetEndPoint(0); if (point.X < minX) { minX = point.X; } if (point.Y < minY) { minY = point.Y; } if (point.Z < minZ) { minZ = point.Z; } if (point.X > maxX) { maxX = point.X; } if (point.Y > maxY) { maxY = point.Y; } if (point.Z > maxZ) { maxZ = point.Z; } } if (curveLoop.Count() > 0) { firstPoint = new XYZ(minX, minY, minZ); secondPoint = new XYZ(maxX, maxY, maxZ); result = true; } #endif } catch (Exception ex) { errorMessage.AppendLine("Failed to get corner points of callout.\n" + ex.Message); //MessageBox.Show("Failed to get corner points of callout\n" + ex.Message, "Get Corner Points of Callout", MessageBoxButton.OK, MessageBoxImage.Warning); } return(result); }
/// <summary> /// Trims the CurveLoop contained in an IFCCurve by the start and optional end parameter values. /// </summary> /// <param name="id">The id of the IFC entity containing the directrix, for messaging purposes.</param> /// <param name="ifcCurve">The IFCCurve entity containing the CurveLoop to be trimmed.</param> /// <param name="startVal">The starting trim parameter.</param> /// <param name="origEndVal">The optional end trim parameter. If not supplied, assume no end trim.</param> /// <returns>The original curve loop, if no trimming has been done, otherwise a trimmed copy.</returns> private static CurveLoop TrimCurveLoop(int id, IFCCurve ifcCurve, double startVal, double?origEndVal) { CurveLoop origCurveLoop = ifcCurve.GetTheCurveLoop(); if (origCurveLoop == null || origCurveLoop.Count() == 0) { return(null); } // Trivial case: unbound curve. Curve possiblyUnboundCurve = origCurveLoop.First(); if (!possiblyUnboundCurve.IsBound) { if (!origEndVal.HasValue) { Importer.TheLog.LogError(id, "Can't trim unbound curve with no given end parameter.", true); } CurveLoop boundCurveLoop = new CurveLoop(); Curve boundCurve = possiblyUnboundCurve.Clone(); boundCurve.MakeBound(startVal, origEndVal.Value * ifcCurve.ParametericScaling); boundCurveLoop.Append(boundCurve); return(boundCurveLoop); } IList <double> curveLengths = new List <double>(); IList <Curve> loopCurves = new List <Curve>(); double totalParamLength = 0.0; bool allLines = true; foreach (Curve curve in origCurveLoop) { if (!(curve is Line)) { allLines = false; } double curveLength = curve.GetEndParameter(1) - curve.GetEndParameter(0); double currLength = ScaleCurveLengthForSweptSolid(curve, curveLength); loopCurves.Add(curve); curveLengths.Add(currLength); totalParamLength += currLength; } double endVal = origEndVal.HasValue ? origEndVal.Value : totalParamLength; double eps = MathUtil.Eps(); if (!CheckIfTrimParametersAreNeeded(id, ifcCurve, startVal, endVal, totalParamLength)) { return(origCurveLoop); } // Special cases: // if startval = 0 and endval = 1 and we have a polyline, then this likely means that the importing application // incorrectly set the extents to be the "whole" curve, when really this is just a portion of the curves // (the parametrization is described above). // As such, if the totalParamLength is not 1 but startVal = 0 and endVal = 1, we will warn but not trim. // This is not a hypothetical case: it occurs in several AllPlan 2017 files at least. if (allLines) { if ((!MathUtil.IsAlmostEqual(totalParamLength, 1.0)) && (MathUtil.IsAlmostZero(startVal) && MathUtil.IsAlmostEqual(endVal, 1.0))) { Importer.TheLog.LogWarning(id, "The Start Parameter for the trimming of this curve was set to 0, and the End Parameter was set to 1. " + "Most likely, this is an error in the sending application, and the trim extents are being ignored. " + "If this trim was intended, please contact Autodesk.", true); return(origCurveLoop); } } int numCurves = loopCurves.Count; double currentPosition = 0.0; int currCurve = 0; IList <Curve> newLoopCurves = new List <Curve>(); if (startVal > MathUtil.Eps()) { for (; currCurve < numCurves; currCurve++) { if (currentPosition + curveLengths[currCurve] < startVal + eps) { currentPosition += curveLengths[currCurve]; continue; } Curve newCurve = loopCurves[currCurve].Clone(); if (!MathUtil.IsAlmostEqual(currentPosition, startVal)) { double startParam = UnscaleSweptSolidCurveParam(loopCurves[currCurve], startVal - currentPosition); double endParam = newCurve.GetEndParameter(1); AdjustParamsIfNecessary(newCurve, ref startParam, ref endParam); newCurve.MakeBound(startParam, endParam); } newLoopCurves.Add(newCurve); break; } } if (endVal < totalParamLength - eps) { for (; currCurve < numCurves; currCurve++) { if (currentPosition + curveLengths[currCurve] < endVal - eps) { currentPosition += curveLengths[currCurve]; newLoopCurves.Add(loopCurves[currCurve]); continue; } Curve newCurve = loopCurves[currCurve].Clone(); if (!MathUtil.IsAlmostEqual(currentPosition + curveLengths[currCurve], endVal)) { double startParam = newCurve.GetEndParameter(0); double endParam = UnscaleSweptSolidCurveParam(loopCurves[currCurve], endVal - currentPosition); AdjustParamsIfNecessary(newCurve, ref startParam, ref endParam); newCurve.MakeBound(startParam, endParam); } newLoopCurves.Add(newCurve); break; } } CurveLoop trimmedCurveLoop = new CurveLoop(); foreach (Curve curve in newLoopCurves) { trimmedCurveLoop.Append(curve); } return(trimmedCurveLoop); }
/// <summary> /// Determines if a curveloop can be exported as an I-Shape profile. /// </summary> /// <param name="exporterIFC">The exporter.</param> /// <param name="profileName">The name of the profile.</param> /// <param name="curveLoop">The curve loop.</param> /// <param name="origPlane">The plane of the loop.</param> /// <param name="projDir">The projection direction.</param> /// <returns>The IfcIShapeProfileDef, or null if not possible.</returns> /// <remarks>This routine works with I-shaped curveloops projected onto origPlane, in either orientation; /// it does not work with H-shaped curveloops.</remarks> private static IFCAnyHandle CreateIShapeProfileDefIfPossible(ExporterIFC exporterIFC, string profileName, CurveLoop curveLoop, Plane origPlane, XYZ projDir) { IFCFile file = exporterIFC.GetFile(); if (curveLoop.IsOpen()) return null; if (curveLoop.Count() != 12 && curveLoop.Count() != 16) return null; // All curves must be lines, except for 4 optional fillets; get direction vectors and start points. XYZ xDir = origPlane.XVec; XYZ yDir = origPlane.YVec; // The list of vertices, in order. startVertex below is the upper-right hand vertex, in UV-space. IList<UV> vertices = new List<UV>(); // The directions in UV of the line segments. directions[ii] is the direction of the line segment starting with vertex[ii]. IList<UV> directions = new List<UV>(); // The lengths in UV of the line segments. lengths[ii] is the length of the line segment starting with vertex[ii]. IList<double> lengths = new List<double>(); // turnsCCW[ii] is true if directions[ii+1] is clockwise relative to directions[ii] in UV-space. IList<bool> turnsCCW = new List<bool>(); IList<Arc> fillets = new List<Arc>(); IList<int> filletPositions = new List<int>(); int idx = 0; int startVertex = -1; int startFillet = -1; UV upperRight = null; double lowerBoundU = 1e+30; double upperBoundU = -1e+30; foreach (Curve curve in curveLoop) { if (!(curve is Line)) { if (!(curve is Arc)) return null; fillets.Add(curve as Arc); filletPositions.Add(idx); // share the index of the next line segment. continue; } Line line = curve as Line; XYZ point = line.GetEndPoint(0); UV pointProjUV = GeometryUtil.ProjectPointToPlane(origPlane, projDir, point); if (pointProjUV == null) return null; pointProjUV = UnitUtil.ScaleLength(pointProjUV); if ((upperRight == null) || ((pointProjUV.U > upperRight.U - MathUtil.Eps()) && (pointProjUV.V > upperRight.V - MathUtil.Eps()))) { upperRight = pointProjUV; startVertex = idx; startFillet = filletPositions.Count; } if (pointProjUV.U < lowerBoundU) lowerBoundU = pointProjUV.U; if (pointProjUV.U > upperBoundU) upperBoundU = pointProjUV.U; vertices.Add(pointProjUV); XYZ direction3d = line.Direction; UV direction = new UV(direction3d.DotProduct(xDir), direction3d.DotProduct(yDir)); lengths.Add(UnitUtil.ScaleLength(line.Length)); bool zeroU = MathUtil.IsAlmostZero(direction.U); bool zeroV = MathUtil.IsAlmostZero(direction.V); if (zeroU && zeroV) return null; // Accept only non-rotated I-Shapes. if (!zeroU && !zeroV) return null; direction.Normalize(); if (idx > 0) { if (!MathUtil.IsAlmostZero(directions[idx - 1].DotProduct(direction))) return null; turnsCCW.Add(directions[idx - 1].CrossProduct(direction) > 0); } directions.Add(direction); idx++; } if (directions.Count != 12) return null; if (!MathUtil.IsAlmostZero(directions[11].DotProduct(directions[0]))) return null; turnsCCW.Add(directions[11].CrossProduct(directions[0]) > 0); bool firstTurnIsCCW = turnsCCW[startVertex]; // Check proper turning of lines. // The orientation of the turns should be such that 8 match the original orientation, and 4 go in the opposite direction. // The opposite ones are: // For I-Shape: // if the first turn is clockwise (i.e., in -Y direction): 1,2,7,8. // if the first turn is counterclockwise (i.e., in the -X direction): 2,3,8,9. // For H-Shape: // if the first turn is clockwise (i.e., in -Y direction): 2,3,8,9. // if the first turn is counterclockwise (i.e., in the -X direction): 1,2,7,8. int iShapeCCWOffset = firstTurnIsCCW ? 1 : 0; int hShapeCWOffset = firstTurnIsCCW ? 0 : 1; bool isIShape = true; bool isHShape = false; for (int ii = 0; ii < 12 && isIShape; ii++) { int currOffset = 12 + (startVertex - iShapeCCWOffset); int currIdx = (ii + currOffset) % 12; if (currIdx == 1 || currIdx == 2 || currIdx == 7 || currIdx == 8) { if (firstTurnIsCCW == turnsCCW[ii]) isIShape = false; } else { if (firstTurnIsCCW == !turnsCCW[ii]) isIShape = false; } } if (!isIShape) { // Check if it is orientated like an H - if neither I nor H, fail. isHShape = true; for (int ii = 0; ii < 12 && isHShape; ii++) { int currOffset = 12 + (startVertex - hShapeCWOffset); int currIdx = (ii + currOffset) % 12; if (currIdx == 1 || currIdx == 2 || currIdx == 7 || currIdx == 8) { if (firstTurnIsCCW == turnsCCW[ii]) return null; } else { if (firstTurnIsCCW == !turnsCCW[ii]) return null; } } } // Check that the lengths of parallel and symmetric line segments are equal. double overallWidth = 0.0; double overallDepth = 0.0; double flangeThickness = 0.0; double webThickness = 0.0; // I-Shape: // CCW pairs:(0,6), (1,5), (1,7), (1,11), (2,4), (2,8), (2,10), (3, 9) // CW pairs: (11,5), (0,4), (0,6), (0,10), (1,3), (1,7), (1,9), (2, 8) // H-Shape is reversed. int cwPairOffset = (firstTurnIsCCW == isIShape) ? 0 : 11; overallWidth = lengths[(startVertex + cwPairOffset) % 12]; flangeThickness = lengths[(startVertex + 1 + cwPairOffset) % 12]; if (isIShape) { if (firstTurnIsCCW) { overallDepth = vertices[startVertex].V - vertices[(startVertex + 7) % 12].V; webThickness = vertices[(startVertex + 9) % 12].U - vertices[(startVertex + 3) % 12].U; } else { overallDepth = vertices[startVertex].V - vertices[(startVertex + 5) % 12].V; webThickness = vertices[(startVertex + 2) % 12].U - vertices[(startVertex + 8) % 12].U; } } else { if (!firstTurnIsCCW) { overallDepth = vertices[startVertex].U - vertices[(startVertex + 7) % 12].U; webThickness = vertices[(startVertex + 9) % 12].V - vertices[(startVertex + 3) % 12].V; } else { overallDepth = vertices[startVertex].U - vertices[(startVertex + 5) % 12].U; webThickness = vertices[(startVertex + 2) % 12].V - vertices[(startVertex + 8) % 12].V; } } if (!MathUtil.IsAlmostEqual(overallWidth, lengths[(startVertex + 6 + cwPairOffset) % 12])) return null; if (!MathUtil.IsAlmostEqual(flangeThickness, lengths[(startVertex + 5 + cwPairOffset) % 12]) || !MathUtil.IsAlmostEqual(flangeThickness, lengths[(startVertex + 7 + cwPairOffset) % 12]) || !MathUtil.IsAlmostEqual(flangeThickness, lengths[(startVertex + 11 + cwPairOffset) % 12])) return null; double innerTopLeftLength = lengths[(startVertex + 2 + cwPairOffset) % 12]; if (!MathUtil.IsAlmostEqual(innerTopLeftLength, lengths[(startVertex + 4 + cwPairOffset) % 12]) || !MathUtil.IsAlmostEqual(innerTopLeftLength, lengths[(startVertex + 8 + cwPairOffset) % 12]) || !MathUtil.IsAlmostEqual(innerTopLeftLength, lengths[(startVertex + 10 + cwPairOffset) % 12])) return null; double iShaftLength = lengths[(startVertex + 3 + cwPairOffset) % 12]; if (!MathUtil.IsAlmostEqual(iShaftLength, lengths[(startVertex + 9 + cwPairOffset) % 12])) return null; // Check fillet validity. int numFillets = fillets.Count(); double? filletRadius = null; if (numFillets != 0) { if (numFillets != 4) return null; // startFillet can have any value from 0 to 4; if it is 4, need to reset it to 0. // The fillet positions relative to the upper right hand corner are: // For I-Shape: // if the first turn is clockwise (i.e., in -Y direction): 2,3,8,9. // if the first turn is counterclockwise (i.e., in the -X direction): 3,4,9,10. // For H-Shape: // if the first turn is clockwise (i.e., in -Y direction): 3,4,9,10. // if the first turn is counterclockwise (i.e., in the -X direction): 2,3,8,9. int filletOffset = (isIShape == firstTurnIsCCW) ? 1 : 0; if (filletPositions[startFillet % 4] != ((2 + filletOffset + startVertex) % 12) || filletPositions[(startFillet + 1) % 4] != ((3 + filletOffset + startVertex) % 12) || filletPositions[(startFillet + 2) % 4] != ((8 + filletOffset + startVertex) % 12) || filletPositions[(startFillet + 3) % 4] != ((9 + filletOffset + startVertex) % 12)) return null; double tmpFilletRadius = fillets[0].Radius; for (int ii = 1; ii < 4; ii++) { if (!MathUtil.IsAlmostEqual(tmpFilletRadius, fillets[ii].Radius)) return null; } if (!MathUtil.IsAlmostZero(tmpFilletRadius)) filletRadius = UnitUtil.ScaleLength(tmpFilletRadius); } XYZ planeNorm = origPlane.Normal; for (int ii = 0; ii < numFillets; ii++) { bool filletIsCCW = (fillets[ii].Normal.DotProduct(planeNorm) > MathUtil.Eps()); if (filletIsCCW == firstTurnIsCCW) return null; } if (MathUtil.IsAlmostZero(overallWidth) || MathUtil.IsAlmostZero(overallDepth) || MathUtil.IsAlmostZero(flangeThickness) || MathUtil.IsAlmostZero(webThickness)) return null; // We have an I-Shape Profile! IList<double> newCtr = new List<double>(); newCtr.Add((vertices[0].U + vertices[6].U) / 2); newCtr.Add((vertices[0].V + vertices[6].V) / 2); IFCAnyHandle location = IFCInstanceExporter.CreateCartesianPoint(file, newCtr); IList<double> refDir = new List<double>(); if (isIShape) { refDir.Add(1.0); refDir.Add(0.0); } else { refDir.Add(0.0); refDir.Add(1.0); } IFCAnyHandle refDirectionOpt = ExporterUtil.CreateDirection(file, refDir); IFCAnyHandle positionHnd = IFCInstanceExporter.CreateAxis2Placement2D(file, location, null, refDirectionOpt); return IFCInstanceExporter.CreateIShapeProfileDef(file, IFCProfileType.Area, profileName, positionHnd, overallWidth, overallDepth, webThickness, flangeThickness, filletRadius); }
public Polygon2D GetStairAreaPolygon(UIDocument currentDocument, Stairs stair, string key) { ICollection <ElementId> allRunsIds = stair.GetStairsRuns(); ElementId currentId = allRunsIds.ElementAt(0); if (key.Equals(RevitObjectManager.BASE_LEVEL_KEY)) { foreach (ElementId currentBaseId in allRunsIds) { StairsRun currentStairsRun = currentDocument.Document.GetElement(currentBaseId) as StairsRun; StairsRun selectedStairsRun = currentDocument.Document.GetElement(currentId) as StairsRun; if (currentStairsRun.BaseElevation < selectedStairsRun.BaseElevation) { currentId = currentBaseId; } } } else if (key.Equals(RevitObjectManager.TOP_LEVEL_KEY)) { foreach (ElementId currentTopId in allRunsIds) { StairsRun currentStairsRun = currentDocument.Document.GetElement(currentTopId) as StairsRun; StairsRun selectedStairsRun = currentDocument.Document.GetElement(currentId) as StairsRun; if (currentStairsRun.TopElevation > selectedStairsRun.TopElevation) { currentId = currentTopId; } } } List <Vector2D> areaNodes = new List <Vector2D>(); StairsRun finalStairsRun = currentDocument.Document.GetElement(currentId) as StairsRun; CurveLoop stairPath = finalStairsRun.GetStairsPath(); Curve firstStairPathCurve; if (stairPath.Count() > 1) { if ((finalStairsRun.StairsRunStyle.Equals(StairsRunStyle.Winder) || finalStairsRun.StairsRunStyle.Equals(StairsRunStyle.Spiral)) && key.Equals(RevitObjectManager.TOP_LEVEL_KEY)) { firstStairPathCurve = stairPath.ElementAt(stairPath.Count() - 1); } else { firstStairPathCurve = stairPath.ElementAt(0); } } else { firstStairPathCurve = stairPath.ElementAt(0); } double stairsRunsWidth = ConvertFeetToMeters(finalStairsRun.ActualRunWidth * STAIRS_AREA_SHRINK_FACTOR); if (stairsRunsWidth < DEFAULT_AREA_RADIUS) { stairsRunsWidth = DEFAULT_AREA_RADIUS; } double pathsLength = ConvertFeetToMeters(firstStairPathCurve.Length * STAIRS_AREA_SHRINK_FACTOR); if (pathsLength < DEFAULT_AREA_RADIUS) { pathsLength = DEFAULT_AREA_RADIUS; } Vector2D pathsStart = GeometryFactory.CreateVector2D(ConvertFeetToMeters(firstStairPathCurve.GetEndPoint(0).X), ConvertFeetToMeters(firstStairPathCurve.GetEndPoint(0).Y), MEASUREMENTUNITFACTOR); Vector2D pathsEnd = GeometryFactory.CreateVector2D(ConvertFeetToMeters(firstStairPathCurve.GetEndPoint(1).X), ConvertFeetToMeters(firstStairPathCurve.GetEndPoint(1).Y), MEASUREMENTUNITFACTOR); Vector2D pathDirection = pathsEnd.Difference(pathsStart); Vector2D pathDirectionPerpenticular = pathDirection.Rotate((-1) * Math.PI / 2).GetAsNormalized(); Vector2D firstNode = pathsStart.Add(pathDirectionPerpenticular.Multiply(stairsRunsWidth / 2)); areaNodes.Add(firstNode); Vector2D pointToAdd = firstNode.Add(pathDirection); areaNodes.Add(pointToAdd); pointToAdd = pointToAdd.Add(pathDirectionPerpenticular.Multiply((-1) * stairsRunsWidth)); areaNodes.Add(pointToAdd); pointToAdd = pointToAdd.Add(pathDirection.Multiply(-1)); areaNodes.Add(pointToAdd); areaNodes.Add(firstNode); Polygon2D areaPolygon = GeometryFactory.CreatePolygon2D(areaNodes.ToArray(), finalStairsRun.Name); return(areaPolygon); }