// // (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 System.Linq; using System.Text; using Autodesk.Revit.DB; using Autodesk.Revit.DB.Architecture; namespace Revit.SDK.Samples.StairsAutomation.CS { /// /// Represents a stairs configuration consisting of straight runs and rectangular landings. The runs will /// switch based upon the input parameters. The landings width can be adjusted independently. /// public class StairsStandardConfiguration : IStairsConfiguration { List m_runConfigurations = new List(); List m_landingConfigurations = new List(); private int m_numberOfLandings; private Stairs m_stairs; private StairsType m_stairsType; private double m_bottomElevation; private Transform m_transform; /// /// The number of risers to include in the stairs. /// public int RiserNumber { get; set; } private double m_runWidth; private bool m_runWidthOverride = false; private double m_runOffset; private bool m_runOffsetOverride = false; private double m_landingWidth; private bool m_landingWidthOverride = false; private int m_riserIncrement = 1; /// /// True to ensure that all runs have the same number of treads (which might require adjustment to the tread height to /// compensate for the number of runs). False to allow a different number of treads (which practically, might result in /// some runs getting one less tread than others). /// public bool EqualizeRuns { get; set; } /// /// The width of the landings. If not explicitly set, the width of the runs is used. /// public double LandingWidth { get { if (m_landingWidthOverride) { return m_landingWidth; } return RunWidth; } set { m_landingWidth = value; m_landingWidthOverride = true; } } /// /// The width of the runs. If not explicitly set, the stairs type default width is used. /// public double RunWidth { get { if (m_runWidthOverride) { return m_runWidth; } return m_stairsType.MinRunWidth; } set { m_runWidth = value; m_runWidthOverride = true; } } /// /// The offset between the corner of one run and the corner of the next during a switchback at a landing. /// public double RunOffset { get { if (m_runOffsetOverride) { return m_runOffset; } return 2.0; } set { m_runOffset = value; m_runOffsetOverride = true; } } /// /// Creates a new StairsStandardConfiguration of runs and landings at the default location and orientation. /// /// The stairs element. /// The bottom level. /// The number of landings to be included. public StairsStandardConfiguration(Stairs stairs, Level bottomLevel, int numberOfLandings) { m_stairs = stairs; m_stairsType = m_stairs.Document.GetElement(m_stairs.GetTypeId()) as StairsType; m_bottomElevation = bottomLevel.Elevation; RiserNumber = stairs.DesiredRisersNumber; m_numberOfLandings = numberOfLandings; m_transform = Transform.Identity; EqualizeRuns = false; } /// /// Creates a new StairsStandardConfiguration of runs and landings at a specified location and orientation. /// /// The stairs element. /// The bottom level. /// The number of landings to be included. /// The transformation (location and orientation). public StairsStandardConfiguration(Stairs stairs, Level bottomLevel, int numberOfLandings, Transform transform) { m_stairs = stairs; m_stairsType = m_stairs.Document.GetElement(m_stairs.GetTypeId()) as StairsType; m_bottomElevation = bottomLevel.Elevation; RiserNumber = stairs.DesiredRisersNumber; m_numberOfLandings = numberOfLandings; m_transform = transform; EqualizeRuns = false; } /// /// Initializes the run and landing data in the configuration. /// public void Initialize() { if (m_numberOfLandings < 0) { throw new ArgumentOutOfRangeException("numberOfLandings", "Number of landings must be 0 or more"); } if (m_numberOfLandings > RiserNumber) { throw new ArgumentOutOfRangeException("numberOfLandings", "Number of landings must be less than calculated riser number for the stairs"); } if (EqualizeRuns) { int remainder = RiserNumber % (m_numberOfLandings + 1); if (remainder != 0) { m_stairs.DesiredRisersNumber = m_stairs.DesiredRisersNumber + m_numberOfLandings + 1 - remainder; m_stairs.Document.Regenerate(); RiserNumber = m_stairs.DesiredRisersNumber; } } // Split as evenly as possible m_riserIncrement = RiserNumber / (m_numberOfLandings + 1); } /// /// Generates the landing configuration for the end lines of 2 risers. /// /// The end line of the first riser. /// The start line of the second riser. /// The run direction. /// The elevation. /// The landing configuration. public virtual IStairsLandingComponent GenerateLanding(Line riser1Line, Line riser2Line, XYZ runDirection, double landingElevation) { IStairsLandingComponent landingConfiguration = new StairsRectangleLandingComponent(riser1Line, riser2Line, runDirection, landingElevation, LandingWidth); return landingConfiguration; } /// /// Generates the run configuration for the given elevation and properties. /// /// The number of risers in the run. /// The start elevation. /// The minimum tread depth. /// The width of the run. /// The transformation applied to the run start point and orientation. /// public virtual IStairsRunComponent GenerateRun(int numberOfRisers, double elevation, double minTreadDepth, double runWidth, Transform transform) { IStairsRunComponent run = new StraightStairsRunComponent(numberOfRisers, elevation, minTreadDepth, runWidth, transform); return run; } /// /// A helper to obtain the Autodesk.Revit.Creation.Application handle. /// protected Autodesk.Revit.Creation.Application AppCreate { get { return m_stairs.Document.Application.Create; } } #region IStairsConfiguration Members /// /// Implements the interface method. /// public int GetNumberOfRuns() { return m_numberOfLandings + 1; } /// /// Implements the interface method. /// public int GetNumberOfLandings() { return m_numberOfLandings; } /// /// Implements the interface method. /// public void CreateStairsRun(Document document, ElementId stairsElementId, int runIndex) { // Calculate where the previous run ended. XYZ previousRunEndPoint = runIndex == 0 ? XYZ.Zero : m_runConfigurations[runIndex - 1].GetRunEndpoint(); double elevation = runIndex == 0 ? m_bottomElevation : m_runConfigurations[runIndex - 1].TopElevation; // Setup number of risers for the run. int runNumberOfRisers = m_riserIncrement; if (runIndex == m_numberOfLandings) { runNumberOfRisers = RiserNumber - m_numberOfLandings * m_riserIncrement; } // Setup the transform for the run. Every second run must be reversed in direction and start point generated from // the offet to the previous run. Transform transform = Transform.Identity; XYZ pivotPoint = previousRunEndPoint + m_transform.OfPoint(new XYZ(RunWidth + RunOffset, 0, 0)); if (runIndex % 2 == 1) { Transform translation = Transform.CreateTranslation(pivotPoint); Transform rotation = Transform.CreateRotationAtPoint(XYZ.BasisZ, Math.PI, pivotPoint); transform = rotation.Multiply(translation); } transform = transform.Multiply(m_transform); // Generate the run configuration and it. IStairsRunComponent configuration = GenerateRun(runNumberOfRisers, elevation, m_stairsType.MinTreadDepth, RunWidth, transform); m_runConfigurations.Add(configuration); // Create the run now (subsequent runs and landings need to use its geometric properties). configuration.CreateStairsRun(document, stairsElementId); } /// /// Implements the interface method. /// public void CreateLanding(Document document, ElementId stairsElementId, int landingIndex) { // Get the run configurations for the runs before and after this landing. IStairsRunComponent configuration1 = m_runConfigurations[landingIndex]; IStairsRunComponent configuration2 = m_runConfigurations[landingIndex + 1]; // Get the stairs path from the lower run for run direction. Curve curve = configuration1.GetStairsPath()[0]; XYZ runDirection = (curve.GetEndPoint(1) - curve.GetEndPoint(0)).Normalize(); // Generate the landing configuration IStairsLandingComponent configuration = GenerateLanding(configuration1.GetLastCurve() as Line, configuration2.GetFirstCurve() as Line, runDirection, configuration2.RunElevation); m_landingConfigurations.Add(configuration); // Create the landing now configuration.CreateLanding(document, stairsElementId); } /// /// Implements the interface method. /// public void SetRunWidth(double value) { //cache width for new runs. RunWidth = value; //assign to existing run configurations foreach (var config in m_runConfigurations) { config.Width = value; } } #endregion /// /// Returns the transform assigned to the configuration. /// /// The transform. protected Transform GetTransform() { return m_transform; } } }