private double?GetTrimParameter(IFCData trim, IFCCurve basisCurve, IFCTrimmingPreference trimPreference, bool secondAttempt) { bool preferParam = !(trimPreference == IFCTrimmingPreference.Cartesian); if (secondAttempt) { preferParam = !preferParam; } double vertexEps = MathUtil.VertexEps; IFCAggregate trimAggregate = trim.AsAggregate(); foreach (IFCData trimParam in trimAggregate) { if (!preferParam && (trimParam.PrimitiveType == IFCDataPrimitiveType.Instance)) { IFCAnyHandle trimParamInstance = trimParam.AsInstance(); XYZ trimParamPt = IFCPoint.ProcessScaledLengthIFCCartesianPoint(trimParamInstance); if (trimParamPt == null) { IFCImportFile.TheLog.LogWarning(basisCurve.Id, "Invalid trim point for basis curve.", false); continue; } try { IntersectionResult result = basisCurve.Curve.Project(trimParamPt); if (result.Distance < vertexEps) { return(result.Parameter); } IFCImportFile.TheLog.LogWarning(basisCurve.Id, "Cartesian value for trim point not on the basis curve.", false); } catch { IFCImportFile.TheLog.LogWarning(basisCurve.Id, "Cartesian value for trim point not on the basis curve.", false); } } else if (preferParam && (trimParam.PrimitiveType == IFCDataPrimitiveType.Double)) { double trimParamDouble = trimParam.AsDouble(); if (basisCurve.Curve.IsCyclic) { trimParamDouble = IFCUnitUtil.ScaleAngle(trimParamDouble); } return(trimParamDouble); } } // Try again with opposite preference. if (!secondAttempt) { return(GetTrimParameter(trim, basisCurve, trimPreference, true)); } return(null); }
private bool NeedToReverseBaseCurve(Curve baseCurve, double param1, double param2, IFCTrimmingPreference trimPreference) { // In the very specific case where the trim preference is Cartesian, // and the trim parameters are reversed, we can try again by reversing // the base curve, if it is not cyclic. // This is an error on the input that we see in some files. return(baseCurve != null && param1 > param2 + MathUtil.Eps() && trimPreference == IFCTrimmingPreference.Cartesian && !baseCurve.IsCyclic); }
private void GetTrimParameters(int id, IFCData trim1, IFCData trim2, Curve baseCurve, IFCTrimmingPreference trimPreference, out double param1, out double param2) { double?condParam1 = GetTrimParameter(id, trim1, baseCurve, trimPreference, false); if (!condParam1.HasValue) { throw new InvalidOperationException("#" + id + ": Couldn't apply first trimming parameter of IfcTrimmedCurve."); } param1 = condParam1.Value; double?condParam2 = GetTrimParameter(id, trim2, baseCurve, trimPreference, false); if (!condParam2.HasValue) { throw new InvalidOperationException("#" + id + ": Couldn't apply second trimming parameter of IfcTrimmedCurve."); } param2 = condParam2.Value; if (MathUtil.IsAlmostEqual(param1, param2)) { // If we had a cartesian parameter as the trim preference, check if the parameter values are better. if (trimPreference == IFCTrimmingPreference.Cartesian) { condParam1 = GetTrimParameter(id, trim1, baseCurve, IFCTrimmingPreference.Parameter, true); if (!condParam1.HasValue) { throw new InvalidOperationException("#" + id + ": Couldn't apply first trimming parameter of IfcTrimmedCurve."); } param1 = condParam1.Value; condParam2 = GetTrimParameter(id, trim2, baseCurve, IFCTrimmingPreference.Parameter, true); if (!condParam2.HasValue) { throw new InvalidOperationException("#" + id + ": Couldn't apply second trimming parameter of IfcTrimmedCurve."); } param2 = condParam2.Value; } else { throw new InvalidOperationException("#" + id + ": Ignoring 0 length curve."); } } }
/// <summary> /// Creates a handle representing an IfcTrimmedCurve and assigns it to the file. /// </summary> /// <param name="file">The file.</param> /// <param name="basisCurve">The base curve.</param> /// <param name="trim1">The cartesian point, parameter, or both of end 1.</param> /// <param name="trim2">The cartesian point, parameter, or both of end 2.</param> /// <param name="senseAgreement">True if the end points match the orientation of the curve.</param> /// <param name="masterRepresentation">An enum stating which trim parameters are available.</param> /// <returns>The handle.</returns> public static IFCAnyHandle CreateTrimmedCurve(IFCFile file, IFCAnyHandle basisCurve, HashSet<IFCData> trim1, HashSet<IFCData> trim2, bool senseAgreement, IFCTrimmingPreference masterRepresentation) { IFCAnyHandleUtil.ValidateSubTypeOf(basisCurve, false, IFCEntityType.IfcCurve); IFCAnyHandle trimmedCurve = CreateInstance(file, IFCEntityType.IfcTrimmedCurve); IFCAnyHandleUtil.SetAttribute(trimmedCurve, "BasisCurve", basisCurve); IFCAnyHandleUtil.SetAttribute(trimmedCurve, "Trim1", trim1); IFCAnyHandleUtil.SetAttribute(trimmedCurve, "Trim2", trim2); IFCAnyHandleUtil.SetAttribute(trimmedCurve, "SenseAgreement", senseAgreement); IFCAnyHandleUtil.SetAttribute(trimmedCurve, "MasterRepresentation", masterRepresentation); return trimmedCurve; }
protected override void Process(IFCAnyHandle ifcCurve) { base.Process(ifcCurve); bool found = false; bool sameSense = IFCImportHandleUtil.GetRequiredBooleanAttribute(ifcCurve, "SenseAgreement", out found); if (!found) { sameSense = true; } IFCAnyHandle basisCurve = IFCImportHandleUtil.GetRequiredInstanceAttribute(ifcCurve, "BasisCurve", true); IFCCurve ifcBasisCurve = IFCCurve.ProcessIFCCurve(basisCurve); if (ifcBasisCurve == null || (ifcBasisCurve.IsEmpty())) { // LOG: ERROR: Error processing BasisCurve # for IfcTrimmedCurve #. return; } if (ifcBasisCurve.Curve == null) { // LOG: ERROR: Expected a single curve, not a curve loop for BasisCurve # for IfcTrimmedCurve #. return; } IFCData trim1 = ifcCurve.GetAttribute("Trim1"); if (trim1.PrimitiveType != IFCDataPrimitiveType.Aggregate) { // LOG: ERROR: Invalid data type for Trim1 attribute for IfcTrimmedCurve #. return; } IFCData trim2 = ifcCurve.GetAttribute("Trim2"); if (trim2.PrimitiveType != IFCDataPrimitiveType.Aggregate) { // LOG: ERROR: Invalid data type for Trim1 attribute for IfcTrimmedCurve #. return; } // Note that these are the "unprocessed" values. These can be used for, e.g., adding up the IFC parameter length // of the file, to account for export errors. The "processed" values can be determined from the Revit curves. Trim1 = GetRawTrimParameter(trim1); Trim2 = GetRawTrimParameter(trim2); IFCTrimmingPreference trimPreference = IFCEnums.GetSafeEnumerationAttribute <IFCTrimmingPreference>(ifcCurve, "MasterRepresentation", IFCTrimmingPreference.Parameter); double param1 = 0.0, param2 = 0.0; Curve baseCurve = ifcBasisCurve.Curve; try { GetTrimParameters(ifcBasisCurve.Id, trim1, trim2, baseCurve, trimPreference, out param1, out param2); if (NeedToReverseBaseCurve(baseCurve, param1, param2, trimPreference)) { Importer.TheLog.LogWarning(Id, "Invalid Param1 > Param2 for non-cyclic IfcTrimmedCurve using Cartesian trimming preference, reversing.", false); baseCurve = baseCurve.CreateReversed(); GetTrimParameters(ifcBasisCurve.Id, trim1, trim2, baseCurve, trimPreference, out param1, out param2); } } catch (Exception ex) { Importer.TheLog.LogError(ifcCurve.StepId, ex.Message, false); return; } if (MathUtil.IsAlmostEqual(param1, param2)) { Importer.TheLog.LogError(Id, "Param1 = Param2 for IfcTrimmedCurve #, ignoring.", false); return; } Curve curve = null; if (baseCurve.IsCyclic) { double period = baseCurve.Period; if (!sameSense) { MathUtil.Swap(ref param1, ref param2); } // We want to make sure both values are within period of one another. param1 = MathUtil.PutInRange(param1, 0, period); param2 = MathUtil.PutInRange(param2, 0, period); if (param2 < param1) { param2 = MathUtil.PutInRange(param2, param1 + period / 2, period); } // This is effectively an unbound curve. double numberOfPeriods = (param2 - param1) / period; if (MathUtil.IsAlmostEqual(numberOfPeriods, Math.Round(numberOfPeriods))) { Importer.TheLog.LogWarning(Id, "Start and end parameters indicate a zero-length closed curve, assuming unbound is intended.", false); curve = baseCurve; } else { curve = baseCurve.Clone(); if (!SafelyBoundCurve(curve, param1, param2)) { return; } } } else { if (param1 > param2 - MathUtil.Eps()) { Importer.TheLog.LogWarning(Id, "Param1 > Param2 for IfcTrimmedCurve #, reversing.", false); MathUtil.Swap(ref param1, ref param2); sameSense = !sameSense; } Curve copyCurve = baseCurve.Clone(); double length = param2 - param1; if (length <= IFCImportFile.TheFile.ShortCurveTolerance) { string lengthAsString = IFCUnitUtil.FormatLengthAsString(length); Importer.TheLog.LogError(Id, "curve length of " + lengthAsString + " is invalid, ignoring.", false); return; } if (!SafelyBoundCurve(copyCurve, param1, param2)) { return; } if (sameSense) { curve = copyCurve; } else { curve = copyCurve.CreateReversed(); } } CurveLoop curveLoop = new CurveLoop(); curveLoop.Append(curve); SetCurveLoop(curveLoop); }
protected override void Process(IFCAnyHandle ifcCurve) { base.Process(ifcCurve); bool found = false; bool sameSense = IFCImportHandleUtil.GetRequiredBooleanAttribute(ifcCurve, "SenseAgreement", out found); if (!found) { sameSense = true; } IFCAnyHandle basisCurve = IFCImportHandleUtil.GetRequiredInstanceAttribute(ifcCurve, "BasisCurve", true); IFCCurve ifcBasisCurve = IFCCurve.ProcessIFCCurve(basisCurve); if (ifcBasisCurve == null || (ifcBasisCurve.Curve == null && ifcBasisCurve.CurveLoop == null)) { // LOG: ERROR: Error processing BasisCurve # for IfcTrimmedCurve #. return; } if (ifcBasisCurve.Curve == null) { // LOG: ERROR: Expected a single curve, not a curve loop for BasisCurve # for IfcTrimmedCurve #. return; } IFCData trim1 = ifcCurve.GetAttribute("Trim1"); if (trim1.PrimitiveType != IFCDataPrimitiveType.Aggregate) { // LOG: ERROR: Invalid data type for Trim1 attribute for IfcTrimmedCurve #. return; } IFCData trim2 = ifcCurve.GetAttribute("Trim2"); if (trim2.PrimitiveType != IFCDataPrimitiveType.Aggregate) { // LOG: ERROR: Invalid data type for Trim1 attribute for IfcTrimmedCurve #. return; } IFCTrimmingPreference trimPreference = IFCEnums.GetSafeEnumerationAttribute <IFCTrimmingPreference>(ifcCurve, "MasterRepresentation", IFCTrimmingPreference.Parameter); double param1 = 0.0, param2 = 0.0; try { GetTrimParameters(trim1, trim2, ifcBasisCurve, trimPreference, out param1, out param2); } catch (Exception ex) { Importer.TheLog.LogError(ifcCurve.StepId, ex.Message, false); return; } Curve baseCurve = ifcBasisCurve.Curve; if (baseCurve.IsCyclic) { if (!sameSense) { MathUtil.Swap(ref param1, ref param2); } if (param2 < param1) { param2 = MathUtil.PutInRange(param2, param1 + Math.PI, 2 * Math.PI); } if (param2 - param1 > 2.0 * Math.PI - MathUtil.Eps()) { Importer.TheLog.LogWarning(ifcCurve.StepId, "IfcTrimmedCurve length is greater than 2*PI, leaving unbound.", false); Curve = baseCurve; return; } Curve = baseCurve.Clone(); try { Curve.MakeBound(param1, param2); } catch (Exception ex) { if (ex.Message.Contains("too small")) { Curve = null; Importer.TheLog.LogError(Id, "curve length is invalid, ignoring.", false); return; } else { throw ex; } } } else { if (MathUtil.IsAlmostEqual(param1, param2)) { Importer.TheLog.LogError(Id, "Param1 = Param2 for IfcTrimmedCurve #, ignoring.", false); return; } if (param1 > param2 - MathUtil.Eps()) { Importer.TheLog.LogWarning(Id, "Param1 > Param2 for IfcTrimmedCurve #, reversing.", false); MathUtil.Swap(ref param1, ref param2); return; } Curve copyCurve = baseCurve.Clone(); double length = param2 - param1; if (length <= IFCImportFile.TheFile.Document.Application.ShortCurveTolerance) { string lengthAsString = IFCUnitUtil.FormatLengthAsString(length); Importer.TheLog.LogError(Id, "curve length of " + lengthAsString + " is invalid, ignoring.", false); return; } copyCurve.MakeBound(param1, param2); if (sameSense) { Curve = copyCurve; } else { Curve = copyCurve.CreateReversed(); } } CurveLoop = new CurveLoop(); CurveLoop.Append(Curve); }
private double? GetTrimParameter(IFCData trim, IFCCurve basisCurve, IFCTrimmingPreference trimPreference, bool secondAttempt) { bool preferParam = !(trimPreference == IFCTrimmingPreference.Cartesian); if (secondAttempt) preferParam = !preferParam; double vertexEps = IFCImportFile.TheFile.Document.Application.VertexTolerance; IFCAggregate trimAggregate = trim.AsAggregate(); foreach (IFCData trimParam in trimAggregate) { if (!preferParam && (trimParam.PrimitiveType == IFCDataPrimitiveType.Instance)) { IFCAnyHandle trimParamInstance = trimParam.AsInstance(); XYZ trimParamPt = IFCPoint.ProcessScaledLengthIFCCartesianPoint(trimParamInstance); if (trimParamPt == null) { Importer.TheLog.LogWarning(basisCurve.Id, "Invalid trim point for basis curve.", false); continue; } try { IntersectionResult result = basisCurve.Curve.Project(trimParamPt); if (result.Distance < vertexEps) return result.Parameter; Importer.TheLog.LogWarning(basisCurve.Id, "Cartesian value for trim point not on the basis curve.", false); } catch { Importer.TheLog.LogWarning(basisCurve.Id, "Cartesian value for trim point not on the basis curve.", false); } } else if (preferParam && (trimParam.PrimitiveType == IFCDataPrimitiveType.Double)) { double trimParamDouble = trimParam.AsDouble(); if (basisCurve.Curve.IsCyclic) trimParamDouble = IFCUnitUtil.ScaleAngle(trimParamDouble); else trimParamDouble = IFCUnitUtil.ScaleLength(trimParamDouble); return trimParamDouble; } } // Try again with opposite preference. if (!secondAttempt) return GetTrimParameter(trim, basisCurve, trimPreference, true); return null; }
private void GetTrimParameters(IFCData trim1, IFCData trim2, IFCCurve basisCurve, IFCTrimmingPreference trimPreference, out double param1, out double param2) { double? condParam1 = GetTrimParameter(trim1, basisCurve, trimPreference, false); if (!condParam1.HasValue) throw new InvalidOperationException("#" + basisCurve.Id + ": Couldn't apply first trimming parameter of IfcTrimmedCurve."); param1 = condParam1.Value; double? condParam2 = GetTrimParameter(trim2, basisCurve, trimPreference, false); if (!condParam2.HasValue) throw new InvalidOperationException("#" + basisCurve.Id + ": Couldn't apply second trimming parameter of IfcTrimmedCurve."); param2 = condParam2.Value; if (MathUtil.IsAlmostEqual(param1, param2)) { // If we had a cartesian parameter as the trim preference, check if the parameter values are better. if (trimPreference == IFCTrimmingPreference.Cartesian) { condParam1 = GetTrimParameter(trim1, basisCurve, IFCTrimmingPreference.Parameter, true); if (!condParam1.HasValue) throw new InvalidOperationException("#" + basisCurve.Id + ": Couldn't apply first trimming parameter of IfcTrimmedCurve."); param1 = condParam1.Value; condParam2 = GetTrimParameter(trim2, basisCurve, IFCTrimmingPreference.Parameter, true); if (!condParam2.HasValue) throw new InvalidOperationException("#" + basisCurve.Id + ": Couldn't apply second trimming parameter of IfcTrimmedCurve."); param2 = condParam2.Value; } else throw new InvalidOperationException("#" + basisCurve.Id + ": Ignoring 0 length curve."); } }
private void ProcessIFCTrimmedCurve(IFCAnyHandle ifcCurve) { bool found = false; bool sameSense = IFCImportHandleUtil.GetRequiredBooleanAttribute(ifcCurve, "SenseAgreement", out found); if (!found) { sameSense = true; } IFCAnyHandle basisCurve = IFCImportHandleUtil.GetRequiredInstanceAttribute(ifcCurve, "BasisCurve", true); IFCCurve ifcBasisCurve = IFCCurve.ProcessIFCCurve(basisCurve); if (ifcBasisCurve == null || (ifcBasisCurve.Curve == null && ifcBasisCurve.CurveLoop == null)) { // LOG: ERROR: Error processing BasisCurve # for IfcTrimmedCurve #. return; } if (ifcBasisCurve.Curve == null) { // LOG: ERROR: Expected a single curve, not a curve loop for BasisCurve # for IfcTrimmedCurve #. return; } IFCData trim1 = ifcCurve.GetAttribute("Trim1"); if (trim1.PrimitiveType != IFCDataPrimitiveType.Aggregate) { // LOG: ERROR: Invalid data type for Trim1 attribute for IfcTrimmedCurve #. return; } IFCData trim2 = ifcCurve.GetAttribute("Trim2"); if (trim2.PrimitiveType != IFCDataPrimitiveType.Aggregate) { // LOG: ERROR: Invalid data type for Trim1 attribute for IfcTrimmedCurve #. return; } string trimPreferenceAsString = IFCAnyHandleUtil.GetEnumerationAttribute(ifcCurve, "MasterRepresentation"); IFCTrimmingPreference trimPreference = IFCTrimmingPreference.Parameter; if (trimPreferenceAsString != null) { trimPreference = (IFCTrimmingPreference)Enum.Parse(typeof(IFCTrimmingPreference), trimPreferenceAsString, true); } double param1 = 0.0, param2 = 0.0; try { GetTrimParameters(trim1, trim2, ifcBasisCurve, trimPreference, out param1, out param2); } catch (Exception ex) { IFCImportFile.TheLog.LogError(ifcCurve.StepId, ex.Message, false); return; } Curve baseCurve = ifcBasisCurve.Curve; if (baseCurve.IsCyclic) { if (!sameSense) { MathUtil.Swap(ref param1, ref param2); } if (param2 < param1) { param2 = MathUtil.PutInRange(param2, param1 + Math.PI, 2 * Math.PI); } if (param2 - param1 > 2.0 * Math.PI - MathUtil.Eps()) { // LOG: WARNING: #Id: IfcTrimmedCurve length is greater than 2*PI, leaving unbound. Curve = baseCurve; return; } Curve = baseCurve.Clone(); Curve.MakeBound(param1, param2); } else { if (MathUtil.IsAlmostEqual(param1, param2)) { // LOG: ERROR: Param1 = Param2 for IfcTrimmedCurve #, ignoring. return; } if (param1 > param2 - MathUtil.Eps()) { // LOG: WARNING: Param1 > Param2 for IfcTrimmedCurve #, reversing. MathUtil.Swap(ref param1, ref param2); return; } Curve copyCurve = baseCurve.Clone(); copyCurve.MakeBound(param1, param2); if (sameSense) { Curve = copyCurve; } else { Curve = copyCurve.CreateReversed(); } } }