- Contents in this wiki are for entertainment purposes only
Talk:Claude Code on Torus Knot Winding Form (FreeCAD)
Jump to navigation
Jump to search
- FreeCAD proves unable to create large boolean-cuts of long, continuous Torus Knot Windings... development has changed to OpenSCAD and custom Go.
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.6
torusKnotGrooves.FCMacro
C:/Users/XenoEngineer/AppData/Roaming/FreeCAD/Macro
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.6 - Handle Compound result from boolean cut by extracting solids
v1.5 - Fuse-then-cut boolean strategy, added shape validation diagnostics
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 = 1 # 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 = 3.175 # 1/8 inch in mm (standard bit size)
GROOVE_DIAMETER_MIN = 0.5
GROOVE_DIAMETER_MAX = 20.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: fuse all grooves first, then cut once.
Handles Compound results by extracting valid 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:
App.Console.PrintMessage("Boolean operation starting...\n")
# Validate inputs
App.Console.PrintMessage(f" Base torus valid: {base_torus_shape.isValid()}\n")
App.Console.PrintMessage(f" Base torus type: {base_torus_shape.ShapeType}\n")
App.Console.PrintMessage(f" Base torus volume: {base_torus_shape.Volume:.2f}\n")
for i, groove in enumerate(groove_solids):
App.Console.PrintMessage(f" Groove {i} valid: {groove.isValid()}, type: {groove.ShapeType}\n")
# Fuse all groove solids into one shape
App.Console.PrintMessage("Fusing groove solids...\n")
if len(groove_solids) == 1:
combined_grooves = groove_solids[0]
else:
combined_grooves = groove_solids[0]
for i, groove in enumerate(groove_solids[1:], start=1):
App.Console.PrintMessage(f" Fusing groove {i}...\n")
combined_grooves = combined_grooves.fuse(groove)
App.Console.PrintMessage(f"Combined grooves type: {combined_grooves.ShapeType}\n")
App.Console.PrintMessage(f"Combined grooves valid: {combined_grooves.isValid()}\n")
# Single cut operation
App.Console.PrintMessage("Performing cut...\n")
result_shape = base_torus_shape.cut(combined_grooves)
App.Console.PrintMessage(f"Cut result type: {result_shape.ShapeType}\n")
App.Console.PrintMessage(f"Cut result valid: {result_shape.isValid()}\n")
# Handle Compound result - extract solids
if result_shape.ShapeType == "Compound":
App.Console.PrintMessage("Result is Compound - extracting solids...\n")
solids = result_shape.Solids
App.Console.PrintMessage(f" Found {len(solids)} solid(s) in compound\n")
if len(solids) == 0:
# No solids - check for shells
shells = result_shape.Shells
App.Console.PrintMessage(f" Found {len(shells)} shell(s) in compound\n")
if len(shells) > 0:
# Try to make solid from shells
App.Console.PrintMessage(" Attempting to create solid from shells...\n")
try:
result_shape = Part.makeSolid(shells[0])
App.Console.PrintMessage(f" Created solid from shell: {result_shape.ShapeType}\n")
except Exception as e:
App.Console.PrintError(f" Could not create solid from shell: {e}\n")
return None
else:
App.Console.PrintError("No solids or shells in compound!\n")
return None
elif len(solids) == 1:
result_shape = solids[0]
App.Console.PrintMessage(f" Using single solid, volume: {result_shape.Volume:.2f}\n")
else:
# Multiple solids - report all and select largest
App.Console.PrintMessage(" Multiple solids found:\n")
for i, s in enumerate(solids):
vol = s.Volume if hasattr(s, 'Volume') else 0
App.Console.PrintMessage(f" Solid {i}: volume = {vol:.2f}\n")
# Filter out degenerate solids (near-zero volume)
valid_solids = [s for s in solids if s.Volume > 1.0]
App.Console.PrintMessage(f" Valid solids (volume > 1): {len(valid_solids)}\n")
if len(valid_solids) == 0:
App.Console.PrintError("All solids have negligible volume!\n")
return None
elif len(valid_solids) == 1:
result_shape = valid_solids[0]
else:
# Fuse all valid solids back together
App.Console.PrintMessage(" Fusing valid solids together...\n")
result_shape = valid_solids[0]
for s in valid_solids[1:]:
result_shape = result_shape.fuse(s)
# Try to make it a proper solid
if result_shape.ShapeType != "Solid":
try:
result_shape = Part.makeSolid(result_shape)
except:
pass # Keep as-is if makeSolid fails
App.Console.PrintMessage(f" Final result type: {result_shape.ShapeType}\n")
# Final validation
if hasattr(result_shape, 'Volume'):
App.Console.PrintMessage(f"Final result volume: {result_shape.Volume:.2f}\n")
if result_shape.isNull():
App.Console.PrintError("Final result is null!\n")
return None
if hide_base:
base_torus.ViewObject.Visibility = False
return result_shape
except Exception as e:
App.Console.PrintError(f"Boolean cut failed: {e}\n")
import traceback
App.Console.PrintError(traceback.format_exc())
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 and not result_shape.isNull():
final = doc.addObject("Part::Feature", "TorusWithGrooves")
final.Shape = result_shape
App.Console.PrintMessage("TorusWithGrooves object created.\n")
else:
App.Console.PrintWarning("Boolean cut did not produce valid geometry.\n")
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()