//-------------------------------------------------------------------------------------------------- bool _MakeBendSection(MakeContext context) { if (_Angle <= 0) { return(true); } // Make face for revolving var bendFace = _MakeBendSectionFace(context); if (bendFace == null) { return(false); } // Build bend section var makeRevol = new BRepPrimAPI_MakeRevol(bendFace, context.BendAxis, _Angle.Clamp(0.0, 180.0).ToRad()); if (!makeRevol.IsDone()) { Messages.Error("Failed building bending edge."); return(false); } context.BendSectionShape = makeRevol.Shape(); return(true); }
//-------------------------------------------------------------------------------------------------- bool _BuildResultShape(MakeContext context) { context.Sewer = new BRepBuilderAPI_Sewing(1.0e-06); if (!_AddSectionToResult(context, context.RootSection, Trsf.Identity)) { return(false); } context.Sewer.Perform(); var sewedShape = context.Sewer.SewedShape(); if (sewedShape.ShapeType() == TopAbs_ShapeEnum.TopAbs_SHELL) { var makeSolid = new BRepBuilderAPI_MakeSolid(sewedShape.ToShell()); if (makeSolid.IsDone()) { context.ResultShape = makeSolid.Solid(); return(true); } } Messages.Error("Resulting faces could not be sewn to a solid."); context.ResultShape = sewedShape; return(true); }
//-------------------------------------------------------------------------------------------------- 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); }
//-------------------------------------------------------------------------------------------------- protected override bool MakeInternal(MakeFlags flags) { ClearSubshapeLists(); var context = new MakeContext(); if (!_DoInitContext(context)) { return(false); } if (!_DoBuildWireList(context)) { return(false); } if (!_DoThruSections(context)) { return(false); } if (!_DoThicken(context)) { return(false); } BRep = context.Result; return(base.MakeInternal(flags)); }
//-------------------------------------------------------------------------------------------------- protected override bool MakeInternal(MakeFlags flags) { ClearSubshapeLists(); var context = new MakeContext(); if (!_DoInitContext(context)) { return(false); } if (!_DoComputeArguments(context)) { return(false); } _DoOffset(context); // Offset can fail if (!_DoDraft(context)) { return(false); } BRep = context.Result; return(base.MakeInternal(flags)); }
//-------------------------------------------------------------------------------------------------- bool _DoInitContext(MakeContext context) { // We take only one source shapes if (Operands.Count != 1) { Messages.Error("This modifier needs exactly one source shape."); return(false); } context.Source = GetOperandBRep(0); if (context.Source == null) { Messages.Error("The preceeding shape does not provide a valid geometry."); return(false); } context.Face = GetOperandFace(0, _Face); if (context.Face == null) { Messages.Error("The face is not valid, check source shape or re-select face."); return(false); } return(true); }
//-------------------------------------------------------------------------------------------------- bool _DoCutoutBoxes(MakeContext context) { var listOfTools = new TopTools_ListOfShape(); foreach (var boxes in context.Boxes) { for (int boxIndex = _ReverseOrder == _IsFirst ? 0 : 1; boxIndex < boxes.Count; boxIndex += 2) { listOfTools.Append(boxes[boxIndex]); } } var listOfArguments = new TopTools_ListOfShape(); listOfArguments.Append(context.OwnBrep); var cutter = new BRepAlgoAPI_Cut(); cutter.SetArguments(listOfArguments); cutter.SetTools(listOfTools); cutter.Build(); if (!cutter.IsDone()) { Messages.Error("Cannot cut boxes out of shape."); return(false); } context.Result = cutter.Shape(); UpdateModifiedSubshapes(context.OwnBrep, cutter); return(true); }
//-------------------------------------------------------------------------------------------------- bool _DoMakeCommon(MakeContext context) { // Make common var makeCommon = _IsFirst ? new BRepAlgoAPI_Common(context.OwnBrep, context.OtherBrep) : new BRepAlgoAPI_Common(context.OtherBrep, context.OwnBrep); if (!makeCommon.IsDone()) { Messages.Error("Cannot find a common between both shapes."); return(false); } var commonBrep = makeCommon.Shape(); var solids = commonBrep.Solids(); if (solids.Count == 0) { // Nothing in common return(true); } context.Common = solids; return(true); }
//-------------------------------------------------------------------------------------------------- bool _BuildResult(MakeContext context) { var shapeListArgs = new TopTools_ListOfShape(); shapeListArgs.Append(context.ModifiedTargetShape ?? context.TargetShape); var shapeListTools = new TopTools_ListOfShape(); if (context.BendSectionShape != null) { shapeListTools.Append(context.BendSectionShape); } if (context.FlangeShape != null) { shapeListTools.Append(context.FlangeShape); } var fuseOp = new BRepAlgoAPI_Fuse(); fuseOp.SetArguments(shapeListArgs); fuseOp.SetTools(shapeListTools); fuseOp.SetGlue(BOPAlgo_GlueEnum.BOPAlgo_GlueShift); fuseOp.Build(); if (!fuseOp.IsDone()) { Messages.Error("Failed fusing generated geometry."); return(false); } context.ResultShape = fuseOp.Shape(); if (context.BendSectionShape != null) { AddNamedSubshapes("Bend", context.BendSectionShape, fuseOp); } if (context.FlangeShape != null) { AddNamedSubshapes("Flange", context.FlangeShape, fuseOp); } if (context.StartGapFace != null) { AddNamedSubshapes("StartGap", context.StartGapFace, fuseOp); if (context.EndGapFace == null) { AddNamedSubshapes("EndGap", context.StartGapFace, fuseOp); } } if (context.EndGapFace != null) { AddNamedSubshapes("EndGap", context.EndGapFace, fuseOp); if (context.StartGapFace == null) { AddNamedSubshapes("StartGap", context.EndGapFace, fuseOp); } } UpdateModifiedSubshapes(context.ModifiedTargetShape ?? context.TargetShape, fuseOp); return(true); }
//-------------------------------------------------------------------------------------------------- bool _DoCutoffExcess(MakeContext context) { var listOfArguments = new TopTools_ListOfShape(); listOfArguments.Append(context.OwnBrep); // Cutout Commons var listOfTools = new TopTools_ListOfShape(); foreach (var commonSolid in context.Common) { listOfTools.Append(commonSolid); } var cutter = new BRepAlgoAPI_Cut(); cutter.SetArguments(listOfArguments); cutter.SetTools(listOfTools); cutter.Build(); if (!cutter.IsDone()) { Messages.Error("Cannot determine excess of instrusion."); return(false); } // Check if we have more solids than before var originalSolidCount = context.OwnBrep.Solids().Count; var solids = cutter.Shape().Solids(); var excessCount = solids.Count - originalSolidCount; if (excessCount <= 0) { return(true); // No excess found } // Cutout additional solids listOfTools = new TopTools_ListOfShape(); var orderedSolids = solids.OrderBy(solid => solid.Volume()).Take(excessCount); foreach (var solid in orderedSolids) { listOfTools.Append(solid); } cutter = new BRepAlgoAPI_Cut(); cutter.SetArguments(listOfArguments); cutter.SetTools(listOfTools); cutter.Build(); if (!cutter.IsDone()) { Messages.Error("Cannot remove excess of instrusion from shape."); return(false); } UpdateModifiedSubshapes(context.OwnBrep, cutter); context.OwnBrep = cutter.Shape(); return(true); }
//-------------------------------------------------------------------------------------------------- protected override bool MakeInternal(MakeFlags flags) { ClearSubshapeLists(); // Currently we work with 1 source shape only if (Operands.Count != 1 || _Face == null) { return(false); } // Get Targets var context = new MakeContext { TargetShape = GetOperandBRep(0), TargetFace = GetOperandFace(0, Face) }; // Check targets if (context.TargetShape == null) { return(false); } if (context.TargetFace == null) { Messages.Error("Face for adding flange cannot be found in shape and needs to be reselected."); return(false); } // Skip if we have nothing to do if (_Angle == 0 && _Length <= 0) { return(Skip()); } // Make it! if (!(_FindBendAxis(context) && _MakeFlangeFace(context) && _MakeBendSection(context) && _MakeFlangeSection(context) && _BuildResult(context))) { return(false); } if (context.ResultShape == null) { return(Skip()); } BRep = context.ResultShape; return(base.MakeInternal(flags)); }
//-------------------------------------------------------------------------------------------------- protected override bool MakeInternal(MakeFlags flags) { ClearSubshapeLists(); // Check if connection is valid if (AssociatedShape == null) { return(Skip()); } var context = new MakeContext(); if (!_DoInitContext(context)) { return(false); } // Build common Solid if (!_DoMakeCommon(context)) { return(false); } // Nothing in Common if (context.Common == null) { return(Skip()); } // Create box pieces if (!_DoCreateBoxes(context)) { return(false); } // Cutoff Excess if (_RemoveExcess && !_DoCutoffExcess(context)) { return(false); } // Cut box pieces out of the shape if (!_DoCutoutBoxes(context)) { return(false); } BRep = context.Result; return(base.MakeInternal(flags)); }
//-------------------------------------------------------------------------------------------------- TopoDS_Face _MakeBendSectionFace(MakeContext context) { if (_Relief == ReliefFlags.None) { return(context.FlangeFace); } if (context.OppositeEdge == null) { Messages.Error("No opposite edge found for building relief."); return(null); } if (_Relief.HasFlag(Relief & ReliefFlags.Rectangular)) { // Get notch depth = half distance between both bend and op edge var distTool = new BRepExtrema_DistShapeShape(context.BendEdge, context.OppositeEdge); var notchDepth = distTool.Value() * 0.5; var notchVector = context.TopDirection.ToVec().Multiplied(-notchDepth); // Get edge points var points = new Pnt[4]; var bendEdgeAdaptor = new BRepAdaptor_Curve(context.BendEdge); points[0] = bendEdgeAdaptor.Value(bendEdgeAdaptor.FirstParameter()); points[1] = bendEdgeAdaptor.Value(bendEdgeAdaptor.LastParameter()); var opEdgeAdaptor = new BRepAdaptor_Curve(context.OppositeEdge); points[2] = opEdgeAdaptor.Value(opEdgeAdaptor.LastParameter()); points[3] = opEdgeAdaptor.Value(opEdgeAdaptor.FirstParameter()); // Move if (_Relief.HasFlag(ReliefFlags.OppositeSide)) { notchVector.Reverse(); points[2].Translate(notchVector); points[3].Translate(notchVector); } else { points[0].Translate(notchVector); points[1].Translate(notchVector); } // Make face return(TopoUtils.MakeFace(points)); } return(null); }
//-------------------------------------------------------------------------------------------------- bool _DoComputeArguments(MakeContext context) { switch (_BaseEdgeOrVertex.Type) { case SubshapeType.Edge: return(_DoComputeArgumentsByEdge(context)); case SubshapeType.Vertex: return(_DoComputeArgumentsByVertex(context)); default: Messages.Error("The base subshape is not an edge or a vertex."); 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); }
//-------------------------------------------------------------------------------------------------- bool _Slice(MakeContext context) { context.Slicer = new SliceByPlanes(context.SourceShape, context.ReferenceFace, LayerCount); if (!context.Slicer.CreateSlices(false)) { return(false); } _Layers = new SliceByPlanes.Slice[context.Slicer.Slices.Length]; for (int index = 0; index < context.Slicer.Slices.Length; index++) { Layers[index] = context.Slicer.Slices[index]; } return(true); }
//-------------------------------------------------------------------------------------------------- bool _DoInitContext(MakeContext context) { // We need a minimum of 2 source shapes if (Operands.Count < 2) { Messages.Error("This modifier needs at least two sketches defining the loft start and end sections."); return(false); } if (Operands.Any(op => op.GetShapeType() != ShapeType.Sketch)) { Messages.Error("This modifier does only take sketches defining the loft sections as operands."); return(false); } return(true); }
//-------------------------------------------------------------------------------------------------- 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); }
//-------------------------------------------------------------------------------------------------- bool _DoComputeArgumentsByEdge(MakeContext context) { var edge = GetOperandEdge(0, _BaseEdgeOrVertex, context.Face); if (edge == null) { Messages.Error("The face is not valid, check source shape or re-select face."); return(false); } if (!ComputeAxisFromEdge(context.Face, edge, _BaseParameter, out var axis)) { Messages.Error("The axis can not be computed by the base edge. Please re-select edge."); } context.Direction = axis.Direction; context.NeutralPlane = new Pln(axis.Location, axis.Direction); context.ReversedFaceSense = edge.Orientation() == TopAbs_Orientation.TopAbs_REVERSED; return(true); }
//-------------------------------------------------------------------------------------------------- bool _MakeFlangeSection(MakeContext context) { if (_Length <= 0) { return(true); } var brepAdaptor = new BRepAdaptor_Surface(context.FlangeFace); if (brepAdaptor.GetGeomType() != GeomAbs_SurfaceType.GeomAbs_Plane) { Messages.Error("Flanges can only be added to planar faces."); return(false); } var direction = brepAdaptor.Plane().Position.Direction; if (context.FlangeFace.Orientation() == TopAbs_Orientation.TopAbs_REVERSED) { direction.Reverse(); } // Extrude var makePrism = new BRepPrimAPI_MakePrism(context.FlangeFace, direction.ToVec().Multiplied(_Length)); if (!makePrism.IsDone()) { Messages.Error("Failed building flange."); return(false); } var flangeShape = makePrism.Shape(); // Rotate if bended if (_Angle > 0) { var trsf = new Trsf(context.BendAxis, _Angle.Clamp(0.0, 180.0).ToRad()); flangeShape.Location(new TopLoc_Location(trsf)); } context.FlangeShape = flangeShape; return(true); }
//-------------------------------------------------------------------------------------------------- bool _DoInitContext(MakeContext context) { // Currently we work with 2 source shape only if (Operands.Count != 2) { Messages.Error("This modifier needs exactly two operands."); return(false); } // The second operand must be a body var op1Body = GetOperand(1) as BodyShapeOperand; if (op1Body == null) { Messages.Error("The associated operand is not a valid body."); return(false); } if (AssociatedShape.Body != op1Body.Body) { Messages.Error("The associated shape is not found on the operand body."); return(false); } // Get BReps context.OwnBrep = GetOperandBRep(0); if (context.OwnBrep == null) { Messages.Error("The predecessor shape is not valid."); return(false); } context.OtherBrep = AssociatedShape.GetSourceBRep(GetCoordinateSystem()); if (context.OtherBrep == null) { Messages.Error("The associated shape is not valid."); return(false); } return(true); }
//-------------------------------------------------------------------------------------------------- bool _DoComputeArgumentsByVertex(MakeContext context) { var vertex = GetOperandVertex(0, _BaseEdgeOrVertex); if (vertex == null) { Messages.Error("The vertex is not valid, check source shape or re-select vertex."); return(false); } if (!ComputeAxisFromVertex(context.Face, vertex, out var axis)) { Messages.Error("The axis can not be computed by the base vertex. Please re-select vertex."); } // Calc Direction and NeutralPlane context.Direction = axis.Direction; context.NeutralPlane = new Pln(axis.Location, axis.Direction); context.ReversedFaceSense = context.Face.Orientation() != TopAbs_Orientation.TopAbs_REVERSED; return(true); }
//-------------------------------------------------------------------------------------------------- bool _DoThruSections(MakeContext context) { var maker = new BRepOffsetAPI_ThruSections(context.AllWiresClosed); maker.CheckCompatibility(true); foreach (var wireInfo in context.Wires) { maker.AddWire(wireInfo.Wire); } maker.Build(); if (!maker.IsDone()) { Messages.Error("Make thru sections failed."); return(false); } context.Result = maker.Shape(); context.StartFace = maker.FirstShape().ToFace(); context.EndFace = maker.LastShape().ToFace(); return(true); }
//-------------------------------------------------------------------------------------------------- bool _DoDraft(MakeContext context) { var draftAngle = new BRepOffsetAPI_DraftAngle(context.Source); draftAngle.Add(context.Face, context.Direction, _Angle.ToRad(), context.NeutralPlane); if (!draftAngle.AddDone()) { Messages.Error("The tapering has failed due to invalid input data. Please review your inputs."); return(false); } draftAngle.Build(); if (!draftAngle.IsDone()) { Messages.Error("Make draft angle failed."); return(false); } context.Result = draftAngle.Shape(); UpdateModifiedSubshapes(context.Source, draftAngle); return(true); }
//-------------------------------------------------------------------------------------------------- protected override bool MakeInternal(MakeFlags flags) { ClearSubshapeLists(); // Make it! var context = new MakeContext { DebugOutput = flags.HasFlag(MakeFlags.DebugOutput) }; if (!(_InitContext(context) && _AnalyzeTopology(context))) { return(false); } // Skip if we have only one section if (!context.RootSection.Children.Any()) { return(Skip()); } if (!_BuildResultShape(context)) { return(false); } if (context.ResultShape == null) { return(Skip()); } BRep = context.ResultShape; return(base.MakeInternal(flags)); }
//-------------------------------------------------------------------------------------------------- bool _AddSectionToResult(MakeContext context, Section section, Trsf transformation) { if (section.IsBendSection) { // Calculate k-factor and bend allowance // k = 0.65 + 0.5 * log(r/s) // This is DIN/ISO value range of 0..2 var thickness = section.BendParameter.Radii[0].Distance(section.BendParameter.Radii[1]).Abs(); var radius = Math.Min(section.BendParameter.Radii[0], section.BendParameter.Radii[1]); var kfac = (0.65 + 0.5 * Math.Log10(radius / thickness)).Clamp(0.0, 1.0); var bendAllowance = (radius + thickness * 0.5 * kfac) * section.BendParameter.AngleRad; if (context.DebugOutput) { Messages.Trace($"The bend section has following parameters: thickness {thickness} radius {radius} kfactor {kfac} angle {section.BendParameter.AngleRad} bend allowance {bendAllowance}."); } var axes = new [] { section.BendParameter.Axes[0], section.BendParameter.Axes[1] }; // Which side of the bend section is our in-side? // A point translated along the XDirection of the axis using one of the radii must be on the edge. var swapSign = 1.0; var edgeAdaptor = new BRepAdaptor_Curve(section.BendParameter.ConnectedInSharedEdges[0]); var pnt0 = axes[0].Location.Translated(axes[0].XDirection.ToVec() * section.BendParameter.Radii[0]); var pnt1 = axes[0].Location.Translated(axes[0].XDirection.ToVec() * section.BendParameter.Radii[1]); if (!(edgeAdaptor.Line().Contains(pnt0, 0.000001) || edgeAdaptor.Line().Contains(pnt1, 0.000001))) { swapSign = -1.0; axes[0].Rotate(section.BendParameter.Axes[0].Axis, section.BendParameter.AngleRad); axes[1].Rotate(section.BendParameter.Axes[1].Axis, section.BendParameter.AngleRad); } axes[0].Transform(transformation); axes[1].Transform(transformation); // Create no bend section, if kfac==0 if (kfac > 0.0) { // Reconstruct faces var pnts = new Pnt[4]; // Corner points of one section pnts[0] = axes[0].Location.Translated(axes[0].XDirection.ToVec() * section.BendParameter.Radii[0]); pnts[1] = axes[1].Location.Translated(axes[1].XDirection.ToVec() * section.BendParameter.Radii[0]); pnts[2] = axes[0].Location.Translated(axes[0].XDirection.ToVec() * section.BendParameter.Radii[1]); pnts[3] = axes[1].Location.Translated(axes[1].XDirection.ToVec() * section.BendParameter.Radii[1]); var extrudeVec = axes[0].YDirection.ToVec() * swapSign * bendAllowance; // Vector to extrude to the other section // Top/Bottom context.Sewer.Add(TopoUtils.MakeFace(new[] { pnts[0], pnts[0].Translated(extrudeVec), pnts[1].Translated(extrudeVec), pnts[1] })); context.Sewer.Add(TopoUtils.MakeFace(new[] { pnts[2], pnts[2].Translated(extrudeVec), pnts[3].Translated(extrudeVec), pnts[3] })); // Sides context.Sewer.Add(TopoUtils.MakeFace(new[] { pnts[0], pnts[0].Translated(extrudeVec), pnts[2].Translated(extrudeVec), pnts[2] })); context.Sewer.Add(TopoUtils.MakeFace(new[] { pnts[1], pnts[1].Translated(extrudeVec), pnts[3].Translated(extrudeVec), pnts[3] })); } // Create transform for connected sections var rotTrsf = new Trsf(axes[0].Axis, swapSign * -section.BendParameter.AngleRad); transformation.PreMultiply(rotTrsf); var dispTrsf = new Trsf(axes[0].YDirection.ToVec() * swapSign * bendAllowance); transformation.PreMultiply(dispTrsf); //section.Faces.ForEach(f => context.Sewer.Add(f)); } else { // Copy all faces to the result foreach (var face in section.Faces) { var transformedFace = face.Moved(new TopLoc_Location(transformation)); context.Sewer.Add(transformedFace); //context.Sewer.Add(face); } } // Process children foreach (var child in section.Children) { _AddSectionToResult(context, child, transformation); } 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); } }
//-------------------------------------------------------------------------------------------------- /* * Determine if a face belongs to a bend section. To recognise a bend section, it must have the * following structure: * - Four faces, two of them planar (side faces) and two of them cylinder (top/bottom) * - All edges connecting these four faces must be circular * - All circles must have coaxial axes with only two different positions * - All circles must have one of two radii * * If a side face is detected, the iteration of faces should stop, but no section is built. * If a top/bottom face is detected, the whole section is recovered and all parameters are * determined. The faces are found by searching for faces which share the circular edges. * * This function is called recursively if one of the out-faces is also recognized * as part of a connected bend section. */ bool _AnalyzeBendSection(MakeContext context, TopoDS_Face baseFace, TopoDS_Edge sharedEdge, out Section section) { section = null; var bendParams = new BendParameter(); bendParams.Faces[0] = baseFace; if (!_IsFaceOfBendSection(baseFace, bendParams)) { return(false); } var faceAdaptor = new BRepAdaptor_Surface(baseFace); // Surface is flat, but two edges are of circle and coplanar if (faceAdaptor.GetGeomType() == GeomAbs_SurfaceType.GeomAbs_Plane) { // Ignore them for the moment, but sign them as bend section face return(true); } if (faceAdaptor.GetGeomType() != GeomAbs_SurfaceType.GeomAbs_Cylinder) { // Surface must be of type Cylinder, other are not supported currently return(false); } // Find side faces var facesForEdge0 = EdgeAlgo.FindAdjacentFaces(context.SourceShape, bendParams.Edges[0]); bendParams.Faces[1] = facesForEdge0.face1.IsSame(baseFace) ? facesForEdge0.face2 : facesForEdge0.face1; if (!_IsFaceOfBendSection(bendParams.Faces[1], bendParams)) { return(false); } var facesForEdge1 = EdgeAlgo.FindAdjacentFaces(context.SourceShape, bendParams.Edges[1]); bendParams.Faces[2] = facesForEdge1.face1.IsSame(baseFace) ? facesForEdge1.face2 : facesForEdge1.face1; if (!_IsFaceOfBendSection(bendParams.Faces[2], bendParams)) { return(false); } // Find fourth face var facesForEdge2 = EdgeAlgo.FindAdjacentFaces(context.SourceShape, bendParams.Edges[2]); var facesForEdge3 = EdgeAlgo.FindAdjacentFaces(context.SourceShape, bendParams.Edges[3]); if (facesForEdge2.face1.IsSame(facesForEdge3.face1) || facesForEdge2.face1.IsSame(facesForEdge3.face2)) { bendParams.Faces[3] = facesForEdge2.face1; } else if (facesForEdge2.face2.IsSame(facesForEdge3.face1) || facesForEdge2.face2.IsSame(facesForEdge3.face2)) { bendParams.Faces[3] = facesForEdge2.face2; } else { return(false); // fourth face not found } if (!_IsFaceOfBendSection(bendParams.Faces[3], bendParams)) { return(false); } // Create Section section = new Section(); section.Faces.AddRange(bendParams.Faces.Where(face => face != null)); section.BendParameter = bendParams; // Find connected faces _FindFacesConnectedToBendSection(context, sharedEdge, bendParams); // Check if the connected section is also an bend section if (bendParams.ConnectedOutFaces[0] != null && _AnalyzeBendSection(context, bendParams.ConnectedOutFaces[0], bendParams.ConnectedOutSharedEdges[0], out var connectedBendSection)) { if (connectedBendSection == null) { Messages.Error("A bend section is connected to another bend section with an perpendicular direction.", "To fix this, you need to add a small flange between these two bend sections."); } section.Children.Add(connectedBendSection); } else { section.Children.Add(new Section()); // Bend sections have exactly one connected section } return(true); }
//-------------------------------------------------------------------------------------------------- /* * 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); }
//-------------------------------------------------------------------------------------------------- bool _DoThicken(MakeContext context) { // Do we need to thicken? if (context.AllWiresClosed && _StartCapping != CappingMode.None && _EndCapping != CappingMode.None) { return(true); } // Get tolerance var anaTolerance = new ShapeAnalysis_ShapeTolerance(); var tolerance = anaTolerance.Tolerance(context.Result, 1, TopAbs_ShapeEnum.TopAbs_SHAPE); // Get max of all var joinType = _ThickenCornerType == CornerType.Round ? GeomAbs_JoinType.GeomAbs_Arc : GeomAbs_JoinType.GeomAbs_Intersection; if (context.AllWiresClosed) { // Thicken Solid and remove Caps var removeFaceList = new TopTools_ListOfShape(); if (_StartCapping == CappingMode.None) { removeFaceList.Append(context.StartFace); } if (_EndCapping == CappingMode.None) { removeFaceList.Append(context.EndFace); } // Build thicken solid var makeThick = new BRepOffsetAPI_MakeThickSolid(); var thicknessSign = ThickenDirection == Direction.Inwards ? -1 : 1; makeThick.MakeThickSolidByJoin(context.Result, removeFaceList, Thickness * thicknessSign, tolerance, BRepOffset_Mode.BRepOffset_Skin, true, false, joinType); if (!makeThick.IsDone()) { Messages.Error($"Failed to thicken solid: {makeThick.MakeOffset().Error().ToString()}."); return(false); } context.Result = makeThick.Shape(); } else { // Thicken shell var shell = context.Result.Shells()[0]; if (shell == null) { Messages.Error("Generated shell is invalid."); return(false); } var makeThick = new BRepOffset_MakeOffset(); var thicknessSign = ThickenDirection == Direction.Inwards ? -1 : 1; makeThick.Initialize(shell, Thickness * thicknessSign, tolerance, BRepOffset_Mode.BRepOffset_Skin, true, false, joinType, true); makeThick.MakeOffsetShape(); if (!makeThick.IsDone()) { Messages.Error($"Failed converting shell to thick solid: {makeThick.Error().ToString()}."); return(false); } context.Result = makeThick.Shape(); TopoUtils.UpdateSolidOrientation(context.Result); } return(true); }