[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6ADKH7KXQ3K4895MW7Z6M6C":3,"skill-same:01M6ADKH7KXQ3K4895MW7Z6M6C":100},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":45,"linkOnly":46,"markdownContent":47,"markdownBody":48,"frontMatter":49,"refName":50,"commitSha":51,"skillDirectory":52,"forksCount":53,"repositoryHtmlUrl":54,"repositoryHomepageUrl":55,"ownerHtmlUrl":56,"ownerBlogUrl":57,"ownerDescription":58,"updatedAt":59,"install":60,"resources":73},"01M6ADKH7KXQ3K4895MW7Z6M6C","freecad-scripts",null,"Expert skill for writing FreeCAD Python scripts, macros, and automation. Use when asked to create FreeCAD models, parametric objects, Part\u002FMesh\u002FSketcher scripts, workbench tools, GUI dialogs with PySide, Coin3D scenegraph manipulation, or any FreeCAD Python API task. Covers FreeCAD scripting basics, geometry creation, FeaturePython objects, interface tools, and macro development.","github\u002Fawesome-copilot","github","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F9919?v=4","01M6AC83DS133TPXFSP1DHNCC6","GitHub","01M6AD3PBDYPK7NVFJNJ66Y8RS",41133,"MIT","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Ffreecad-scripts\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44],"scripts","freecad scripts","basics","parametric","when","for","interface","gui","creation","geometry","objects","expert","writing","api","Tools","macro","create","development","automation","mesh","python","2026-11-10 10:12:30",false,"---\nname: freecad-scripts\ndescription: 'Expert skill for writing FreeCAD Python scripts, macros, and automation. Use when asked to create FreeCAD models, parametric objects, Part\u002FMesh\u002FSketcher scripts, workbench tools, GUI dialogs with PySide, Coin3D scenegraph manipulation, or any FreeCAD Python API task. Covers FreeCAD scripting basics, geometry creation, FeaturePython objects, interface tools, and macro development.'\n---\n\n# FreeCAD Scripts\n\nExpert skill for generating production-quality Python scripts for the FreeCAD CAD application. Interprets shorthand, quasi-code, and natural language descriptions of 3D modeling tasks and translates them into correct FreeCAD Python API calls.\n\n## When to Use This Skill\n\n- Writing Python scripts for FreeCAD's built-in console or macro system\n- Creating or manipulating 3D geometry (Part, Mesh, Sketcher, Path, FEM)\n- Building parametric FeaturePython objects with custom properties\n- Developing GUI tools using PySide\u002FQt within FreeCAD\n- Manipulating the Coin3D scenegraph via Pivy\n- Creating custom workbenches or Gui Commands\n- Automating repetitive CAD operations with macros\n- Converting between mesh and solid representations\n- Scripting FEM analyses, raytracing, or drawing exports\n\n## Prerequisites\n\n- FreeCAD installed (0.19+ recommended; 0.21+\u002F1.0+ for latest API)\n- Python 3.x (bundled with FreeCAD)\n- For GUI work: PySide2 (bundled with FreeCAD)\n- For scenegraph: Pivy (bundled with FreeCAD)\n\n## FreeCAD Python Environment\n\nFreeCAD embeds a Python interpreter. Scripts run in an environment where these key modules are available:\n\n```python\nimport FreeCAD          # Core module (also aliased as 'App')\nimport FreeCADGui       # GUI module (also aliased as 'Gui') — only in GUI mode\nimport Part             # Part workbench — BRep\u002FOpenCASCADE shapes\nimport Mesh             # Mesh workbench — triangulated meshes\nimport Sketcher         # Sketcher workbench — 2D constrained sketches\nimport Draft            # Draft workbench — 2D drawing tools\nimport Arch             # Arch\u002FBIM workbench\nimport Path             # Path\u002FCAM workbench\nimport FEM              # FEM workbench\nimport TechDraw         # TechDraw workbench (replaces Drawing)\nimport BOPTools         # Boolean operations\nimport CompoundTools    # Compound shape utilities\n```\n\n### The FreeCAD Document Model\n\n```python\n# Create or access a document\ndoc = FreeCAD.newDocument(\"MyDoc\")\ndoc = FreeCAD.ActiveDocument\n\n# Add objects\nbox = doc.addObject(\"Part::Box\", \"MyBox\")\nbox.Length = 10.0\nbox.Width = 10.0\nbox.Height = 10.0\n\n# Recompute\ndoc.recompute()\n\n# Access objects\nobj = doc.getObject(\"MyBox\")\nobj = doc.MyBox  # Attribute access also works\n\n# Remove objects\ndoc.removeObject(\"MyBox\")\n```\n\n## Core Concepts\n\n### Vectors and Placements\n\n```python\nimport FreeCAD\n\n# Vectors\nv1 = FreeCAD.Vector(1, 0, 0)\nv2 = FreeCAD.Vector(0, 1, 0)\nv3 = v1.cross(v2)          # Cross product\nd = v1.dot(v2)              # Dot product\nv4 = v1 + v2                # Addition\nlength = v1.Length           # Magnitude\nv_norm = FreeCAD.Vector(v1)\nv_norm.normalize()           # In-place normalize\n\n# Rotations\nrot = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 45)  # axis, angle(deg)\nrot = FreeCAD.Rotation(0, 0, 45)                       # Euler angles (yaw, pitch, roll)\n\n# Placements (position + orientation)\nplacement = FreeCAD.Placement(\n    FreeCAD.Vector(10, 20, 0),    # translation\n    FreeCAD.Rotation(0, 0, 45),   # rotation\n    FreeCAD.Vector(0, 0, 0)       # center of rotation\n)\nobj.Placement = placement\n\n# Matrix (4x4 transformation)\nimport math\nmat = FreeCAD.Matrix()\nmat.move(FreeCAD.Vector(10, 0, 0))\nmat.rotateZ(math.radians(45))\n```\n\n### Creating and Manipulating Geometry (Part Module)\n\nThe Part module wraps OpenCASCADE and provides BRep solid modeling:\n\n```python\nimport FreeCAD\nimport Part\n\n# --- Primitive Shapes ---\nbox = Part.makeBox(10, 10, 10)               # length, width, height\ncyl = Part.makeCylinder(5, 20)               # radius, height\nsphere = Part.makeSphere(10)                  # radius\ncone = Part.makeCone(5, 2, 10)               # r1, r2, height\ntorus = Part.makeTorus(10, 2)                 # major_r, minor_r\n\n# --- Wires and Edges ---\nedge1 = Part.makeLine((0, 0, 0), (10, 0, 0))\nedge2 = Part.makeLine((10, 0, 0), (10, 10, 0))\nedge3 = Part.makeLine((10, 10, 0), (0, 0, 0))\nwire = Part.Wire([edge1, edge2, edge3])\n\n# Circles and arcs\ncircle = Part.makeCircle(5)                   # radius\narc = Part.makeCircle(5, FreeCAD.Vector(0, 0, 0),\n                       FreeCAD.Vector(0, 0, 1), 0, 180)  # start\u002Fend angle\n\n# --- Faces ---\nface = Part.Face(wire)                        # From a closed wire\n\n# --- Solids from Faces\u002FWires ---\nextrusion = face.extrude(FreeCAD.Vector(0, 0, 10))       # Extrude\nrevolved = face.revolve(FreeCAD.Vector(0, 0, 0),\n                         FreeCAD.Vector(0, 0, 1), 360)    # Revolve\n\n# --- Boolean Operations ---\nfused = box.fuse(cyl)           # Union\ncut = box.cut(cyl)              # Subtraction\ncommon = box.common(cyl)        # Intersection\nfused_clean = fused.removeSplitter()  # Clean up seams\n\n# --- Fillets and Chamfers ---\nfilleted = box.makeFillet(1.0, box.Edges)          # radius, edges\nchamfered = box.makeChamfer(1.0, box.Edges)        # dist, edges\n\n# --- Loft and Sweep ---\nloft = Part.makeLoft([wire1, wire2], True)          # wires, solid\nswept = Part.Wire([path_edge]).makePipeShell([profile_wire],\n                                              True, False)  # solid, frenet\n\n# --- BSpline Curves ---\nfrom FreeCAD import Vector\npoints = [Vector(0,0,0), Vector(1,2,0), Vector(3,1,0), Vector(4,3,0)]\nbspline = Part.BSplineCurve()\nbspline.interpolate(points)\nedge = bspline.toShape()\n\n# --- Show in document ---\nPart.show(box, \"MyBox\")    # Quick display (adds to active doc)\n# Or explicitly:\ndoc = FreeCAD.ActiveDocument or FreeCAD.newDocument()\nobj = doc.addObject(\"Part::Feature\", \"MyShape\")\nobj.Shape = box\ndoc.recompute()\n```\n\n### Topological Exploration\n\n```python\nshape = obj.Shape\n\n# Access sub-elements\nshape.Vertexes    # List of Vertex objects\nshape.Edges       # List of Edge objects\nshape.Wires       # List of Wire objects\nshape.Faces       # List of Face objects\nshape.Shells      # List of Shell objects\nshape.Solids      # List of Solid objects\n\n# Bounding box\nbb = shape.BoundBox\nprint(bb.XMin, bb.XMax, bb.YMin, bb.YMax, bb.ZMin, bb.ZMax)\nprint(bb.Center)\n\n# Properties\nshape.Volume\nshape.Area\nshape.Length       # For edges\u002Fwires\nface.Surface       # Underlying geometric surface\nedge.Curve         # Underlying geometric curve\n\n# Shape type\nshape.ShapeType    # \"Solid\", \"Shell\", \"Face\", \"Wire\", \"Edge\", \"Vertex\", \"Compound\"\n```\n\n### Mesh Module\n\n```python\nimport Mesh\n\n# Create mesh from vertices and facets\nmesh = Mesh.Mesh()\nmesh.addFacet(\n    0.0, 0.0, 0.0,   # vertex 1\n    1.0, 0.0, 0.0,   # vertex 2\n    0.0, 1.0, 0.0    # vertex 3\n)\n\n# Import\u002FExport\nmesh = Mesh.Mesh(\"\u002Fpath\u002Fto\u002Ffile.stl\")\nmesh.write(\"\u002Fpath\u002Fto\u002Foutput.stl\")\n\n# Convert Part shape to Mesh\nimport Part\nimport MeshPart\nshape = Part.makeBox(1, 1, 1)\nmesh = MeshPart.meshFromShape(Shape=shape, LinearDeflection=0.1,\n                                AngularDeflection=0.5)\n\n# Convert Mesh to Part shape\nshape = Part.Shape()\nshape.makeShapeFromMesh(mesh.Topology, 0.05)  # tolerance\nsolid = Part.makeSolid(shape)\n```\n\n### Sketcher Module\n\n# Create a sketch on XY plane\nsketch = doc.addObject(\"Sketcher::SketchObject\", \"MySketch\")\nsketch.Placement = FreeCAD.Placement(\n    FreeCAD.Vector(0, 0, 0),\n    FreeCAD.Rotation(0, 0, 0, 1)\n)\n\n# Add geometry (returns geometry index)\nidx_line = sketch.addGeometry(Part.LineSegment(\n    FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(10, 0, 0)))\nidx_circle = sketch.addGeometry(Part.Circle(\n    FreeCAD.Vector(5, 5, 0), FreeCAD.Vector(0, 0, 1), 3))\n\n# Add constraints\nsketch.addConstraint(Sketcher.Constraint(\"Coincident\", 0, 2, 1, 1))\nsketch.addConstraint(Sketcher.Constraint(\"Horizontal\", 0))\nsketch.addConstraint(Sketcher.Constraint(\"DistanceX\", 0, 1, 0, 2, 10.0))\nsketch.addConstraint(Sketcher.Constraint(\"Radius\", 1, 3.0))\nsketch.addConstraint(Sketcher.Constraint(\"Fixed\", 0, 1))\n# Constraint types: Coincident, Horizontal, Vertical, Parallel, Perpendicular,\n#   Tangent, Equal, Symmetric, Distance, DistanceX, DistanceY, Radius, Angle,\n#   Fixed (Block), InternalAlignment\n\ndoc.recompute()\n```\n\n### Draft Module\n\n```python\nimport Draft\nimport FreeCAD\n\n# 2D shapes\nline = Draft.makeLine(FreeCAD.Vector(0,0,0), FreeCAD.Vector(10,0,0))\ncircle = Draft.makeCircle(5)\nrect = Draft.makeRectangle(10, 5)\npoly = Draft.makePolygon(6, radius=5)   # hexagon\n\n# Operations\nmoved = Draft.move(obj, FreeCAD.Vector(10, 0, 0), copy=True)\nrotated = Draft.rotate(obj, 45, FreeCAD.Vector(0,0,0),\n                        axis=FreeCAD.Vector(0,0,1), copy=True)\nscaled = Draft.scale(obj, FreeCAD.Vector(2,2,2), center=FreeCAD.Vector(0,0,0),\n                      copy=True)\noffset = Draft.offset(obj, FreeCAD.Vector(1,0,0))\narray = Draft.makeArray(obj, FreeCAD.Vector(15,0,0),\n                         FreeCAD.Vector(0,15,0), 3, 3)\n```\n\n## Creating Parametric Objects (FeaturePython)\n\nFeaturePython objects are custom parametric objects with properties that trigger recomputation:\n\n```python\nimport FreeCAD\nimport Part\n\nclass MyBox:\n    \"\"\"A custom parametric box.\"\"\"\n\n    def __init__(self, obj):\n        obj.Proxy = self\n        obj.addProperty(\"App::PropertyLength\", \"Length\", \"Dimensions\",\n                         \"Box length\").Length = 10.0\n        obj.addProperty(\"App::PropertyLength\", \"Width\", \"Dimensions\",\n                         \"Box width\").Width = 10.0\n        obj.addProperty(\"App::PropertyLength\", \"Height\", \"Dimensions\",\n                         \"Box height\").Height = 10.0\n\n    def execute(self, obj):\n        \"\"\"Called on document recompute.\"\"\"\n        obj.Shape = Part.makeBox(obj.Length, obj.Width, obj.Height)\n\n    def onChanged(self, obj, prop):\n        \"\"\"Called when a property changes.\"\"\"\n        pass\n\n    def __getstate__(self):\n        return None\n\n    def __setstate__(self, state):\n        return None\n\n\nclass ViewProviderMyBox:\n    \"\"\"View provider for custom icon and display settings.\"\"\"\n\n    def __init__(self, vobj):\n        vobj.Proxy = self\n\n    def getIcon(self):\n        return \":\u002Ficons\u002FPart_Box.svg\"\n\n    def attach(self, vobj):\n        self.Object = vobj.Object\n\n    def updateData(self, obj, prop):\n        pass\n\n    def onChanged(self, vobj, prop):\n        pass\n\n    def __getstate__(self):\n        return None\n\n    def __setstate__(self, state):\n        return None\n\n\n# --- Usage ---\ndoc = FreeCAD.ActiveDocument or FreeCAD.newDocument(\"Test\")\nobj = doc.addObject(\"Part::FeaturePython\", \"CustomBox\")\nMyBox(obj)\nViewProviderMyBox(obj.ViewObject)\ndoc.recompute()\n```\n\n### Common Property Types\n\n| Property Type | Python Type | Description |\n|---|---|---|\n| `App::PropertyBool` | `bool` | Boolean |\n| `App::PropertyInteger` | `int` | Integer |\n| `App::PropertyFloat` | `float` | Float |\n| `App::PropertyString` | `str` | String |\n| `App::PropertyLength` | `float` (units) | Length with units |\n| `App::PropertyAngle` | `float` (deg) | Angle in degrees |\n| `App::PropertyVector` | `FreeCAD.Vector` | 3D vector |\n| `App::PropertyPlacement` | `FreeCAD.Placement` | Position + rotation |\n| `App::PropertyLink` | object ref | Link to another object |\n| `App::PropertyLinkList` | list of refs | Links to multiple objects |\n| `App::PropertyEnumeration` | `list`\u002F`str` | Dropdown selection |\n| `App::PropertyFile` | `str` | File path |\n| `App::PropertyColor` | `tuple` | RGB color (0.0-1.0) |\n| `App::PropertyPythonObject` | any | Serializable Python object |\n\n## Creating GUI Tools\n\n### Gui Commands\n\n```python\nimport FreeCAD\nimport FreeCADGui\n\nclass MyCommand:\n    \"\"\"A custom toolbar\u002Fmenu command.\"\"\"\n\n    def GetResources(self):\n        return {\n            \"Pixmap\": \":\u002Ficons\u002FPart_Box.svg\",\n            \"MenuText\": \"My Custom Command\",\n            \"ToolTip\": \"Creates a custom box\",\n            \"Accel\": \"Ctrl+Shift+B\"\n        }\n\n    def IsActive(self):\n        return FreeCAD.ActiveDocument is not None\n\n    def Activated(self):\n        # Command logic here\n        FreeCAD.Console.PrintMessage(\"Command activated\\n\")\n\nFreeCADGui.addCommand(\"My_CustomCommand\", MyCommand())\n```\n\n### PySide Dialogs\n\n```python\nfrom PySide2 import QtWidgets, QtCore, QtGui\n\nclass MyDialog(QtWidgets.QDialog):\n    def __init__(self, parent=None):\n        super().__init__(parent or FreeCADGui.getMainWindow())\n        self.setWindowTitle(\"My Tool\")\n        self.setMinimumWidth(300)\n\n        layout = QtWidgets.QVBoxLayout(self)\n\n        # Input fields\n        self.label = QtWidgets.QLabel(\"Length:\")\n        self.spinbox = QtWidgets.QDoubleSpinBox()\n        self.spinbox.setRange(0.1, 1000.0)\n        self.spinbox.setValue(10.0)\n        self.spinbox.setSuffix(\" mm\")\n\n        form = QtWidgets.QFormLayout()\n        form.addRow(self.label, self.spinbox)\n        layout.addLayout(form)\n\n        # Buttons\n        btn_layout = QtWidgets.QHBoxLayout()\n        self.btn_ok = QtWidgets.QPushButton(\"OK\")\n        self.btn_cancel = QtWidgets.QPushButton(\"Cancel\")\n        btn_layout.addWidget(self.btn_ok)\n        btn_layout.addWidget(self.btn_cancel)\n        layout.addLayout(btn_layout)\n\n        self.btn_ok.clicked.connect(self.accept)\n        self.btn_cancel.clicked.connect(self.reject)\n\n# Usage\ndialog = MyDialog()\nif dialog.exec_() == QtWidgets.QDialog.Accepted:\n    length = dialog.spinbox.value()\n    FreeCAD.Console.PrintMessage(f\"Length: {length}\\n\")\n```\n\n### Task Panel (Recommended for FreeCAD integration)\n\n```python\nclass MyTaskPanel:\n    \"\"\"Task panel shown in the left sidebar.\"\"\"\n\n    def __init__(self):\n        self.form = QtWidgets.QWidget()\n        layout = QtWidgets.QVBoxLayout(self.form)\n        self.spinbox = QtWidgets.QDoubleSpinBox()\n        self.spinbox.setValue(10.0)\n        layout.addWidget(QtWidgets.QLabel(\"Length:\"))\n        layout.addWidget(self.spinbox)\n\n    def accept(self):\n        # Called when user clicks OK\n        length = self.spinbox.value()\n        FreeCAD.Console.PrintMessage(f\"Accepted: {length}\\n\")\n        FreeCADGui.Control.closeDialog()\n        return True\n\n    def reject(self):\n        FreeCADGui.Control.closeDialog()\n        return True\n\n    def getStandardButtons(self):\n        return int(QtWidgets.QDialogButtonBox.Ok |\n                   QtWidgets.QDialogButtonBox.Cancel)\n\n# Show the panel\npanel = MyTaskPanel()\nFreeCADGui.Control.showDialog(panel)\n```\n\n## Coin3D Scenegraph (Pivy)\n\n```python\nfrom pivy import coin\nimport FreeCADGui\n\n# Access the scenegraph root\nsg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph()\n\n# Add a custom separator with a sphere\nsep = coin.SoSeparator()\nmat = coin.SoMaterial()\nmat.diffuseColor.setValue(1.0, 0.0, 0.0)  # Red\ntrans = coin.SoTranslation()\ntrans.translation.setValue(10, 10, 10)\nsphere = coin.SoSphere()\nsphere.radius.setValue(2.0)\nsep.addChild(mat)\nsep.addChild(trans)\nsep.addChild(sphere)\nsg.addChild(sep)\n\n# Remove later\nsg.removeChild(sep)\n```\n\n## Custom Workbench Creation\n\n```python\nimport FreeCADGui\n\nclass MyWorkbench(FreeCADGui.Workbench):\n    MenuText = \"My Workbench\"\n    ToolTip = \"A custom workbench\"\n    Icon = \":\u002Ficons\u002Ffreecad.svg\"\n\n    def Initialize(self):\n        \"\"\"Called at workbench activation.\"\"\"\n        import MyCommands  # Import your command module\n        self.appendToolbar(\"My Tools\", [\"My_CustomCommand\"])\n        self.appendMenu(\"My Menu\", [\"My_CustomCommand\"])\n\n    def Activated(self):\n        pass\n\n    def Deactivated(self):\n        pass\n\n    def GetClassName(self):\n        return \"Gui::PythonWorkbench\"\n\nFreeCADGui.addWorkbench(MyWorkbench)\n```\n\n## Macro Best Practices\n\n```python\n# Standard macro header\n# -*- coding: utf-8 -*-\n# FreeCAD Macro: MyMacro\n# Description: Brief description of what the macro does\n# Author: YourName\n# Version: 1.0\n# Date: 2026-04-07\n\nimport FreeCAD\nimport Part\nfrom FreeCAD import Base\n\n# Guard for GUI availability\nif FreeCAD.GuiUp:\n    import FreeCADGui\n    from PySide2 import QtWidgets, QtCore\n\ndef main():\n    doc = FreeCAD.ActiveDocument\n    if doc is None:\n        FreeCAD.Console.PrintError(\"No active document\\n\")\n        return\n\n    if FreeCAD.GuiUp:\n        sel = FreeCADGui.Selection.getSelection()\n        if not sel:\n            FreeCAD.Console.PrintWarning(\"No objects selected\\n\")\n\n    # ... macro logic ...\n\n    doc.recompute()\n    FreeCAD.Console.PrintMessage(\"Macro completed\\n\")\n\nif __name__ == \"__main__\":\n    main()\n```\n\n### Selection Handling\n\n```python\n# Get selected objects\nsel = FreeCADGui.Selection.getSelection()           # List of objects\nsel_ex = FreeCADGui.Selection.getSelectionEx()       # Extended (sub-elements)\n\nfor selobj in sel_ex:\n    obj = selobj.Object\n    for sub in selobj.SubElementNames:\n        print(f\"{obj.Name}.{sub}\")\n        shape = obj.getSubObject(sub)  # Get sub-shape\n\n# Select programmatically\nFreeCADGui.Selection.addSelection(doc.MyBox)\nFreeCADGui.Selection.addSelection(doc.MyBox, \"Face1\")\nFreeCADGui.Selection.clearSelection()\n```\n\n### Console Output\n\n```python\nFreeCAD.Console.PrintMessage(\"Info message\\n\")\nFreeCAD.Console.PrintWarning(\"Warning message\\n\")\nFreeCAD.Console.PrintError(\"Error message\\n\")\nFreeCAD.Console.PrintLog(\"Debug\u002Flog message\\n\")\n```\n\n## Common Patterns\n\n### Parametric Pad from Sketch\n\n```python\ndoc = FreeCAD.ActiveDocument\n\n# Create sketch\nsketch = doc.addObject(\"Sketcher::SketchObject\", \"Sketch\")\nsketch.addGeometry(Part.LineSegment(FreeCAD.Vector(0,0,0), FreeCAD.Vector(10,0,0)))\nsketch.addGeometry(Part.LineSegment(FreeCAD.Vector(10,0,0), FreeCAD.Vector(10,10,0)))\nsketch.addGeometry(Part.LineSegment(FreeCAD.Vector(10,10,0), FreeCAD.Vector(0,10,0)))\nsketch.addGeometry(Part.LineSegment(FreeCAD.Vector(0,10,0), FreeCAD.Vector(0,0,0)))\n# Close with coincident constraints\nfor i in range(3):\n    sketch.addConstraint(Sketcher.Constraint(\"Coincident\", i, 2, i+1, 1))\nsketch.addConstraint(Sketcher.Constraint(\"Coincident\", 3, 2, 0, 1))\n\n# Pad (PartDesign)\npad = doc.addObject(\"PartDesign::Pad\", \"Pad\")\npad.Profile = sketch\npad.Length = 5.0\nsketch.Visibility = False\ndoc.recompute()\n```\n\n### Export Shapes\n\n```python\n# STEP export\nPart.export([doc.MyBox], \"\u002Fpath\u002Fto\u002Foutput.step\")\n\n# STL export (mesh)\nimport Mesh\nMesh.export([doc.MyBox], \"\u002Fpath\u002Fto\u002Foutput.stl\")\n\n# IGES export\nPart.export([doc.MyBox], \"\u002Fpath\u002Fto\u002Foutput.iges\")\n\n# Multiple formats via importlib\nimport importlib\nimportlib.import_module(\"importOBJ\").export([doc.MyBox], \"\u002Fpath\u002Fto\u002Foutput.obj\")\n```\n\n### Units and Quantities\n\n```python\n# FreeCAD uses mm internally\nq = FreeCAD.Units.Quantity(\"10 mm\")\nq_inch = FreeCAD.Units.Quantity(\"1 in\")\nprint(q_inch.getValueAs(\"mm\"))  # 25.4\n\n# Parse user input with units\nq = FreeCAD.Units.parseQuantity(\"2.5 in\")\nvalue_mm = float(q)  # Value in mm (internal unit)\n```\n\n## Compensation Rules (Quasi-Coder Integration)\n\nWhen interpreting shorthand or quasi-code for FreeCAD scripts:\n\n1. **Terminology mapping**: \"box\" → `Part.makeBox()`, \"cylinder\" → `Part.makeCylinder()`, \"sphere\" → `Part.makeSphere()`, \"merge\u002Fcombine\u002Fjoin\" → `.fuse()`, \"subtract\u002Fcut\u002Fremove\" → `.cut()`, \"intersect\" → `.common()`, \"round edges\u002Ffillet\" → `.makeFillet()`, \"bevel\u002Fchamfer\" → `.makeChamfer()`\n2. **Implicit document**: If no document handling is mentioned, wrap in standard `doc = FreeCAD.ActiveDocument or FreeCAD.newDocument()`\n3. **Units assumption**: Default to millimeters unless stated otherwise\n4. **Recompute**: Always call `doc.recompute()` after modifications\n5. **GUI guard**: Wrap GUI-dependent code in `if FreeCAD.GuiUp:` when the script may run headless\n6. **Part.show()**: Use `Part.show(shape, \"Name\")` for quick display, or `doc.addObject(\"Part::Feature\", \"Name\")` for named persistent objects\n\n## References\n\n### Primary Links\n\n- [Writing Python code](https:\u002F\u002Fwiki.freecad.org\u002FManual:A_gentle_introduction#Writing_Python_code)\n- [Manipulating FreeCAD objects](https:\u002F\u002Fwiki.freecad.org\u002FManual:A_gentle_introduction#Manipulating_FreeCAD_objects)\n- [Vectors and Placements](https:\u002F\u002Fwiki.freecad.org\u002FManual:A_gentle_introduction#Vectors_and_Placements)\n- [Creating and manipulating geometry](https:\u002F\u002Fwiki.freecad.org\u002FManual:Creating_and_manipulating_geometry)\n- [Creating parametric objects](https:\u002F\u002Fwiki.freecad.org\u002FManual:Creating_parametric_objects)\n- [Creating interface tools](https:\u002F\u002Fwiki.freecad.org\u002FManual:Creating_interface_tools)\n- [Python](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FPython_%28programming_language%29)\n- [Introduction to Python](https:\u002F\u002Fwiki.freecad.org\u002FIntroduction_to_Python)\n- [Python scripting tutorial](https:\u002F\u002Fwiki.freecad.org\u002FPython_scripting_tutorial)\n- [FreeCAD scripting basics](https:\u002F\u002Fwiki.freecad.org\u002FFreeCAD_Scripting_Basics)\n- [Gui Command](https:\u002F\u002Fwiki.freecad.org\u002FGui_Command)\n\n### Bundled Reference Documents\n\nSee the [references\u002F](references\u002F) directory for topic-organized guides:\n\n1. [scripting-fundamentals.md](references\u002Fscripting-fundamentals.md) — Core scripting, document model, console\n2. [geometry-and-shapes.md](references\u002Fgeometry-and-shapes.md) — Part, Mesh, Sketcher, topology\n3. [parametric-objects.md](references\u002Fparametric-objects.md) — FeaturePython, properties, scripted objects\n4. [gui-and-interface.md](references\u002Fgui-and-interface.md) — PySide, dialogs, task panels, Coin3D\n5. [workbenches-and-advanced.md](references\u002Fworkbenches-and-advanced.md) — Workbenches, macros, FEM, Path, recipes\n","# FreeCAD Scripts\n\nExpert skill for generating production-quality Python scripts for the FreeCAD CAD application. Interprets shorthand, quasi-code, and natural language descriptions of 3D modeling tasks and translates them into correct FreeCAD Python API calls.\n\n## When to Use This Skill\n\n- Writing Python scripts for FreeCAD's built-in console or macro system\n- Creating or manipulating 3D geometry (Part, Mesh, Sketcher, Path, FEM)\n- Building parametric FeaturePython objects with custom properties\n- Developing GUI tools using PySide\u002FQt within FreeCAD\n- Manipulating the Coin3D scenegraph via Pivy\n- Creating custom workbenches or Gui Commands\n- Automating repetitive CAD operations with macros\n- Converting between mesh and solid representations\n- Scripting FEM analyses, raytracing, or drawing exports\n\n## Prerequisites\n\n- FreeCAD installed (0.19+ recommended; 0.21+\u002F1.0+ for latest API)\n- Python 3.x (bundled with FreeCAD)\n- For GUI work: PySide2 (bundled with FreeCAD)\n- For scenegraph: Pivy (bundled with FreeCAD)\n\n## FreeCAD Python Environment\n\nFreeCAD embeds a Python interpreter. Scripts run in an environment where these key modules are available:\n\n```python\nimport FreeCAD          # Core module (also aliased as 'App')\nimport FreeCADGui       # GUI module (also aliased as 'Gui') — only in GUI mode\nimport Part             # Part workbench — BRep\u002FOpenCASCADE shapes\nimport Mesh             # Mesh workbench — triangulated meshes\nimport Sketcher         # Sketcher workbench — 2D constrained sketches\nimport Draft            # Draft workbench — 2D drawing tools\nimport Arch             # Arch\u002FBIM workbench\nimport Path             # Path\u002FCAM workbench\nimport FEM              # FEM workbench\nimport TechDraw         # TechDraw workbench (replaces Drawing)\nimport BOPTools         # Boolean operations\nimport CompoundTools    # Compound shape utilities\n```\n\n### The FreeCAD Document Model\n\n```python\n# Create or access a document\ndoc = FreeCAD.newDocument(\"MyDoc\")\ndoc = FreeCAD.ActiveDocument\n\n# Add objects\nbox = doc.addObject(\"Part::Box\", \"MyBox\")\nbox.Length = 10.0\nbox.Width = 10.0\nbox.Height = 10.0\n\n# Recompute\ndoc.recompute()\n\n# Access objects\nobj = doc.getObject(\"MyBox\")\nobj = doc.MyBox  # Attribute access also works\n\n# Remove objects\ndoc.removeObject(\"MyBox\")\n```\n\n## Core Concepts\n\n### Vectors and Placements\n\n```python\nimport FreeCAD\n\n# Vectors\nv1 = FreeCAD.Vector(1, 0, 0)\nv2 = FreeCAD.Vector(0, 1, 0)\nv3 = v1.cross(v2)          # Cross product\nd = v1.dot(v2)              # Dot product\nv4 = v1 + v2                # Addition\nlength = v1.Length           # Magnitude\nv_norm = FreeCAD.Vector(v1)\nv_norm.normalize()           # In-place normalize\n\n# Rotations\nrot = FreeCAD.Rotation(FreeCAD.Vector(0, 0, 1), 45)  # axis, angle(deg)\nrot = FreeCAD.Rotation(0, 0, 45)                       # Euler angles (yaw, pitch, roll)\n\n# Placements (position + orientation)\nplacement = FreeCAD.Placement(\n    FreeCAD.Vector(10, 20, 0),    # translation\n    FreeCAD.Rotation(0, 0, 45),   # rotation\n    FreeCAD.Vector(0, 0, 0)       # center of rotation\n)\nobj.Placement = placement\n\n# Matrix (4x4 transformation)\nimport math\nmat = FreeCAD.Matrix()\nmat.move(FreeCAD.Vector(10, 0, 0))\nmat.rotateZ(math.radians(45))\n```\n\n### Creating and Manipulating Geometry (Part Module)\n\nThe Part module wraps OpenCASCADE and provides BRep solid modeling:\n\n```python\nimport FreeCAD\nimport Part\n\n# --- Primitive Shapes ---\nbox = Part.makeBox(10, 10, 10)               # length, width, height\ncyl = Part.makeCylinder(5, 20)               # radius, height\nsphere = Part.makeSphere(10)                  # radius\ncone = Part.makeCone(5, 2, 10)               # r1, r2, height\ntorus = Part.makeTorus(10, 2)                 # major_r, minor_r\n\n# --- Wires and Edges ---\nedge1 = Part.makeLine((0, 0, 0), (10, 0, 0))\nedge2 = Part.makeLine((10, 0, 0), (10, 10, 0))\nedge3 = Part.makeLine((10, 10, 0), (0, 0, 0))\nwire = Part.Wire([edge1, edge2, edge3])\n\n# Circles and arcs\ncircle = Part.makeCircle(5)                   # radius\narc = Part.makeCircle(5, FreeCAD.Vector(0, 0, 0),\n                       FreeCAD.Vector(0, 0, 1), 0, 180)  # start\u002Fend angle\n\n# --- Faces ---\nface = Part.Face(wire)                        # From a closed wire\n\n# --- Solids from Faces\u002FWires ---\nextrusion = face.extrude(FreeCAD.Vector(0, 0, 10))       # Extrude\nrevolved = face.revolve(FreeCAD.Vector(0, 0, 0),\n                         FreeCAD.Vector(0, 0, 1), 360)    # Revolve\n\n# --- Boolean Operations ---\nfused = box.fuse(cyl)           # Union\ncut = box.cut(cyl)              # Subtraction\ncommon = box.common(cyl)        # Intersection\nfused_clean = fused.removeSplitter()  # Clean up seams\n\n# --- Fillets and Chamfers ---\nfilleted = box.makeFillet(1.0, box.Edges)          # radius, edges\nchamfered = box.makeChamfer(1.0, box.Edges)        # dist, edges\n\n# --- Loft and Sweep ---\nloft = Part.makeLoft([wire1, wire2], True)          # wires, solid\nswept = Part.Wire([path_edge]).makePipeShell([profile_wire],\n                                              True, False)  # solid, frenet\n\n# --- BSpline Curves ---\nfrom FreeCAD import Vector\npoints = [Vector(0,0,0), Vector(1,2,0), Vector(3,1,0), Vector(4,3,0)]\nbspline = Part.BSplineCurve()\nbspline.interpolate(points)\nedge = bspline.toShape()\n\n# --- Show in document ---\nPart.show(box, \"MyBox\")    # Quick display (adds to active doc)\n# Or explicitly:\ndoc = FreeCAD.ActiveDocument or FreeCAD.newDocument()\nobj = doc.addObject(\"Part::Feature\", \"MyShape\")\nobj.Shape = box\ndoc.recompute()\n```\n\n### Topological Exploration\n\n```python\nshape = obj.Shape\n\n# Access sub-elements\nshape.Vertexes    # List of Vertex objects\nshape.Edges       # List of Edge objects\nshape.Wires       # List of Wire objects\nshape.Faces       # List of Face objects\nshape.Shells      # List of Shell objects\nshape.Solids      # List of Solid objects\n\n# Bounding box\nbb = shape.BoundBox\nprint(bb.XMin, bb.XMax, bb.YMin, bb.YMax, bb.ZMin, bb.ZMax)\nprint(bb.Center)\n\n# Properties\nshape.Volume\nshape.Area\nshape.Length       # For edges\u002Fwires\nface.Surface       # Underlying geometric surface\nedge.Curve         # Underlying geometric curve\n\n# Shape type\nshape.ShapeType    # \"Solid\", \"Shell\", \"Face\", \"Wire\", \"Edge\", \"Vertex\", \"Compound\"\n```\n\n### Mesh Module\n\n```python\nimport Mesh\n\n# Create mesh from vertices and facets\nmesh = Mesh.Mesh()\nmesh.addFacet(\n    0.0, 0.0, 0.0,   # vertex 1\n    1.0, 0.0, 0.0,   # vertex 2\n    0.0, 1.0, 0.0    # vertex 3\n)\n\n# Import\u002FExport\nmesh = Mesh.Mesh(\"\u002Fpath\u002Fto\u002Ffile.stl\")\nmesh.write(\"\u002Fpath\u002Fto\u002Foutput.stl\")\n\n# Convert Part shape to Mesh\nimport Part\nimport MeshPart\nshape = Part.makeBox(1, 1, 1)\nmesh = MeshPart.meshFromShape(Shape=shape, LinearDeflection=0.1,\n                                AngularDeflection=0.5)\n\n# Convert Mesh to Part shape\nshape = Part.Shape()\nshape.makeShapeFromMesh(mesh.Topology, 0.05)  # tolerance\nsolid = Part.makeSolid(shape)\n```\n\n### Sketcher Module\n\n# Create a sketch on XY plane\nsketch = doc.addObject(\"Sketcher::SketchObject\", \"MySketch\")\nsketch.Placement = FreeCAD.Placement(\n    FreeCAD.Vector(0, 0, 0),\n    FreeCAD.Rotation(0, 0, 0, 1)\n)\n\n# Add geometry (returns geometry index)\nidx_line = sketch.addGeometry(Part.LineSegment(\n    FreeCAD.Vector(0, 0, 0), FreeCAD.Vector(10, 0, 0)))\nidx_circle = sketch.addGeometry(Part.Circle(\n    FreeCAD.Vector(5, 5, 0), FreeCAD.Vector(0, 0, 1), 3))\n\n# Add constraints\nsketch.addConstraint(Sketcher.Constraint(\"Coincident\", 0, 2, 1, 1))\nsketch.addConstraint(Sketcher.Constraint(\"Horizontal\", 0))\nsketch.addConstraint(Sketcher.Constraint(\"DistanceX\", 0, 1, 0, 2, 10.0))\nsketch.addConstraint(Sketcher.Constraint(\"Radius\", 1, 3.0))\nsketch.addConstraint(Sketcher.Constraint(\"Fixed\", 0, 1))\n# Constraint types: Coincident, Horizontal, Vertical, Parallel, Perpendicular,\n#   Tangent, Equal, Symmetric, Distance, DistanceX, DistanceY, Radius, Angle,\n#   Fixed (Block), InternalAlignment\n\ndoc.recompute()\n```\n\n### Draft Module\n\n```python\nimport Draft\nimport FreeCAD\n\n# 2D shapes\nline = Draft.makeLine(FreeCAD.Vector(0,0,0), FreeCAD.Vector(10,0,0))\ncircle = Draft.makeCircle(5)\nrect = Draft.makeRectangle(10, 5)\npoly = Draft.makePolygon(6, radius=5)   # hexagon\n\n# Operations\nmoved = Draft.move(obj, FreeCAD.Vector(10, 0, 0), copy=True)\nrotated = Draft.rotate(obj, 45, FreeCAD.Vector(0,0,0),\n                        axis=FreeCAD.Vector(0,0,1), copy=True)\nscaled = Draft.scale(obj, FreeCAD.Vector(2,2,2), center=FreeCAD.Vector(0,0,0),\n                      copy=True)\noffset = Draft.offset(obj, FreeCAD.Vector(1,0,0))\narray = Draft.makeArray(obj, FreeCAD.Vector(15,0,0),\n                         FreeCAD.Vector(0,15,0), 3, 3)\n```\n\n## Creating Parametric Objects (FeaturePython)\n\nFeaturePython objects are custom parametric objects with properties that trigger recomputation:\n\n```python\nimport FreeCAD\nimport Part\n\nclass MyBox:\n    \"\"\"A custom parametric box.\"\"\"\n\n    def __init__(self, obj):\n        obj.Proxy = self\n        obj.addProperty(\"App::PropertyLength\", \"Length\", \"Dimensions\",\n                         \"Box length\").Length = 10.0\n        obj.addProperty(\"App::PropertyLength\", \"Width\", \"Dimensions\",\n                         \"Box width\").Width = 10.0\n        obj.addProperty(\"App::PropertyLength\", \"Height\", \"Dimensions\",\n                         \"Box height\").Height = 10.0\n\n    def execute(self, obj):\n        \"\"\"Called on document recompute.\"\"\"\n        obj.Shape = Part.makeBox(obj.Length, obj.Width, obj.Height)\n\n    def onChanged(self, obj, prop):\n        \"\"\"Called when a property changes.\"\"\"\n        pass\n\n    def __getstate__(self):\n        return None\n\n    def __setstate__(self, state):\n        return None\n\n\nclass ViewProviderMyBox:\n    \"\"\"View provider for custom icon and display settings.\"\"\"\n\n    def __init__(self, vobj):\n        vobj.Proxy = self\n\n    def getIcon(self):\n        return \":\u002Ficons\u002FPart_Box.svg\"\n\n    def attach(self, vobj):\n        self.Object = vobj.Object\n\n    def updateData(self, obj, prop):\n        pass\n\n    def onChanged(self, vobj, prop):\n        pass\n\n    def __getstate__(self):\n        return None\n\n    def __setstate__(self, state):\n        return None\n\n\n# --- Usage ---\ndoc = FreeCAD.ActiveDocument or FreeCAD.newDocument(\"Test\")\nobj = doc.addObject(\"Part::FeaturePython\", \"CustomBox\")\nMyBox(obj)\nViewProviderMyBox(obj.ViewObject)\ndoc.recompute()\n```\n\n### Common Property Types\n\n| Property Type | Python Type | Description |\n|---|---|---|\n| `App::PropertyBool` | `bool` | Boolean |\n| `App::PropertyInteger` | `int` | Integer |\n| `App::PropertyFloat` | `float` | Float |\n| `App::PropertyString` | `str` | String |\n| `App::PropertyLength` | `float` (units) | Length with units |\n| `App::PropertyAngle` | `float` (deg) | Angle in degrees |\n| `App::PropertyVector` | `FreeCAD.Vector` | 3D vector |\n| `App::PropertyPlacement` | `FreeCAD.Placement` | Position + rotation |\n| `App::PropertyLink` | object ref | Link to another object |\n| `App::PropertyLinkList` | list of refs | Links to multiple objects |\n| `App::PropertyEnumeration` | `list`\u002F`str` | Dropdown selection |\n| `App::PropertyFile` | `str` | File path |\n| `App::PropertyColor` | `tuple` | RGB color (0.0-1.0) |\n| `App::PropertyPythonObject` | any | Serializable Python object |\n\n## Creating GUI Tools\n\n### Gui Commands\n\n```python\nimport FreeCAD\nimport FreeCADGui\n\nclass MyCommand:\n    \"\"\"A custom toolbar\u002Fmenu command.\"\"\"\n\n    def GetResources(self):\n        return {\n            \"Pixmap\": \":\u002Ficons\u002FPart_Box.svg\",\n            \"MenuText\": \"My Custom Command\",\n            \"ToolTip\": \"Creates a custom box\",\n            \"Accel\": \"Ctrl+Shift+B\"\n        }\n\n    def IsActive(self):\n        return FreeCAD.ActiveDocument is not None\n\n    def Activated(self):\n        # Command logic here\n        FreeCAD.Console.PrintMessage(\"Command activated\\n\")\n\nFreeCADGui.addCommand(\"My_CustomCommand\", MyCommand())\n```\n\n### PySide Dialogs\n\n```python\nfrom PySide2 import QtWidgets, QtCore, QtGui\n\nclass MyDialog(QtWidgets.QDialog):\n    def __init__(self, parent=None):\n        super().__init__(parent or FreeCADGui.getMainWindow())\n        self.setWindowTitle(\"My Tool\")\n        self.setMinimumWidth(300)\n\n        layout = QtWidgets.QVBoxLayout(self)\n\n        # Input fields\n        self.label = QtWidgets.QLabel(\"Length:\")\n        self.spinbox = QtWidgets.QDoubleSpinBox()\n        self.spinbox.setRange(0.1, 1000.0)\n        self.spinbox.setValue(10.0)\n        self.spinbox.setSuffix(\" mm\")\n\n        form = QtWidgets.QFormLayout()\n        form.addRow(self.label, self.spinbox)\n        layout.addLayout(form)\n\n        # Buttons\n        btn_layout = QtWidgets.QHBoxLayout()\n        self.btn_ok = QtWidgets.QPushButton(\"OK\")\n        self.btn_cancel = QtWidgets.QPushButton(\"Cancel\")\n        btn_layout.addWidget(self.btn_ok)\n        btn_layout.addWidget(self.btn_cancel)\n        layout.addLayout(btn_layout)\n\n        self.btn_ok.clicked.connect(self.accept)\n        self.btn_cancel.clicked.connect(self.reject)\n\n# Usage\ndialog = MyDialog()\nif dialog.exec_() == QtWidgets.QDialog.Accepted:\n    length = dialog.spinbox.value()\n    FreeCAD.Console.PrintMessage(f\"Length: {length}\\n\")\n```\n\n### Task Panel (Recommended for FreeCAD integration)\n\n```python\nclass MyTaskPanel:\n    \"\"\"Task panel shown in the left sidebar.\"\"\"\n\n    def __init__(self):\n        self.form = QtWidgets.QWidget()\n        layout = QtWidgets.QVBoxLayout(self.form)\n        self.spinbox = QtWidgets.QDoubleSpinBox()\n        self.spinbox.setValue(10.0)\n        layout.addWidget(QtWidgets.QLabel(\"Length:\"))\n        layout.addWidget(self.spinbox)\n\n    def accept(self):\n        # Called when user clicks OK\n        length = self.spinbox.value()\n        FreeCAD.Console.PrintMessage(f\"Accepted: {length}\\n\")\n        FreeCADGui.Control.closeDialog()\n        return True\n\n    def reject(self):\n        FreeCADGui.Control.closeDialog()\n        return True\n\n    def getStandardButtons(self):\n        return int(QtWidgets.QDialogButtonBox.Ok |\n                   QtWidgets.QDialogButtonBox.Cancel)\n\n# Show the panel\npanel = MyTaskPanel()\nFreeCADGui.Control.showDialog(panel)\n```\n\n## Coin3D Scenegraph (Pivy)\n\n```python\nfrom pivy import coin\nimport FreeCADGui\n\n# Access the scenegraph root\nsg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph()\n\n# Add a custom separator with a sphere\nsep = coin.SoSeparator()\nmat = coin.SoMaterial()\nmat.diffuseColor.setValue(1.0, 0.0, 0.0)  # Red\ntrans = coin.SoTranslation()\ntrans.translation.setValue(10, 10, 10)\nsphere = coin.SoSphere()\nsphere.radius.setValue(2.0)\nsep.addChild(mat)\nsep.addChild(trans)\nsep.addChild(sphere)\nsg.addChild(sep)\n\n# Remove later\nsg.removeChild(sep)\n```\n\n## Custom Workbench Creation\n\n```python\nimport FreeCADGui\n\nclass MyWorkbench(FreeCADGui.Workbench):\n    MenuText = \"My Workbench\"\n    ToolTip = \"A custom workbench\"\n    Icon = \":\u002Ficons\u002Ffreecad.svg\"\n\n    def Initialize(self):\n        \"\"\"Called at workbench activation.\"\"\"\n        import MyCommands  # Import your command module\n        self.appendToolbar(\"My Tools\", [\"My_CustomCommand\"])\n        self.appendMenu(\"My Menu\", [\"My_CustomCommand\"])\n\n    def Activated(self):\n        pass\n\n    def Deactivated(self):\n        pass\n\n    def GetClassName(self):\n        return \"Gui::PythonWorkbench\"\n\nFreeCADGui.addWorkbench(MyWorkbench)\n```\n\n## Macro Best Practices\n\n```python\n# Standard macro header\n# -*- coding: utf-8 -*-\n# FreeCAD Macro: MyMacro\n# Description: Brief description of what the macro does\n# Author: YourName\n# Version: 1.0\n# Date: 2026-04-07\n\nimport FreeCAD\nimport Part\nfrom FreeCAD import Base\n\n# Guard for GUI availability\nif FreeCAD.GuiUp:\n    import FreeCADGui\n    from PySide2 import QtWidgets, QtCore\n\ndef main():\n    doc = FreeCAD.ActiveDocument\n    if doc is None:\n        FreeCAD.Console.PrintError(\"No active document\\n\")\n        return\n\n    if FreeCAD.GuiUp:\n        sel = FreeCADGui.Selection.getSelection()\n        if not sel:\n            FreeCAD.Console.PrintWarning(\"No objects selected\\n\")\n\n    # ... macro logic ...\n\n    doc.recompute()\n    FreeCAD.Console.PrintMessage(\"Macro completed\\n\")\n\nif __name__ == \"__main__\":\n    main()\n```\n\n### Selection Handling\n\n```python\n# Get selected objects\nsel = FreeCADGui.Selection.getSelection()           # List of objects\nsel_ex = FreeCADGui.Selection.getSelectionEx()       # Extended (sub-elements)\n\nfor selobj in sel_ex:\n    obj = selobj.Object\n    for sub in selobj.SubElementNames:\n        print(f\"{obj.Name}.{sub}\")\n        shape = obj.getSubObject(sub)  # Get sub-shape\n\n# Select programmatically\nFreeCADGui.Selection.addSelection(doc.MyBox)\nFreeCADGui.Selection.addSelection(doc.MyBox, \"Face1\")\nFreeCADGui.Selection.clearSelection()\n```\n\n### Console Output\n\n```python\nFreeCAD.Console.PrintMessage(\"Info message\\n\")\nFreeCAD.Console.PrintWarning(\"Warning message\\n\")\nFreeCAD.Console.PrintError(\"Error message\\n\")\nFreeCAD.Console.PrintLog(\"Debug\u002Flog message\\n\")\n```\n\n## Common Patterns\n\n### Parametric Pad from Sketch\n\n```python\ndoc = FreeCAD.ActiveDocument\n\n# Create sketch\nsketch = doc.addObject(\"Sketcher::SketchObject\", \"Sketch\")\nsketch.addGeometry(Part.LineSegment(FreeCAD.Vector(0,0,0), FreeCAD.Vector(10,0,0)))\nsketch.addGeometry(Part.LineSegment(FreeCAD.Vector(10,0,0), FreeCAD.Vector(10,10,0)))\nsketch.addGeometry(Part.LineSegment(FreeCAD.Vector(10,10,0), FreeCAD.Vector(0,10,0)))\nsketch.addGeometry(Part.LineSegment(FreeCAD.Vector(0,10,0), FreeCAD.Vector(0,0,0)))\n# Close with coincident constraints\nfor i in range(3):\n    sketch.addConstraint(Sketcher.Constraint(\"Coincident\", i, 2, i+1, 1))\nsketch.addConstraint(Sketcher.Constraint(\"Coincident\", 3, 2, 0, 1))\n\n# Pad (PartDesign)\npad = doc.addObject(\"PartDesign::Pad\", \"Pad\")\npad.Profile = sketch\npad.Length = 5.0\nsketch.Visibility = False\ndoc.recompute()\n```\n\n### Export Shapes\n\n```python\n# STEP export\nPart.export([doc.MyBox], \"\u002Fpath\u002Fto\u002Foutput.step\")\n\n# STL export (mesh)\nimport Mesh\nMesh.export([doc.MyBox], \"\u002Fpath\u002Fto\u002Foutput.stl\")\n\n# IGES export\nPart.export([doc.MyBox], \"\u002Fpath\u002Fto\u002Foutput.iges\")\n\n# Multiple formats via importlib\nimport importlib\nimportlib.import_module(\"importOBJ\").export([doc.MyBox], \"\u002Fpath\u002Fto\u002Foutput.obj\")\n```\n\n### Units and Quantities\n\n```python\n# FreeCAD uses mm internally\nq = FreeCAD.Units.Quantity(\"10 mm\")\nq_inch = FreeCAD.Units.Quantity(\"1 in\")\nprint(q_inch.getValueAs(\"mm\"))  # 25.4\n\n# Parse user input with units\nq = FreeCAD.Units.parseQuantity(\"2.5 in\")\nvalue_mm = float(q)  # Value in mm (internal unit)\n```\n\n## Compensation Rules (Quasi-Coder Integration)\n\nWhen interpreting shorthand or quasi-code for FreeCAD scripts:\n\n1. **Terminology mapping**: \"box\" → `Part.makeBox()`, \"cylinder\" → `Part.makeCylinder()`, \"sphere\" → `Part.makeSphere()`, \"merge\u002Fcombine\u002Fjoin\" → `.fuse()`, \"subtract\u002Fcut\u002Fremove\" → `.cut()`, \"intersect\" → `.common()`, \"round edges\u002Ffillet\" → `.makeFillet()`, \"bevel\u002Fchamfer\" → `.makeChamfer()`\n2. **Implicit document**: If no document handling is mentioned, wrap in standard `doc = FreeCAD.ActiveDocument or FreeCAD.newDocument()`\n3. **Units assumption**: Default to millimeters unless stated otherwise\n4. **Recompute**: Always call `doc.recompute()` after modifications\n5. **GUI guard**: Wrap GUI-dependent code in `if FreeCAD.GuiUp:` when the script may run headless\n6. **Part.show()**: Use `Part.show(shape, \"Name\")` for quick display, or `doc.addObject(\"Part::Feature\", \"Name\")` for named persistent objects\n\n## References\n\n### Primary Links\n\n- [Writing Python code](https:\u002F\u002Fwiki.freecad.org\u002FManual:A_gentle_introduction#Writing_Python_code)\n- [Manipulating FreeCAD objects](https:\u002F\u002Fwiki.freecad.org\u002FManual:A_gentle_introduction#Manipulating_FreeCAD_objects)\n- [Vectors and Placements](https:\u002F\u002Fwiki.freecad.org\u002FManual:A_gentle_introduction#Vectors_and_Placements)\n- [Creating and manipulating geometry](https:\u002F\u002Fwiki.freecad.org\u002FManual:Creating_and_manipulating_geometry)\n- [Creating parametric objects](https:\u002F\u002Fwiki.freecad.org\u002FManual:Creating_parametric_objects)\n- [Creating interface tools](https:\u002F\u002Fwiki.freecad.org\u002FManual:Creating_interface_tools)\n- [Python](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FPython_%28programming_language%29)\n- [Introduction to Python](https:\u002F\u002Fwiki.freecad.org\u002FIntroduction_to_Python)\n- [Python scripting tutorial](https:\u002F\u002Fwiki.freecad.org\u002FPython_scripting_tutorial)\n- [FreeCAD scripting basics](https:\u002F\u002Fwiki.freecad.org\u002FFreeCAD_Scripting_Basics)\n- [Gui Command](https:\u002F\u002Fwiki.freecad.org\u002FGui_Command)\n\n### Bundled Reference Documents\n\nSee the [references\u002F](references\u002F) directory for topic-organized guides:\n\n1. [scripting-fundamentals.md](references\u002Fscripting-fundamentals.md) — Core scripting, document model, console\n2. [geometry-and-shapes.md](references\u002Fgeometry-and-shapes.md) — Part, Mesh, Sketcher, topology\n3. [parametric-objects.md](references\u002Fparametric-objects.md) — FeaturePython, properties, scripted objects\n4. [gui-and-interface.md](references\u002Fgui-and-interface.md) — PySide, dialogs, task panels, Coin3D\n5. [workbenches-and-advanced.md](references\u002Fworkbenches-and-advanced.md) — Workbenches, macros, FEM, Path, recipes\n",{"name":5,"description":7},"main","5269f7ea2d6c3a9bfda5ed878b6726cf509121fd","skills\u002Ffreecad-scripts",5354,"https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot","https:\u002F\u002Fawesome-copilot.github.com\u002F","https:\u002F\u002Fgithub.com\u002Fgithub","https:\u002F\u002Fgithub.com\u002Fabout","How people build software.","2026-11-14 05:37:52",{"source":8,"skillName":5,"commands":61,"prompt":71,"zipAvailable":72},[62,65,68],{"tool":63,"command":64},"npx","npx skills add github\u002Fawesome-copilot --skill freecad-scripts",{"tool":66,"command":67},"bunx","bunx skills add github\u002Fawesome-copilot --skill freecad-scripts",{"tool":69,"command":70},"pnpm","pnpm dlx skills add github\u002Fawesome-copilot --skill freecad-scripts","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Ffreecad-scripts\u002FSKILL.md",true,[74,80,84,88,92,96],{"path":75,"resourceType":76,"resourceTypeDesc":77,"sizeBytes":78,"sourceHtmlUrl":79},"agents\u002Fopenai.yaml","REFERENCE","参考文档",156,"https:\u002F\u002Fgithub.com\u002FHasarAli\u002Fdotfiles\u002Fblob\u002Fmain\u002Fdot-agents\u002Fskills\u002Fcommit\u002Fagents\u002Fopenai.yaml",{"path":81,"resourceType":76,"resourceTypeDesc":77,"sizeBytes":82,"sourceHtmlUrl":83},"references\u002Fgeometry-and-shapes.md",8807,"https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Ffreecad-scripts\u002Freferences\u002Fgeometry-and-shapes.md",{"path":85,"resourceType":76,"resourceTypeDesc":77,"sizeBytes":86,"sourceHtmlUrl":87},"references\u002Fgui-and-interface.md",11257,"https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Ffreecad-scripts\u002Freferences\u002Fgui-and-interface.md",{"path":89,"resourceType":76,"resourceTypeDesc":77,"sizeBytes":90,"sourceHtmlUrl":91},"references\u002Fparametric-objects.md",10840,"https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Ffreecad-scripts\u002Freferences\u002Fparametric-objects.md",{"path":93,"resourceType":76,"resourceTypeDesc":77,"sizeBytes":94,"sourceHtmlUrl":95},"references\u002Fscripting-fundamentals.md",6131,"https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Ffreecad-scripts\u002Freferences\u002Fscripting-fundamentals.md",{"path":97,"resourceType":76,"resourceTypeDesc":77,"sizeBytes":98,"sourceHtmlUrl":99},"references\u002Fworkbenches-and-advanced.md",10068,"https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Ffreecad-scripts\u002Freferences\u002Fworkbenches-and-advanced.md",[101,131,158,188,221,238],{"skillNo":102,"name":103,"nameZh":6,"description":104,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":105,"categories":106,"keywords":111,"publishedAt":45},"01M70KXZNX9ES38N2A8M58ZZMZ","supabase-rls-policies","Write, review, and test Supabase Row Level Security (RLS) policies so that one user cannot read or change another user's rows. Use when creating or changing tables, policies, views, or security definer functions in a Supabase project, when reviewing a migration that touches RLS, or when asked to prove user isolation with pgTAP tests that sign in as two users.","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fsupabase-rls-policies\u002FSKILL.md",[107,110],{"categoryNo":108,"categoryName":109,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[112,113,114,115,116,28,117,118,119,120,121,122,123,124,125,126,127,128,129,130],"supabase","rls","supabase rls","supabase rls policies","project","review","change","read","write","security","isolation","tables","as","tests","prove","use","users","functions","with",{"skillNo":132,"name":133,"nameZh":6,"description":134,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":135,"categories":136,"keywords":138,"publishedAt":45},"01M70KWA5YWXDV6V8DZ19E3T2Y","skill-distribution-audit","Audit a skill collection before release or investigate installer omissions, archived\u002Ftemplate leakage, duplicate identities, and manifest drift. Reconcile the intended public source set, distribution manifest, and actual discovery output for a specified consumer version. Includes a deterministic set-comparison helper.","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fskill-distribution-audit\u002FSKILL.md",[137],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[139,140,141,142,143,144,29,145,146,147,148,149,150,151,152,153,154,155,156,157],"distribution","skill distribution audit","audit","SET","deterministic","installer","discovery","comparison","helper","manifest","version","collection","drift","output","investigate","duplicate","template","release","reconcile",{"skillNo":159,"name":160,"nameZh":6,"description":161,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":162,"sourceHtmlUrl":163,"categories":164,"keywords":170,"publishedAt":45},"01M70KCJPFVW7PPQ49CC5R8JGK","logicapps-custom-code-net10-migration","Migrate Azure Logic Apps Standard local-function custom-code projects from .NET 8 to .NET 10. Use for legacy Logic Apps custom-code workspace upgrades, project\u002Fpackage configuration, custom-worker settings, publication, and integrated VS Code debugging. Not for ordinary .NET applications, standalone Azure Functions, Consumption workflows, or cloud deployment.","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Flogicapps-custom-code-net10-migration\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Flogicapps-custom-code-net10-migration\u002FSKILL.md",[165],{"categoryNo":166,"categoryName":167,"taxonomyType":168,"taxonomyTypeDesc":169},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[171,172,173,174,175,176,29,116,177,178,179,180,181,182,183,127,184,185,186,187],"custom","code","logicapps custom code net10 migration","migration","projects","standalone","deployment","applications","local","azure","worker","upgrades","package","apps","Legacy","debugging","workflows",{"skillNo":189,"name":190,"nameZh":6,"description":191,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":192,"sourceHtmlUrl":193,"categories":194,"keywords":199,"publishedAt":45},"01M6AEBN37RQMXJ3298PEY7PG1","winmd-api-search","Find and explore Windows desktop APIs. Use when building features that need platform capabilities — camera, file access, notifications, UI controls, AI\u002FML, sensors, networking, etc. Discovers the right API for a task and retrieves full type details (methods, properties, events, enumeration values).","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fwinmd-api-search\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fwinmd-api-search\u002FSKILL.md",[195,198],{"categoryNo":196,"categoryName":197,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":166,"categoryName":167,"taxonomyType":168,"taxonomyTypeDesc":169},[200,201,37,202,203,204,205,206,207,208,209,210,211,212,213,28,214,215,216,217,218,29,219,220],"winmd api search","search","windows","desktop","file access","enumeration","ui","ml","file","networking","capabilities","notifications","explore","access","features","full","camera","events","platform","methods","controls",{"skillNo":222,"name":223,"nameZh":6,"description":224,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":225,"licenseFileUrl":226,"sourceHtmlUrl":227,"categories":228,"keywords":234,"publishedAt":45},"01M6AE571R3SC1ZGE202B3NB5S","resemble-detect","Deepfake detection and media safety — detect AI-generated audio, images, video, and text, trace synthesis sources, apply watermarks, verify speaker identity, and analyze media intelligence using Resemble AI","Apache-2.0","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fresemble-detect\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fresemble-detect\u002FSKILL.md",[229,230,233],{"categoryNo":196,"categoryName":197,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":231,"categoryName":232,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000016","图像与多媒体",{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[235,142,208,236,237],"resemble detect","https","identity",{"skillNo":239,"name":240,"nameZh":6,"description":241,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":225,"licenseFileUrl":242,"sourceHtmlUrl":243,"categories":244,"keywords":249,"publishedAt":45},"01M6AE30BSK2HCY4TQQ22Z8JRD","quality-playbook","Run a complete quality engineering audit on any codebase. Derives behavioral requirements from the code, generates spec-traced functional tests, runs a three-pass code review with regression tests, executes a multi-model spec audit (Council of Three), and produces a consolidated bug report with TDD-verified patches. Finds the 35% of real defects that structural code review alone cannot catch. Works with any language. Trigger on 'quality playbook', 'spec audit', 'Council of Three', 'fitness-to-purpose', or 'coverage theater'.","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fquality-playbook\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fquality-playbook\u002FSKILL.md",[245,248],{"categoryNo":246,"categoryName":247,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000007","代码审查",{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[250,251,252,253,172,117,9,141,254,255,236,256,257,258,259,260,261,262,263,264,265,266,125,267,268,269,270],"playbook","quality playbook","quality","code review","tdd","run","report","spec","bug report","council","engineering","functional","spec audit","bug","verified","multi-model","three","coverage","model","regression","structural"]