Ejemplo n.º 1
0
        //--------------------------------------------------------------------------------------------------

        bool _FindBendAxis(MakeContext context)
        {
            // Find the longest edge to revolve around
            var foundEdges = EdgeAlgo.FindLongestEdge(context.TargetFace);

            if (!foundEdges.axis.HasValue || !foundEdges.opAxis.HasValue)
            {
                Messages.Error("No linear edge found to bend around.");
                return(false);
            }

            context.BendEdge     = _Reverse ? foundEdges.opEdge : foundEdges.edge;
            context.OppositeEdge = _Reverse ? foundEdges.edge : foundEdges.opEdge;
            context.BendAxis     = _Reverse ? foundEdges.opAxis.Value : foundEdges.axis.Value;

            // Direction of the inner: Get face plane, get cross vector of edge axis and plane normal
            if (!FaceAlgo.GetCenteredPlaneFromFace(context.TargetFace, out var facePlane))
            {
                Messages.Error("Face must be of planar type.");
                return(false);
            }

            context.TopDirection = context.BendAxis.Direction.Crossed(facePlane.Axis.Direction);

            // Move axis by radius to the center of the revolve
            var radius = Math.Max(0.001, _Radius);

            context.BendAxis.Translate(context.TopDirection.ToVec().Multiplied(radius));
            return(true);
        }
        public void GetPlaneFromFace()
        {
            var shape = new Box
            {
                DimensionX = 20,
                DimensionY = 20,
                DimensionZ = 1,
            };
            var body = TestGeomGenerator.CreateBody(shape, new Pnt(-10, -20, 0));

            // Local space
            Assert.IsTrue(FaceAlgo.GetCenteredPlaneFromFace(shape, 5, true, out var plane));
            TestContext.WriteLine(plane.Location.X + " " + plane.Location.Y + " " + plane.Location.Z);
            Assert.IsTrue(plane.Location.IsEqual(new Pnt(10, 10, 1), Double.Epsilon));
            Assert.IsTrue(plane.Rotation().IsEqual(Quaternion.Identity));

            // Transformed
            Assert.IsTrue(FaceAlgo.GetCenteredPlaneFromFace(shape, 5, false, out plane));
            Assert.IsNotNull(plane);
            TestContext.WriteLine(plane.Location.X + " " + plane.Location.Y + " " + plane.Location.Z);
            Assert.AreEqual(new Pnt(0, -10, 1), plane.Location);
            Assert.AreEqual(Quaternion.Identity, plane.Rotation());
            Assert.IsTrue(plane.Location.IsEqual(new Pnt(0, -10, 1), Double.Epsilon));
            Assert.IsTrue(plane.Rotation().IsEqual(Quaternion.Identity));
        }
Ejemplo n.º 3
0
        //--------------------------------------------------------------------------------------------------

        void _OnActionFinished(ToolAction toolAction)
        {
            bool finished     = false;
            var  selectAction = toolAction as SelectSubshapeAction;

            Debug.Assert(selectAction != null);

            if (selectAction.SelectedSubshapeType == SubshapeTypes.Face)
            {
                var face        = TopoDS.Face(selectAction.SelectedSubshape);
                var brepAdaptor = new BRepAdaptor_Surface(face, true);
                if (brepAdaptor.GetGeomType() != GeomAbs_SurfaceType.GeomAbs_Plane)
                {
                    StatusText = "Selected face is not a plane type surface.";
                }
                else
                {
                    selectAction.Stop();
                    Stop();
                    finished = true;

                    FaceAlgo.GetCenteredPlaneFromFace(face, out _Plane);
                    CreateSketch();
                }
            }

            if (!finished)
            {
                selectAction.Reset();
            }

            WorkspaceController.Invalidate();
        }
Ejemplo n.º 4
0
        //--------------------------------------------------------------------------------------------------

        public static bool CanFindStartFace(TopoDS_Shape shape)
        {
            try
            {
                return(FaceAlgo.FindFaceNearestToPlane(shape, Pln.XOY) != null);
            }
            catch (Exception)
            {
                return(false);
            }
        }
Ejemplo n.º 5
0
        //--------------------------------------------------------------------------------------------------

        public static Imprint Create(Body body, SubshapeReference faceRef)
        {
            var face = body?.Shape.FindSubshape(faceRef, null).FirstOrDefault()?.ToFace();
            if (face == null)
                return null;

            if (!FaceAlgo.GetCenteredPlaneFromFace(face, out var plane))
            {
                return null;
            }
            return Create(body, faceRef, new Sketch() {Body = body});
        }
Ejemplo n.º 6
0
        //--------------------------------------------------------------------------------------------------

        bool _Make3D()
        {
            // We work with solid shape and face as source
            var solid = GetOperandBRep(0);

            if (solid == null || _Face == null)
            {
                return(false);
            }

            // If extrusion vector has zero length, just copy the source shape converted to faces
            if (Depth == 0)
            {
                BRep = solid;
                return(base.MakeInternal(MakeFlags.NoTransformation));
            }

            // Get face
            var face = GetOperandFace(0, _Face);

            if (face == null)
            {
                Messages.Error("The selected face is lost, please reselect.");
                return(false);
            }

            // Generate direction vector
            var direction = FaceAlgo.GetFaceCenterNormal(face).Direction;

            // Do it!
            var makePrism = new BRepFeat_MakePrism(solid, face, face, direction, Depth > 0 ? 1 : 0, false);

            makePrism.Perform(Depth);
            if (!makePrism.IsDone())
            {
                Messages.Error("Failed extruding the selected face with this parameters.");
                return(false);
            }

            var bRep = makePrism.Shape();

            if (bRep.Solids().Count == 0)
            {
                Messages.Error("Failed extruding the selected face with this parameters.");
                return(false);
            }

            UpdateModifiedSubshapes(solid, makePrism);

            // Get final shape
            BRep = bRep;
            return(true);
        }
        public void PlaneFromFace_Direction()
        {
            var shape = Box.Create(10, 10, 2);

            Assume.That(shape, Is.Not.Null);

            Assert.IsTrue(FaceAlgo.GetCenteredPlaneFromFace(shape, 5, true, out var topFacePlane));
            Assert.IsTrue(topFacePlane.Axis.Direction.IsEqual(Dir.DZ, 0.1));

            Assert.IsTrue(FaceAlgo.GetCenteredPlaneFromFace(shape, 4, true, out var bottomFacePlane));
            Assert.IsTrue(bottomFacePlane.Axis.Direction.IsEqual(Dir.DZ.Reversed(), 0.1));
        }
        //--------------------------------------------------------------------------------------------------

        #endregion

        #region Statics

        public static bool CanFindReferenceFace(TopoDS_Shape shape)
        {
            try
            {
                // Find biggest face
                var(_, plane1, _, plane2) = FaceAlgo.FindFaceByAreaSize(shape, (area1, area2) => area1 > area2);
                return(plane1 != null && plane2 != null);
            }
            catch (Exception)
            {
                return(false);
            }
        }
        //--------------------------------------------------------------------------------------------------

        bool _InitMake(out MakeContext context)
        {
            var body = Owner as Body;

            context = new MakeContext();

            var shape = GetShape();

            if (shape == null)
            {
                Messages.Error("The source shape cannot be found. Please reselect the shape or use top shape.");
                return(false);
            }

            context.SourceShape = shape?.GetBRep();

            if (context.SourceShape == null)
            {
                Messages.Error("The shape does not generate valid data.");
                return(false);
            }

            // Get Reference Face
            if (ReferenceFace == null)
            {
                // Find biggest face
                var(face1, plane1, face2, plane2) = FaceAlgo.FindFaceByAreaSize(context.SourceShape, (area1, area2) => area1 > area2);
                if (plane1 == null || plane2 == null)
                {
                    Messages.Error("Reference face cannot be automatically detected.");
                    return(false);
                }

                // Find the face which is nearer to the XoY-Plane
                context.ReferenceFace = plane1.Value.Distance(Pln.XOY) > plane2.Value.Distance(Pln.XOY) ? face2 : face1;
            }
            else
            {
                context.ReferenceFace = shape.FindSubshape(ReferenceFace, null).FirstOrDefault()?.ToFace();
                if (context.ReferenceFace == null)
                {
                    Messages.Error("Manual reference face cannot be found and needs to be reselected.");
                    return(false);
                }
            }

            return(true);
        }
Ejemplo n.º 10
0
        public void FindOppositeFace()
        {
            var source = TestData.GetTestDataBRep(Path.Combine(_BasePath, "FindFaceNearestToPlane_Source.brep"));

            Assume.That(source, Is.Not.Null);
            var faces = source.Faces();

            Assume.That(faces.Count == 112);

            Assert.Multiple(() =>
            {
                var result = FaceAlgo.FindOppositeFace(source, faces[1]);
                Assert.AreEqual(2, faces.IndexOf(result));
                result = FaceAlgo.FindOppositeFace(source, faces[88]);
                Assert.AreEqual(96, faces.IndexOf(result));
            });
        }
Ejemplo n.º 11
0
        //--------------------------------------------------------------------------------------------------

        bool _InitContext(MakeContext context)
        {
            // Currently we work with 1 source shape only
            if (Operands.Count != 1)
            {
                return(false);
            }

            // Get Targets
            context.SourceShape = GetOperandBRep(0);

            if (context.SourceShape == null)
            {
                return(false);
            }

            if (_StartFace == null)
            {
                context.StartFace = FaceAlgo.FindFaceNearestToPlane(context.SourceShape, Pln.XOY);
                if (context.StartFace == null)
                {
                    Messages.Error("Reference face cannot be automatically detected needs to be manually selected.");
                    return(false);
                }

                if (context.DebugOutput)
                {
                    Messages.Trace($"Automatically detected reference face has index {context.SourceShape.Faces().IndexOf(context.StartFace)}");
                }
            }
            else
            {
                context.StartFace = GetOperandFace(0, _StartFace);
                if (context.StartFace == null)
                {
                    Messages.Error("Manual reference face cannot be found and needs to be reselected.");
                    return(false);
                }
            }


            return(true);
        }
Ejemplo n.º 12
0
        public void FindConnectedFaces()
        {
            var shape = TestData.GetTestDataBRep(Path.Combine(_BasePath, "FindConnectedFaces_Source.brep"));

            Assume.That(shape, Is.Not.Null);

            var faces          = shape.Faces();
            var edges          = shape.Edges();
            var connectedFaces = FaceAlgo.FindConnectedFaces(shape, faces[10]);

            Assert.That(connectedFaces.Count == 4);
            Assert.That(connectedFaces.ContainsKey(faces[11]));
            Assert.That(connectedFaces[faces[11]].IsSame(edges[24]));
            Assert.That(connectedFaces.ContainsKey(faces[13]));
            Assert.That(connectedFaces[faces[13]].IsSame(edges[23]));
            Assert.That(connectedFaces.ContainsKey(faces[9]));
            Assert.That(connectedFaces[faces[9]].IsSame(edges[21]));
            Assert.That(connectedFaces.ContainsKey(faces[6]));
            Assert.That(connectedFaces[faces[6]].IsSame(edges[16]));
        }
Ejemplo n.º 13
0
        //--------------------------------------------------------------------------------------------------

        bool _Make2D()
        {
            // We work with 2D shapes as source
            var faceShape = GetOperand2DFaces(0, null);

            if (faceShape == null)
            {
                return(false);
            }

            // Get plane
            if (!FaceAlgo.GetPlaneOfFaces(faceShape, out var plane))
            {
                return(false);
            }

            // If extrusion vector has zero length, just copy the source shape converted to faces
            if (Depth == 0)
            {
                BRep = faceShape;
                return(base.MakeInternal(MakeFlags.NoTransformation));
            }

            // Generate vector
            var vector = plane.Axis.Direction.ToVec().Multiplied(Depth);

            // Offset if symmetric
            if (Symmetric)
            {
                var offset    = vector.Reversed().Multiplied(0.5);
                var transform = new BRepBuilderAPI_Transform(faceShape, new Trsf(Pnt.Origin, offset.ToPnt()));
                faceShape = transform.Shape();
            }

            // Do it!
            var makePrism = new BRepPrimAPI_MakePrism(faceShape, vector);

            // Get final shape
            BRep = makePrism.Shape();
            return(true);
        }
Ejemplo n.º 14
0
        public void FindFaceNearestToPlane()
        {
            var source = TestData.GetTestDataBRep(Path.Combine(_BasePath, "FindFaceNearestToPlane_Source.brep"));

            Assume.That(source, Is.Not.Null);
            var faces = source.Faces();

            Assume.That(faces.Count == 112);

            Assert.Multiple(() =>
            {
                var result = FaceAlgo.FindFaceNearestToPlane(source, Pln.XOY);
                Assert.AreEqual(13, faces.IndexOf(result), "Face not found for XY");

                result = FaceAlgo.FindFaceNearestToPlane(source, Pln.ZOX);
                Assert.AreEqual(35, faces.IndexOf(result), "Face not found for ZX");

                result = FaceAlgo.FindFaceNearestToPlane(source, Pln.YOZ);
                Assert.AreEqual(1, faces.IndexOf(result), "Face not found for YZ");
            });
        }
Ejemplo n.º 15
0
        //--------------------------------------------------------------------------------------------------

        #endregion

        #region Make

        protected override bool MakeInternal(MakeFlags flags)
        {
            ClearSubshapeLists();

            // Currently we work with 2 source shape only
            if ((Operands.Count != 2) || (_Face == null))
            {
                return(false);
            }

            // Get Target
            var targetShape = GetOperandBRep(0);

            if (targetShape == null)
            {
                return(false);
            }

            var faceShape = GetOperandFace(0, _Face);

            if (faceShape == null)
            {
                return(false);
            }

            if (!FaceAlgo.GetCenteredPlaneFromFace(faceShape, out var facePlane))
            {
                Messages.Error("Target face is not planar.");
                return(false);
            }

            var baseFacesShape = GetOperand2DFaces(1, facePlane);

            if (baseFacesShape == null)
            {
                Messages.Error("Cannot create faces from 2D operand.");
                return(false);
            }

            // Check if we have inner wires (holes), the algo will not work with them when using BRepFeat_MakeDPrism
            bool useDraft = DraftAngle != 0;

            if (useDraft)
            {
                foreach (var face in baseFacesShape.Faces())
                {
                    if (face.Wires().Count > 1)
                    {
                        Messages.Warning("Imprinting a face with holes and draft angle isn't supported. Remove holes and imprint them in a second imprint.");
                        useDraft = false;
                        break;
                    }
                }
            }

            // If extrusion vector has zero length, or the shape is empty, just copy the source shape
            if (Depth == 0 || !baseFacesShape.Faces().Any())
            {
                return(Skip());
            }

            // Do it!
            if (useDraft)
            {
                // DraftPrism needs to change the sign of depth and draft angle
                var sign = _Mode == ImprintMode.Raise ? 1 : -1;

                // Make with draft
                var singleBaseFaces = baseFacesShape.Faces();
                foreach (var singleBaseFace in singleBaseFaces)
                {
                    var makePrism = new BRepFeat_MakeDPrism(targetShape, singleBaseFace, faceShape, _DraftAngle.ToRad() * sign, _Mode == ImprintMode.Raise ? 1 : 0, true);
                    if (_Mode == ImprintMode.Cutout)
                    {
                        makePrism.PerformThruAll();
                    }
                    else
                    {
                        makePrism.Perform(Depth * sign);
                    }

                    UpdateModifiedSubshapes(targetShape, makePrism);
                    UpdateModifiedSubshapes(singleBaseFace, makePrism);
                    targetShape = makePrism.Shape();
                }
                BRep = targetShape;
            }
            else
            {
                // Generate direction
                var direction = _Mode == ImprintMode.Raise ? facePlane.Axis.Direction : facePlane.Axis.Direction.Reversed();

                // Make w/o draft
                var makePrism = new BRepFeat_MakePrism(targetShape, baseFacesShape, faceShape, direction, _Mode == ImprintMode.Raise ? 1 : 0, true);
                if (_Mode == ImprintMode.Cutout)
                {
                    makePrism.PerformThruAll();
                }
                else
                {
                    makePrism.Perform(Depth);
                }

                UpdateModifiedSubshapes(targetShape, makePrism);
                UpdateModifiedSubshapes(baseFacesShape, makePrism);
                BRep = makePrism.Shape();
            }

            return(base.MakeInternal(flags));
        }
Ejemplo n.º 16
0
        //--------------------------------------------------------------------------------------------------

        bool _DoCreateBoxes(MakeContext context)
        {
            foreach (var commonSolid in context.Common)
            {
                // Find smallest face
                var(_, startPlane, _, stopPlane) = FaceAlgo.FindFaceByAreaSize(commonSolid, (area1, area2) => area1 < area2);
                if (startPlane == null || stopPlane == null)
                {
                    Messages.Error("Cannot find two parallel plane faces in common shape.");
                    return(false);
                }

                // Create offsets
                var vector = new Vec(startPlane.Value.Location, stopPlane.Value.Location);

                var cutPlaneOffsets = new double[BoxCount - 1];
                if (_CustomBoxRatios != null && _CustomBoxRatios.Length != 0)
                {
                    // Create by custom sizes
                    var sumParts    = _CustomBoxRatios.Sum();
                    var planeOffset = 0.0;

                    for (int cutPlaneIndex = 0; cutPlaneIndex < _BoxCount - 1; cutPlaneIndex++)
                    {
                        planeOffset += _CustomBoxRatios[cutPlaneIndex];
                        cutPlaneOffsets[cutPlaneIndex] = planeOffset / sumParts;
                    }
                }
                else
                {
                    // Create by ratio
                    var firstPart   = _IsFirst != _ReverseOrder ? _Ratio : 1.0 - _Ratio;
                    var sumParts    = (BoxCount >> 1) * 1.0 + (_BoxCount & 1) * firstPart;
                    var planeOffset = 0.0;

                    for (int cutPlaneIndex = 0; cutPlaneIndex < _BoxCount - 1; cutPlaneIndex++)
                    {
                        planeOffset += (cutPlaneIndex & 1) > 0 ? 1.0 - firstPart : firstPart;
                        cutPlaneOffsets[cutPlaneIndex] = planeOffset / sumParts;
                    }
                }

                // Create cut tools
                var build       = new BRep_Builder();
                var listOfTools = new TopTools_ListOfShape();


                for (int cutPlaneIndex = 0; cutPlaneIndex < cutPlaneOffsets.Length; cutPlaneIndex++)
                {
                    var cutplane = startPlane.Value.Translated(vector.Multiplied(cutPlaneOffsets[cutPlaneIndex]));
                    var face     = new TopoDS_Face();
                    build.MakeFace(face, new Geom_Plane(cutplane), 1e-7);
                    listOfTools.Append(face);
                }

                // Split
                var listOfArguments = new TopTools_ListOfShape();
                listOfArguments.Append(commonSolid);
                var splitter = new BRepAlgoAPI_Splitter();
                splitter.SetArguments(listOfArguments);
                splitter.SetTools(listOfTools);
                splitter.Build();
                if (!splitter.IsDone())
                {
                    Messages.Error("Cannot split common shape into boxes.");
                    return(false);
                }

                context.Boxes.Add(splitter.Shape().Solids());
            }

            return(true);
        }
Ejemplo n.º 17
0
        //--------------------------------------------------------------------------------------------------

        void _FindFacesConnectedToBendSection(MakeContext context, TopoDS_Edge sharedEdge, BendParameter bendParams)
        {
            // Find connected section faces
            bendParams.ConnectedInSharedEdges[0] = sharedEdge;
            bendParams.ConnectedInFaces[0]       = FaceAlgo.FindConnectedFace(context.SourceShape, bendParams.Faces[0], sharedEdge);
            var opEdge = bendParams.Faces[0].Edges().Find(e => !e.IsSame(sharedEdge) && !e.IsSame(bendParams.Edges[0]) && !e.IsSame(bendParams.Edges[1]));

            if (opEdge != null)
            {
                bendParams.ConnectedOutSharedEdges[0] = opEdge;
                bendParams.ConnectedOutFaces[0]       = FaceAlgo.FindConnectedFace(context.SourceShape, bendParams.Faces[0], opEdge);
            }

            // Find the other connection edge
            TopoDS_Vertex sharedVtx1 = null;

            foreach (var edge in bendParams.Faces[1].Edges())
            {
                var vtx = EdgeAlgo.FindSharedVertex(edge, sharedEdge);
                if (vtx != null)
                {
                    // Get the other (not shared) vertex
                    sharedVtx1 = edge.Vertices().Find(v => !v.IsSame(vtx));
                    break;
                }
            }
            TopoDS_Vertex sharedVtx2 = null;

            foreach (var edge in bendParams.Faces[2].Edges())
            {
                var vtx = EdgeAlgo.FindSharedVertex(edge, sharedEdge);
                if (vtx != null)
                {
                    // Get the other (not shared) vertex
                    sharedVtx2 = edge.Vertices().Find(v => !v.IsSame(vtx));
                    break;
                }
            }
            TopoDS_Edge otherSharedEdge = null;

            foreach (var edge in bendParams.Faces[3].Edges())
            {
                var vertices = edge.Vertices();
                if (vertices.ContainsSame(sharedVtx1) &&
                    vertices.ContainsSame(sharedVtx2))
                {
                    otherSharedEdge = edge;
                    break;
                }
            }
            if (otherSharedEdge == null)
            {
                return;
            }

            // Find connected section faces for the other edge
            bendParams.ConnectedOutSharedEdges[1] = otherSharedEdge;
            bendParams.ConnectedInFaces[1]        = FaceAlgo.FindConnectedFace(context.SourceShape, bendParams.Faces[3], otherSharedEdge);
            var otherOpEdge = bendParams.Faces[3].Edges().Find(e => !e.IsSame(otherSharedEdge) && !e.IsSame(bendParams.Edges[2]) && !e.IsSame(bendParams.Edges[3]));

            if (otherOpEdge != null)
            {
                bendParams.ConnectedOutSharedEdges[1] = otherOpEdge;
                bendParams.ConnectedOutFaces[1]       = FaceAlgo.FindConnectedFace(context.SourceShape, bendParams.Faces[3], otherOpEdge);
            }
        }
Ejemplo n.º 18
0
        //--------------------------------------------------------------------------------------------------

        #endregion

        #region Make

        protected override bool MakeInternal(MakeFlags flags)
        {
            ClearSubshapeLists();

            // Currently we work with 2 source shape only
            if ((Operands.Count != 2) || (_Face == null))
                return false;

            // Get Target
            var targetShape = GetOperandBRep(0);
            if (targetShape == null)
                return false;

            var faceShape = GetOperandFace(0, _Face);
            if (faceShape == null)
                return false;

            if (!FaceAlgo.GetCenteredPlaneFromFace(faceShape, out var facePlane))
            {
                Messages.Error("Target face is not planar.");
                return false;
            }

            var baseFacesShape = GetOperand2DFaces(1, facePlane);
            if (baseFacesShape == null)
            {
                Messages.Error("Cannot create faces from 2D operand.");
                return false;
            }

            // If extrusion vector has zero length, or the shape is empty, just copy the source shape
            if (Depth == 0 || !baseFacesShape.Faces().Any())
            {
                return Skip();
            }

            // Do it!
            if (DraftAngle != 0)
            {
                // DraftPrism needs to change the sign of depth and draft angle
                var sign = _Mode == ImprintMode.Raise ? 1 : -1;

                // Make with draft 
                var singleBaseFaces = baseFacesShape.Faces();
                foreach (var singleBaseFace in singleBaseFaces)
                {
                    var makePrism = new BRepFeat_MakeDPrism(targetShape, singleBaseFace, faceShape, _DraftAngle.ToRad()*sign, _Mode == ImprintMode.Raise ? 1 : 0, true);
                    if (_Mode == ImprintMode.Cutout)
                        makePrism.PerformThruAll();
                    else
                        makePrism.Perform(Depth*sign);

                    UpdateModifiedSubshapes(targetShape, makePrism);
                    UpdateModifiedSubshapes(singleBaseFace, makePrism);
                    targetShape = makePrism.Shape();
                }
                BRep = targetShape;
            }
            else
            {
                // Generate direction
                var direction = _Mode == ImprintMode.Raise ? facePlane.Axis.Direction : facePlane.Axis.Direction.Reversed();

                // Make w/o draft
                var makePrism = new BRepFeat_MakePrism(targetShape, baseFacesShape, faceShape, direction, _Mode == ImprintMode.Raise ? 1 : 0, true);
                if (_Mode == ImprintMode.Cutout)
                    makePrism.PerformThruAll();
                else
                    makePrism.Perform(Depth);

                UpdateModifiedSubshapes(targetShape, makePrism);
                UpdateModifiedSubshapes(baseFacesShape, makePrism);
                BRep = makePrism.Shape();
            }

            return base.MakeInternal(flags);
        }
Ejemplo n.º 19
0
        //--------------------------------------------------------------------------------------------------

        /*
         * Division of the shape in different sections. Sections contain all faces of a flange, and
         * are connected with bend sections.
         * Start with the start face and examine all connected faces. If an face is part of a bend section,
         * stop here. If not, add it to the current section and add it to the queue of faces whose connected
         * faces are to examined.
         * Don't process any face twice.
         * If a bend section is determined, add it to the list of connected sections and to the queue of
         * sections to process.
         * If a bend section is processed, all connected faces, which are part of the bend section or
         * the parent section, are sorted out by the processedFaces list, so they must be part of the then
         * current section.
         */
        bool _AnalyzeTopology(MakeContext context)
        {
            var queuedSections = new Queue <Section>();
            var queuedFaces    = new Queue <TopoDS_Face>();
            var processedFaces = new List <TopoDS_Face>();
            var abandonedFaces = new List <TopoDS_Face>(); // Faces that are falsely detected as side faces of a bend section

            // Init algorithm with root section and reference face
            context.RootSection = new Section();
            context.RootSection.Faces.Add(context.StartFace);
            processedFaces.Add(context.StartFace);
            queuedSections.Enqueue(context.RootSection);

            // Iterate
            while (queuedSections.Any())
            {
                var currentSection = queuedSections.Dequeue();
                queuedFaces.Clear();
                queuedFaces.EnqueueMany(currentSection.Faces);

                while (queuedFaces.Any())
                {
                    var currentFace = queuedFaces.Dequeue();

                    // Enumerate connected faces
                    var connectedFaces = FaceAlgo.FindConnectedFaces(context.SourceShape, currentFace);
                    foreach (var connectedFace in connectedFaces.Keys)
                    {
                        if (processedFaces.ContainsSame(connectedFace))
                        {
                            continue;
                        }
                        processedFaces.Add(connectedFace);

                        if (currentSection.IsBendSection)
                        {
                            if (currentSection.Faces.ContainsSame(connectedFace))
                            {
                                continue;
                            }
                            if (_IsFaceOfBendSection(connectedFace, null))
                            {
                                continue;
                            }

                            // Add to following section
                            currentSection.Children.First().Faces.Add(connectedFace);
                        }
                        else
                        {
                            // Bend section detection
                            if (_AnalyzeBendSection(context, connectedFace, connectedFaces[connectedFace], out var bendSection))
                            {
                                if (bendSection == null)
                                {
                                    // No section was created, face is suspect, but not convicted
                                    // Keep it as abandoned until it is clearly part of a bend section
                                    abandonedFaces.Add(connectedFace);
                                }
                                else
                                {
                                    currentSection.Children.Add(bendSection);
                                    processedFaces.AddRange(bendSection.Faces);

                                    // Add the opposite face to the processing list,for the case that it is connected only to bend sections
                                    var connectedOpFace = bendSection.BendParameter.ConnectedInFaces[1];
                                    if (connectedOpFace != null && !currentSection.Faces.ContainsSame(connectedOpFace))
                                    {
                                        currentSection.Faces.Add(connectedOpFace);
                                        processedFaces.Add(connectedOpFace);
                                        queuedFaces.Enqueue(connectedOpFace);
                                    }

                                    // Clear all detected faces out of the abandoned list
                                    foreach (var sectionFace in bendSection.Faces)
                                    {
                                        var sameFaceId = abandonedFaces.IndexOfSame(sectionFace);
                                        if (sameFaceId >= 0)
                                        {
                                            abandonedFaces.RemoveAt(sameFaceId);
                                        }
                                    }
                                }
                                continue;
                            }

                            // No bend section detected, take it into this node
                            currentSection.Faces.Add(connectedFace);
                            queuedFaces.Enqueue(connectedFace);
                        }
                    }
                }

                // Abandoned faces should now put to the current section
                foreach (var abandonedFace in abandonedFaces)
                {
                    if (!currentSection.Faces.ContainsSame(abandonedFace))
                    {
                        currentSection.Faces.Add(abandonedFace);
                    }
                }
                abandonedFaces.Clear();

                // Add next sections
                queuedSections.EnqueueMany(currentSection.Children);
            }

            // Debug output
            void _PrintSectionInfo(Section section, string id)
            {
                if (section.IsBendSection)
                {
                    Messages.Trace($"Analyze: {id} BendSection with {section.Faces.Count} faces, Radii {section.BendParameter.Radii[0]} and {section.BendParameter.Radii[1]} .");
                }
                else
                {
                    Messages.Trace($"Analyze: {id} Section with {section.Faces.Count} faces and {section.Children.Count} children.");
                }

                for (var sectionIndex = 0; sectionIndex < section.Children.Count; sectionIndex++)
                {
                    _PrintSectionInfo(section.Children[sectionIndex], id + ":" + sectionIndex);
                }
            }

            if (context.DebugOutput)
            {
                _PrintSectionInfo(context.RootSection, "0");
            }

            return(true);
        }