Macro Rubik Cube: Difference between revisions
m (Update version numner and date) |
(Fix problems when macro is used to create more than one document) |
||
Line 4: | Line 4: | ||
<translate> |
<translate> |
||
<!--T:1--> |
<!--T:1--> |
||
{{Macro|Icon=Text-x-python|Name=Macro_Rubik_Cube|Description=Macro to Display a Rubik Cube and interactively do slice rotations.|Author=Aleph0|Version=00. |
{{Macro|Icon=Text-x-python|Name=Macro_Rubik_Cube|Description=Macro to Display a Rubik Cube and interactively do slice rotations.|Author=Aleph0|Version=00.03|Date=2017-10-25}} |
||
<!--T:2--> |
<!--T:2--> |
||
Line 65: | Line 65: | ||
__title__ = "Rubik_cube" |
__title__ = "Rubik_cube" |
||
__author__ = "Aleph0" |
__author__ = "Aleph0" |
||
__version__ = "00. |
__version__ = "00.03" |
||
__date__ = " |
__date__ = "25/10/2017" |
||
__Comment__ = "Virtual Rubik Cube" |
__Comment__ = "Virtual Rubik Cube" |
||
__Wiki__ = "http://www.freecadweb.org/wiki/index.php?title=Macro_Rubik_Cube" |
__Wiki__ = "http://www.freecadweb.org/wiki/index.php?title=Macro_Rubik_Cube" |
||
Line 107: | Line 107: | ||
except Exception: |
except Exception: |
||
from PySide import QtCore, QtGui |
from PySide import QtCore, QtGui |
||
# If this is the first time this macro has been run in this invocation of FreeCAD, |
|||
# create our dictionary of document-specific data structures |
|||
if not hasattr(FreeCAD, "Rubik_Cube_executed"): |
|||
FreeCAD.Rubik_Cube_executed = 1 |
|||
Dictionary = {} |
|||
# Create a new document and make it current |
# Create a new document and make it current |
||
App.newDocument("Rubik_Cube") |
App.ActiveDocument = App.newDocument("Rubik_Cube") |
||
Gui.ActiveDocument = Gui.getDocument(str(App.ActiveDocument.Name)) |
|||
App.setActiveDocument("Rubik_Cube") |
|||
gad = Gui.getDocument("Rubik_Cube") |
|||
Gui.ActiveDocument = gad |
|||
⚫ | |||
# This bit of code pops up the dialog to ask for the size |
# This bit of code pops up the dialog to ask for the size |
||
size = [3] |
|||
width = 300 |
width = 300 |
||
height = 150 |
height = 150 |
||
defaultSize = 3 |
|||
class AskSizeWindow(QtGui.QDialog): |
class AskSizeWindow(QtGui.QDialog): |
||
# automagically called when the window is created |
# automagically called when the window is created |
||
Line 149: | Line 152: | ||
self.spinBox.setMinimum(2) |
self.spinBox.setMinimum(2) |
||
self.spinBox.setMaximum(100) |
self.spinBox.setMaximum(100) |
||
Dictionary[str(App.ActiveDocument.Name)+"Size"] = defaultSize |
|||
self.spinBox.setValue( |
self.spinBox.setValue(defaultSize) |
||
self.spinBox.setSingleStep(1) |
self.spinBox.setSingleStep(1) |
||
self.spinBox.setObjectName("spinBox") |
self.spinBox.setObjectName("spinBox") |
||
Line 159: | Line 163: | ||
self.OKbutton.clicked.connect(self.onOK) |
self.OKbutton.clicked.connect(self.onOK) |
||
def on_spinBox_valueChanged(self, val): |
def on_spinBox_valueChanged(self, val): |
||
Dictionary[str(App.ActiveDocument.Name)+"Size"] = val |
|||
def onOK(self): |
def onOK(self): |
||
self.close() |
self.close() |
||
self.destroy() |
self.destroy() |
||
AskSizeWindow().exec_() |
AskSizeWindow().exec_() |
||
n = size[0] |
|||
# Display a wait cursor while we are making the cube |
# Display a wait cursor while we are making the cube |
||
Line 184: | Line 187: | ||
if event.getState() == 1: |
if event.getState() == 1: |
||
wh = obname[5:7] |
wh = obname[5:7] |
||
i = int(obname[7:]) |
|||
if wh == "mX": |
if wh == "mX": |
||
rotmX( |
rotmX(i) |
||
elif wh == "pX": |
elif wh == "pX": |
||
rotpX( |
rotpX(i) |
||
elif wh == "mY": |
elif wh == "mY": |
||
rotmY( |
rotmY(i) |
||
elif wh == "pY": |
elif wh == "pY": |
||
rotpY( |
rotpY(i) |
||
elif wh == "mZ": |
elif wh == "mZ": |
||
rotmZ( |
rotmZ(i) |
||
elif wh == "pZ": |
elif wh == "pZ": |
||
rotpZ( |
rotpZ(i) |
||
event_cb.setHandled() |
event_cb.setHandled() |
||
Dictionary[str(App.ActiveDocument.Name)+"ViewObserver"] = ViewObserver() |
|||
# The ViewObserver will get destroyed when you close its document |
|||
# This bit of code creates the basic cube model |
# This bit of code creates the basic cube model |
||
# It is composed of faces rather than cubes because I haven't found a way of making |
# It is composed of faces rather than cubes because I haven't found a way of making |
||
# a cube with different coloured faces |
# a cube with different coloured faces |
||
⚫ | |||
n = Dictionary[str(App.ActiveDocument.Name)+"Size"] |
|||
fc = [[range(n) for iy in range(n)] for ix in range(n)] |
fc = [[range(n) for iy in range(n)] for ix in range(n)] |
||
Dictionary[str(App.ActiveDocument.Name)+"cubies"] = fc |
|||
⚫ | |||
⚫ | |||
aa = [[i for i in range(n)] for j in range(6)] |
|||
for ix in range(n): |
for ix in range(n): |
||
fx = ix - (n - 1) / 2.0 |
fx = ix - (n - 1) / 2.0 |
||
Line 224: | Line 226: | ||
x1y1z1 = Base.Vector(fx+0.5,fy+0.5,fz+0.5) |
x1y1z1 = Base.Vector(fx+0.5,fy+0.5,fz+0.5) |
||
face = Part.Face(Part.makePolygon([x0y0z0,x0y0z1,x0y1z1,x0y1z0,x0y0z0])) |
face = Part.Face(Part.makePolygon([x0y0z0,x0y0z1,x0y1z1,x0y1z0,x0y0z0])) |
||
f1=fcd.addObject("Part::Feature", fs+"x0") |
f1 = fcd.addObject("Part::Feature", fs+"x0") |
||
f1.Shape = face |
f1.Shape = face |
||
if ix == 0: |
if ix == 0: |
||
Line 232: | Line 234: | ||
f1.ViewObject.RootNode.setName(coin.SbName(fs+"x0")) |
f1.ViewObject.RootNode.setName(coin.SbName(fs+"x0")) |
||
face = Part.Face(Part.makePolygon([x1y0z0,x1y0z1,x1y1z1,x1y1z0,x1y0z0])) |
face = Part.Face(Part.makePolygon([x1y0z0,x1y0z1,x1y1z1,x1y1z0,x1y0z0])) |
||
f2=fcd.addObject("Part::Feature", fs+"x1") |
f2 = fcd.addObject("Part::Feature", fs+"x1") |
||
f2.Shape = face |
f2.Shape = face |
||
if ix == n - 1: |
if ix == n - 1: |
||
Line 240: | Line 242: | ||
f2.ViewObject.RootNode.setName(coin.SbName(fs+"x1")) |
f2.ViewObject.RootNode.setName(coin.SbName(fs+"x1")) |
||
face = Part.Face(Part.makePolygon([x0y0z0,x0y0z1,x1y0z1,x1y0z0,x0y0z0])) |
face = Part.Face(Part.makePolygon([x0y0z0,x0y0z1,x1y0z1,x1y0z0,x0y0z0])) |
||
f3=fcd.addObject("Part::Feature", fs+"y0") |
f3 = fcd.addObject("Part::Feature", fs+"y0") |
||
f3.Shape = face |
f3.Shape = face |
||
if iy == 0: |
if iy == 0: |
||
Line 248: | Line 250: | ||
f3.ViewObject.RootNode.setName(coin.SbName(fs+"y0")) |
f3.ViewObject.RootNode.setName(coin.SbName(fs+"y0")) |
||
face = Part.Face(Part.makePolygon([x0y1z0,x0y1z1,x1y1z1,x1y1z0,x0y1z0])) |
face = Part.Face(Part.makePolygon([x0y1z0,x0y1z1,x1y1z1,x1y1z0,x0y1z0])) |
||
f4=fcd.addObject("Part::Feature", fs+"y1") |
f4 = fcd.addObject("Part::Feature", fs+"y1") |
||
f4.Shape = face |
f4.Shape = face |
||
if iy == n - 1: |
if iy == n - 1: |
||
Line 256: | Line 258: | ||
f4.ViewObject.RootNode.setName(coin.SbName(fs+"y1")) |
f4.ViewObject.RootNode.setName(coin.SbName(fs+"y1")) |
||
face = Part.Face(Part.makePolygon([x0y0z0,x0y1z0,x1y1z0,x1y0z0,x0y0z0])) |
face = Part.Face(Part.makePolygon([x0y0z0,x0y1z0,x1y1z0,x1y0z0,x0y0z0])) |
||
f5=fcd.addObject("Part::Feature", fs+"z0") |
f5 = fcd.addObject("Part::Feature", fs+"z0") |
||
f5.Shape = face |
f5.Shape = face |
||
if iz == 0: |
if iz == 0: |
||
Line 264: | Line 266: | ||
f5.ViewObject.RootNode.setName(coin.SbName(fs+"z0")) |
f5.ViewObject.RootNode.setName(coin.SbName(fs+"z0")) |
||
face = Part.Face(Part.makePolygon([x0y0z1,x0y1z1,x1y1z1,x1y0z1,x0y0z1])) |
face = Part.Face(Part.makePolygon([x0y0z1,x0y1z1,x1y1z1,x1y0z1,x0y0z1])) |
||
f6=fcd.addObject("Part::Feature", fs+"z1") |
f6 = fcd.addObject("Part::Feature", fs+"z1") |
||
f6.Shape = face |
f6.Shape = face |
||
if iz == n - 1: |
if iz == n - 1: |
||
Line 288: | Line 290: | ||
v5 = Base.Vector(fx+0.1,fy,fz-0.5) |
v5 = Base.Vector(fx+0.1,fy,fz-0.5) |
||
v6 = Base.Vector(fx+0.1,fy,fz) |
v6 = Base.Vector(fx+0.1,fy,fz) |
||
arrow = fcd.addObject("Part::Feature", fs) |
|||
arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0])) |
|||
arrow.ViewObject.DiffuseColor=[(0.0,0.0,0.0)] |
|||
arrow.ViewObject.RootNode.setName(coin.SbName(fs)) |
|||
arrow.ViewObject.Selectable = False |
|||
fy = 0.2 + n / 2.0 |
fy = 0.2 + n / 2.0 |
||
fz = n / 2.0 |
fz = n / 2.0 |
||
Line 303: | Line 305: | ||
v5 = Base.Vector(fx+0.1,fy+0.5,fz) |
v5 = Base.Vector(fx+0.1,fy+0.5,fz) |
||
v6 = Base.Vector(fx+0.1,fy,fz) |
v6 = Base.Vector(fx+0.1,fy,fz) |
||
arrow = fcd.addObject("Part::Feature", fs) |
|||
arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0])) |
|||
arrow.ViewObject.DiffuseColor=[(0.0,0.0,0.0)] |
|||
arrow.ViewObject.RootNode.setName(coin.SbName(fs)) |
|||
arrow.ViewObject.Selectable = False |
|||
fx = n / 2.0 |
fx = n / 2.0 |
||
fy = i - (n - 1) / 2.0 |
fy = i - (n - 1) / 2.0 |
||
Line 319: | Line 321: | ||
v5 = Base.Vector(fx,fy+0.1,fz-0.5) |
v5 = Base.Vector(fx,fy+0.1,fz-0.5) |
||
v6 = Base.Vector(fx,fy+0.1,fz) |
v6 = Base.Vector(fx,fy+0.1,fz) |
||
arrow = fcd.addObject("Part::Feature", fs) |
|||
arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0])) |
|||
arrow.ViewObject.DiffuseColor=[(0.0,0.0,0.0)] |
|||
arrow.ViewObject.RootNode.setName(coin.SbName(fs)) |
|||
arrow.ViewObject.Selectable = False |
|||
fx = -(0.2 + n / 2.0) |
fx = -(0.2 + n / 2.0) |
||
fy = i - (n - 1) / 2.0 |
fy = i - (n - 1) / 2.0 |
||
Line 335: | Line 337: | ||
v5 = Base.Vector(fx-0.5,fy+0.1,fz) |
v5 = Base.Vector(fx-0.5,fy+0.1,fz) |
||
v6 = Base.Vector(fx,fy+0.1,fz) |
v6 = Base.Vector(fx,fy+0.1,fz) |
||
arrow = fcd.addObject("Part::Feature", fs) |
|||
arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0])) |
|||
arrow.ViewObject.DiffuseColor=[(0.0,0.0,0.0)] |
|||
arrow.ViewObject.RootNode.setName(coin.SbName(fs)) |
|||
arrow.ViewObject.Selectable = False |
|||
fx = n / 2.0 |
fx = n / 2.0 |
||
fy = 0.2 + n / 2.0 |
fy = 0.2 + n / 2.0 |
||
Line 351: | Line 353: | ||
v5 = Base.Vector(fx,fy+0.5,fz+0.1) |
v5 = Base.Vector(fx,fy+0.5,fz+0.1) |
||
v6 = Base.Vector(fx,fy,fz+0.1) |
v6 = Base.Vector(fx,fy,fz+0.1) |
||
arrow = fcd.addObject("Part::Feature", fs) |
|||
arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0])) |
|||
arrow.ViewObject.DiffuseColor=[(0.0,0.0,0.0)] |
|||
arrow.ViewObject.RootNode.setName(coin.SbName(fs)) |
|||
arrow.ViewObject.Selectable = False |
|||
fx = -(0.2 + n / 2.0) |
fx = -(0.2 + n / 2.0) |
||
fy = -(n / 2.0) |
fy = -(n / 2.0) |
||
Line 367: | Line 369: | ||
v5 = Base.Vector(fx-0.5,fy,fz+0.1) |
v5 = Base.Vector(fx-0.5,fy,fz+0.1) |
||
v6 = Base.Vector(fx,fy,fz+0.1) |
v6 = Base.Vector(fx,fy,fz+0.1) |
||
arrow = fcd.addObject("Part::Feature", fs) |
|||
arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0])) |
|||
arrow.ViewObject.DiffuseColor=[(0.0,0.0,0.0)] |
|||
arrow.ViewObject.RootNode.setName(coin.SbName(fs)) |
|||
arrow.ViewObject.Selectable = False |
|||
# This gets FreeCAD's top level SceneGraph (including camera node), |
# This gets FreeCAD's top level SceneGraph (including camera node), |
||
# not the document's SceneGraph which hangs off of it |
# not the document's SceneGraph which hangs off of it |
||
sceneGraph = |
sceneGraph = Gui.ActiveDocument.ActiveView.getViewer().getSoEventManager().getSceneGraph() |
||
# Viewfit doesn't seem to do the right thing with MultiViews |
# Viewfit doesn't seem to do the right thing with MultiViews |
||
# so we adjust the camera height manually before creating them |
# so we adjust the camera height manually before creating them |
||
Gui.ActiveDocument.ActiveView.viewAxonometric() |
|||
Gui.SendMsgToActiveView("ViewFit") |
Gui.SendMsgToActiveView("ViewFit") |
||
camera = sceneGraph.getChild(2) |
camera = sceneGraph.getChild(2) |
||
Line 387: | Line 389: | ||
# This bit of code finds the widget corresponding to the View3DInventor |
# This bit of code finds the widget corresponding to the View3DInventor |
||
⚫ | |||
def findView(widget): |
def findView(widget): |
||
if widget.metaObject().className().__str__() == "Gui::View3DInventor": |
if widget.metaObject().className().__str__() == "Gui::View3DInventor": |
||
return widget |
|||
else: |
else: |
||
result = None |
|||
for child in widget.children(): |
for child in widget.children(): |
||
findView(child) |
v = findView(child) |
||
if v != None: |
|||
⚫ | |||
result = v |
|||
return result |
|||
⚫ | |||
# This bit of code creates the buttons at the top of the view window |
# This bit of code creates the buttons at the top of the view window |
||
# The buttons are in frameless window to save screen space |
# The buttons are in a frameless window to save screen space |
||
height = 40 |
height = 40 |
||
class ButtonRow(QtGui.QWidget): |
class ButtonRow(QtGui.QWidget): |
||
def __init__(self): |
def __init__(self): |
||
super(ButtonRow, self).__init__() |
super(ButtonRow, self).__init__() |
||
self.setParent(view3DWidget) |
|||
self.setParent(view3DWidget[0]) |
|||
self.initUI() |
|||
def initUI(self): |
|||
self.setAutoFillBackground(True) |
self.setAutoFillBackground(True) |
||
xpos = 0 |
xpos = 0 |
||
geom = view3DWidget |
geom = view3DWidget.geometry() |
||
self.setGeometry(xpos, 0, geom.width(), height) |
self.setGeometry(xpos, 0, geom.width(), height) |
||
buttonWidth = 80 |
buttonWidth = 80 |
||
gap = geom.width() / 4 - buttonWidth |
gap = geom.width() / 4 - buttonWidth |
||
if gap < 0: |
if gap < 0: |
||
gap = 0 |
gap = 0 |
||
xpos = gap |
xpos = gap |
||
self.undoButton = QtGui.QPushButton(self) |
self.undoButton = QtGui.QPushButton(self) |
||
Line 436: | Line 438: | ||
def onSave(self): |
def onSave(self): |
||
saveHistory() |
saveHistory() |
||
⚫ | |||
buttonRow = ButtonRow() |
|||
Dictionary[str(App.ActiveDocument.Name)+"buttons"] = ButtonRow() |
|||
# This bit of code disables the default Phong shading |
# This bit of code disables the default Phong shading |
||
# and avoids the face colours appearing to change during rotation |
# and avoids the face colours appearing to change during rotation |
||
# We only do it once per FreeCAD invocation |
|||
if str(sceneGraph.getChild(0).getName()) <> "LightModel": |
if str(sceneGraph.getChild(0).getName()) <> "LightModel": |
||
lm=coin.SoLightModel() |
lm=coin.SoLightModel() |
||
Line 509: | Line 511: | ||
def createMultiViews(): |
def createMultiViews(): |
||
sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph() |
sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph() |
||
if sg. |
if sg.getNumChildren() != 0: |
||
for i in range(sg.getNumChildren()): |
for i in range(sg.getNumChildren()): |
||
child=sg.getChild(i) |
child = sg.getChild(i) |
||
type = child.getTypeId().getName().__str__() |
type = child.getTypeId().getName().__str__() |
||
if child.getTypeId().getName().__str__() == 'Separator': |
if child.getTypeId().getName().__str__() == 'Separator': |
||
Line 517: | Line 519: | ||
MultiViews(sg,child,i) |
MultiViews(sg,child,i) |
||
if child.getTypeId().getName().__str__() == 'MultipleCopy': |
if child.getTypeId().getName().__str__() == 'MultipleCopy': |
||
if child.getNumChildren() != 0: |
|||
name = child.getChild(0).getName().__str__() |
|||
if fcd.getObject(name) == None: |
|||
child.removeAllChildren() |
|||
createMultiViews() |
createMultiViews() |
||
Line 527: | Line 530: | ||
# This bit of code animates a slice rotation |
# This bit of code animates a slice rotation |
||
def slowrotate(dir, num): |
def slowrotate(dir, num): |
||
n = Dictionary[str(App.ActiveDocument.Name)+"Size"] |
|||
fc = Dictionary[str(App.ActiveDocument.Name)+"cubies"] |
|||
⚫ | |||
⚫ | |||
steps = slowness / (n * n) |
steps = slowness / (n * n) |
||
if dir.x > 0: |
if dir.x > 0: |
||
Line 532: | Line 539: | ||
for iz in range(n): |
for iz in range(n): |
||
for j in range(6): |
for j in range(6): |
||
c=fc[num][iy][iz][j] |
c = fc[num][iy][iz][j] |
||
fp[num][iy][iz][j]=c.Placement |
fp[num][iy][iz][j] = c.Placement |
||
for i in range(steps + 1): |
for i in range(steps + 1): |
||
fm = Base.Matrix() |
fm = Base.Matrix() |
||
Line 540: | Line 547: | ||
for iz in range(n): |
for iz in range(n): |
||
for j in range(6): |
for j in range(6): |
||
c=fc[num][iy][iz][j] |
c = fc[num][iy][iz][j] |
||
p=fp[num][iy][iz][j] |
p = fp[num][iy][iz][j] |
||
c.Placement = Base.Placement(fm).multiply(p) |
c.Placement = Base.Placement(fm).multiply(p) |
||
Gui.updateGui() |
Gui.updateGui() |
||
Line 556: | Line 563: | ||
for iz in range(n): |
for iz in range(n): |
||
for j in range(6): |
for j in range(6): |
||
c=fc[num][iy][iz][j] |
c = fc[num][iy][iz][j] |
||
fp[num][iy][iz][j]=c.Placement |
fp[num][iy][iz][j]=c.Placement |
||
for i in range(steps + 1): |
for i in range(steps + 1): |
||
Line 564: | Line 571: | ||
for iz in range(n): |
for iz in range(n): |
||
for j in range(6): |
for j in range(6): |
||
c=fc[num][iy][iz][j] |
c = fc[num][iy][iz][j] |
||
p=fp[num][iy][iz][j] |
p = fp[num][iy][iz][j] |
||
c.Placement = Base.Placement(fm).multiply(p) |
c.Placement = Base.Placement(fm).multiply(p) |
||
Gui.updateGui() |
Gui.updateGui() |
||
Line 580: | Line 587: | ||
for iz in range(n): |
for iz in range(n): |
||
for j in range(6): |
for j in range(6): |
||
c=fc[ix][num][iz][j] |
c = fc[ix][num][iz][j] |
||
fp[ix][num][iz][j]=c.Placement |
fp[ix][num][iz][j]=c.Placement |
||
for i in range(steps + 1): |
for i in range(steps + 1): |
||
Line 588: | Line 595: | ||
for iz in range(n): |
for iz in range(n): |
||
for j in range(6): |
for j in range(6): |
||
c=fc[ix][num][iz][j] |
c = fc[ix][num][iz][j] |
||
p=fp[ix][num][iz][j] |
p = fp[ix][num][iz][j] |
||
c.Placement = Base.Placement(fm).multiply(p) |
c.Placement = Base.Placement(fm).multiply(p) |
||
Gui.updateGui() |
Gui.updateGui() |
||
Line 604: | Line 611: | ||
for iz in range(n): |
for iz in range(n): |
||
for j in range(6): |
for j in range(6): |
||
c=fc[ix][num][iz][j] |
c = fc[ix][num][iz][j] |
||
fp[ix][num][iz][j]=c.Placement |
fp[ix][num][iz][j]=c.Placement |
||
for i in range(steps + 1): |
for i in range(steps + 1): |
||
Line 612: | Line 619: | ||
for iz in range(n): |
for iz in range(n): |
||
for j in range(6): |
for j in range(6): |
||
c=fc[ix][num][iz][j] |
c = fc[ix][num][iz][j] |
||
p=fp[ix][num][iz][j] |
p = fp[ix][num][iz][j] |
||
c.Placement = Base.Placement(fm).multiply(p) |
c.Placement = Base.Placement(fm).multiply(p) |
||
Gui.updateGui() |
Gui.updateGui() |
||
Line 628: | Line 635: | ||
for iy in range(n): |
for iy in range(n): |
||
for j in range(6): |
for j in range(6): |
||
c=fc[ix][iy][num][j] |
c = fc[ix][iy][num][j] |
||
fp[ix][iy][num][j]=c.Placement |
fp[ix][iy][num][j]=c.Placement |
||
for i in range(steps + 1): |
for i in range(steps + 1): |
||
Line 636: | Line 643: | ||
for iy in range(n): |
for iy in range(n): |
||
for j in range(6): |
for j in range(6): |
||
c=fc[ix][iy][num][j] |
c = fc[ix][iy][num][j] |
||
p=fp[ix][iy][num][j] |
p = fp[ix][iy][num][j] |
||
c.Placement = Base.Placement(fm).multiply(p) |
c.Placement = Base.Placement(fm).multiply(p) |
||
Gui.updateGui() |
Gui.updateGui() |
||
Line 652: | Line 659: | ||
for iy in range(n): |
for iy in range(n): |
||
for j in range(6): |
for j in range(6): |
||
c=fc[ix][iy][num][j] |
c = fc[ix][iy][num][j] |
||
fp[ix][iy][num][j]=c.Placement |
fp[ix][iy][num][j]=c.Placement |
||
for i in range(steps + 1): |
for i in range(steps + 1): |
||
Line 660: | Line 667: | ||
for iy in range(n): |
for iy in range(n): |
||
for j in range(6): |
for j in range(6): |
||
c=fc[ix][iy][num][j] |
c = fc[ix][iy][num][j] |
||
p=fp[ix][iy][num][j] |
p = fp[ix][iy][num][j] |
||
c.Placement = Base.Placement(fm).multiply(p) |
c.Placement = Base.Placement(fm).multiply(p) |
||
Gui.updateGui() |
Gui.updateGui() |
||
Line 676: | Line 683: | ||
# Once you have created a cube, these functions will be defined and in scope |
# Once you have created a cube, these functions will be defined and in scope |
||
# and you can call them from another macro craeted by saving history |
# and you can call them from another macro craeted by saving history |
||
history=[] |
history = [] |
||
Dictionary[str(App.ActiveDocument.Name)+"history"] = history |
|||
def rotpX(i): |
def rotpX(i): |
||
slowrotate(Base.Vector(1,0,0),i) |
slowrotate(Base.Vector(1,0,0),i) |
||
history.append("rotpX("+str(i)+")") |
Dictionary[str(App.ActiveDocument.Name)+"history"].append("rotpX("+str(i)+")") |
||
def rotpY(i): |
def rotpY(i): |
||
slowrotate(Base.Vector(0,1,0),i) |
slowrotate(Base.Vector(0,1,0),i) |
||
history.append("rotpY("+str(i)+")") |
Dictionary[str(App.ActiveDocument.Name)+"history"].append("rotpY("+str(i)+")") |
||
def rotpZ(i): |
def rotpZ(i): |
||
slowrotate(Base.Vector(0,0,1),i) |
slowrotate(Base.Vector(0,0,1),i) |
||
history.append("rotpZ("+str(i)+")") |
Dictionary[str(App.ActiveDocument.Name)+"history"].append("rotpZ("+str(i)+")") |
||
def rotmX(i): |
def rotmX(i): |
||
slowrotate(Base.Vector(-1,0,0),i) |
slowrotate(Base.Vector(-1,0,0),i) |
||
history.append("rotmX("+str(i)+")") |
Dictionary[str(App.ActiveDocument.Name)+"history"].append("rotmX("+str(i)+")") |
||
def rotmY(i): |
def rotmY(i): |
||
slowrotate(Base.Vector(0,-1,0),i) |
slowrotate(Base.Vector(0,-1,0),i) |
||
history.append("rotmY("+str(i)+")") |
Dictionary[str(App.ActiveDocument.Name)+"history"].append("rotmY("+str(i)+")") |
||
def rotmZ(i): |
def rotmZ(i): |
||
slowrotate(Base.Vector(0,0,-1),i) |
slowrotate(Base.Vector(0,0,-1),i) |
||
history.append("rotmZ("+str(i)+")") |
Dictionary[str(App.ActiveDocument.Name)+"history"].append("rotmZ("+str(i)+")") |
||
def undo(): |
def undo(): |
||
history = Dictionary[str(App.ActiveDocument.Name)+"history"] |
|||
if len(history) > 0: |
if len(history) > 0: |
||
fs = history.pop() |
fs = history.pop() |
||
Line 713: | Line 722: | ||
slowrotate(Base.Vector(0,0,-1),i) |
slowrotate(Base.Vector(0,0,-1),i) |
||
def saveHistory(): |
def saveHistory(): |
||
history = Dictionary[str(App.ActiveDocument.Name)+"history"] |
|||
n = Dictionary[str(App.ActiveDocument.Name)+"Size"] |
|||
if len(history) > 0: |
if len(history) > 0: |
||
fs = "" |
fs = "" |
||
Line 727: | Line 738: | ||
clip.setText(fs) |
clip.setText(fs) |
||
def reset(): |
def reset(): |
||
fcd = App.ActiveDocument |
|||
while len(history) > 0: |
|||
Dictionary[str(fcd.Name)+"history"] = [] |
|||
history.pop() |
|||
n = Dictionary[str(fcd.Name)+"Size"] |
|||
fc = Dictionary[str(fcd.Name)+"cubies"] |
|||
for ix in range(n): |
for ix in range(n): |
||
for iy in range(n): |
for iy in range(n): |
||
Line 734: | Line 747: | ||
fs = "fs"+str(ix)+"q"+str(iy)+"q"+str(iz) |
fs = "fs"+str(ix)+"q"+str(iy)+"q"+str(iz) |
||
for j in range(6): |
for j in range(6): |
||
c=fcd.getObject(fs+"x0") |
c = fcd.getObject(fs+"x0") |
||
c.Placement=Base.Placement() |
c.Placement = Base.Placement() |
||
fc[ix][iy][iz][0]=c |
fc[ix][iy][iz][0] = c |
||
c=fcd.getObject(fs+"x1") |
c = fcd.getObject(fs+"x1") |
||
c.Placement=Base.Placement() |
c.Placement = Base.Placement() |
||
fc[ix][iy][iz][1]=c |
fc[ix][iy][iz][1] = c |
||
c=fcd.getObject(fs+"y0") |
c = fcd.getObject(fs+"y0") |
||
c.Placement=Base.Placement() |
c.Placement = Base.Placement() |
||
fc[ix][iy][iz][2]=c |
fc[ix][iy][iz][2] = c |
||
c=fcd.getObject(fs+"y1") |
c = fcd.getObject(fs+"y1") |
||
c.Placement=Base.Placement() |
c.Placement = Base.Placement() |
||
fc[ix][iy][iz][3]=c |
fc[ix][iy][iz][3] = c |
||
c=fcd.getObject(fs+"z0") |
c = fcd.getObject(fs+"z0") |
||
c.Placement=Base.Placement() |
c.Placement = Base.Placement() |
||
fc[ix][iy][iz][4]=c |
fc[ix][iy][iz][4] = c |
||
c=fcd.getObject(fs+"z1") |
c = fcd.getObject(fs+"z1") |
||
c.Placement=Base.Placement() |
c.Placement = Base.Placement() |
||
fc[ix][iy][iz][5]=c |
fc[ix][iy][iz][5] = c |
||
</pre> |
</pre> |
Revision as of 10:07, 25 October 2017
Description
File:Text-x-python Macro_Rubik_Cube |
Description |
---|
Macro to Display a Rubik Cube and interactively do slice rotations. Macro version: 00.03 Last modified: 2017-10-25 Author: Aleph0 |
Author |
Aleph0 |
Download |
None |
Links |
Macros recipes How to install macros How to customize toolbars |
Macro Version |
00.03 |
Date last modified |
2017-10-25 |
FreeCAD Version(s) |
None |
Default shortcut |
None |
See also |
None |
Macro to Display a Rubik Cube and interactively do slice rotations.
Script
# -*- coding: utf-8 -*- """ *************************************************************************** * * * This macro creates a virtual Rubik Cube and enable you to manipulate * * it. * * You can chooose the size (number of small cubes along an edge). * * It then makes the cube: large sizes can take a while. * * It then displays several views of the cube. * * The central and largest view is an axonometric projection. * * This has arrows around it which you can click on to rotate slices. * * Another view is an axonometric projection from the other side. * * Another view combines views towards each face so as to * * look like a net of the cube's surface unfolded. * * * The macro maintains a history of the slice rotations you have done. * * It puts three buttons at the top of the window. * * One undoes the last slice rotation and removes it from the history. * * One saves the history to the clipboard as a sequence of function * * calls which can be pasted into a macro which can then be called * * to replay the same set of rotations. Thus you can save an "operator" * * (a sequence of slice rotation which does something useful). * * The third button resets the cube to its initial state * * and clears the history. * * * *************************************************************************** * Copyright © 2017 Richard P. Parkins, M. A. * * * * This file is a supplement to the FreeCAD CAx development system. * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License (LGPL) * * as published by the Free Software Foundation; either version 2 of * * the License, or (at your option) any later version. * * for detail see the LICENCE text file. * * * * This software is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this macro; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * * USA * * * *************************************************************************** """ __title__ = "Rubik_cube" __author__ = "Aleph0" __version__ = "00.03" __date__ = "25/10/2017" __Comment__ = "Virtual Rubik Cube" __Wiki__ = "http://www.freecadweb.org/wiki/index.php?title=Macro_Rubik_Cube" __Help__ = "see first few lines of macro text" __Status__ = "stable" __Requires__ = "freecad 0.16" #OS: Ubuntu 14.04.5 LTS #Word size of OS: 64-bit #Word size of FreeCAD: 64-bit #Version: 0.16.6703 (Git) #Build type: None #Branch: releases/FreeCAD-0-16 #Hash: 2ce5c8d2e3020d05005ed71f710e09e9aa561f40 #Python version: 2.7.6 #Qt version: 4.8.6 #Coin version: 4.0.0a #OCC version: 6.8.0.oce-0.17 # This parameter determines the speed at which slice rotations are animated # If you have a faster computer you can increase the value # This will make the animation smoother # If you have a slow computer you can decrease the value # This will make the animation faster but more jerky for large cubes slowness = 500 import FreeCAD import Part import time from FreeCAD import Base from FreeCAD import Console from pivy import coin from pivy.coin import * import PySide # I copied this code from Macro_Mouse_Cross # It seems a bit version dependent, but it works try: from PyQt4 import QtCore, QtGui except Exception: from PySide import QtCore, QtGui # If this is the first time this macro has been run in this invocation of FreeCAD, # create our dictionary of document-specific data structures if not hasattr(FreeCAD, "Rubik_Cube_executed"): FreeCAD.Rubik_Cube_executed = 1 Dictionary = {} # Create a new document and make it current App.ActiveDocument = App.newDocument("Rubik_Cube") Gui.ActiveDocument = Gui.getDocument(str(App.ActiveDocument.Name)) # This bit of code pops up the dialog to ask for the size width = 300 height = 150 defaultSize = 3 class AskSizeWindow(QtGui.QDialog): # automagically called when the window is created def __init__(self): super(AskSizeWindow, self).__init__() self.initUI() # Lay out the interactive elements def initUI(self): self.setWindowTitle("Rubik Cube Size") geom = Gui.getMainWindow().geometry() xpos = geom.center().x() - width / 2 ypos = geom.center().y() - height / 2 self.setGeometry(xpos, ypos, width, height) margin = 10 ypos = margin self.label_1 = QtGui.QLabel(self) self.label_1.setGeometry(QtCore.QRect(width/2 - 100, ypos, 200, 25)) self.label_1.setObjectName("label_1") self.label_1.setText("Number of small cubes") self.label_1.setAlignment(QtCore.Qt.AlignCenter) ypos = ypos + 25 self.label_2 = QtGui.QLabel(self) self.label_2.setGeometry(QtCore.QRect(width/2 - 100, ypos, 200, 25)) self.label_2.setObjectName("label_1") self.label_2.setText("along an edge") self.label_2.setAlignment(QtCore.Qt.AlignCenter) ypos = ypos + 25 self.spinBox = QtGui.QSpinBox(self) self.spinBox.setGeometry(QtCore.QRect(width/2 - 50, ypos, 100, 30)) self.spinBox.setMinimum(2) self.spinBox.setMaximum(100) Dictionary[str(App.ActiveDocument.Name)+"Size"] = defaultSize self.spinBox.setValue(defaultSize) self.spinBox.setSingleStep(1) self.spinBox.setObjectName("spinBox") self.spinBox.valueChanged.connect(self.on_spinBox_valueChanged) ypos = ypos + 40 self.OKbutton = QtGui.QPushButton(self) self.OKbutton.setGeometry(QtCore.QRect(width/2 - 40, ypos, 80, 40)) self.OKbutton.setText("OK") self.OKbutton.clicked.connect(self.onOK) def on_spinBox_valueChanged(self, val): Dictionary[str(App.ActiveDocument.Name)+"Size"] = val def onOK(self): self.close() self.destroy() AskSizeWindow().exec_() # Display a wait cursor while we are making the cube QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor)) # This bit of code catches clicks on the rotation arrows class ViewObserver: def __init__(self): self.view = FreeCADGui.ActiveDocument.ActiveView self.callback = self.view.addEventCallbackPivy(SoMouseButtonEvent.getClassTypeId(),self.getpoint) def getpoint(self,event_cb): event = event_cb.getEvent() if event.getButton() == 1: pos = event.getPosition().getValue() obj = self.view.getObjectInfo((int(pos[0]),int(pos[1]))) if obj != None: obname = obj["Object"] if obname[:5] == "arrow": if event.getState() == 1: wh = obname[5:7] i = int(obname[7:]) if wh == "mX": rotmX(i) elif wh == "pX": rotpX(i) elif wh == "mY": rotmY(i) elif wh == "pY": rotpY(i) elif wh == "mZ": rotmZ(i) elif wh == "pZ": rotpZ(i) event_cb.setHandled() Dictionary[str(App.ActiveDocument.Name)+"ViewObserver"] = ViewObserver() # This bit of code creates the basic cube model # It is composed of faces rather than cubes because I haven't found a way of making # a cube with different coloured faces fcd = App.ActiveDocument n = Dictionary[str(App.ActiveDocument.Name)+"Size"] fc = [[range(n) for iy in range(n)] for ix in range(n)] Dictionary[str(App.ActiveDocument.Name)+"cubies"] = fc for ix in range(n): fx = ix - (n - 1) / 2.0 for iy in range(n): fy = iy - (n - 1) / 2.0 for iz in range(n): fz = iz - (n - 1) / 2.0 fs = "fs"+str(ix)+"q"+str(iy)+"q"+str(iz) x0y0z0 = Base.Vector(fx-0.5,fy-0.5,fz-0.5) x0y0z1 = Base.Vector(fx-0.5,fy-0.5,fz+0.5) x0y1z0 = Base.Vector(fx-0.5,fy+0.5,fz-0.5) x0y1z1 = Base.Vector(fx-0.5,fy+0.5,fz+0.5) x1y0z0 = Base.Vector(fx+0.5,fy-0.5,fz-0.5) x1y0z1 = Base.Vector(fx+0.5,fy-0.5,fz+0.5) x1y1z0 = Base.Vector(fx+0.5,fy+0.5,fz-0.5) x1y1z1 = Base.Vector(fx+0.5,fy+0.5,fz+0.5) face = Part.Face(Part.makePolygon([x0y0z0,x0y0z1,x0y1z1,x0y1z0,x0y0z0])) f1 = fcd.addObject("Part::Feature", fs+"x0") f1.Shape = face if ix == 0: f1.ViewObject.DiffuseColor=[(1.0,1.0,1.0)] else: f1.ViewObject.DiffuseColor=[(0.0,0.0,0.0)] f1.ViewObject.RootNode.setName(coin.SbName(fs+"x0")) face = Part.Face(Part.makePolygon([x1y0z0,x1y0z1,x1y1z1,x1y1z0,x1y0z0])) f2 = fcd.addObject("Part::Feature", fs+"x1") f2.Shape = face if ix == n - 1: f2.ViewObject.DiffuseColor=[(1.0,0.0,0.0)] else: f2.ViewObject.DiffuseColor=[(0.0,0.0,0.0)] f2.ViewObject.RootNode.setName(coin.SbName(fs+"x1")) face = Part.Face(Part.makePolygon([x0y0z0,x0y0z1,x1y0z1,x1y0z0,x0y0z0])) f3 = fcd.addObject("Part::Feature", fs+"y0") f3.Shape = face if iy == 0: f3.ViewObject.DiffuseColor=[(0.0,1.0,0.0)] else: f3.ViewObject.DiffuseColor=[(0.0,0.0,0.0)] f3.ViewObject.RootNode.setName(coin.SbName(fs+"y0")) face = Part.Face(Part.makePolygon([x0y1z0,x0y1z1,x1y1z1,x1y1z0,x0y1z0])) f4 = fcd.addObject("Part::Feature", fs+"y1") f4.Shape = face if iy == n - 1: f4.ViewObject.DiffuseColor=[(1.0,0.0,1.0)] else: f4.ViewObject.DiffuseColor=[(0.0,0.0,0.0)] f4.ViewObject.RootNode.setName(coin.SbName(fs+"y1")) face = Part.Face(Part.makePolygon([x0y0z0,x0y1z0,x1y1z0,x1y0z0,x0y0z0])) f5 = fcd.addObject("Part::Feature", fs+"z0") f5.Shape = face if iz == 0: f5.ViewObject.DiffuseColor=[(1.0,1.0,0.0)] else: f5.ViewObject.DiffuseColor=[(0.0,0.0,0.0)] f5.ViewObject.RootNode.setName(coin.SbName(fs+"z0")) face = Part.Face(Part.makePolygon([x0y0z1,x0y1z1,x1y1z1,x1y0z1,x0y0z1])) f6 = fcd.addObject("Part::Feature", fs+"z1") f6.Shape = face if iz == n - 1: f6.ViewObject.DiffuseColor=[(0.0,0.0,1.0)] else: f6.ViewObject.DiffuseColor=[(0.0,0.0,0.0)] f6.ViewObject.RootNode.setName(coin.SbName(fs+"z1")) fc[ix][iy][iz]=[f1,f2,f3,f4,f5,f6] # This bit of code creates the clickable arrows # Note we make them not selectable because mouse clicking on them # does a slice rotation instead of selecting the arrow. for i in range(n): fx = i - (n - 1) / 2.0 fy = -(n / 2.0) fz = -(0.2 + n / 2.0) fs = "arrowpX"+str(i) v0 = Base.Vector(fx-0.1,fy,fz) v1 = Base.Vector(fx-0.1,fy,fz-0.5) v2 = Base.Vector(fx-0.2,fy,fz-0.5) v3 = Base.Vector(fx,fy,fz-0.7) v4 = Base.Vector(fx+0.2,fy,fz-0.5) v5 = Base.Vector(fx+0.1,fy,fz-0.5) v6 = Base.Vector(fx+0.1,fy,fz) arrow = fcd.addObject("Part::Feature", fs) arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0])) arrow.ViewObject.DiffuseColor=[(0.0,0.0,0.0)] arrow.ViewObject.RootNode.setName(coin.SbName(fs)) arrow.ViewObject.Selectable = False fy = 0.2 + n / 2.0 fz = n / 2.0 fs = "arrowmX"+str(i) v0 = Base.Vector(fx-0.1,fy,fz) v1 = Base.Vector(fx-0.1,fy+0.5,fz) v2 = Base.Vector(fx-0.2,fy+0.5,fz) v3 = Base.Vector(fx,fy+0.7,fz) v4 = Base.Vector(fx+0.2,fy+0.5,fz) v5 = Base.Vector(fx+0.1,fy+0.5,fz) v6 = Base.Vector(fx+0.1,fy,fz) arrow = fcd.addObject("Part::Feature", fs) arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0])) arrow.ViewObject.DiffuseColor=[(0.0,0.0,0.0)] arrow.ViewObject.RootNode.setName(coin.SbName(fs)) arrow.ViewObject.Selectable = False fx = n / 2.0 fy = i - (n - 1) / 2.0 fz = -(0.2 + n / 2.0) fs = "arrowpY"+str(i) v0 = Base.Vector(fx,fy-0.1,fz) v1 = Base.Vector(fx,fy-0.1,fz-0.5) v2 = Base.Vector(fx,fy-0.2,fz-0.5) v3 = Base.Vector(fx,fy,fz-0.7) v4 = Base.Vector(fx,fy+0.2,fz-0.5) v5 = Base.Vector(fx,fy+0.1,fz-0.5) v6 = Base.Vector(fx,fy+0.1,fz) arrow = fcd.addObject("Part::Feature", fs) arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0])) arrow.ViewObject.DiffuseColor=[(0.0,0.0,0.0)] arrow.ViewObject.RootNode.setName(coin.SbName(fs)) arrow.ViewObject.Selectable = False fx = -(0.2 + n / 2.0) fy = i - (n - 1) / 2.0 fz = n / 2.0 fs = "arrowmY"+str(i) v0 = Base.Vector(fx,fy-0.1,fz) v1 = Base.Vector(fx-0.5,fy-0.1,fz) v2 = Base.Vector(fx-0.5,fy-0.2,fz) v3 = Base.Vector(fx-0.7,fy,fz) v4 = Base.Vector(fx-0.5,fy+0.2,fz) v5 = Base.Vector(fx-0.5,fy+0.1,fz) v6 = Base.Vector(fx,fy+0.1,fz) arrow = fcd.addObject("Part::Feature", fs) arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0])) arrow.ViewObject.DiffuseColor=[(0.0,0.0,0.0)] arrow.ViewObject.RootNode.setName(coin.SbName(fs)) arrow.ViewObject.Selectable = False fx = n / 2.0 fy = 0.2 + n / 2.0 fz = i - (n - 1) / 2.0 fs = "arrowpZ"+str(i) v0 = Base.Vector(fx,fy,fz-0.1) v1 = Base.Vector(fx,fy+0.5,fz-0.1) v2 = Base.Vector(fx,fy+0.5,fz-0.2) v3 = Base.Vector(fx,fy+0.7,fz) v4 = Base.Vector(fx,fy+0.5,fz+0.2) v5 = Base.Vector(fx,fy+0.5,fz+0.1) v6 = Base.Vector(fx,fy,fz+0.1) arrow = fcd.addObject("Part::Feature", fs) arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0])) arrow.ViewObject.DiffuseColor=[(0.0,0.0,0.0)] arrow.ViewObject.RootNode.setName(coin.SbName(fs)) arrow.ViewObject.Selectable = False fx = -(0.2 + n / 2.0) fy = -(n / 2.0) fz = i - (n - 1) / 2.0 fs = "arrowmZ"+str(i) v0 = Base.Vector(fx,fy,fz-0.1) v1 = Base.Vector(fx-0.5,fy,fz-0.1) v2 = Base.Vector(fx-0.5,fy,fz-0.2) v3 = Base.Vector(fx-0.7,fy,fz) v4 = Base.Vector(fx-0.5,fy,fz+0.2) v5 = Base.Vector(fx-0.5,fy,fz+0.1) v6 = Base.Vector(fx,fy,fz+0.1) arrow = fcd.addObject("Part::Feature", fs) arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0])) arrow.ViewObject.DiffuseColor=[(0.0,0.0,0.0)] arrow.ViewObject.RootNode.setName(coin.SbName(fs)) arrow.ViewObject.Selectable = False # This gets FreeCAD's top level SceneGraph (including camera node), # not the document's SceneGraph which hangs off of it sceneGraph = Gui.ActiveDocument.ActiveView.getViewer().getSoEventManager().getSceneGraph() # Viewfit doesn't seem to do the right thing with MultiViews # so we adjust the camera height manually before creating them Gui.ActiveDocument.ActiveView.viewAxonometric() Gui.SendMsgToActiveView("ViewFit") camera = sceneGraph.getChild(2) if camera.getTypeId().getName().__str__() == "OrthographicCamera": camera.height.setValue((2.2 + n / 12.0) * camera.height.getValue()) rotation = camera.orientation.getValue() # will be needed later # This bit of code finds the widget corresponding to the View3DInventor def findView(widget): if widget.metaObject().className().__str__() == "Gui::View3DInventor": return widget else: result = None for child in widget.children(): v = findView(child) if v != None: result = v return result view3DWidget = findView(QtGui.qApp.activeWindow().centralWidget()) # This bit of code creates the buttons at the top of the view window # The buttons are in a frameless window to save screen space height = 40 class ButtonRow(QtGui.QWidget): def __init__(self): super(ButtonRow, self).__init__() self.setParent(view3DWidget) self.setAutoFillBackground(True) xpos = 0 geom = view3DWidget.geometry() self.setGeometry(xpos, 0, geom.width(), height) buttonWidth = 80 gap = geom.width() / 4 - buttonWidth if gap < 0: gap = 0 xpos = gap self.undoButton = QtGui.QPushButton(self) self.undoButton.setGeometry(xpos, 0, buttonWidth, 30) self.undoButton.setText("Undo") self.undoButton.clicked.connect(self.onUndo) xpos = xpos + buttonWidth + gap self.saveButton = QtGui.QPushButton(self) self.saveButton.setGeometry(xpos, 0, 3 * buttonWidth, 30) self.saveButton.setText("Copy history to clipboard") self.saveButton.clicked.connect(self.onSave) xpos = xpos + 3 * buttonWidth + gap self.resetButton = QtGui.QPushButton(self) self.resetButton.setGeometry(xpos, 0, buttonWidth, 30) self.resetButton.setText("Reset") self.resetButton.clicked.connect(self.onReset) self.show() def onUndo(self): undo() def onReset(self): reset() def onSave(self): saveHistory() if view3DWidget != None: Dictionary[str(App.ActiveDocument.Name)+"buttons"] = ButtonRow() # This bit of code disables the default Phong shading # and avoids the face colours appearing to change during rotation if str(sceneGraph.getChild(0).getName()) <> "LightModel": lm=coin.SoLightModel() lm.model.setValue(0) lm.setName("LightModel") sceneGraph.insertChild(lm,0) # This bit of code persuades FreeCAD'a renderer to put # several views of the cube into the same window def MultiViews(parent, child, i): newchild=coin.SoMultipleCopy() newchild.addChild(child) views=coin.SoMFMatrix() views.setNum(8) m1=coin.SbMatrix() m1.makeIdentity() views.set1Value(0,m1) m2=coin.SbMatrix() m2.setTransform( coin.SbVec3f(n,n,0.0), coin.SbRotation(coin.SbVec3f(-0.5,0.5,1),3.14159), coin.SbVec3f(0.5,0.5,0.5)) views.set1Value(1,m2) m3=coin.SbMatrix() m3.setTransform( coin.SbVec3f(-(n*1.3),-(n*1.3),0.0), rotation, coin.SbVec3f(0.5,0.5,0.5)) views.set1Value(2,m3) m4=coin.SbMatrix() m4.setTransform( coin.SbVec3f(0,n,0), coin.SbRotation(coin.SbVec3f(1,0,0),3.14159*90/180.0), coin.SbVec3f(1,1,1)) m4.multRight(m3) views.set1Value(3,m4) m5=coin.SbMatrix() m5.setTransform( coin.SbVec3f(n,0,0), coin.SbRotation(coin.SbVec3f(0,1,0),-3.14159*90/180.0), coin.SbVec3f(1,1,1)) m5.multRight(m3) views.set1Value(4,m5) m6=coin.SbMatrix() m6.makeIdentity() m6.setTransform( coin.SbVec3f(-n,0,0), coin.SbRotation(coin.SbVec3f(0,1,0),3.14159*90/180.0), coin.SbVec3f(1,1,1)) m6.multRight(m3) views.set1Value(5,m6) m7=coin.SbMatrix() m7.setTransform( coin.SbVec3f(0,-n,0), coin.SbRotation(coin.SbVec3f(-1,0,0),3.14159*90/180.0), coin.SbVec3f(1,1,1)) m7.multRight(m3) views.set1Value(6,m7) m8=coin.SbMatrix() m8.setTransform( coin.SbVec3f(0,-n*2,0), coin.SbRotation(coin.SbVec3f(-1,0,0),3.14159), coin.SbVec3f(1,1,1)) m8.multRight(m3) views.set1Value(7,m8) newchild.matrix=views parent.replaceChild(i,newchild) def createMultiViews(): sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph() if sg.getNumChildren() != 0: for i in range(sg.getNumChildren()): child = sg.getChild(i) type = child.getTypeId().getName().__str__() if child.getTypeId().getName().__str__() == 'Separator': if child.getName().__str__()[:5] != "arrow": MultiViews(sg,child,i) if child.getTypeId().getName().__str__() == 'MultipleCopy': if child.getNumChildren() != 0: name = child.getChild(0).getName().__str__() if fcd.getObject(name) == None: child.removeAllChildren() createMultiViews() # Restore the normal cursor now that we have done all the slow stuff QtGui.QApplication.restoreOverrideCursor() # This bit of code animates a slice rotation def slowrotate(dir, num): n = Dictionary[str(App.ActiveDocument.Name)+"Size"] fc = Dictionary[str(App.ActiveDocument.Name)+"cubies"] fp = [[[[Base.Placement() for j in range(6)] for ix in range(n)] for iy in range(n)] for ix in range(n)] fq = [[[[Part.Shape() for j in range(6)] for ix in range(n)] for iy in range(n)] for ix in range(n)] steps = slowness / (n * n) if dir.x > 0: for iy in range(n): for iz in range(n): for j in range(6): c = fc[num][iy][iz][j] fp[num][iy][iz][j] = c.Placement for i in range(steps + 1): fm = Base.Matrix() fm.rotateX((1.570795 * i) / steps) for iy in range(n): for iz in range(n): for j in range(6): c = fc[num][iy][iz][j] p = fp[num][iy][iz][j] c.Placement = Base.Placement(fm).multiply(p) Gui.updateGui() for iy in range(n): for iz in range(n): for j in range(6): fq[num][iy][iz][j]=fc[num][iz][n-1-iy][j] for iy in range(n): for iz in range(n): for j in range(6): fc[num][iy][iz][j]=fq[num][iy][iz][j] elif dir.x < 0: for iy in range(n): for iz in range(n): for j in range(6): c = fc[num][iy][iz][j] fp[num][iy][iz][j]=c.Placement for i in range(steps + 1): fm = Base.Matrix() fm.rotateX((-1.570795 * i) / steps) for iy in range(n): for iz in range(n): for j in range(6): c = fc[num][iy][iz][j] p = fp[num][iy][iz][j] c.Placement = Base.Placement(fm).multiply(p) Gui.updateGui() for iy in range(n): for iz in range(n): for j in range(6): fq[num][iy][iz][j]=fc[num][n-1-iz][iy][j] for iy in range(n): for iz in range(n): for j in range(6): fc[num][iy][iz][j]=fq[num][iy][iz][j] elif dir.y > 0: for ix in range(n): for iz in range(n): for j in range(6): c = fc[ix][num][iz][j] fp[ix][num][iz][j]=c.Placement for i in range(steps + 1): fm = Base.Matrix() fm.rotateY((1.570795 * i) / steps) for ix in range(n): for iz in range(n): for j in range(6): c = fc[ix][num][iz][j] p = fp[ix][num][iz][j] c.Placement = Base.Placement(fm).multiply(p) Gui.updateGui() for ix in range(n): for iz in range(n): for j in range(6): fq[ix][num][iz][j]=fc[n-1-iz][num][ix][j] for ix in range(n): for iz in range(n): for j in range(6): fc[ix][num][iz][j]=fq[ix][num][iz][j] elif dir.y < 0: for ix in range(n): for iz in range(n): for j in range(6): c = fc[ix][num][iz][j] fp[ix][num][iz][j]=c.Placement for i in range(steps + 1): fm = Base.Matrix() fm.rotateY((-1.570795 * i) / steps) for ix in range(n): for iz in range(n): for j in range(6): c = fc[ix][num][iz][j] p = fp[ix][num][iz][j] c.Placement = Base.Placement(fm).multiply(p) Gui.updateGui() for ix in range(n): for iz in range(n): for j in range(6): fq[ix][num][iz][j]=fc[iz][num][n-1-ix][j] for ix in range(n): for iz in range(n): for j in range(6): fc[ix][num][iz][j]=fq[ix][num][iz][j] elif dir.z > 0: for ix in range(n): for iy in range(n): for j in range(6): c = fc[ix][iy][num][j] fp[ix][iy][num][j]=c.Placement for i in range(steps + 1): fm = Base.Matrix() fm.rotateZ((1.570795 * i) / steps) for ix in range(n): for iy in range(n): for j in range(6): c = fc[ix][iy][num][j] p = fp[ix][iy][num][j] c.Placement = Base.Placement(fm).multiply(p) Gui.updateGui() for ix in range(n): for iy in range(n): for j in range(6): fq[ix][iy][num][j]=fc[iy][n-1-ix][num][j] for ix in range(n): for iy in range(n): for j in range(6): fc[ix][iy][num][j]=fq[ix][iy][num][j] elif dir.z < 0: for ix in range(n): for iy in range(n): for j in range(6): c = fc[ix][iy][num][j] fp[ix][iy][num][j]=c.Placement for i in range(steps + 1): fm = Base.Matrix() fm.rotateZ((-1.570795 * i) / steps) for ix in range(n): for iy in range(n): for j in range(6): c = fc[ix][iy][num][j] p = fp[ix][iy][num][j] c.Placement = Base.Placement(fm).multiply(p) Gui.updateGui() for ix in range(n): for iy in range(n): for j in range(6): fq[ix][iy][num][j]=fc[n-1-iy][ix][num][j] for ix in range(n): for iy in range(n): for j in range(6): fc[ix][iy][num][j]=fq[ix][iy][num][j] # This bit of code manages the history # Once you have created a cube, these functions will be defined and in scope # and you can call them from another macro craeted by saving history history = [] Dictionary[str(App.ActiveDocument.Name)+"history"] = history def rotpX(i): slowrotate(Base.Vector(1,0,0),i) Dictionary[str(App.ActiveDocument.Name)+"history"].append("rotpX("+str(i)+")") def rotpY(i): slowrotate(Base.Vector(0,1,0),i) Dictionary[str(App.ActiveDocument.Name)+"history"].append("rotpY("+str(i)+")") def rotpZ(i): slowrotate(Base.Vector(0,0,1),i) Dictionary[str(App.ActiveDocument.Name)+"history"].append("rotpZ("+str(i)+")") def rotmX(i): slowrotate(Base.Vector(-1,0,0),i) Dictionary[str(App.ActiveDocument.Name)+"history"].append("rotmX("+str(i)+")") def rotmY(i): slowrotate(Base.Vector(0,-1,0),i) Dictionary[str(App.ActiveDocument.Name)+"history"].append("rotmY("+str(i)+")") def rotmZ(i): slowrotate(Base.Vector(0,0,-1),i) Dictionary[str(App.ActiveDocument.Name)+"history"].append("rotmZ("+str(i)+")") def undo(): history = Dictionary[str(App.ActiveDocument.Name)+"history"] if len(history) > 0: fs = history.pop() wh = fs[:5] i = int(fs[6:len(fs)-1]) if wh == "rotmX": slowrotate(Base.Vector(1,0,0),i) elif wh == "rotpX": slowrotate(Base.Vector(-1,0,0),i) elif wh == "rotmY": slowrotate(Base.Vector(0,1,0),i) elif wh == "rotpY": slowrotate(Base.Vector(0,-1,0),i) elif wh == "rotmZ": slowrotate(Base.Vector(0,0,1),i) elif wh == "rotpZ": slowrotate(Base.Vector(0,0,-1),i) def saveHistory(): history = Dictionary[str(App.ActiveDocument.Name)+"history"] n = Dictionary[str(App.ActiveDocument.Name)+"Size"] if len(history) > 0: fs = "" while len(history) > 0: s = history.pop(0) i = int(s[6:len(s)-1]) if 2 * i == n - 1: fs = fs + s[:6] + "n/2)\n" elif i <= n / 2: fs = fs + s[:6] + str(i) + ")\n" else: fs = fs + s[:6] + "n-1-" + str(n-1-i) + ")\n" clip = QtCore.QCoreApplication.instance().clipboard() clip.setText(fs) def reset(): fcd = App.ActiveDocument Dictionary[str(fcd.Name)+"history"] = [] n = Dictionary[str(fcd.Name)+"Size"] fc = Dictionary[str(fcd.Name)+"cubies"] for ix in range(n): for iy in range(n): for iz in range(n): fs = "fs"+str(ix)+"q"+str(iy)+"q"+str(iz) for j in range(6): c = fcd.getObject(fs+"x0") c.Placement = Base.Placement() fc[ix][iy][iz][0] = c c = fcd.getObject(fs+"x1") c.Placement = Base.Placement() fc[ix][iy][iz][1] = c c = fcd.getObject(fs+"y0") c.Placement = Base.Placement() fc[ix][iy][iz][2] = c c = fcd.getObject(fs+"y1") c.Placement = Base.Placement() fc[ix][iy][iz][3] = c c = fcd.getObject(fs+"z0") c.Placement = Base.Placement() fc[ix][iy][iz][4] = c c = fcd.getObject(fs+"z1") c.Placement = Base.Placement() fc[ix][iy][iz][5] = c