Repository: FreeCAD/FreeCAD
File: src/Mod/CAM/Path/Op/Vcarve.py (Python)

Code before:
            else:
                obj.OpFinalDepth = -0.1

    def isToolSupported(self, obj, tool):
        """isToolSupported(obj, tool) ... returns True if v-carve op can work with tool."""
        return (
            hasattr(tool, "Diameter")
            and hasattr(tool, "CuttingEdgeAngle")
            and hasattr(tool, "TipDiameter")
        )

    def debugVoronoi(self, obj):
        """Debug function to display calculated voronoi edges"""

        if not getattr(self, "voronoiDebugCache", None):
            Path.Log.error("debugVoronoi: empty debug cache. Recompute VCarve operation first")
            return

        vPart = FreeCAD.activeDocument().addObject("App::Part", f"{obj.Name}-VoronoiDebug")

        wiresToShow = []

        for face, wires in self.voronoiDebugCache.items():
            for wire in wires:
                currentPartWire = Part.Wire()
                currentPartWire.fixTolerance(0.01)
                for edge in wire:
                    currentEdge = edge.toShape()

                    for v in currentEdge.Vertexes:
                        v.fixTolerance(0.1)

                    currentPartWire.add(currentEdge)
                wiresToShow.append(currentPartWire)

        for w in wiresToShow:
            vPart.addObject(Part.show(w))


def SetupProperties():
    return ["Discretize"]


def Create(name, obj=None, parentJob=None):
    """Create(name) ... Creates and returns a Vcarve operation."""
    if obj is None:
        obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
    obj.Proxy = ObjectVcarve(obj, name, parentJob)
    return obj


Review comment (suggestion):
Not sure is it useful for Vcarve
After PR #25218, can add argument `tol` (tolerance) to `Path.Geom.cmdsForEdge()`

Example #26128



Code after:

                path.append(
                    Path.Command(
                        "G1",
                        {
                            "X": newPosition.x,
                            "Y": newPosition.y,
                            "Z": newPosition.z,
                            "F": vSpeed,
                        },
                    )
                )
            else:  # skip repositioning
                # technically hSpeed + vSpeed should be properly recalculated into F parameter
                # as cmdsForEdge does but we either cut max 0.5 mm through stock or backtrack
                # over already carved edges, so hSpeed will be just fine
                path.append(
                    Path.Command(
                        "G1 X{} Y{} Z{} F{}".format(
                            newPosition.x, newPosition.y, newPosition.z, hSpeed
                        )
                    )
                )

            for e in edge_list:
                path.extend(Path.Geom.cmdsForEdge(e, hSpeed=hSpeed, vSpeed=vSpeed))

            return path

        pathlist = []
        pathlist.append(Path.Command("(starting)"))

        geom = _Geometry.FromObj(obj, self.model[0])

        # iterate over each face separately
        for face, wires in self.buildMedialWires(obj, faces).items():

            # If using depth step-down, calculate maximum usable depth for current face.
            # This is done to avoid adding additional step-down engraving passes when it
            # would make no sense as depth is limited by Maximum Inscribed Circle anyway.

            maximumUsableDepth = geom.stop

            if geom.stepDown > 0:
                _maximumUsableDepth = _get_maximumUsableDepth(wires, geom)
                if _maximumUsableDepth is not None:
                    maximumUsableDepth = _maximumUsableDepth
                    Path.Log.debug(f"Maximum usable depth for current face: {maximumUsableDepth}")

            # first pass
            cutWires(wires, pathlist, obj.OptimizeMovements)