//-------------------------------------------------------------------------------------------------- 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)); }
//-------------------------------------------------------------------------------------------------- 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(); }
//-------------------------------------------------------------------------------------------------- public static bool CanFindStartFace(TopoDS_Shape shape) { try { return(FaceAlgo.FindFaceNearestToPlane(shape, Pln.XOY) != null); } catch (Exception) { return(false); } }
//-------------------------------------------------------------------------------------------------- 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}); }
//-------------------------------------------------------------------------------------------------- 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); }
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)); }); }
//-------------------------------------------------------------------------------------------------- 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); }
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])); }
//-------------------------------------------------------------------------------------------------- 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); }
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"); }); }
//-------------------------------------------------------------------------------------------------- #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)); }
//-------------------------------------------------------------------------------------------------- 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); }
//-------------------------------------------------------------------------------------------------- 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); } }
//-------------------------------------------------------------------------------------------------- #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); }
//-------------------------------------------------------------------------------------------------- /* * 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); }