// // (C) Copyright 2003-2019 by Autodesk, Inc. // // Permission to use, copy, modify, and distribute this software in // object code form for any purpose and without fee is hereby granted, // provided that the above copyright notice appears in all copies and // that both that copyright notice and the limited warranty and // restricted rights notice below appear in all supporting // documentation. // // AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. // AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF // MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC. // DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE // UNINTERRUPTED OR ERROR FREE. // // Use, duplication, or disclosure by the U.S. Government is subject to // restrictions set forth in FAR 52.227-19 (Commercial Computer // Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) // (Rights in Technical Data and Computer Software), as applicable. // using System; using System.Collections.Generic; using Autodesk.Revit.DB; namespace Revit.SDK.Samples.WinderStairs.CS { /// /// Utility verifies the input parameters, like curves from Revit document. /// It also adapts the input data for winder stairs creation. /// class WinderUtil { /// /// Return the control points from the connected curve elements. /// /// Revit Document /// Connected curve element /// Control points public static IList CalculateControlPoints(Document rvtDoc, IList crvElements) { double maxOffset = 0.0; // All the elements should be line base. for (int i = 0; i < crvElements.Count; i++) { Element curve = rvtDoc.GetElement(crvElements[i]); LocationCurve locationCrv = curve.Location as LocationCurve; if (locationCrv == null || !(locationCrv.Curve is Line)) { throw new ArgumentException("The input elements are not Line base."); } if (Math.Abs(locationCrv.Curve.GetEndPoint(0).Z - locationCrv.Curve.GetEndPoint(1).Z) > 1.0e-9) { throw new AggregateException( "The input curve elements are not in the same elevation plane."); } if (curve is Wall) { maxOffset = Math.Max(maxOffset, ((Wall)curve).Width * 0.5); } } IList controlPoints = CalculateControlPoints2(rvtDoc, crvElements); if (controlPoints.Count == 0) { throw new ArgumentException("The input curve elements are not continues."); } if (!CheckOrientation(controlPoints)) { throw new ArgumentException( "The input curve elements should have the same orientation: CW or CCW."); } if (maxOffset > 0.0) { controlPoints = CalculateOffset(controlPoints, maxOffset); } return controlPoints; } /// /// Calculate the max straight steps determined by the control points. /// /// Control points of the stairs /// Stairs Run width /// Stairs tread depth /// public static IList CalculateMaxStepsCount(IList controlPoints, double runWidth, double treadDepth) { IList innerPnts = CalculateOffset(controlPoints, runWidth); IList counts = new List(); for (int i = 1; i < innerPnts.Count; i++) { double dist = innerPnts[i].DistanceTo(innerPnts[i - 1]); uint count = (uint)(dist / treadDepth); counts.Add(count); } return counts; } /// /// Check to see if the control points bend to the same direction(CW or CCW). /// /// Control points to test /// true if the controls points bend to the same direction static bool CheckOrientation(IList controlPoints) { XYZ previousDir = null; for (int i = 1; i < controlPoints.Count - 1; i++) { XYZ dir1 = controlPoints[i] - controlPoints[i - 1]; XYZ dir2 = controlPoints[i + 1] - controlPoints[i]; if (previousDir == null) { previousDir = dir1.CrossProduct(dir2).Normalize(); } else if (!previousDir.IsAlmostEqualTo(dir1.CrossProduct(dir2).Normalize())) { return false; } } return true; } /// /// Offset the control points by specified distance. /// /// Control point to offset /// offset distance /// Control points after offsetting static IList CalculateOffset(IList controlPoints, double offset) { IList innerPnts = new List(); for (int i = 1; i < controlPoints.Count - 1; i++) { XYZ dir1 = (controlPoints[i] - controlPoints[i - 1]).Normalize(); XYZ dir2 = (controlPoints[i + 1] - controlPoints[i]).Normalize(); // Calculate the bisect direction of the corner. XYZ bisectDir = (dir2 - dir1).Normalize(); // Calculate the step direction of the fist line. XYZ stepInside1stDir = new XYZ(-dir1.Y, dir1.X, 0.0); if (stepInside1stDir.DotProduct(bisectDir) < 0.0) stepInside1stDir = stepInside1stDir.Negate(); if (i == 1) { innerPnts.Add(controlPoints[i - 1] + stepInside1stDir * offset); } // Calculate the step direction of the second line. XYZ stepInside2ndDir = new XYZ(-dir2.Y, dir2.X, 0.0); if (stepInside2ndDir.DotProduct(bisectDir) < 0.0) stepInside2ndDir = stepInside2ndDir.Negate(); double semiAngle = bisectDir.AngleTo(dir2); double slopDist = offset / Math.Sin(semiAngle); // Calculate the corner points XYZ innerCornerPnt = controlPoints[i] + bisectDir * slopDist; innerPnts.Add(innerCornerPnt); if (i == controlPoints.Count - 2) { innerPnts.Add(controlPoints[i + 1] + stepInside2ndDir * offset); } } return innerPnts; } /// /// Calculate the control points from the connected curve elements. /// /// Revit Document /// Connected Curve Elements /// Control points static IList CalculateControlPoints2(Document rvtDoc, IList elements) { IList curves = new List(); for (int i = 0; i < elements.Count; i++) { Element curve = rvtDoc.GetElement(elements[i]); LocationCurve locationCrv = curve.Location as LocationCurve; curves.Add(locationCrv.Curve); } IList controlPoints = new List(); if (curves.Count == 2) { Curve curve1 = curves[0]; Curve curve2 = curves[1]; XYZ commonPnt = null; int index1 = -1, index2 = -1; if (HasCommonEndPoint(curve1, curve2, out commonPnt, out index1, out index2)) { XYZ start = curve1.GetEndPoint(1 - index1); XYZ end = curve2.GetEndPoint(1 - index2); controlPoints.Add(start); controlPoints.Add(commonPnt); controlPoints.Add(end); } } else if (curves.Count == 3) { Curve curve1 = curves[0]; Curve curve2 = curves[1]; Curve curve3 = curves[2]; XYZ start = null, commonPnt1 = null, commonPnt2 = null, end = null; int index1 = -1, index2 = -1, index3 = -1, index4 = -1; if (HasCommonEndPoint(curve1, curve2, out commonPnt1, out index1, out index2)) { if (HasCommonEndPoint(curve1, curve3, out commonPnt2, out index3, out index4)) { // common curve is curve1 start = curve2.GetEndPoint(1 - index2); end = curve3.GetEndPoint(1 - index4); } else if (HasCommonEndPoint(curve2, curve3, out commonPnt2, out index3, out index4)) { // common curve is curve2 start = curve1.GetEndPoint(1 - index1); end = curve3.GetEndPoint(1 - index4); } } else { if (HasCommonEndPoint(curve1, curve3, out commonPnt1, out index1, out index2) && HasCommonEndPoint(curve2, curve3, out commonPnt2, out index3, out index4)) { // common curve is curve3 start = curve1.GetEndPoint(1 - index1); end = curve2.GetEndPoint(1 - index3); } } if (start != null) { controlPoints.Add(start); controlPoints.Add(commonPnt1); controlPoints.Add(commonPnt2); controlPoints.Add(end); } } return controlPoints; } /// /// Calculate the common end point of two curves. /// /// Curve 1 /// Curve 2 /// Comment point of the two curves /// index of the common point in curve 1 /// index of the common point in curve 2 /// true if there is one common point, false otherwise static bool HasCommonEndPoint(Curve crv1, Curve crv2, out XYZ common, out int index1, out int index2) { XYZ pnt1 = crv1.GetEndPoint(0); XYZ pnt2 = crv1.GetEndPoint(1); XYZ pnt3 = crv2.GetEndPoint(0); XYZ pnt4 = crv2.GetEndPoint(1); if (pnt1.IsAlmostEqualTo(pnt3)) { index1 = 0; index2 = 0; common = pnt1; return true; } else if (pnt1.IsAlmostEqualTo(pnt4)) { index1 = 0; index2 = 1; common = pnt1; return true; } else if (pnt2.IsAlmostEqualTo(pnt3)) { index1 = 1; index2 = 0; common = pnt2; return true; } else if (pnt2.IsAlmostEqualTo(pnt4)) { index1 = 1; index2 = 1; common = pnt2; return true; } common = null; index1 = index2 = -1; return false; } } }