private string CalculateDoorOperationStyle(FamilyInstance currElem, Transform trf) { int leftPosYArcCount = 0; int leftNegYArcCount = 0; int rightPosYArcCount = 0; int rightNegYArcCount = 0; int fullCircleCount = 0; int leftHalfCircleCount = 0; int rightHalfCircleCount = 0; double allowance = 0.0001; if (currElem == null) { return("NOTDEFINED"); } FamilySymbol famSymbol = currElem.Symbol; if (famSymbol == null) { return("NOTDEFINED"); } Family fam = famSymbol.Family; if (fam == null) { return("NOTDEFINED"); } Transform doorWindowTrf = ExporterIFCUtils.GetTransformForDoorOrWindow(currElem, famSymbol, FlippedX, FlippedY); IList <Curve> origArcs = GeometryUtil.Get2DArcOrLineFromSymbol(currElem, allCurveType: false, inclArc: true); if (origArcs == null || (origArcs.Count == 0)) { return("NOTDEFINED"); } BoundingBoxXYZ doorBB = GetBoundingBoxFromSolids(currElem); XYZ bbMin = doorWindowTrf.OfPoint(doorBB.Min); XYZ bbMax = doorWindowTrf.OfPoint(doorBB.Max); // Reorganize the bbox min and max after transform double xmin = bbMin.X, xmax = bbMax.X, ymin = bbMin.Y, ymax = bbMax.Y, zmin = bbMin.Z, zmax = bbMax.Z; if (bbMin.X > bbMax.X) { xmin = bbMax.X; xmax = bbMin.X; } if (bbMin.Y > bbMax.Y) { ymin = bbMax.Y; ymax = bbMin.Y; } if (bbMin.Z > bbMax.Z) { zmin = bbMax.Z; zmax = bbMin.Z; } bbMin = new XYZ(xmin - tolForArcCenter, ymin - tolForArcCenter, zmin - tolForArcCenter); bbMax = new XYZ(xmax + tolForArcCenter, ymax + tolForArcCenter, zmax + tolForArcCenter); IList <XYZ> arcCenterLocations = new List <XYZ>(); SortedSet <double> arcRadii = new SortedSet <double>(); foreach (Arc arc in origArcs) { Arc trfArc = arc.CreateTransformed(doorWindowTrf) as Arc; // Filter only Arcs that is on XY plane and at the Z=0 of the Door/Window transform if (!(MathUtil.IsAlmostEqual(Math.Abs(trfArc.Normal.Z), 1.0) /*&& MathUtil.IsAlmostEqual(Math.Abs(trfArc.Center.Z), Math.Abs(doorWindowTrf.Origin.Z))*/)) { continue; } // Filter only Arcs that have center within the bounding box if (trfArc.Center.X > bbMax.X || trfArc.Center.X < bbMin.X || trfArc.Center.Y > bbMax.Y || trfArc.Center.Y < bbMin.Y) { continue; } if (!trfArc.IsBound) { fullCircleCount++; } else { double angleOffOfXY = 0; XYZ v1 = CorrectNearlyZeroValueToZero((trfArc.GetEndPoint(0) - trfArc.Center).Normalize()); XYZ v2 = CorrectNearlyZeroValueToZero((trfArc.GetEndPoint(1) - trfArc.Center).Normalize()); angleOffOfXY = Math.Acos(v1.DotProduct(v2)); if ((Math.Abs(angleOffOfXY) > (60.0 / 180.0) * Math.PI && Math.Abs(angleOffOfXY) < (240.0 / 180.0) * Math.PI) && ((v1.Y > 0.0 && v2.Y < 0.0) || (v1.Y < 0.0 && v2.Y > 0.0))) // Consider the opening swing between -30 to +30 up to -120 to +120 degree, where Y axes must be at the opposite sides { if (trfArc.Center.X >= -tolForArcCenter && trfArc.Center.X <= tolForArcCenter) { leftHalfCircleCount++; } else { rightHalfCircleCount++; } } else if ((Math.Abs(angleOffOfXY) > (30.0 / 180.0) * Math.PI && Math.Abs(angleOffOfXY) < (170.0 / 180.0) * Math.PI) && (MathUtil.IsAlmostEqual(Math.Abs(v1.X), 1.0, allowance) || MathUtil.IsAlmostEqual(Math.Abs(v2.X), 1.0, allowance))) // Consider the opening swing between 30 to 170 degree, beginning at X axis { XYZ yDir; if (MathUtil.IsAlmostEqual(Math.Abs(v1.Y), Math.Abs(Math.Sin(angleOffOfXY)), 0.01)) { yDir = v1; } else { yDir = v2; } // if the Normal is pointing to -Z, it is flipped. Flip the Y if it is if (MathUtil.IsAlmostEqual(trfArc.Normal.Z, -1.0)) { yDir = yDir.Negate(); } // Check the center location in the X-direction to determine LEFT/RIGHT if (trfArc.Center.X >= -tolForArcCenter && trfArc.Center.X <= tolForArcCenter) { // on the LEFT if ((yDir.Y > 0.0 && trfArc.YDirection.Y > 0.0) || (yDir.Y < 0.0 && trfArc.YDirection.Y < 0.0)) { leftPosYArcCount++; } else if ((yDir.Y > 0.0 && trfArc.YDirection.Y < 0.0) || (yDir.Y < 0.0 && trfArc.YDirection.Y > 0.0)) { leftNegYArcCount++; } else { continue; } } else { // on the RIGHT if ((yDir.Y > 0.0 && trfArc.YDirection.Y > 0.0) || (yDir.Y < 0.0 && trfArc.YDirection.Y < 0.0)) { rightPosYArcCount++; } else if ((yDir.Y > 0.0 && trfArc.YDirection.Y < 0.0) || (yDir.Y < 0.0 && trfArc.YDirection.Y > 0.0)) { rightNegYArcCount++; } else { continue; } } } else { continue; } // Collect all distinct Arc Center if it is counted as the door opening, to ensure that for cases that there are more than 2 leafs, it is not worngly labelled bool foundExisting = false; foreach (XYZ existingCenter in arcCenterLocations) { if ((trfArc.Center.X > existingCenter.X - tolForArcCenter) && (trfArc.Center.X <= existingCenter.X + tolForArcCenter) && (trfArc.Center.Y > existingCenter.Y - tolForArcCenter) && (trfArc.Center.Y <= existingCenter.Y + tolForArcCenter)) { foundExisting = true; break; } } if (!foundExisting) { arcCenterLocations.Add(trfArc.Center); arcRadii.Add(trfArc.Radius); } } } // When only full circle(s) exists if (fullCircleCount > 0 && rightHalfCircleCount == 0 && leftHalfCircleCount == 0 && leftPosYArcCount == 0 && leftNegYArcCount == 0 && rightPosYArcCount == 0 && rightNegYArcCount == 0) { return("REVOLVING"); } // There are more than 2 arc centers, no IFC Door operation type fits this, return NOTDEFINED if (arcCenterLocations.Count > 2) { return("NOTDEFINED"); } // When half circle arc(s) exists if (leftHalfCircleCount > 0 && fullCircleCount == 0) { if (rightHalfCircleCount == 0 && leftPosYArcCount == 0 && leftNegYArcCount == 0 && rightPosYArcCount == 0 && rightNegYArcCount == 0) { return("DOUBLE_SWING_LEFT"); } if ((rightHalfCircleCount > 0 || (rightPosYArcCount > 0 && rightNegYArcCount > 0)) && leftPosYArcCount == 0 && leftNegYArcCount == 0) { return("DOUBLE_DOOR_DOUBLE_SWING"); } } if (rightHalfCircleCount > 0 && fullCircleCount == 0) { if (leftHalfCircleCount == 0 && leftPosYArcCount == 0 && leftNegYArcCount == 0 && rightPosYArcCount == 0 && rightNegYArcCount == 0) { return("DOUBLE_SWING_RIGHT"); } if ((leftHalfCircleCount > 0 || (leftPosYArcCount > 0 && leftNegYArcCount > 0)) && rightPosYArcCount == 0 && rightNegYArcCount == 0) { return("DOUBLE_DOOR_DOUBLE_SWING"); } } // When only 90-degree arc(s) exists if (leftPosYArcCount > 0 && fullCircleCount == 0 && rightHalfCircleCount == 0 && leftHalfCircleCount == 0 && leftNegYArcCount == 0 && rightPosYArcCount == 0 && rightNegYArcCount == 0) { // if the arc is less than 50%of the boundingbox, treat this to be a door with partially fixed panel if (arcRadii.Max < (bbMax.X - bbMin.X) * 0.5) { if (ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4) { return("NOTDEFINED"); } else { return("SWING_FIXED_LEFT"); } } else { return("SINGLE_SWING_LEFT"); } } if (rightPosYArcCount > 0 && fullCircleCount == 0 && rightHalfCircleCount == 0 && leftHalfCircleCount == 0 && leftNegYArcCount == 0 && leftPosYArcCount == 0 && rightNegYArcCount == 0) { // if the arc is less than 50%of the boundingbox, treat this to be a door with partially fixed panel if (arcRadii.Max < (bbMax.X - bbMin.X) * 0.5) { if (ExporterCacheManager.ExportOptionsCache.ExportAsOlderThanIFC4) { return("NOTDEFINED"); } else { return("SWING_FIXED_RIGHT"); } } else { return("SINGLE_SWING_RIGHT"); } } if (leftPosYArcCount > 0 && leftNegYArcCount > 0 && fullCircleCount == 0 && rightHalfCircleCount == 0 && leftHalfCircleCount == 0 && rightPosYArcCount == 0 && rightNegYArcCount == 0) { return("DOUBLE_SWING_LEFT"); } if (rightPosYArcCount > 0 && rightNegYArcCount > 0 && fullCircleCount == 0 && rightHalfCircleCount == 0 && leftHalfCircleCount == 0 && leftNegYArcCount == 0 && leftPosYArcCount == 0) { return("DOUBLE_SWING_RIGHT"); } if (leftPosYArcCount > 0 && rightPosYArcCount > 0 && fullCircleCount == 0 && rightHalfCircleCount == 0 && leftHalfCircleCount == 0 && leftNegYArcCount == 0 && rightNegYArcCount == 0) { return("DOUBLE_DOOR_SINGLE_SWING"); } if (leftPosYArcCount > 0 && rightPosYArcCount > 0 && leftNegYArcCount > 0 && rightNegYArcCount > 0 && fullCircleCount == 0 && rightHalfCircleCount == 0 && leftHalfCircleCount == 0) { return("DOUBLE_DOOR_DOUBLE_SWING"); } if (leftPosYArcCount > 0 && rightNegYArcCount > 0 && fullCircleCount == 0 && rightHalfCircleCount == 0 && leftHalfCircleCount == 0 && leftNegYArcCount == 0 && rightPosYArcCount == 0) { return("DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_RIGHT"); } if (leftNegYArcCount > 0 && rightPosYArcCount > 0 && fullCircleCount == 0 && rightHalfCircleCount == 0 && leftHalfCircleCount == 0 && leftPosYArcCount == 0 && rightNegYArcCount == 0) { return("DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_LEFT"); } return("NOTDEFINED"); }