public int Compare( XYZ x, XYZ y ) { double dx = x.DistanceTo( _p ); double dy = y.DistanceTo( _p ); return Util.IsEqual( dx, dy ) ? 0 : ( dx < dy ? -1 : 1 ); }
/// <summary> /// Return the signed distance from /// a plane to a given point. /// </summary> public static double SignedDistanceTo( this Plane plane, XYZ p) { Debug.Assert( Util.IsEqual(plane.Normal.GetLength(), 1), "expected normalised plane normal"); XYZ v = p - plane.Origin; return(plane.Normal.DotProduct(v)); }
/// <summary> /// Eliminate the Z coordinate. /// </summary> static List <List <UV> > Flatten(List <List <XYZ> > polygons) { double z = polygons[0][0].Z; List <List <UV> > a = new List <List <UV> >(polygons.Count); foreach (List <XYZ> polygon in polygons) { Debug.Assert(Util.IsEqual(polygon[0].Z, z), "expected horizontal polygons"); a.Add(Flatten(polygon)); } return(a); }
/// <summary> /// Eliminate the Z coordinate. /// </summary> static public List <UV> Flatten(List <XYZ> polygon) { double z = polygon[0].Z; List <UV> a = new List <UV>(polygon.Count); foreach (XYZ p in polygon) { Debug.Assert(Util.IsEqual(p.Z, z), "expected horizontal polygon"); a.Add(Flatten(p)); } return(a); }
/// <summary> /// Add new points to the list. /// Skip the first new point if it equals the last /// old existing one. Actually, we can test all points /// and always ignore very close consecutive ones. /// </summary> static void AddNewPoints( IList <XYZ> pts, IList <XYZ> newpts) { foreach (XYZ p in newpts) { if (0 == pts.Count || !Util.IsEqual(p, pts.Last(), _tolerance)) { pts.Add(p); } } }
/// <summary> /// Return room boundary points retrieved /// from the room boundary segments. /// </summary> static List <XYZ> GetBoundaryPoints( IList <IList <BoundarySegment> > boundary) { List <XYZ> pts = new List <XYZ>(); int n = boundary.Count; if (1 > n) { Debug.Print("Boundary contains no loops"); } else { if (1 < n) { Debug.Print( "Boundary contains {0} loop{1}; " + "skipping all but first.", n, Util.PluralSuffix(n)); } foreach (IList <BoundarySegment> loop in boundary) { foreach (BoundarySegment seg in loop) { Curve c = seg.GetCurve(); AddNewPoints(pts, c.Tessellate()); } double z = pts[0].Z; foreach (XYZ p in pts) { Debug.Assert( Util.IsEqual(p.Z, z, _tolerance), "expected horizontal room boundary"); } // Break after first loop, which is hopefully // the exterior one, and hopefully the only one. // Todo: add better handling for more complex cases. break; } } return(pts); }
Transform GetTransformToZ(XYZ v) { Transform t; double a = XYZ.BasisZ.AngleTo(v); if (Util.IsZero(a)) { t = Transform.Identity; } else { XYZ axis = Util.IsEqual(a, Math.PI) ? XYZ.BasisX : v.CrossProduct(XYZ.BasisZ); //t = Transform.get_Rotation( XYZ.Zero, axis, a ); // 2013 t = Transform.CreateRotation(axis, a); // 2014 } return(t); }
public Result Execute( ExternalCommandData commandData, ref string message, ElementSet elements) { UIApplication app = commandData.Application; UIDocument uidoc = app.ActiveUIDocument; Document doc = uidoc.Document; List <Element> walls = new List <Element>(); if (!Util.GetSelectedElementsOrAll( walls, uidoc, typeof(Wall))) { Selection sel = uidoc.Selection; message = (0 < sel.GetElementIds().Count) ? "Please select some wall elements." : "No wall elements found."; return(Result.Failed); } Options opt = app.Application.Create.NewGeometryOptions(); List <List <XYZ> > polygons = CmdWallProfile.GetWallProfilePolygons( walls, opt); int i = 0, n = polygons.Count; double[] areas = new double[n]; double d, a, maxArea = 0.0; XYZ normal; foreach (List <XYZ> polygon in polygons) { GetPolygonPlane(polygon, out normal, out d, out a); if (Math.Abs(maxArea) < Math.Abs(a)) { maxArea = a; } areas[i++] = a; #if DEBUG // transform the 3D polygon into a horizontal plane // so we can use the 2D GetSignedPolygonArea() and // compare its results with the 3D calculation. // todo: compare the relative speed of // transforming 3d to 2d and using 2d area // calculation versus direct 3d area calculation. Transform t = GetTransformToZ(normal); List <XYZ> polygonHorizontal = ApplyTransform(polygon, t); List <UV> polygon2d = CmdSlabBoundaryArea.Flatten( polygonHorizontal); double a2 = CmdSlabBoundaryArea.GetSignedPolygonArea( polygon2d); Debug.Assert(Util.IsEqual(a, a2), "expected same area from 2D and 3D calculations"); #endif } Debug.Print( "{0} boundary loop{1} found.", n, Util.PluralSuffix(n)); for (i = 0; i < n; ++i) { Debug.Print( " Loop {0} area is {1} square feet{2}", i, Util.RealString(areas[i]), (areas[i].Equals(maxArea) ? ", outer loop of largest wall" : "")); } Creator creator = new Creator(doc); using (Transaction tx = new Transaction(doc)) { tx.Start("Draw wall profile loops"); creator.DrawPolygons(polygons); tx.Commit(); } return(Result.Succeeded); }
public bool Parallel(Segment a) { return((IsVertical && a.IsVertical) || (IsHorizontal && a.IsHorizontal) || Util.IsEqual(Slope, a.Slope)); }
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; //// Select all pipes in the entire model. //List<Pipe> pipes = new List<Pipe>( // new FilteredElementCollector( doc ) // .OfClass( typeof( Pipe ) ) // .ToElements() // .Cast<Pipe>() ); //int n = pipes.Count; //// If there are less than two, //// there is nothing we can do. //if( 2 > n ) //{ // message = _prompt; // return Result.Failed; //} //// If there are exactly two, pick those. //if( 2 < n ) //{ // // Else, check for a pre-selection. // pipes.Clear(); // Selection sel = uidoc.Selection; // //n = sel.Elements.Size; // 2014 // ICollection<ElementId> ids // = sel.GetElementIds(); // 2015 // n = ids.Count; // 2015 // Debug.Print( "{0} pre-selected elements.", // n ); // // If two or more model pipes were pre- // // selected, use the first two encountered. // if( 1 < n ) // { // //foreach( Element e in sel.Elements ) // 2014 // foreach( ElementId id in ids ) // 2015 // { // Pipe c = doc.GetElement( id ) as Pipe; // if( null != c ) // { // pipes.Add( c ); // if( 2 == pipes.Count ) // { // Debug.Print( "Found two model pipes, " // + "ignoring everything else." ); // break; // } // } // } // } // // Else, prompt for an // // interactive post-selection. // if( 2 != pipes.Count ) // { // pipes.Clear(); // try // { // Reference r = sel.PickObject( // ObjectType.Element, // new PipeElementSelectionFilter(), // "Please pick first pipe." ); // pipes.Add( doc.GetElement( r.ElementId ) // as Pipe ); // } // catch( Autodesk.Revit.Exceptions // .OperationCanceledException ) // { // return Result.Cancelled; // } // try // { // Reference r = sel.PickObject( // ObjectType.Element, // new PipeElementSelectionFilter(), // "Please pick second pipe." ); // pipes.Add( doc.GetElement( r.ElementId ) // as Pipe ); // } // catch( Autodesk.Revit.Exceptions // .OperationCanceledException ) // { // return Result.Cancelled; // } // } //} JtPairPicker <Pipe> picker = new JtPairPicker <Pipe>(uidoc); Result rc = picker.Pick(); if (Result.Failed == rc) { message = _prompt; } if (Result.Succeeded != rc) { return(rc); } IList <Pipe> pipes = picker.Selected; // Check for same pipe system type. ElementId systemTypeId = pipes[0].MEPSystem.GetTypeId(); Debug.Assert(pipes[1].MEPSystem.GetTypeId() .IntegerValue.Equals( systemTypeId.IntegerValue), "expected two similar pipes"); // Check for same pipe level. ElementId levelId = pipes[0].LevelId; Debug.Assert( pipes[1].LevelId.IntegerValue.Equals( levelId.IntegerValue), "expected two pipes on same level"); // Extract data from the two selected pipes. double wall_thickness = GetWallThickness(pipes[0]); Debug.Print("{0} has wall thickness {1}", Util.ElementDescription(pipes[0]), Util.RealString(wall_thickness)); Curve c0 = pipes[0].GetCurve(); Curve c1 = pipes[1].GetCurve(); if (!(c0 is Line) || !(c1 is Line)) { message = _prompt + " Expected straight pipes."; return(Result.Failed); } XYZ p00 = c0.GetEndPoint(0); XYZ p01 = c0.GetEndPoint(1); XYZ p10 = c1.GetEndPoint(0); XYZ p11 = c1.GetEndPoint(1); XYZ v0 = p01 - p00; XYZ v1 = p11 - p10; if (!Util.IsParallel(v0, v1)) { message = _prompt + " Expected parallel pipes."; return(Result.Failed); } // Select the two pipe endpoints // that are farthest apart. XYZ p0 = p00.DistanceTo(p10) > p01.DistanceTo(p10) ? p00 : p01; XYZ p1 = p10.DistanceTo(p0) > p11.DistanceTo(p0) ? p10 : p11; XYZ pm = 0.5 * (p0 + p1); XYZ v = p1 - p0; if (Util.IsParallel(v, v0)) { message = "The selected pipes are colinear."; return(Result.Failed); } // Normal vector of the plane defined by the // two parallel and offset pipes, which is // the plane hosting the rolling offset XYZ z = v.CrossProduct(v1); // Vector perpendicular to v0 and v0 and // z, i.e. vector pointing from the first pipe // to the second in the cross sectional view. XYZ w = z.CrossProduct(v1).Normalize(); // Offset distance perpendicular to pipe direction double distanceAcross = Math.Abs( v.DotProduct(w)); // Distance between endpoints parallel // to pipe direction double distanceAlong = Math.Abs( v.DotProduct(v1.Normalize())); Debug.Assert(Util.IsEqual(v.GetLength(), Math.Sqrt(distanceAcross * distanceAcross + distanceAlong * distanceAlong)), "expected Pythagorean equality here"); // The required offset pipe angle. double angle = 45 * Math.PI / 180.0; // The angle on the other side. double angle2 = 0.5 * Math.PI - angle; double length = distanceAcross * Math.Tan(angle2); double halfLength = 0.5 * length; // How long should the pipe stubs become? double remainingPipeLength = 0.5 * (distanceAlong - length); if (0 > v1.DotProduct(v)) { v1.Negate(); } v1 = v1.Normalize(); XYZ q0 = p0 + remainingPipeLength * v1; XYZ q1 = p1 - remainingPipeLength * v1; using (Transaction tx = new Transaction(doc)) { // Determine pipe diameter for creating // matching pipes and fittings Pipe pipe = pipes[0]; double diameter = pipe .get_Parameter(bipDiameter) // "Diameter" .AsDouble(); // Pipe type for calls to doc.Create.NewPipe PipeType pipe_type_standard = new FilteredElementCollector(doc) .OfClass(typeof(PipeType)) .Cast <PipeType>() .Where <PipeType>(e => e.Name.Equals("Standard")) .FirstOrDefault <PipeType>(); Debug.Assert( pipe_type_standard.Id.IntegerValue.Equals( pipe.PipeType.Id.IntegerValue), "expected all pipes in this simple " + "model to use the same pipe type"); tx.Start("Rolling Offset"); if (_place_model_line) { // Trim or extend existing pipes (pipes[0].Location as LocationCurve).Curve = Line.CreateBound(p0, q0); (pipes[1].Location as LocationCurve).Curve = Line.CreateBound(p1, q1); // Add a model line for the rolling offset pipe Creator creator = new Creator(doc); Line line = Line.CreateBound(q0, q1); creator.CreateModelCurve(line); pipe = null; } else if (_place_fittings) { // Set active work plane to the rolling // offset plane... removed again, since // this has no effect at all on the // fitting placement or rotation. // //Plane plane = new Plane( z, q0 ); // //SketchPlane sp = SketchPlane.Create( // doc, plane ); // //uidoc.ActiveView.SketchPlane = sp; //uidoc.ActiveView.ShowActiveWorkPlane(); FamilySymbol symbol = new FilteredElementCollector(doc) .OfClass(typeof(FamilySymbol)) .OfCategory(BuiltInCategory.OST_PipeFitting) .Cast <FamilySymbol>() .Where <FamilySymbol>(e => e.Family.Name.Contains("Elbow - Generic")) .FirstOrDefault <FamilySymbol>(); // Set up first 45 degree elbow fitting FamilyInstance fitting0 = doc.Create .NewFamilyInstance(q0, symbol, StructuralType.NonStructural); fitting0.LookupParameter("Angle").Set( 45.0 * Math.PI / 180.0); //fitting0.get_Parameter( bipDiameter ) // does not exist // .Set( diameter ); fitting0.LookupParameter("Nominal Radius") .Set(0.5 * diameter); Line axis = Line.CreateBound(p0, q0); angle = z.AngleTo(XYZ.BasisZ); ElementTransformUtils.RotateElement( doc, fitting0.Id, axis, Math.PI - angle); Connector con0 = Util.GetConnectorClosestTo( fitting0, p0); // Trim or extend existing pipe (pipes[0].Location as LocationCurve).Curve = Line.CreateBound(p0, con0.Origin); // Connect pipe to fitting Util.Connect(con0.Origin, pipe, fitting0); // Set up second 45 degree elbow fitting FamilyInstance fitting1 = doc.Create .NewFamilyInstance(q1, symbol, StructuralType.NonStructural); //fitting1.get_Parameter( "Angle" ).Set( 45.0 * Math.PI / 180.0 ); // 2014 //fitting1.get_Parameter( "Nominal Radius" ).Set( 0.5 * diameter ); // 2014 fitting1.LookupParameter("Angle").Set(45.0 * Math.PI / 180.0); // 2015 fitting1.LookupParameter("Nominal Radius").Set(0.5 * diameter); // 2015 axis = Line.CreateBound( q1, q1 + XYZ.BasisZ); ElementTransformUtils.RotateElement( doc, fitting1.Id, axis, Math.PI); axis = Line.CreateBound(q1, p1); ElementTransformUtils.RotateElement( doc, fitting1.Id, axis, Math.PI - angle); Connector con1 = Util.GetConnectorClosestTo( fitting1, p1); (pipes[1].Location as LocationCurve).Curve = Line.CreateBound(con1.Origin, p1); Util.Connect(con1.Origin, fitting1, pipes[1]); con0 = Util.GetConnectorClosestTo( fitting0, pm); con1 = Util.GetConnectorClosestTo( fitting1, pm); // Connecting one fitting to the other does // not insert a pipe in between. If the // system is edited later, however, the two // fittings snap together. // //con0.ConnectTo( con1 ); // Create rolling offset pipe segment //pipe = doc.Create.NewPipe( con0.Origin, // 2014 // con1.Origin, pipe_type_standard ); pipe = Pipe.Create(doc, pipe_type_standard.Id, levelId, con0, con1); // 2015 pipe.get_Parameter(bipDiameter) .Set(diameter); // Connect rolling offset pipe segment // with elbow fittings at each end Util.Connect(con0.Origin, fitting0, pipe); Util.Connect(con1.Origin, pipe, fitting1); } else { if (_use_static_pipe_create) { // Element id arguments to Pipe.Create. ElementId idSystem; ElementId idType; ElementId idLevel; // All these values are invalid for idSystem: ElementId idSystem1 = pipe.MEPSystem.Id; ElementId idSystem2 = ElementId.InvalidElementId; ElementId idSystem3 = PipingSystem.Create( doc, pipe.MEPSystem.GetTypeId(), "Tbc") .Id; // This throws an argument exception saying // The systemTypeId is not valid piping system type. // Parameter name: systemTypeId //pipe = Pipe.Create( doc, idSystem, // idType, idLevel, q0, q1 ); // Retrieve pipe system type, e.g. // hydronic supply. PipingSystemType pipingSystemType = new FilteredElementCollector(doc) .OfClass(typeof(PipingSystemType)) .OfType <PipingSystemType>() .FirstOrDefault(st => st.SystemClassification == MEPSystemClassification .SupplyHydronic); if (null == pipingSystemType) { message = "Could not find hydronic supply piping system type"; return(Result.Failed); } idSystem = pipingSystemType.Id; Debug.Assert(pipe.get_Parameter( BuiltInParameter.RBS_PIPING_SYSTEM_TYPE_PARAM) .AsElementId().IntegerValue.Equals( idSystem.IntegerValue), "expected same piping system element id"); // Retrieve the PipeType. PipeType pipeType = new FilteredElementCollector(doc) .OfClass(typeof(PipeType)) .OfType <PipeType>() .FirstOrDefault(); if (null == pipeType) { message = "Could not find pipe type"; return(Result.Failed); } idType = pipeType.Id; Debug.Assert(pipe.get_Parameter( BuiltInParameter.ELEM_TYPE_PARAM) .AsElementId().IntegerValue.Equals( idType.IntegerValue), "expected same pipe type element id"); Debug.Assert(pipe.PipeType.Id.IntegerValue .Equals(idType.IntegerValue), "expected same pipe type element id"); // Retrieve the reference level. // pipe.LevelId is not the correct source! idLevel = pipe.get_Parameter( BuiltInParameter.RBS_START_LEVEL_PARAM) .AsElementId(); // Create the rolling offset pipe. pipe = Pipe.Create(doc, idSystem, idType, idLevel, q0, q1); } else { //pipe = doc.Create.NewPipe( q0, q1, pipe_type_standard ); // 2014 pipe = Pipe.Create(doc, systemTypeId, pipe_type_standard.Id, levelId, q0, q1); // 2015 } pipe.get_Parameter(bipDiameter) .Set(diameter); // Connect rolling offset pipe segment // directly with the neighbouring original // pipes // //Util.Connect( q0, pipes[0], pipe ); //Util.Connect( q1, pipe, pipes[1] ); // NewElbowFitting performs the following: // - select appropriate fitting family and type // - place and orient a family instance // - set its parameters appropriately // - connect it with its neighbours Connector con0 = Util.GetConnectorClosestTo( pipes[0], q0); Connector con = Util.GetConnectorClosestTo( pipe, q0); doc.Create.NewElbowFitting(con0, con); Connector con1 = Util.GetConnectorClosestTo( pipes[1], q1); con = Util.GetConnectorClosestTo( pipe, q1); doc.Create.NewElbowFitting(con, con1); } tx.Commit(); } return(Result.Succeeded); }
public bool Equals(XYZ a, XYZ b) { return(Util.IsEqual(a, b)); }
/// <summary> /// Retrieve all wall openings, /// including at start and end of wall. /// </summary> List <WallOpening2d> GetWallOpenings( Wall wall, View3D view) { Document doc = wall.Document; Level level = doc.GetElement(wall.LevelId) as Level; double elevation = level.Elevation; Curve c = (wall.Location as LocationCurve).Curve; XYZ wallOrigin = c.GetEndPoint(0); XYZ wallEndPoint = c.GetEndPoint(1); XYZ wallDirection = wallEndPoint - wallOrigin; double wallLength = wallDirection.GetLength(); wallDirection = wallDirection.Normalize(); UV offsetOut = _offset * new UV(wallDirection.X, wallDirection.Y); XYZ rayStart = new XYZ(wallOrigin.X - offsetOut.U, wallOrigin.Y - offsetOut.V, elevation + _offset); ReferenceIntersector intersector = new ReferenceIntersector(wall.Id, FindReferenceTarget.Face, view); IList <ReferenceWithContext> refs = intersector.Find(rayStart, wallDirection); // Extract the intersection points: // - only surfaces // - within wall length plus offset at each end // - sorted by proximity // - eliminating duplicates List <XYZ> pointList = new List <XYZ>(refs .Where <ReferenceWithContext>(r => IsSurface( r.GetReference())) .Where <ReferenceWithContext>(r => r.Proximity < wallLength + _offset + _offset) .OrderBy <ReferenceWithContext, double>( r => r.Proximity) .Select <ReferenceWithContext, XYZ>(r => r.GetReference().GlobalPoint) .Distinct <XYZ>(new XyzEqualityComparer())); // Check if first point is at the wall start. // If so, the wall does not begin with an opening, // so that point can be removed. Else, add it. XYZ q = wallOrigin + _offset * XYZ.BasisZ; bool wallHasFaceAtStart = Util.IsEqual( pointList[0], q); if (wallHasFaceAtStart) { pointList.RemoveAll(p //=> _eps > p.DistanceTo( q ) ); => Util.IsEqual(p, q)); } else { pointList.Insert(0, wallOrigin); } // Check if last point is at the wall end. // If so, the wall does not end with an opening, // so that point can be removed. Else, add it. q = wallEndPoint + _offset * XYZ.BasisZ; bool wallHasFaceAtEnd = Util.IsEqual( pointList.Last(), q); if (wallHasFaceAtEnd) { pointList.RemoveAll(p //=> _eps > p.DistanceTo( q ) ); => Util.IsEqual(p, q)); } else { pointList.Add(wallEndPoint); } int n = pointList.Count; Debug.Assert(IsEven(n), "expected an even number of opening sides"); var wallOpenings = new List <WallOpening2d>( n / 2); for (int i = 0; i < n; i += 2) { wallOpenings.Add(new WallOpening2d { Start = pointList[i], End = pointList[i + 1] }); } return(wallOpenings); }
/// <summary> /// Resize ducts to ensure that branch ducts are no /// larger than the main duct they are tapping into. /// </summary> void DuctResize(Document doc) { FilteredElementCollector ductCollector = new FilteredElementCollector(doc) .OfClass(typeof(Duct)); using (Transaction transaction = new Transaction(doc)) { if (transaction.Start("Resize Ducts for Taps") == TransactionStatus.Started) { int i = 0; foreach (Duct d in ductCollector) { ConnectorSet dctCnnctrs = d.ConnectorManager.Connectors; int nDCs = dctCnnctrs.Size; if (nDCs < 3) { // do nothing } else { double ductDim = GetDuctDim(d); double largestConnector = 0.0; foreach (Connector c in dctCnnctrs) { if (c.ConnectorType.ToString().Equals("End")) { // Do nothing because I am not // interested in the "End" Connectors } else { ConnectorSet taps = c.AllRefs; double maxTapDim = 0.0; foreach (Connector cd in taps) { double tapDim = GetConnectorDim(cd); if (maxTapDim < tapDim) { maxTapDim = tapDim; } } if (largestConnector < maxTapDim) { largestConnector = maxTapDim; } } } if (largestConnector > ductDim) { double updatedHeight = largestConnector + twoInches; Parameter ductHeight = d.get_Parameter(bipHeight) ?? d.get_Parameter(bipDiameter); double oldHeight = ductHeight.AsDouble(); if (!Util.IsEqual(oldHeight, updatedHeight)) { ductHeight.Set(updatedHeight); ++i; } } } } // Ask the end user whether the // changes are to be committed or not TaskDialog taskDialog = new TaskDialog( "Resize Ducts"); TaskDialogCommonButtons buttons; if (0 < i) { int n = ductCollector.GetElementCount(); taskDialog.MainContent = i + " out of " + n.ToString() + " ducts will be re-sized." + "\n\nClick [OK] to Commit or [Cancel] " + "to Roll back the transaction."; buttons = TaskDialogCommonButtons.Ok | TaskDialogCommonButtons.Cancel; } else { taskDialog.MainContent = "None of the ducts need to be re-sized."; buttons = TaskDialogCommonButtons.Ok; } taskDialog.CommonButtons = buttons; if (TaskDialogResult.Ok == taskDialog.Show() && 0 < i) { // For many various reasons, a transaction may not be committed // if the changes made during the transaction do not result a valid model. // If committing a transaction fails or is canceled by the end user, // the resulting status would be RolledBack instead of Committed. if (TransactionStatus.Committed != transaction.Commit()) { TaskDialog.Show("Failure", "Transaction could not be committed"); } } // No need to roll back, just do not call Commit //else //{ // transaction.RollBack(); //} } } }
public bool Equals(XYZ a, XYZ b) { //return _eps > a.DistanceTo( b ); return(Util.IsEqual(a, b)); }
/// <summary> /// Cumulate the compound wall layer volumes for the given wall. /// </summary> void GetWallLayerVolumes( Wall wall, ref MapLayerToVolume totalVolumes) { WallType wt = wall.WallType; //CompoundStructure structure= wt.CompoundStructure; // 2011 CompoundStructure structure = wt.GetCompoundStructure(); // 2012 //CompoundStructureLayerArray layers = structure.Layers; // 2011 IList <CompoundStructureLayer> layers = structure.GetLayers(); // 2012 //int i, n = layers.Size; // 2011 int i, n = layers.Count; // 2012 double area = GetWallParameter(wall, _bipArea); double volume = GetWallParameter(wall, _bipVolume); double thickness = wt.Width; string desc = Util.ElementDescription(wall); Debug.Print( "{0} with thickness {1}" + " and volume {2}" + " has {3} layer{4}{5}", desc, Util.MmString(thickness), Util.RealString(volume), n, Util.PluralSuffix(n), Util.DotOrColon(n)); // volume for entire wall: string key = wall.WallType.Name; totalVolumes.Cumulate(key, volume); // volume for compound wall layers: if (0 < n) { i = 0; double total = 0.0; double layerVolume; foreach (CompoundStructureLayer layer in layers) { key = wall.WallType.Name + " : " + layer.Function; //layerVolume = area * layer.Thickness; // 2011 layerVolume = area * layer.Width; // 2012 totalVolumes.Cumulate(key, layerVolume); total += layerVolume; Debug.Print( " Layer {0}: function {1}, " + "thickness {2}, volume {3}", ++i, layer.Function, Util.MmString(layer.Width), Util.RealString(layerVolume)); } Debug.Print("Wall volume = {0}," + " total layer volume = {1}", Util.RealString(volume), Util.RealString(total)); if (!Util.IsEqual(volume, total)) { Debug.Print("Wall host volume parameter" + " value differs from sum of all layer" + " volumes: {0}", volume - total); } } }