- Contents in this wiki are for entertainment purposes only
Talk:Claude Code on Torus Knot Winding Form (FreeCAD)
Jump to navigation
Jump to search
In Development
(Python runs inside FreeCAD to provide coding against the FreeCAD object model exposed to the Python environment.) Local: C:\Users\XenoEngineer\AppData\Roaming\FreeCAD\Macro
XenoEngineer —research and development 18:42, 16 January 2026 (UTC)
"""
Torus Knot Groove Generator for FreeCAD - v1.4
torusKnotGrooves.FCMacro
This FreeCAD macro generates a parametric torus knot with parallel grooves based on the golden ratio.
The user can specify the knot parameters (P, Q, phases), scale, and groove dimensions.
The macro provides options to perform boolean cut operations and hide the base torus.
Changelog:
v1.4 - Fixed periodic BSpline: removed duplicate point, removed T_OFFSET
v1.3 - Attempted periodic fix (caused OCC error)
v1.2 - Extracted default parameters to module-level constants
v1.1 - Initial working version
Author: Claude (Anthropic AI) <-- on Codexa Omega v.1
Refactored: Claude (Anthropic AI) <-- on Codexa Omega v.2
"""
import FreeCAD as App
import FreeCADGui as Gui
import Part
import math
from FreeCAD import Vector
from PySide import QtCore, QtGui
# =============================================================================
# MATHEMATICAL CONSTANTS
# =============================================================================
PHI = (1 + math.sqrt(5)) / 2 # Golden ratio
PHI_4 = PHI ** 4 # Fourth power of phi
PHI_4_MINUS_1 = PHI_4 - 1 # Minor radius ratio factor
# =============================================================================
# DEFAULT KNOT PARAMETERS
# =============================================================================
DEFAULT_P = 3 # Toroidal windings
DEFAULT_Q = 2 # Poloidal windings
DEFAULT_PHASES = 3 # Number of parallel grooves
# Parameter ranges
P_MIN, P_MAX = 1, 50
Q_MIN, Q_MAX = 1, 50
PHASES_MIN, PHASES_MAX = 1, 6
# =============================================================================
# DEFAULT SCALE PARAMETERS (mm)
# =============================================================================
DEFAULT_MINOR_RADIUS = 200.0 # r - tube radius
MINOR_RADIUS_MIN = 10.0
MINOR_RADIUS_MAX = 500.0
MINOR_RADIUS_DECIMALS = 3
# =============================================================================
# DEFAULT GROOVE PARAMETERS
# =============================================================================
DEFAULT_GROOVE_DIAMETER = 30 # 1/8 inch in mm (standard bit size)
GROOVE_DIAMETER_MIN = 0.5
GROOVE_DIAMETER_MAX = 50.0
GROOVE_DIAMETER_DECIMALS = 3
DEFAULT_PATH_STEPS = 400 # Curve resolution
PATH_STEPS_MIN = 100
PATH_STEPS_MAX = 3000
PATH_STEPS_STEP = 50 # Spinbox increment
# =============================================================================
# DEFAULT OUTPUT OPTIONS
# =============================================================================
DEFAULT_DO_BOOLEAN = False # Boolean cut is slow
DEFAULT_HIDE_BASE = True # Hide torus after cut
# =============================================================================
# DISPLAY CONSTANTS
# =============================================================================
PHASE_COLORS = [
(1, 0, 0), # Red
(0, 1, 0), # Green
(0, 0, 1), # Blue
(1, 1, 0), # Yellow
(1, 0, 1), # Magenta
(0, 1, 1), # Cyan
]
DEFAULT_LINE_WIDTH = 2.0
DEFAULT_GROOVE_TRANSPARENCY = 30
DEFAULT_TORUS_TRANSPARENCY = 70
class TorusKnotDialog(QtGui.QDialog):
def __init__(self, parent=None):
super(TorusKnotDialog, self).__init__(parent)
self.setWindowTitle("3-Phase Golden Quartic Torus Knot")
self.setMinimumWidth(380)
self.init_ui()
def init_ui(self):
layout = QtGui.QVBoxLayout(self)
info = QtGui.QLabel(
"Golden Quartic: R = phi^4, r = phi^4 - 1\n"
"Hole:Major ratio = 1 : 6.8541"
)
info.setStyleSheet("color: #555; font-style: italic;")
layout.addWidget(info)
knot_group = QtGui.QGroupBox("Knot Parameters")
knot_layout = QtGui.QFormLayout(knot_group)
self.spin_p = QtGui.QSpinBox()
self.spin_p.setRange(P_MIN, P_MAX)
self.spin_p.setValue(DEFAULT_P)
knot_layout.addRow("P (toroidal):", self.spin_p)
self.spin_q = QtGui.QSpinBox()
self.spin_q.setRange(Q_MIN, Q_MAX)
self.spin_q.setValue(DEFAULT_Q)
knot_layout.addRow("Q (poloidal):", self.spin_q)
self.spin_phases = QtGui.QSpinBox()
self.spin_phases.setRange(PHASES_MIN, PHASES_MAX)
self.spin_phases.setValue(DEFAULT_PHASES)
knot_layout.addRow("Phases:", self.spin_phases)
layout.addWidget(knot_group)
scale_group = QtGui.QGroupBox("Scale (Golden Quartic Locked)")
scale_layout = QtGui.QFormLayout(scale_group)
self.spin_minor = QtGui.QDoubleSpinBox()
self.spin_minor.setRange(MINOR_RADIUS_MIN, MINOR_RADIUS_MAX)
self.spin_minor.setValue(DEFAULT_MINOR_RADIUS)
self.spin_minor.setDecimals(MINOR_RADIUS_DECIMALS)
self.spin_minor.setSuffix(" mm")
self.spin_minor.valueChanged.connect(self.update_calculated_values)
scale_layout.addRow("Minor Radius (r):", self.spin_minor)
self.label_major = QtGui.QLabel()
scale_layout.addRow("Major Radius (R):", self.label_major)
self.label_hole = QtGui.QLabel()
scale_layout.addRow("Hole Radius:", self.label_hole)
self.label_outer = QtGui.QLabel()
scale_layout.addRow("Outer Diameter:", self.label_outer)
layout.addWidget(scale_group)
groove_group = QtGui.QGroupBox("Groove Parameters")
groove_layout = QtGui.QFormLayout(groove_group)
self.spin_groove = QtGui.QDoubleSpinBox()
self.spin_groove.setRange(GROOVE_DIAMETER_MIN, GROOVE_DIAMETER_MAX)
self.spin_groove.setValue(DEFAULT_GROOVE_DIAMETER)
self.spin_groove.setDecimals(GROOVE_DIAMETER_DECIMALS)
self.spin_groove.setSuffix(" mm")
groove_layout.addRow("Groove Diameter:", self.spin_groove)
self.spin_steps = QtGui.QSpinBox()
self.spin_steps.setRange(PATH_STEPS_MIN, PATH_STEPS_MAX)
self.spin_steps.setValue(DEFAULT_PATH_STEPS)
self.spin_steps.setSingleStep(PATH_STEPS_STEP)
groove_layout.addRow("Path Steps:", self.spin_steps)
layout.addWidget(groove_group)
output_group = QtGui.QGroupBox("Output Options")
output_layout = QtGui.QVBoxLayout(output_group)
self.check_boolean = QtGui.QCheckBox("Perform Boolean Cut (slow)")
self.check_boolean.setChecked(DEFAULT_DO_BOOLEAN)
output_layout.addWidget(self.check_boolean)
self.check_hide_base = QtGui.QCheckBox("Hide base torus after cut")
self.check_hide_base.setChecked(DEFAULT_HIDE_BASE)
output_layout.addWidget(self.check_hide_base)
layout.addWidget(output_group)
button_box = QtGui.QDialogButtonBox(
QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel
)
button_box.accepted.connect(self.accept)
button_box.rejected.connect(self.reject)
layout.addWidget(button_box)
self.update_calculated_values()
def update_calculated_values(self):
r = self.spin_minor.value()
unit = r / PHI_4_MINUS_1
R = PHI_4 * unit
hole_radius = unit
outer_diameter = 2 * (R + r)
self.label_major.setText(f"{R:.3f} mm")
self.label_hole.setText(f"{hole_radius:.3f} mm")
self.label_outer.setText(f"{outer_diameter:.3f} mm")
def get_parameters(self):
r = self.spin_minor.value()
unit = r / PHI_4_MINUS_1
R = PHI_4 * unit
return {
'P': self.spin_p.value(),
'Q': self.spin_q.value(),
'phases': self.spin_phases.value(),
'major_radius': R,
'minor_radius': r,
'groove_diameter': self.spin_groove.value(),
'steps': self.spin_steps.value(),
'do_boolean': self.check_boolean.isChecked(),
'hide_base': self.check_hide_base.isChecked()
}
def setup_document():
"""Create a new document or clear the existing one."""
if App.ActiveDocument is None:
doc = App.newDocument("GoldenTorusKnot")
else:
doc = App.ActiveDocument
for obj in doc.Objects:
doc.removeObject(obj.Name)
doc.recompute()
Gui.updateGui()
return doc
def generate_knot_path(P, Q, phase_offset, major_radius, minor_radius, steps):
"""
Generate a CLOSED knot path for a given phase.
Uses PeriodicFlag=True to create a seamless closed curve.
No duplicate endpoint is added — the periodic flag handles wrap-around.
"""
knot_points = []
for i in range(steps):
# No T_OFFSET needed for periodic curves
t = (2 * math.pi * i) / steps
poloidal_angle = Q * t + phase_offset
radius = major_radius + minor_radius * math.cos(poloidal_angle)
x = radius * math.cos(P * t)
y = radius * math.sin(P * t)
z = minor_radius * math.sin(poloidal_angle)
knot_points.append(Vector(x, y, z))
# PeriodicFlag=True makes the curve seamlessly closed
# Do NOT append duplicate point — that causes OCC error
knot_curve = Part.BSplineCurve()
knot_curve.interpolate(knot_points, PeriodicFlag=True)
knot_edge = knot_curve.toShape()
knot_wire = Part.Wire([knot_edge])
return knot_wire
def sweep_groove(knot_wire, groove_diameter):
"""Sweep a circular profile along a knot path to create a groove solid."""
start_point = knot_wire.Vertexes[0].Point
first_edge = knot_wire.Edges[0]
tangent = first_edge.tangentAt(first_edge.FirstParameter)
profile_circle = Part.makeCircle(groove_diameter / 2, start_point, tangent)
profile_wire = Part.Wire([profile_circle])
solid = sweep_with_fallbacks(profile_wire, knot_wire)
return solid
def perform_boolean_cuts(base_torus_shape, groove_solids, base_torus, hide_base):
"""
Perform boolean cut operations on the base torus with groove solids.
Args:
base_torus_shape: The Part.Shape of the torus
groove_solids: List of groove solid shapes to subtract
base_torus: The document object (for visibility control)
hide_base: Whether to hide the base torus after cutting
"""
try:
result_shape = base_torus_shape
for groove in groove_solids:
result_shape = result_shape.cut(groove)
if hide_base:
base_torus.ViewObject.Visibility = False
return result_shape
except Exception as e:
App.Console.PrintError(f"Boolean cut failed: {e}\n")
return None
def setup_phase_objects(doc, phase_index, knot_wire, groove_solid, do_boolean):
"""Set up display properties for phase-specific objects."""
phase_name = chr(ord('A') + phase_index)
knot_obj = doc.addObject("Part::Feature", f"KnotPath_{phase_name}")
knot_obj.Shape = knot_wire
knot_obj.ViewObject.LineColor = PHASE_COLORS[phase_index % len(PHASE_COLORS)]
knot_obj.ViewObject.LineWidth = DEFAULT_LINE_WIDTH
groove_obj = doc.addObject("Part::Feature", f"GrooveSolid_{phase_name}")
groove_obj.Shape = groove_solid
if not do_boolean:
groove_obj.ViewObject.Visibility = True
groove_obj.ViewObject.ShapeColor = PHASE_COLORS[phase_index % len(PHASE_COLORS)]
groove_obj.ViewObject.Transparency = DEFAULT_GROOVE_TRANSPARENCY
else:
groove_obj.ViewObject.Visibility = False
def generate_torus_knot(params):
"""
Generate a torus with parallel knot grooves.
Args:
params (dict): Dictionary of input parameters.
"""
doc = setup_document()
base_torus_shape, base_torus = create_base_torus(doc, params)
groove_solids = []
for phase_index in range(params['phases']):
knot_wire, groove_solid = generate_phase_geometry(doc, phase_index, params)
if knot_wire is None or groove_solid is None:
continue
setup_phase_objects(doc, phase_index, knot_wire, groove_solid, params['do_boolean'])
groove_solids.append(groove_solid)
if params['do_boolean'] and groove_solids:
# Pass base_torus object so we can hide it after cut
result_shape = perform_boolean_cuts(
base_torus_shape, groove_solids, base_torus, params['hide_base']
)
if result_shape:
final = doc.addObject("Part::Feature", "TorusWithGrooves")
final.Shape = result_shape
else:
App.Console.PrintMessage("Preview mode - boolean skipped\n")
doc.recompute()
Gui.updateGui()
App.Console.PrintMessage("=" * 50 + "\n")
App.Console.PrintMessage("Done!\n")
App.Console.PrintMessage("=" * 50 + "\n")
def create_base_torus(doc, params):
"""Create the base torus shape."""
R = params['major_radius']
r = params['minor_radius']
base_torus_shape = Part.makeTorus(R, r)
base_torus = doc.addObject("Part::Feature", "BaseTorus")
base_torus.Shape = base_torus_shape
if not params['do_boolean']:
base_torus.ViewObject.Transparency = DEFAULT_TORUS_TRANSPARENCY
return base_torus_shape, base_torus
def generate_phase_geometry(doc, phase_index, params):
"""Generate the knot wire and groove solid for a single phase."""
phase_offset = phase_index * 2 * math.pi / params['phases']
knot_wire = generate_knot_path(
params['P'], params['Q'], phase_offset,
params['major_radius'], params['minor_radius'], params['steps']
)
groove_solid = sweep_groove(knot_wire, params['groove_diameter'])
if groove_solid is None:
App.Console.PrintError(f"Failed to create groove solid for phase {phase_index}\n")
return None, None
return knot_wire, groove_solid
def sweep_with_fallbacks(profile_wire, path_wire):
"""Attempt multiple sweep methods with graceful fallback."""
methods = [
("makePipeShell Frenet+Solid",
lambda: path_wire.makePipeShell([profile_wire], True, True)),
("makePipeShell Frenet+Shell->Solid",
lambda: Part.makeSolid(path_wire.makePipeShell([profile_wire], False, True))),
("makePipe",
lambda: path_wire.makePipe(profile_wire)),
("makePipeShell NonFrenet",
lambda: path_wire.makePipeShell([profile_wire], True, False)),
]
for name, method in methods:
try:
App.Console.PrintMessage(f" Trying {name}...\n")
shape = method()
if shape is None:
continue
if shape.ShapeType == "Solid":
App.Console.PrintMessage(f" Success: {name}\n")
return shape
if shape.ShapeType in ("Shell", "Compound"):
solid = Part.makeSolid(shape)
if solid.ShapeType == "Solid":
App.Console.PrintMessage(f" Success: {name} + makeSolid\n")
return solid
except Exception as e:
App.Console.PrintMessage(f" {name} failed: {e}\n")
return None
def run():
"""Entry point: show dialog and generate geometry."""
dialog = TorusKnotDialog(Gui.getMainWindow())
if dialog.exec_() == QtGui.QDialog.Accepted:
params = dialog.get_parameters()
generate_torus_knot(params)
else:
App.Console.PrintMessage("Cancelled.\n")
if __name__ == "__main__":
run()