Rich's Seed Eco-Home Design Software

From Open Source Ecology
Revision as of 21:46, 7 January 2026 by Rich A. (talk | contribs) (SUPER ALIGN!)
Jump to navigation Jump to search

SUPER ALIGN!

I had AI make for me an alignment tool in FreeCAD 1.0 so that it can align a folder of objects with objects, or other folder of objects, so compound objects don't have to be created. Compound objects prevent the editing of component objects that are baked in. Super Align allows for the alignment of folders without creating these compounds.

Instructions:

Run Macro:

-Click Macro > 📝 Macros...
-Copy and Paste the code below in the macro window
-Highlight all the code
-Click Macro > Execute Macro

A window with a red button that says Align! should appear

-Click on the 3D view tab below to get back to your project.

The Align! window should be there. You can minimize it and dock it to other windows.

-Click a point, edge, or face on your movable object
-On Mac, command Click a point, edge, or face on your fixed object
-Click Align!

Macro Code: {{#code:python|Your Macro Name.FCMacro}}

import FreeCAD as App
import FreeCADGui as Gui
from PySide import QtGui, QtCore

def get_parent_container(obj):
    """Find container (Part/Group) one level up, or return obj itself."""
    for container in App.ActiveDocument.Objects:
        if hasattr(container, 'Group') and obj in container.Group:
            return container
    return obj

def super_align():
    sel_ex = Gui.Selection.getSelectionEx()
    if len(sel_ex) != 2:
        print("❌ Ctrl+click point/edge/face on MOVABLE, then FIXED")
        return
    
    # Store visibility states
    original_vis = {}
    for obj in App.ActiveDocument.Objects:
        if hasattr(obj, 'Visibility'):
            original_vis[obj.Name] = obj.Visibility
    
    # Get parents (container or object itself)
    movable_parent = get_parent_container(sel_ex[0].Object)
    fixed_parent = get_parent_container(sel_ex[1].Object)
    
    print(f"MOVABLE: '{movable_parent.Label}' → FIXED: '{fixed_parent.Label}'")
    
    # Get movable objects (whole container or single)
    if hasattr(movable_parent, 'Group') and movable_parent.Group:
        movable_objects = list(movable_parent.Group)
    else:
        movable_objects = [movable_parent]
    
    # Get subelement positions
    def get_sub_pos(sub_ex):
        sub_obj = sub_ex.SubObjects[0]
        if hasattr(sub_obj, 'Point'):
            return sub_obj.Point
        elif hasattr(sub_obj, 'CenterOfMass'):
            return sub_obj.CenterOfMass
        else:
            # Bounding box center
            bb = sub_obj.BoundBox
            return App.Vector((bb.XMin+bb.XMax)/2, (bb.YMin+bb.YMax)/2, (bb.ZMin+bb.ZMax)/2)
    
    movable_pos = get_sub_pos(sel_ex[0])
    fixed_pos = get_sub_pos(sel_ex[1])
    offset = fixed_pos - movable_pos
    
    print(f"📐 Incremental offset: {offset.x:.1f}, {offset.y:.1f}, {offset.z:.1f}")
    
    # INCREMENTAL ALIGNMENT (preserves relative positions)
    aligned_count = 0
    for obj in movable_objects:
        if hasattr(obj, 'Placement'):
            new_base = obj.Placement.Base + offset
            obj.Placement = App.Placement(new_base, obj.Placement.Rotation)
            aligned_count += 1
    
    # Restore visibility
    for name, vis in original_vis.items():
        obj = App.ActiveDocument.getObject(name)
        if obj and hasattr(obj, 'Visibility'):
            obj.Visibility = vis
    
    Gui.Selection.clearSelection()
    Gui.Selection.addSelection(movable_parent)
    print(f"✅ INCREMENTALLY aligned {aligned_count} objects!")

# CREATE REFINED DOCKED PANEL
mw = Gui.getMainWindow()
dock = QtGui.QDockWidget("Super Align", mw)
dock.setObjectName("SuperAlignDock")
dock.setFeatures(QtGui.QDockWidget.DockWidgetMovable | 
                QtGui.QDockWidget.DockWidgetFloatable | 
                QtGui.QDockWidget.DockWidgetClosable)

widget = QtGui.QWidget()
layout = QtGui.QVBoxLayout(widget)

# BIGGER TITLE
title = QtGui.QLabel("Super Align")
title.setStyleSheet("font-weight: bold; font-size: 20px; color: #2E8B57; padding: 5px;")
layout.addWidget(title)

# INSTRUCTIONS (small text under title)
instructions = QtGui.QLabel("""
• Select point/edge/face on movable object
• Select point/edge/face on fixed object  
• Click Align!
""")
instructions.setStyleSheet("font-size: 10px; color: #666; padding: 2px; margin: 0px;")
instructions.setWordWrap(True)
layout.addWidget(instructions)

# THINNER ALIGN BUTTON
align_btn = QtGui.QPushButton("Align!")
align_btn.setToolTip("Ctrl-click movable point/edge/face → Ctrl-click fixed → Align!")
align_btn.clicked.connect(super_align)
align_btn.setStyleSheet("""
    QPushButton { 
        font-weight: bold; 
        min-height: 25px; 
        max-height: 25px; 
        background-color: #FF6B6B; 
        border-radius: 5px;
    }
""")
layout.addWidget(align_btn)

widget.setLayout(layout)
dock.setWidget(widget)
mw.addDockWidget(QtCore.Qt.RightDockWidgetArea, dock)
dock.show()

print("✅ Super Align REFINED panel ready!")