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 );
 }
Пример #2
0
        /// <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);
        }
Пример #5
0
 /// <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);
         }
     }
 }
Пример #6
0
        /// <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);
        }
Пример #11
0
 public bool Equals(XYZ a, XYZ b)
 {
     return(Util.IsEqual(a, b));
 }
Пример #12
0
        /// <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);
                }
            }
        }