Macro Rubik Cube: Difference between revisions

From FreeCAD Documentation
(Fix problems when macro is used to create more than one document)
No edit summary
Line 13: Line 13:
{{clear}}
{{clear}}


==Script== <!--T:7-->
<pre>
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
"""
"""
Line 26: Line 24:
* The central and largest view is an axonometric projection. *
* The central and largest view is an axonometric projection. *
* This has arrows around it which you can click on to rotate slices. *
* This has arrows around it which you can click on to rotate slices. *
* There are some text direction labels near the arrows: clicking on one *
* of those rotates the whole cube. You have to click on the actual *
* letter: FreeCAD doesn't see clicks on the text background *
* Another view is an axonometric projection from the other side. *
* Another view is an axonometric projection from the other side. *
* Another view combines views towards each face so as to *
* Another view combines views towards each face so as to *
Line 39: Line 40:
* The third button resets the cube to its initial state *
* The third button resets the cube to its initial state *
* and clears the history. *
* and clears the history. *
* *
* There are also some functions defined which can be called from the *
* python console window: *
* fix_reload() modifies a restored FreeCAD save file to add the extra *
* views and interactive controls that this macro and its functions use; *
* ramdomise() randomises the cube; *
* reverse_history(), reflectX_history(), reflectY_history(), *
* reflectZ_history(), rotpX_history(), rotmX_history(), *
* rotpY_history(), rotmY_history(), rotpZ_history(), and *
* rotmZ_history() each replace the history by a version modified in the *
* manner indicated - these are useful for creating modified operators. *
* *
* *
***************************************************************************
***************************************************************************
Line 65: Line 77:
__title__ = "Rubik_cube"
__title__ = "Rubik_cube"
__author__ = "Aleph0"
__author__ = "Aleph0"
__version__ = "00.03"
__version__ = "00.04"
__date__ = "25/10/2017"
__date__ = "05/12/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 94: Line 106:
import FreeCAD
import FreeCAD
import Part
import Part
import Draft
import time
import time
import random
from FreeCAD import Base
from FreeCAD import Base
from FreeCAD import Console
from FreeCAD import Console
Line 181: Line 195:
if event.getButton() == 1:
if event.getButton() == 1:
pos = event.getPosition().getValue()
pos = event.getPosition().getValue()
obj = self.view.getObjectInfo((int(pos[0]),int(pos[1])))
obInfo = self.view.getObjectInfo((int(pos[0]),int(pos[1])))
if obj != None:
if obInfo != None:
obname = obj["Object"]
obj = App.ActiveDocument.getObject(obInfo["Object"])
obname = obj.Label
if obname[:5] == "arrow":
if obname[:5] == "arrow":
if event.getState() == 1:
if event.getState() == 1:
Line 189: Line 204:
i = int(obname[7:])
i = int(obname[7:])
if wh == "mX":
if wh == "mX":
rotmX(i)
mrotmX(i,i+1)
elif wh == "pX":
elif wh == "pX":
rotpX(i)
mrotpX(i,i+1)
elif wh == "mY":
elif wh == "mY":
rotmY(i)
mrotmY(i,i+1)
elif wh == "pY":
elif wh == "pY":
rotpY(i)
mrotpY(i,i+1)
elif wh == "mZ":
elif wh == "mZ":
rotmZ(i)
mrotmZ(i,i+1)
elif wh == "pZ":
elif wh == "pZ":
rotpZ(i)
mrotpZ(i,i+1)
event_cb.setHandled()
event_cb.setHandled()
elif obname[:5] == "label":
if event.getState() == 1:
wh = obname[5:7]
n = Dictionary[str(App.ActiveDocument.Name)+"Size"]
if wh == "mX":
mrotmX(0,n)
elif wh == "pX":
mrotpX(0,n)
elif wh == "mY":
mrotmY(0,n)
elif wh == "pY":
mrotpY(0,n)
elif wh == "mZ":
mrotmZ(0,n)
elif wh == "pZ":
mrotpZ(0,n)
event_cb.setHandled()

Dictionary[str(App.ActiveDocument.Name)+"ViewObserver"] = ViewObserver()
Dictionary[str(App.ActiveDocument.Name)+"ViewObserver"] = ViewObserver()


Line 206: Line 239:
# 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
# Each face has a name and a label
# The name is persistent regardless of how the face moves about the cube
# The label changes and tells where the face is now: this is needed for fix_reload
fcd = App.ActiveDocument
fcd = App.ActiveDocument
n = Dictionary[str(App.ActiveDocument.Name)+"Size"]
n = Dictionary[str(App.ActiveDocument.Name)+"Size"]
fc = [[range(n) for iy in range(n)] for ix in range(n)]
fc = [[[[None for j in range(6)] for ix in range(n)] for iy in range(n)] for ix in range(n)]
Dictionary[str(App.ActiveDocument.Name)+"cubies"] = fc
Dictionary[str(App.ActiveDocument.Name)+"cubies"] = fc
for ix in range(n):
for ix in range(n):
Line 216: Line 252:
for iz in range(n):
for iz in range(n):
fz = iz - (n - 1) / 2.0
fz = iz - (n - 1) / 2.0
fs = "fs"+str(ix)+"q"+str(iy)+"q"+str(iz)
fs = str(ix)+"q"+str(iy)+"q"+str(iz)
x0y0z0 = Base.Vector(fx-0.5,fy-0.5,fz-0.5)
x0y0z0 = Base.Vector(fx-0.5,fy-0.5,fz-0.5)
x0y0z1 = Base.Vector(fx-0.5,fy-0.5,fz+0.5)
x0y0z1 = Base.Vector(fx-0.5,fy-0.5,fz+0.5)
Line 226: Line 262:
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", "ff"+fs+"x0")
f1.Shape = face
f1.Shape = face
f1.Label = "fs"+fs+"x0"
if ix == 0:
if ix == 0:
f1.ViewObject.DiffuseColor=[(1.0,1.0,1.0)]
f1.ViewObject.DiffuseColor=[(1.0,1.0,1.0)]
else:
else:
f1.ViewObject.DiffuseColor=[(0.0,0.0,0.0)]
f1.ViewObject.DiffuseColor=[(0.0,0.0,0.0)]
f1.ViewObject.RootNode.setName(coin.SbName(fs+"x0"))
f1.ViewObject.RootNode.setName(coin.SbName("ff"+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", "ff"+fs+"x1")
f2.Shape = face
f2.Shape = face
f2.Label = "fs"+fs+"x1"
if ix == n - 1:
if ix == n - 1:
f2.ViewObject.DiffuseColor=[(1.0,0.0,0.0)]
f2.ViewObject.DiffuseColor=[(1.0,0.0,0.0)]
else:
else:
f2.ViewObject.DiffuseColor=[(0.0,0.0,0.0)]
f2.ViewObject.DiffuseColor=[(0.0,0.0,0.0)]
f2.ViewObject.RootNode.setName(coin.SbName(fs+"x1"))
f2.ViewObject.RootNode.setName(coin.SbName("ff"+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", "ff"+fs+"y0")
f3.Shape = face
f3.Shape = face
f3.Label = "fs"+fs+"y0"
if iy == 0:
if iy == 0:
f3.ViewObject.DiffuseColor=[(0.0,1.0,0.0)]
f3.ViewObject.DiffuseColor=[(0.0,1.0,0.0)]
else:
else:
f3.ViewObject.DiffuseColor=[(0.0,0.0,0.0)]
f3.ViewObject.DiffuseColor=[(0.0,0.0,0.0)]
f3.ViewObject.RootNode.setName(coin.SbName(fs+"y0"))
f3.ViewObject.RootNode.setName(coin.SbName("ff"+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", "ff"+fs+"y1")
f4.Shape = face
f4.Shape = face
f4.Label = "fs"+fs+"y1"
if iy == n - 1:
if iy == n - 1:
f4.ViewObject.DiffuseColor=[(1.0,0.0,1.0)]
f4.ViewObject.DiffuseColor=[(1.0,0.0,1.0)]
else:
else:
f4.ViewObject.DiffuseColor=[(0.0,0.0,0.0)]
f4.ViewObject.DiffuseColor=[(0.0,0.0,0.0)]
f4.ViewObject.RootNode.setName(coin.SbName(fs+"y1"))
f4.ViewObject.RootNode.setName(coin.SbName("ff"+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", "ff"+fs+"z0")
f5.Shape = face
f5.Shape = face
f5.Label = "fs"+fs+"z0"
if iz == 0:
if iz == 0:
f5.ViewObject.DiffuseColor=[(1.0,1.0,0.0)]
f5.ViewObject.DiffuseColor=[(1.0,1.0,0.0)]
else:
else:
f5.ViewObject.DiffuseColor=[(0.0,0.0,0.0)]
f5.ViewObject.DiffuseColor=[(0.0,0.0,0.0)]
f5.ViewObject.RootNode.setName(coin.SbName(fs+"z0"))
f5.ViewObject.RootNode.setName(coin.SbName("ff"+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", "ff"+fs+"z1")
f6.Shape = face
f6.Shape = face
f6.Label = "fs"+fs+"z1"
if iz == n - 1:
if iz == n - 1:
f6.ViewObject.DiffuseColor=[(0.0,0.0,1.0)]
f6.ViewObject.DiffuseColor=[(0.0,0.0,1.0)]
else:
else:
f6.ViewObject.DiffuseColor=[(0.0,0.0,0.0)]
f6.ViewObject.DiffuseColor=[(0.0,0.0,0.0)]
f6.ViewObject.RootNode.setName(coin.SbName(fs+"z1"))
f6.ViewObject.RootNode.setName(coin.SbName("ff"+fs+"z1"))
fc[ix][iy][iz]=[f1,f2,f3,f4,f5,f6]
fc[ix][iy][iz]=[f1,f2,f3,f4,f5,f6]


Line 292: Line 334:
arrow = fcd.addObject("Part::Feature", fs)
arrow = fcd.addObject("Part::Feature", fs)
arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0]))
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.DiffuseColor = [(0.0,0.0,0.0)]
arrow.ViewObject.RootNode.setName(coin.SbName(fs))
arrow.ViewObject.RootNode.setName(coin.SbName(fs))
arrow.ViewObject.Selectable = False
arrow.ViewObject.Selectable = False
Line 307: Line 349:
arrow = fcd.addObject("Part::Feature", fs)
arrow = fcd.addObject("Part::Feature", fs)
arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0]))
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.DiffuseColor = [(0.0,0.0,0.0)]
arrow.ViewObject.RootNode.setName(coin.SbName(fs))
arrow.ViewObject.RootNode.setName(coin.SbName(fs))
arrow.ViewObject.Selectable = False
arrow.ViewObject.Selectable = False
Line 323: Line 365:
arrow = fcd.addObject("Part::Feature", fs)
arrow = fcd.addObject("Part::Feature", fs)
arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0]))
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.DiffuseColor = [(0.0,0.0,0.0)]
arrow.ViewObject.RootNode.setName(coin.SbName(fs))
arrow.ViewObject.RootNode.setName(coin.SbName(fs))
arrow.ViewObject.Selectable = False
arrow.ViewObject.Selectable = False
Line 339: Line 381:
arrow = fcd.addObject("Part::Feature", fs)
arrow = fcd.addObject("Part::Feature", fs)
arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0]))
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.DiffuseColor = [(0.0,0.0,0.0)]
arrow.ViewObject.RootNode.setName(coin.SbName(fs))
arrow.ViewObject.RootNode.setName(coin.SbName(fs))
arrow.ViewObject.Selectable = False
arrow.ViewObject.Selectable = False
Line 355: Line 397:
arrow = fcd.addObject("Part::Feature", fs)
arrow = fcd.addObject("Part::Feature", fs)
arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0]))
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.DiffuseColor = [(0.0,0.0,0.0)]
arrow.ViewObject.RootNode.setName(coin.SbName(fs))
arrow.ViewObject.RootNode.setName(coin.SbName(fs))
arrow.ViewObject.Selectable = False
arrow.ViewObject.Selectable = False
Line 371: Line 413:
arrow = fcd.addObject("Part::Feature", fs)
arrow = fcd.addObject("Part::Feature", fs)
arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0]))
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.DiffuseColor = [(0.0,0.0,0.0)]
arrow.ViewObject.RootNode.setName(coin.SbName(fs))
arrow.ViewObject.RootNode.setName(coin.SbName(fs))
arrow.ViewObject.Selectable = False
arrow.ViewObject.Selectable = False


# This bit of code creates some labels for the arrows
# This gets FreeCAD's top level SceneGraph (including camera node),
# Clicking on one rotates the whole cube
# not the document's SceneGraph which hangs off of it
def makeLabel(text, mat):
sceneGraph = Gui.ActiveDocument.ActiveView.getViewer().getSoEventManager().getSceneGraph()
n = Dictionary[str(App.ActiveDocument.Name)+"Size"]
tx = Draft.makeText(text)
tx.Label = "label"+text
tx.ViewObject.TextColor = (0.0,0.0,0.0)
tx.ViewObject.FontSize = 0.2 * n
tx.ViewObject.Justification = 'Center'
node = tx.ViewObject.RootNode
node.setName(coin.SbName("label"+text))
if node.getNumChildren() > 0:
child = node.getChild(0)
if child.getTypeId().getName().__str__() == "Transform":
child.setMatrix(mat)

def doLabels():
n = Dictionary[str(App.ActiveDocument.Name)+"Size"]
m = coin.SbMatrix()
m.makeIdentity()
m.setTransform(
coin.SbVec3f(0.0, - n / 2.0, -(1.0 + n * 0.7)),
coin.SbRotation(coin.SbVec3f(1.0, 0.0, 0.0), 3.14159 / 2.0),
coin.SbVec3f(1.0, 1.0, 1.0))
makeLabel("pX", m)
m = coin.SbMatrix()
m.makeIdentity()
m.setTransform(
coin.SbVec3f(0.0, 1.1 + n / 2.0, n / 2.0),
coin.SbRotation(coin.SbVec3f(0.0, 1.0, 0.0), 0.0),
coin.SbVec3f(1.0, 1.0, 1.0))
makeLabel("mX", m)
m = coin.SbMatrix()
m.makeIdentity()
m.setTransform(
coin.SbVec3f(n / 2.0, 0.0, -(1.0 + n * 0.7)),
coin.SbRotation(coin.SbVec3f(0.0, 0.0, 1.0), 3.14159 / 2.0),
coin.SbVec3f(1.0, 1.0, 1.0))
m1 = coin.SbMatrix()
m1.makeIdentity()
m1.setTransform(
coin.SbVec3f(0.0, 0.0, 0.0),
coin.SbRotation(coin.SbVec3f(1.0, 0.0, 0.0), 3.14159 / 2.0),
coin.SbVec3f(1.0, 1.0, 1.0))
m1.multRight(m)
makeLabel("pY", m1)
m = coin.SbMatrix()
m.makeIdentity()
m.setTransform(
coin.SbVec3f(-1.1 - n / 2.0, 0.0, n / 2.0),
coin.SbRotation(coin.SbVec3f(0.0, 0.0, 1.0), 3.14159 / 2.0),
coin.SbVec3f(1.0, 1.0, 1.0))
makeLabel("mY", m)
m = coin.SbMatrix()
m.makeIdentity()
m.setTransform(
coin.SbVec3f(n / 2.0, 0.7 + n * 0.6, 0.0),
coin.SbRotation(coin.SbVec3f(0.0, 1.0, 0.0), 3.14159 / 2.0),
coin.SbVec3f(1.0, 1.0, 1.0))
makeLabel("pZ", m)
m = coin.SbMatrix()
m.makeIdentity()
m.setTransform(
coin.SbVec3f(-1.1 - n / 2.0, - n / 2.0, 0.0),
coin.SbRotation(coin.SbVec3f(1.0, 0.0, 0.0), 3.14159 / 2.0),
coin.SbVec3f(1.0, 1.0, 1.0))
m1 = coin.SbMatrix()
m1.makeIdentity()
m1.setTransform(
coin.SbVec3f(0.0, 0.0, 0.0),
coin.SbRotation(coin.SbVec3f(0.0, 0.0, 1.0), 3.14159 / 2.0),
coin.SbVec3f(1.0, 1.0, 1.0))
m1.multRight(m)
makeLabel("mZ", m1)
doLabels()


# 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.ActiveDocument.ActiveView.viewAxonometric()
Gui.SendMsgToActiveView("ViewFit")
Gui.SendMsgToActiveView("ViewFit")
# Viewfit doesn't seem to do the right thing with MultiViews
camera = sceneGraph.getChild(2)
# so we adjust the camera height manually before creating them
if camera.getTypeId().getName().__str__() == "OrthographicCamera":
def fixCamera(lift):
camera.height.setValue((2.2 + n / 12.0) * camera.height.getValue())
# This gets FreeCAD's top level SceneGraph (including camera node),
rotation = camera.orientation.getValue() # will be needed later
# not the document's SceneGraph which hangs off of it
v = Gui.ActiveDocument.ActiveView.getViewer()
sceneGraph = v.getSoEventManager().getSceneGraph()
camera = sceneGraph.getChild(2)
if camera.getTypeId().getName().__str__() == "OrthographicCamera":
if lift:
camera.height.setValue((2.0 + n / 20.0) * camera.height.getValue())
return camera.orientation.getValue()
rotation = fixCamera(True)


# This bit of code finds the widget corresponding to the View3DInventor
# This bit of code finds the widget corresponding to the View3DInventor
Line 399: Line 520:
result = v
result = v
return result
return result
view3DWidget = findView(QtGui.qApp.activeWindow().centralWidget())


# 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
Line 407: Line 527:
def __init__(self):
def __init__(self):
super(ButtonRow, self).__init__()
super(ButtonRow, self).__init__()
view3DWidget = findView(QtGui.qApp.activeWindow().centralWidget())
self.setParent(view3DWidget)
if view3DWidget != None:
self.setParent(view3DWidget)
self.setAutoFillBackground(True)
self.setAutoFillBackground(True)
xpos = 0
xpos = 0
Line 438: Line 560:
def onSave(self):
def onSave(self):
saveHistory()
saveHistory()
Dictionary[str(App.ActiveDocument.Name)+"buttons"] = ButtonRow()
if view3DWidget != None:
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
def fixLightModel():
if str(sceneGraph.getChild(0).getName()) <> "LightModel":
v = Gui.ActiveDocument.ActiveView.getViewer()
lm=coin.SoLightModel()
sceneGraph = v.getSoEventManager().getSceneGraph()
lm.model.setValue(0)
lm.setName("LightModel")
if str(sceneGraph.getChild(0).getName()) <> "LightModel":
sceneGraph.insertChild(lm,0)
lm=coin.SoLightModel()
lm.model.setValue(0)
lm.setName("LightModel")
sceneGraph.insertChild(lm,0)
fixLightModel()


# This bit of code persuades FreeCAD'a renderer to put
# This bit of code persuades FreeCAD'a renderer to put
# several views of the cube into the same window
# several views of the cube into the same window
def MultiViews(parent, child, i):
def MultiViews(parent, child, i, rotation):
n = Dictionary[str(App.ActiveDocument.Name)+"Size"]
newchild=coin.SoMultipleCopy()
newchild=coin.SoMultipleCopy()
newchild.addChild(child)
newchild.addChild(child)
Line 461: Line 587:
m2=coin.SbMatrix()
m2=coin.SbMatrix()
m2.setTransform(
m2.setTransform(
coin.SbVec3f(n,n,0.0),
coin.SbVec3f(n * 0.9 + 0.4, n * 0.9 + 0.4, 0.0),
coin.SbRotation(coin.SbVec3f(-0.5,0.5,1),3.14159),
coin.SbRotation(coin.SbVec3f(-0.5,0.5,1),3.14159),
coin.SbVec3f(0.5,0.5,0.5))
coin.SbVec3f(0.5,0.5,0.5))
Line 467: Line 593:
m3=coin.SbMatrix()
m3=coin.SbMatrix()
m3.setTransform(
m3.setTransform(
coin.SbVec3f(-(n*1.3),-(n*1.3),0.0),
coin.SbVec3f(- n * 1.15 - 0.7, - n * 1.15 - 0.7, 0.0),
rotation,
rotation,
coin.SbVec3f(0.5,0.5,0.5))
coin.SbVec3f(0.5,0.5,0.5))
Line 509: Line 635:
newchild.matrix=views
newchild.matrix=views
parent.replaceChild(i,newchild)
parent.replaceChild(i,newchild)
def createMultiViews():
def createMultiViews(rotation):
sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph()
sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph()
if sg.getNumChildren() != 0:
if sg.getNumChildren() != 0:
Line 516: Line 642:
type = child.getTypeId().getName().__str__()
type = child.getTypeId().getName().__str__()
if child.getTypeId().getName().__str__() == 'Separator':
if child.getTypeId().getName().__str__() == 'Separator':
if child.getName().__str__()[:5] != "arrow":
name = child.getName().__str__()[:5]
MultiViews(sg,child,i)
if name != "arrow" and name != "label":
MultiViews(sg,child,i,rotation)
if child.getTypeId().getName().__str__() == 'MultipleCopy':
if child.getTypeId().getName().__str__() == 'MultipleCopy':
if child.getNumChildren() != 0:
if child.getNumChildren() != 0:
Line 523: Line 650:
if fcd.getObject(name) == None:
if fcd.getObject(name) == None:
child.removeAllChildren()
child.removeAllChildren()
createMultiViews()
createMultiViews(rotation)


# Restore the normal cursor now that we have done all the slow stuff
# Restore the normal cursor now that we have done all the slow stuff
Line 529: Line 656:


# This bit of code animates a slice rotation
# This bit of code animates a slice rotation
def slowrotate(dir, num):
def rotate(dir, rs, re, steps):
n = Dictionary[str(App.ActiveDocument.Name)+"Size"]
n = Dictionary[str(App.ActiveDocument.Name)+"Size"]
fc = Dictionary[str(App.ActiveDocument.Name)+"cubies"]
fc = Dictionary[str(App.ActiveDocument.Name)+"cubies"]
fd = [[[[None for j in range(6)] for ix in range(n)] for iy in range(n)] for ix in range(n)]
fp = [[[[Base.Placement() for j in range(6)] for ix in range(n)] for iy in range(n)] for ix in range(n)]
fp = [[[[Base.Placement() for j in range(6)] for ix in range(n)] for iy in range(n)] for ix in range(n)]
xyz = ["x0","x1","y0","y1","z0","z1"]
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:
if dir.x > 0:
# We need an explicit matrix for the final rotation step
# to prevent rounding errors accumulating over time
ff = Base.Matrix(1,0,0,0,0,0,-1,0,0,1,0,0,0,0,0,1)
kk =[0,1,5,4,2,3]
for iy in range(n):
for iy in range(n):
for iz in range(n):
for iz in range(n):
for j in range(6):
for j in range(rs, re):
c = fc[num][iy][iz][j]
for k in range(6):
fp[num][iy][iz][j] = c.Placement
c = fc[j][iy][iz][k]
for i in range(steps + 1):
c.Label = c.Name
fd[j][iy][iz][k] = c
fp[j][iy][iz][k] = c.Placement
if steps > 0:
fm = Base.Matrix()
fm = Base.Matrix()
fm.rotateX((1.570795 * i) / steps)
fm.rotateX(1.570795 / steps)
for iy in range(n):
for i in range(steps):
for iz in range(n):
for iy in range(n):
for j in range(6):
for iz in range(n):
c = fc[num][iy][iz][j]
for j in range(rs, re):
p = fp[num][iy][iz][j]
for k in range(6):
c.Placement = Base.Placement(fm).multiply(p)
c = fc[j][iy][iz][k]
Gui.updateGui()
p = c.Placement
c.Placement = Base.Placement(fm).multiply(p)
for iy in range(n):
for iz in range(n):
Gui.updateGui()
for j in range(6):
fq[num][iy][iz][j]=fc[num][iz][n-1-iy][j]
for iy in range(n):
for iy in range(n):
for iz in range(n):
for iz in range(n):
for j in range(6):
for j in range(rs, re):
fc[num][iy][iz][j]=fq[num][iy][iz][j]
for k in range(6):
c = fd[j][iz][n-1-iy][kk[k]]
p = fp[j][iz][n-1-iy][kk[k]]
c.Label = "fs"+str(j)+"q"+str(iy)+"q"+str(iz)+xyz[k]
c.Placement = Base.Placement(ff).multiply(p)
fc[j][iy][iz][k] = c
elif dir.x < 0:
elif dir.x < 0:
ff = Base.Matrix(1,0,0,0,0,0,1,0,0,-1,0,0,0,0,0,1)
kk =[0,1,4,5,3,2]
for iy in range(n):
for iy in range(n):
for iz in range(n):
for iz in range(n):
for j in range(6):
for j in range(rs, re):
c = fc[num][iy][iz][j]
for k in range(6):
fp[num][iy][iz][j]=c.Placement
c = fc[j][iy][iz][k]
for i in range(steps + 1):
c.Label = c.Name
fd[j][iy][iz][k] = c
fp[j][iy][iz][k] = c.Placement
if steps > 0:
fm = Base.Matrix()
fm = Base.Matrix()
fm.rotateX((-1.570795 * i) / steps)
fm.rotateX(-1.570795 / steps)
for iy in range(n):
for i in range(steps):
for iz in range(n):
for iy in range(n):
for j in range(6):
for iz in range(n):
c = fc[num][iy][iz][j]
for j in range(rs, re):
p = fp[num][iy][iz][j]
for k in range(6):
c.Placement = Base.Placement(fm).multiply(p)
c = fc[j][iy][iz][k]
Gui.updateGui()
p = c.Placement
c.Placement = Base.Placement(fm).multiply(p)
for iy in range(n):
for iz in range(n):
Gui.updateGui()
for j in range(6):
fq[num][iy][iz][j]=fc[num][n-1-iz][iy][j]
for iy in range(n):
for iy in range(n):
for iz in range(n):
for iz in range(n):
for j in range(6):
for j in range(rs, re):
fc[num][iy][iz][j]=fq[num][iy][iz][j]
for k in range(6):
c = fd[j][n-1-iz][iy][kk[k]]
p = fp[j][n-1-iz][iy][kk[k]]
c.Label = "fs"+str(j)+"q"+str(iy)+"q"+str(iz)+xyz[k]
c.Placement = Base.Placement(ff).multiply(p)
fc[j][iy][iz][k] = c
elif dir.y > 0:
elif dir.y > 0:
ff = Base.Matrix(0,0,1,0,0,1,0,0,-1,0,0,0,0,0,0,1)
kk =[4,5,2,3,1,0]
for ix in range(n):
for ix in range(n):
for iz in range(n):
for iz in range(n):
for j in range(6):
for j in range(rs, re):
c = fc[ix][num][iz][j]
for k in range(6):
fp[ix][num][iz][j]=c.Placement
c = fc[ix][j][iz][k]
for i in range(steps + 1):
c.Label = c.Name
fd[ix][j][iz][k] = c
fp[ix][j][iz][k] = c.Placement
if steps > 0:
fm = Base.Matrix()
fm = Base.Matrix()
fm.rotateY((1.570795 * i) / steps)
fm.rotateY(1.570795 / steps)
for ix in range(n):
for i in range(steps):
for iz in range(n):
for ix in range(n):
for j in range(6):
for iz in range(n):
c = fc[ix][num][iz][j]
for j in range(rs, re):
p = fp[ix][num][iz][j]
for k in range(6):
c.Placement = Base.Placement(fm).multiply(p)
c = fc[ix][j][iz][k]
Gui.updateGui()
p = c.Placement
c.Placement = Base.Placement(fm).multiply(p)
Gui.updateGui()
for ix in range(n):
for ix in range(n):
for iz in range(n):
for iz in range(n):
for j in range(6):
for j in range(rs, re):
fq[ix][num][iz][j]=fc[n-1-iz][num][ix][j]
for k in range(6):
for ix in range(n):
c = fd[n-1-iz][j][ix][kk[k]]
for iz in range(n):
p = fp[n-1-iz][j][ix][kk[k]]
for j in range(6):
c.Label = "fs"+str(ix)+"q"+str(j)+"q"+str(iz)+xyz[k]
fc[ix][num][iz][j]=fq[ix][num][iz][j]
c.Placement = Base.Placement(ff).multiply(p)
fc[ix][j][iz][k] = c
elif dir.y < 0:
elif dir.y < 0:
ff = Base.Matrix(0,0,-1,0,0,1,0,0,1,0,0,0,0,0,0,1)
kk =[5,4,2,3,0,1]
for ix in range(n):
for ix in range(n):
for iz in range(n):
for iz in range(n):
for j in range(6):
for j in range(rs, re):
c = fc[ix][num][iz][j]
for k in range(6):
fp[ix][num][iz][j]=c.Placement
c = fc[ix][j][iz][k]
for i in range(steps + 1):
c.Label = c.Name
fd[ix][j][iz][k] = c
fp[ix][j][iz][k] = c.Placement
if steps > 0:
fm = Base.Matrix()
fm = Base.Matrix()
fm.rotateY((-1.570795 * i) / steps)
fm.rotateY(-1.570795 / steps)
for ix in range(n):
for i in range(steps):
for iz in range(n):
for ix in range(n):
for j in range(6):
for iz in range(n):
c = fc[ix][num][iz][j]
for j in range(rs, re):
p = fp[ix][num][iz][j]
for k in range(6):
c.Placement = Base.Placement(fm).multiply(p)
c = fc[ix][j][iz][k]
Gui.updateGui()
p = c.Placement
c.Placement = Base.Placement(fm).multiply(p)
Gui.updateGui()
for ix in range(n):
for ix in range(n):
for iz in range(n):
for iz in range(n):
for j in range(6):
for j in range(rs, re):
fq[ix][num][iz][j]=fc[iz][num][n-1-ix][j]
for k in range(6):
for ix in range(n):
c = fd[iz][j][n-1-ix][kk[k]]
for iz in range(n):
p = fp[iz][j][n-1-ix][kk[k]]
for j in range(6):
c.Label = "fs"+str(ix)+"q"+str(j)+"q"+str(iz)+xyz[k]
fc[ix][num][iz][j]=fq[ix][num][iz][j]
c.Placement = Base.Placement(ff).multiply(p)
fc[ix][j][iz][k] = c
elif dir.z > 0:
elif dir.z > 0:
ff = Base.Matrix(0,-1,0,0,1,0,0,0,0,0,1,0,0,0,0,1)
kk = [3,2,0,1,4,5]
for ix in range(n):
for ix in range(n):
for iy in range(n):
for iy in range(n):
for j in range(6):
for j in range(rs, re):
c = fc[ix][iy][num][j]
for k in range(6):
fp[ix][iy][num][j]=c.Placement
c = fc[ix][iy][j][k]
for i in range(steps + 1):
c.Label = c.Name
fd[ix][iy][j][k] = c
fp[ix][iy][j][k] = c.Placement
if steps > 0:
fm = Base.Matrix()
fm = Base.Matrix()
fm.rotateZ((1.570795 * i) / steps)
fm.rotateZ(1.570795 / steps)
for ix in range(n):
for i in range(steps):
for iy in range(n):
for ix in range(n):
for j in range(6):
for iy in range(n):
c = fc[ix][iy][num][j]
for j in range(rs, re):
p = fp[ix][iy][num][j]
for k in range(6):
c.Placement = Base.Placement(fm).multiply(p)
c = fc[ix][iy][j][k]
Gui.updateGui()
p = c.Placement
c.Placement = Base.Placement(fm).multiply(p)
for ix in range(n):
for iy in range(n):
Gui.updateGui()
for j in range(6):
fq[ix][iy][num][j]=fc[iy][n-1-ix][num][j]
for ix in range(n):
for ix in range(n):
for iy in range(n):
for iy in range(n):
for j in range(6):
for j in range(rs, re):
fc[ix][iy][num][j]=fq[ix][iy][num][j]
for k in range(6):
c = fd[iy][n-1-ix][j][kk[k]]
p = fp[iy][n-1-ix][j][kk[k]]
c.Label = "fs"+str(ix)+"q"+str(iy)+"q"+str(j)+xyz[k]
c.Placement = Base.Placement(ff).multiply(p)
fc[ix][iy][j][k] = c
elif dir.z < 0:
elif dir.z < 0:
ff = Base.Matrix(0,1,0,0,-1,0,0,0,0,0,1,0,0,0,0,1)
kk = [2,3,1,0,5,4]
for ix in range(n):
for ix in range(n):
for iy in range(n):
for iy in range(n):
for j in range(6):
for j in range(rs, re):
c = fc[ix][iy][num][j]
for k in range(6):
fp[ix][iy][num][j]=c.Placement
c = fc[ix][iy][j][k]
for i in range(steps + 1):
c.Label = c.Name
fd[ix][iy][j][k] = c
fp[ix][iy][j][k] = c.Placement
if steps > 0:
fm = Base.Matrix()
fm = Base.Matrix()
fm.rotateZ((-1.570795 * i) / steps)
fm.rotateZ(-1.570795 / steps)
for ix in range(n):
for i in range(steps):
for iy in range(n):
for ix in range(n):
for j in range(6):
for iy in range(n):
c = fc[ix][iy][num][j]
for j in range(rs, re):
p = fp[ix][iy][num][j]
for k in range(6):
c.Placement = Base.Placement(fm).multiply(p)
c = fc[ix][iy][j][k]
Gui.updateGui()
p = c.Placement
c.Placement = Base.Placement(fm).multiply(p)
for ix in range(n):
for iy in range(n):
Gui.updateGui()
for j in range(6):
fq[ix][iy][num][j]=fc[n-1-iy][ix][num][j]
for ix in range(n):
for ix in range(n):
for iy in range(n):
for iy in range(n):
for j in range(6):
for j in range(rs, re):
fc[ix][iy][num][j]=fq[ix][iy][num][j]
for k in range(6):
c = fd[n-1-iy][ix][j][kk[k]]
p = fp[n-1-iy][ix][j][kk[k]]
c.Label = "fs"+str(ix)+"q"+str(iy)+"q"+str(j)+xyz[k]
c.Placement = Base.Placement(ff).multiply(p)
fc[ix][iy][j][k] = c


def slowrotate(dir, rs, re):
# This bit of code manages the history
n = Dictionary[str(App.ActiveDocument.Name)+"Size"]
steps = slowness / (n * n)
if re > rs:
rotate(dir, rs, re, steps / (re - rs))
Gui.updateGui()

# Quick rotation for use when randomising or modifying history
def fastrotate(dir, rs, re):
rotate(dir, rs, re, 0)

# These functions manage the history
# 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 created by saving history
# or by hand from the python console window
history = []
history = []
Dictionary[str(App.ActiveDocument.Name)+"history"] = history
Dictionary[str(App.ActiveDocument.Name)+"history"] = history
def rotpX(i):
def mrotpX(i,j):
slowrotate(Base.Vector(1,0,0),i)
slowrotate(Base.Vector(1,0,0),i,j)
Dictionary[str(App.ActiveDocument.Name)+"history"].append("rotpX("+str(i)+")")
Dictionary[str(App.ActiveDocument.Name)+"history"].append("mrotpX("+str(i)+","+str(j)+")")
def rotpY(i):
def mrotpY(i,j):
slowrotate(Base.Vector(0,1,0),i)
slowrotate(Base.Vector(0,1,0),i,j)
Dictionary[str(App.ActiveDocument.Name)+"history"].append("rotpY("+str(i)+")")
Dictionary[str(App.ActiveDocument.Name)+"history"].append("mrotpY("+str(i)+","+str(j)+")")
def rotpZ(i):
def mrotpZ(i,j):
slowrotate(Base.Vector(0,0,1),i)
slowrotate(Base.Vector(0,0,1),i,j)
Dictionary[str(App.ActiveDocument.Name)+"history"].append("rotpZ("+str(i)+")")
Dictionary[str(App.ActiveDocument.Name)+"history"].append("mrotpZ("+str(i)+","+str(j)+")")
def rotmX(i):
def mrotmX(i,j):
slowrotate(Base.Vector(-1,0,0),i)
slowrotate(Base.Vector(-1,0,0),i,j)
Dictionary[str(App.ActiveDocument.Name)+"history"].append("rotmX("+str(i)+")")
Dictionary[str(App.ActiveDocument.Name)+"history"].append("mrotmX("+str(i)+","+str(j)+")")
def rotmY(i):
def mrotmY(i,j):
slowrotate(Base.Vector(0,-1,0),i)
slowrotate(Base.Vector(0,-1,0),i,j)
Dictionary[str(App.ActiveDocument.Name)+"history"].append("rotmY("+str(i)+")")
Dictionary[str(App.ActiveDocument.Name)+"history"].append("mrotmY("+str(i)+","+str(j)+")")
def rotmZ(i):
def mrotmZ(i,j):
slowrotate(Base.Vector(0,0,-1),i)
slowrotate(Base.Vector(0,0,-1),i,j)
Dictionary[str(App.ActiveDocument.Name)+"history"].append("rotmZ("+str(i)+")")
Dictionary[str(App.ActiveDocument.Name)+"history"].append("mrotmZ("+str(i)+","+str(j)+")")
def undo():
def undo():
history = Dictionary[str(App.ActiveDocument.Name)+"history"]
history = Dictionary[str(App.ActiveDocument.Name)+"history"]
if len(history) > 0:
if len(history) > 0:
fs = history.pop()
fs = history.pop()
wh = fs[:5]
wh = fs[:6]
i = int(fs[6:len(fs)-1])
p = fs.index(",")
if wh == "rotmX":
i = int(fs[7:p])
slowrotate(Base.Vector(1,0,0),i)
j = int(fs[(p+1):(len(fs)-1)])
elif wh == "rotpX":
if wh == "mrotmX":
slowrotate(Base.Vector(-1,0,0),i)
slowrotate(Base.Vector(1,0,0),i,j)
elif wh == "rotmY":
elif wh == "mrotpX":
slowrotate(Base.Vector(0,1,0),i)
slowrotate(Base.Vector(-1,0,0),i,j)
elif wh == "rotpY":
elif wh == "mrotmY":
slowrotate(Base.Vector(0,-1,0),i)
slowrotate(Base.Vector(0,1,0),i,j)
elif wh == "rotmZ":
elif wh == "mrotpY":
slowrotate(Base.Vector(0,0,1),i)
slowrotate(Base.Vector(0,-1,0),i,j)
elif wh == "rotpZ":
elif wh == "mrotmZ":
slowrotate(Base.Vector(0,0,-1),i)
slowrotate(Base.Vector(0,0,1),i,j)
elif wh == "mrotpZ":
slowrotate(Base.Vector(0,0,-1),i,j)
def itostring(i, n):
if 2 * i == n - 1:
return "n/2"
elif i <= n / 2:
return str(i)
elif i == n - 1:
return "n-1"
else:
return "n-" + str(n-i)
def jtostring(j, n):
if j <= n / 2:
return str(j)
elif 2 * j == n + 1:
return "n/2+1"
elif j == n:
return "n"
elif j == n - 1:
return "n-1"
else:
return "n-" + str(n-j)
def saveHistory():
def saveHistory():
history = Dictionary[str(App.ActiveDocument.Name)+"history"]
fcd = App.ActiveDocument
n = Dictionary[str(App.ActiveDocument.Name)+"Size"]
history = Dictionary[str(fcd.Name)+"history"]
n = Dictionary[str(fcd.Name)+"Size"]
if len(history) > 0:
if len(history) > 0:
# This statement will be needed at the start of any macro
fs = ""
# to get the size of the cube in the currently active document
while len(history) > 0:
s = history.pop(0)
# in case we have several documents open at once
i = int(s[6:len(s)-1])
fs = "n = Dictionary[str(App.ActiveDocument.Name)+'Size']\n"
if 2 * i == n - 1:
for s in history:
fs = fs + s[:6] + "n/2)\n"
p = s.index(",")
elif i <= n / 2:
i = int(s[7:p])
fs = fs + s[:6] + str(i) + ")\n"
j = int(s[(p+1):(len(s)-1)])
else:
fs = fs + s[:7] + itostring(i, n) + ","
fs = fs + s[:6] + "n-1-" + str(n-1-i) + ")\n"
fs = fs + jtostring(j, n) + ")\n"
clip = QtCore.QCoreApplication.instance().clipboard()
clip = QtCore.QCoreApplication.instance().clipboard()
clip.setText(fs)
clip.setText(fs)
def reset():
def reset():
QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
fcd = App.ActiveDocument
fcd = App.ActiveDocument
Dictionary[str(fcd.Name)+"history"] = []
Dictionary[str(fcd.Name)+"history"] = []
n = Dictionary[str(fcd.Name)+"Size"]
n = Dictionary[str(fcd.Name)+"Size"]
fc = Dictionary[str(fcd.Name)+"cubies"]
fc = Dictionary[str(fcd.Name)+"cubies"]
for ix in range(n):
for obj in fcd.Objects:
for iy in range(n):
fs = obj.Name
for iz in range(n):
if fs[0:2] == "ff":
fs = "fs"+str(ix)+"q"+str(iy)+"q"+str(iz)
obj.Label = fs
for j in range(6):
for obj in fcd.Objects:
c = fcd.getObject(fs+"x0")
fs = obj.Name
c.Placement = Base.Placement()
if fs[0:2] == "ff":
fc[ix][iy][iz][0] = c
obj.Label = "fs"+fs[2:]
c = fcd.getObject(fs+"x1")
obj.Placement = Base.Placement()
c.Placement = Base.Placement()
l = len(fs)
fc[ix][iy][iz][1] = c
q1 = fs.find("q")
c = fcd.getObject(fs+"y0")
q2 = fs.find("q",q1+1)
c.Placement = Base.Placement()
ix = int(fs[2:q1])
fc[ix][iy][iz][2] = c
iy = int(fs[q1+1:q2])
c = fcd.getObject(fs+"y1")
iz = int(fs[q2+1:l-2])
c.Placement = Base.Placement()
k = "x0x1y0y1z0z1".find(fs[l-2:])/2
fc[ix][iy][iz][3] = c
fc[ix][iy][iz][k] = obj
QtGui.QApplication.restoreOverrideCursor()
c = fcd.getObject(fs+"z0")

c.Placement = Base.Placement()
# Randomise the cube
fc[ix][iy][iz][4] = c
def randomise():
c = fcd.getObject(fs+"z1")
QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
c.Placement = Base.Placement()
fcd = App.ActiveDocument
fc[ix][iy][iz][5] = c
history = Dictionary[str(fcd.Name)+"history"]
</pre>
n = Dictionary[str(fcd.Name)+"Size"]
random.seed()
i = random.randrange(0,6)
for x in range(n*24):
i = (i + 2 - i % 2 +random.randrange(0,4)) % 6
j = random.randrange(0,n)
if i == 0:
fastrotate(Base.Vector(1,0,0),j,j+1)
history.append("mrotpX("+str(j)+","+str(j+1)+")")
elif i == 1:
fastrotate(Base.Vector(-1,0,0),j,j+1)
history.append("mrotmX("+str(j)+","+str(j+1)+")")
elif i == 2:
fastrotate(Base.Vector(0,1,0),j,j+1)
history.append("mrotpY("+str(j)+","+str(j+1)+")")
elif i == 3:
fastrotate(Base.Vector(0,-1,0),j,j+1)
history.append("mrotmY("+str(j)+","+str(j+1)+")")
elif i == 4:
fastrotate(Base.Vector(0,0,1),j,j+1)
history.append("mrotpZ("+str(j)+","+str(j+1)+")")
elif i == 5:
fastrotate(Base.Vector(0,0,-1),j,j+1)
history.append("mrotmZ("+str(j)+","+str(j+1)+")")
QtGui.QApplication.restoreOverrideCursor()

# Various macros to modify the history
# They all read the history and undo it, and then perform some modified version
# of the history. The modifications can be time-reversal, reflection along an
# axis (as if the cube were reflected, the history replayed,
# and the cube reflected back again), or rotation (as if the cube were rotated,
# the history replayed, and the cube rotated back again).
def slow_history():
QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
fcd = App.ActiveDocument
history = Dictionary[str(fcd.Name)+"history"]
n = Dictionary[str(fcd.Name)+"Size"]
nh = []
while len(history) > 0:
nh.append(history.pop())
reset()
Gui.updateGui()
while len(nh) > 0:
fs = nh.pop()
wh = fs[:6]
p = fs.index(",")
i = int(fs[7:p])
j = int(fs[p+1:len(fs)-1])
if wh == "mrotpX":
mrotpX(i,j)
elif wh == "mrotmX":
mrotmX(i,j)
elif wh == "mrotpY":
mrotpY(i,j)
elif wh == "mrotmY":
mrotmY(i,j)
elif wh == "mrotpZ":
mrotpZ(i,j)
elif wh == "mrotmZ":
mrotmZ(i,j)
QtGui.QApplication.restoreOverrideCursor()
def reverse_history():
QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
fcd = App.ActiveDocument
history = Dictionary[str(fcd.Name)+"history"]
n = Dictionary[str(fcd.Name)+"Size"]
nh = []
while len(history) > 0:
nh.append(history.pop())
reset()
history = Dictionary[str(fcd.Name)+"history"]
for fs in nh:
wh = fs[:6]
p = fs.index(",")
i = int(fs[7:p])
j = int(fs[p+1:len(fs)-1])
if wh == "mrotpX":
fastrotate(Base.Vector(1,0,0),i,j)
history.append("mrotpX("+str(i)+","+str(j)+")")
elif wh == "mrotmX":
fastrotate(Base.Vector(-1,0,0),i,j)
history.append("mrotmX("+str(i)+","+str(j)+")")
elif wh == "mrotpY":
fastrotate(Base.Vector(0,1,0),i,j)
history.append("mrotpY("+str(i)+","+str(j)+")")
elif wh == "mrotmY":
fastrotate(Base.Vector(0,-1,0),i,j)
history.append("mrotmY("+str(i)+","+str(j)+")")
elif wh == "mrotpZ":
fastrotate(Base.Vector(0,0,1),i,j)
history.append("mrotpZ("+str(i)+","+str(j)+")")
elif wh == "mrotmZ":
fastrotate(Base.Vector(0,0,-1),i,j)
history.append("mrotmZ("+str(i)+","+str(j)+")")
QtGui.QApplication.restoreOverrideCursor()
def undo_history():
QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
fcd = App.ActiveDocument
history = Dictionary[str(fcd.Name)+"history"]
n = Dictionary[str(fcd.Name)+"Size"]
nh = []
while len(history) > 0:
nh.append(history.pop())
reset()
history = Dictionary[str(fcd.Name)+"history"]
for fs in nh:
wh = fs[:6]
p = fs.index(",")
i = int(fs[7:p])
j = int(fs[p+1:len(fs)-1])
if wh == "mrotpX":
fastrotate(Base.Vector(-1,0,0),i,j)
history.append("mrotmX("+str(i)+","+str(j)+")")
elif wh == "mrotmX":
fastrotate(Base.Vector(1,0,0),i,j)
history.append("mrotpX("+str(i)+","+str(j)+")")
elif wh == "mrotpY":
fastrotate(Base.Vector(0,-1,0),i,j)
history.append("mrotmY("+str(i)+","+str(j)+")")
elif wh == "mrotmY":
fastrotate(Base.Vector(0,1,0),i,j)
history.append("mrotpY("+str(i)+","+str(j)+")")
elif wh == "mrotpZ":
fastrotate(Base.Vector(0,0,-1),i,j)
history.append("mrotmZ("+str(i)+","+str(j)+")")
elif wh == "mrotmZ":
fastrotate(Base.Vector(0,0,1),i,j)
history.append("mrotpZ("+str(i)+","+str(j)+")")
QtGui.QApplication.restoreOverrideCursor()
def reflectX_history():
QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
fcd = App.ActiveDocument
history = Dictionary[str(fcd.Name)+"history"]
n = Dictionary[str(fcd.Name)+"Size"]
nh = []
while len(history) > 0:
nh.append(history.pop())
reset()
history = Dictionary[str(fcd.Name)+"history"]
while len(nh) > 0:
fs = nh.pop()
wh = fs[:6]
p = fs.index(",")
i = int(fs[7:p])
j = int(fs[p+1:len(fs)-1])
if wh == "mrotpX":
fastrotate(Base.Vector(1,0,0),n-j,n-i)
history.append("mrotpX("+str(n-j)+","+str(n-i)+")")
elif wh == "mrotmX":
fastrotate(Base.Vector(-1,0,0),n-j,n-i)
history.append("mrotmX("+str(n-j)+","+str(n-i)+")")
elif wh == "mrotpY":
fastrotate(Base.Vector(0,-1,0),i,j)
history.append("mrotmY("+str(i)+","+str(j)+")")
elif wh == "mrotmY":
fastrotate(Base.Vector(0,1,0),i,j)
history.append("mrotpY("+str(i)+","+str(j)+")")
elif wh == "mrotpZ":
fastrotate(Base.Vector(0,0,-1),i,j)
history.append("mrotmZ("+str(i)+","+str(j)+")")
elif wh == "mrotmZ":
fastrotate(Base.Vector(0,0,1),i,j)
history.append("mrotpZ("+str(i)+","+str(j)+")")
QtGui.QApplication.restoreOverrideCursor()
def reflectY_history():
QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
fcd = App.ActiveDocument
history = Dictionary[str(fcd.Name)+"history"]
n = Dictionary[str(fcd.Name)+"Size"]
nh = []
while len(history) > 0:
nh.append(history.pop())
reset()
history = Dictionary[str(fcd.Name)+"history"]
while len(nh) > 0:
fs = nh.pop()
wh = fs[:6]
p = fs.index(",")
i = int(fs[7:p])
j = int(fs[p+1:len(fs)-1])
if wh == "mrotpX":
fastrotate(Base.Vector(-1,0,0),i,j)
history.append("mrotmX("+str(i)+","+str(j)+")")
elif wh == "mrotmX":
fastrotate(Base.Vector(1,0,0),i,j)
history.append("mrotpX("+str(i)+","+str(j)+")")
elif wh == "mrotpY":
fastrotate(Base.Vector(0,1,0),n-j,n-i)
history.append("mrotpY("+str(n-j)+","+str(n-i)+")")
elif wh == "mrotmY":
fastrotate(Base.Vector(0,-1,0),n-j,n-i)
history.append("mrotmY("+str(n-j)+","+str(n-i)+")")
elif wh == "mrotpZ":
fastrotate(Base.Vector(0,0,-1),i,j)
history.append("mrotmZ("+str(i)+","+str(j)+")")
elif wh == "mrotmZ":
fastrotate(Base.Vector(0,0,1),i,j)
history.append("mrotpZ("+str(i)+","+str(j)+")")
QtGui.QApplication.restoreOverrideCursor()
def reflectZ_history():
QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
fcd = App.ActiveDocument
history = Dictionary[str(fcd.Name)+"history"]
n = Dictionary[str(fcd.Name)+"Size"]
nh = []
while len(history) > 0:
nh.append(history.pop())
reset()
history = Dictionary[str(fcd.Name)+"history"]
while len(nh) > 0:
fs = nh.pop()
wh = fs[:6]
p = fs.index(",")
i = int(fs[7:p])
j = int(fs[p+1:len(fs)-1])
if wh == "mrotpX":
fastrotate(Base.Vector(-1,0,0),i,j)
history.append("mrotmX("+str(i)+","+str(j)+")")
elif wh == "mrotmX":
fastrotate(Base.Vector(1,0,0),i,j)
history.append("mrotpX("+str(i)+","+str(j)+")")
elif wh == "mrotpY":
fastrotate(Base.Vector(0,-1,0),i,j)
history.append("mrotmY("+str(i)+","+str(j)+")")
elif wh == "mrotmY":
fastrotate(Base.Vector(0,1,0),i,j)
history.append("mrotpY("+str(i)+","+str(j)+")")
elif wh == "mrotpZ":
fastrotate(Base.Vector(0,0,1),n-j,n-i)
history.append("mrotpZ("+str(n-j)+","+str(n-i)+")")
elif wh == "mrotmZ":
fastrotate(Base.Vector(0,0,-1),n-j,n-i)
history.append("mrotmZ("+str(n-j)+","+str(n-i)+")")
QtGui.QApplication.restoreOverrideCursor()
def rotpX_history():
QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
fcd = App.ActiveDocument
history = Dictionary[str(fcd.Name)+"history"]
n = Dictionary[str(fcd.Name)+"Size"]
nh = []
while len(history) > 0:
nh.append(history.pop())
reset()
history = Dictionary[str(fcd.Name)+"history"]
while len(nh) > 0:
fs = nh.pop()
wh = fs[:6]
p = fs.index(",")
i = int(fs[7:p])
j = int(fs[p+1:len(fs)-1])
if wh == "mrotpX":
fastrotate(Base.Vector(1,0,0),i,j)
history.append("mrotpX("+str(i)+","+str(j)+")")
elif wh == "mrotmX":
fastrotate(Base.Vector(-1,0,0),i,j)
history.append("mrotmX("+str(i)+","+str(j)+")")
elif wh == "mrotpY":
fastrotate(Base.Vector(0,0,1),i,j)
history.append("mrotpZ("+str(i)+","+str(j)+")")
elif wh == "mrotmY":
fastrotate(Base.Vector(0,0,-1),i,j)
history.append("mrotmZ("+str(i)+","+str(j)+")")
elif wh == "mrotpZ":
fastrotate(Base.Vector(0,-1,0),n-j,n-i)
history.append("mrotmY("+str(n-j)+","+str(n-i)+")")
elif wh == "mrotmZ":
fastrotate(Base.Vector(0,1,0),n-j,n-i)
history.append("mrotpY("+str(n-j)+","+str(n-i)+")")
QtGui.QApplication.restoreOverrideCursor()
def rotmX_history():
QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
fcd = App.ActiveDocument
history = Dictionary[str(fcd.Name)+"history"]
n = Dictionary[str(fcd.Name)+"Size"]
nh = []
while len(history) > 0:
nh.append(history.pop())
reset()
history = Dictionary[str(fcd.Name)+"history"]
while len(nh) > 0:
fs = nh.pop()
wh = fs[:6]
p = fs.index(",")
i = int(fs[7:p])
j = int(fs[p+1:len(fs)-1])
if wh == "mrotpX":
fastrotate(Base.Vector(1,0,0),i,j)
history.append("mrotpX("+str(i)+","+str(j)+")")
elif wh == "mrotmX":
fastrotate(Base.Vector(-1,0,0),i,j)
history.append("mrotmX("+str(i)+","+str(j)+")")
elif wh == "mrotpY":
fastrotate(Base.Vector(0,0,-1),n-j,n-i)
history.append("mrotmZ("+str(n-j)+","+str(n-i)+")")
elif wh == "mrotmY":
fastrotate(Base.Vector(0,0,1),n-j,n-i)
history.append("mrotpZ("+str(n-j)+","+str(n-i)+")")
elif wh == "mrotpZ":
fastrotate(Base.Vector(0,1,0),i,j)
history.append("mrotpY("+str(i)+","+str(j)+")")
elif wh == "mrotmZ":
fastrotate(Base.Vector(0,-1,0),i,j)
history.append("mrotmY("+str(i)+","+str(j)+")")
QtGui.QApplication.restoreOverrideCursor()
def rotpY_history():
QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
fcd = App.ActiveDocument
history = Dictionary[str(fcd.Name)+"history"]
n = Dictionary[str(fcd.Name)+"Size"]
nh = []
while len(history) > 0:
nh.append(history.pop())
reset()
history = Dictionary[str(fcd.Name)+"history"]
while len(nh) > 0:
fs = nh.pop()
wh = fs[:6]
p = fs.index(",")
i = int(fs[7:p])
j = int(fs[p+1:len(fs)-1])
if wh == "mrotpX":
fastrotate(Base.Vector(0,0,-1),n-j,n-i)
history.append("mrotmZ("+str(n-j)+","+str(n-i)+")")
elif wh == "mrotmX":
fastrotate(Base.Vector(0,0,1),n-j,n-i)
history.append("mrotpZ("+str(n-j)+","+str(n-i)+")")
elif wh == "mrotpY":
fastrotate(Base.Vector(0,1,0),i,j)
history.append("mrotpY("+str(i)+","+str(j)+")")
elif wh == "mrotmY":
fastrotate(Base.Vector(0,-1,0),i,j)
history.append("mrotmY("+str(i)+","+str(j)+")")
elif wh == "mrotpZ":
fastrotate(Base.Vector(1,0,0),i,j)
history.append("mrotpX("+str(i)+","+str(j)+")")
elif wh == "mrotmZ":
fastrotate(Base.Vector(-1,0,0),i,j)
history.append("mrotmX("+str(i)+","+str(j)+")")
QtGui.QApplication.restoreOverrideCursor()
def rotmY_history():
QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
fcd = App.ActiveDocument
history = Dictionary[str(fcd.Name)+"history"]
n = Dictionary[str(fcd.Name)+"Size"]
nh = []
while len(history) > 0:
nh.append(history.pop())
reset()
history = Dictionary[str(fcd.Name)+"history"]
while len(nh) > 0:
fs = nh.pop()
wh = fs[:6]
p = fs.index(",")
i = int(fs[7:p])
j = int(fs[p+1:len(fs)-1])
if wh == "mrotpX":
fastrotate(Base.Vector(0,0,1),i,j)
history.append("mrotpZ("+str(i)+","+str(j)+")")
elif wh == "mrotmX":
fastrotate(Base.Vector(0,0,-1),i,j)
history.append("mrotmZ("+str(i)+","+str(j)+")")
elif wh == "mrotpY":
fastrotate(Base.Vector(0,1,0),i,j)
history.append("mrotpY("+str(i)+","+str(j)+")")
elif wh == "mrotmY":
fastrotate(Base.Vector(0,-1,0),i,j)
history.append("mrotmY("+str(i)+","+str(j)+")")
elif wh == "mrotpZ":
fastrotate(Base.Vector(-1,0,0),n-j,n-i)
history.append("mrotmX("+str(n-j)+","+str(n-i)+")")
elif wh == "mrotmZ":
fastrotate(Base.Vector(1,0,0),n-j,n-i)
history.append("mrotpX("+str(n-j)+","+str(n-i)+")")
QtGui.QApplication.restoreOverrideCursor()
def rotpZ_history():
QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
fcd = App.ActiveDocument
history = Dictionary[str(fcd.Name)+"history"]
n = Dictionary[str(fcd.Name)+"Size"]
nh = []
while len(history) > 0:
nh.append(history.pop())
reset()
history = Dictionary[str(fcd.Name)+"history"]
while len(nh) > 0:
fs = nh.pop()
wh = fs[:6]
p = fs.index(",")
i = int(fs[7:p])
j = int(fs[p+1:len(fs)-1])
if wh == "mrotpX":
fastrotate(Base.Vector(0,1,0),i,j)
history.append("mrotpY("+str(i)+","+str(j)+")")
elif wh == "mrotmX":
fastrotate(Base.Vector(0,-1,0),i,j)
history.append("mrotmY("+str(i)+","+str(j)+")")
elif wh == "mrotpY":
fastrotate(Base.Vector(-1,0,0),n-j,n-i)
history.append("mrotmX("+str(n-j)+","+str(n-i)+")")
elif wh == "mrotmY":
fastrotate(Base.Vector(1,0,0),n-j,n-i)
history.append("mrotpX("+str(n-j)+","+str(n-i)+")")
elif wh == "mrotpZ":
fastrotate(Base.Vector(0,0,1),i,j)
history.append("mrotpZ("+str(i)+","+str(j)+")")
elif wh == "mrotmZ":
fastrotate(Base.Vector(0,0,-1),i,j)
history.append("mrotmZ("+str(i)+","+str(j)+")")
QtGui.QApplication.restoreOverrideCursor()
def rotmZ_history():
QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
fcd = App.ActiveDocument
history = Dictionary[str(fcd.Name)+"history"]
n = Dictionary[str(fcd.Name)+"Size"]
nh = []
while len(history) > 0:
nh.append(history.pop())
reset()
history = Dictionary[str(fcd.Name)+"history"]
while len(nh) > 0:
fs = nh.pop()
wh = fs[:6]
p = fs.index(",")
i = int(fs[7:p])
j = int(fs[p+1:len(fs)-1])
if wh == "mrotpX":
fastrotate(Base.Vector(0,-1,0),n-j,n-i)
history.append("mrotmY("+str(n-j)+","+str(n-i)+")")
elif wh == "mrotmX":
fastrotate(Base.Vector(0,1,0),n-j,n-i)
history.append("mrotpY("+str(n-j)+","+str(n-i)+")")
elif wh == "mrotpY":
fastrotate(Base.Vector(1,0,0),i,j)
history.append("mrotpX("+str(i)+","+str(j)+")")
elif wh == "mrotmY":
fastrotate(Base.Vector(-1,0,0),i,j)
history.append("mrotmX("+str(i)+","+str(j)+")")
elif wh == "mrotpZ":
fastrotate(Base.Vector(0,0,1),i,j)
history.append("mrotpZ("+str(i)+","+str(j)+")")
elif wh == "mrotmZ":
fastrotate(Base.Vector(0,0,-1),i,j)
history.append("mrotmZ("+str(i)+","+str(j)+")")
QtGui.QApplication.restoreOverrideCursor()

# FreeCAD's file save only saves the state of the camera
# and the shapes, positions, orientations, names, and labels
# of the objects in the scene
# This function recreates all the other stuff after reloading a saved file
# I haven't found a way to make FreeCAD save the history, so it gets reset
def fix_reload():
QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
fcd = App.ActiveDocument
# first we find how big a cube we have
n = 0
for obj in fcd.Objects:
fs = obj.Name
if fs[0:2] == "ff":
i = int(fs[2:fs.index("q")])
if i > n:
n = i
elif fs[0:4] == "Text":
# get rid of this because it's incorrectly positioned
# we'll create a new one later
fcd.removeObject(fs)
elif fs[0:5] == "arrow":
obj.ViewObject.RootNode.setName(coin.SbName(fs))
n = n + 1
fc = [[[[None for j in range(6)] for ix in range(n)] for iy in range(n)] for ix in range(n)]
Dictionary[str(fcd.Name)+"Size"] = n
for obj in fcd.Objects:
fs = obj.Label
if fs[0:2] == "fs":
l = len(fs)
q1 = fs.find("q")
q2 = fs.find("q",q1+1)
ix = int(fs[2:q1])
iy = int(fs[q1+1:q2])
iz = int(fs[q2+1:l-2])
k = "x0x1y0y1z0z1".find(fs[l-2:])/2
fc[ix][iy][iz][k] = obj
Dictionary[str(fcd.Name)+"cubies"] = fc
Dictionary[str(fcd.Name)+"ViewObserver"] = ViewObserver()
doLabels() # create new labels
rotation = fixCamera(False)
Dictionary[str(fcd.Name)+"buttons"] = ButtonRow()
fixLightModel()
createMultiViews(rotation)
history = [] # according to Henry Ford
Dictionary[str(fcd.Name)+"history"] = history
QtGui.QApplication.restoreOverrideCursor()
FreeCAD.Console.PrintLog("fix_reload() done\n")

Revision as of 20:53, 5 December 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
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.

  1. -*- 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. *
  • There are some text direction labels near the arrows: clicking on one *
  • of those rotates the whole cube. You have to click on the actual *
  • letter: FreeCAD doesn't see clicks on the text background *
  • 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. *
  • *
  • There are also some functions defined which can be called from the *
  • python console window: *
  • fix_reload() modifies a restored FreeCAD save file to add the extra *
  • views and interactive controls that this macro and its functions use; *
  • ramdomise() randomises the cube; *
  • reverse_history(), reflectX_history(), reflectY_history(), *
  • reflectZ_history(), rotpX_history(), rotmX_history(), *
  • rotpY_history(), rotmY_history(), rotpZ_history(), and *
  • rotmZ_history() each replace the history by a version modified in the *
  • manner indicated - these are useful for creating modified operators. *
  • *
  • 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.04" __date__ = "05/12/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"

  1. OS: Ubuntu 14.04.5 LTS
  2. Word size of OS: 64-bit
  3. Word size of FreeCAD: 64-bit
  4. Version: 0.16.6703 (Git)
  5. Build type: None
  6. Branch: releases/FreeCAD-0-16
  7. Hash: 2ce5c8d2e3020d05005ed71f710e09e9aa561f40
  8. Python version: 2.7.6
  9. Qt version: 4.8.6
  10. Coin version: 4.0.0a
  11. OCC version: 6.8.0.oce-0.17
  1. This parameter determines the speed at which slice rotations are animated
  2. If you have a faster computer you can increase the value
  3. This will make the animation smoother
  4. If you have a slow computer you can decrease the value
  5. This will make the animation faster but more jerky for large cubes

slowness = 500

import FreeCAD import Part import Draft import time import random from FreeCAD import Base from FreeCAD import Console from pivy import coin from pivy.coin import * import PySide

  1. I copied this code from Macro_Mouse_Cross
  2. It seems a bit version dependent, but it works

try:

   from PyQt4 import QtCore, QtGui

except Exception:

   from PySide import QtCore, QtGui
  1. If this is the first time this macro has been run in this invocation of FreeCAD,
  2. create our dictionary of document-specific data structures

if not hasattr(FreeCAD, "Rubik_Cube_executed"):

   FreeCAD.Rubik_Cube_executed = 1
   Dictionary = {}
  1. Create a new document and make it current

App.ActiveDocument = App.newDocument("Rubik_Cube") Gui.ActiveDocument = Gui.getDocument(str(App.ActiveDocument.Name))

  1. 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_()

  1. Display a wait cursor while we are making the cube

QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))

  1. 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()
           obInfo = self.view.getObjectInfo((int(pos[0]),int(pos[1])))
           if obInfo != None:
               obj = App.ActiveDocument.getObject(obInfo["Object"])
               obname = obj.Label
               if obname[:5] == "arrow":
                   if event.getState() == 1:
                       wh = obname[5:7]
                       i = int(obname[7:])
                       if wh == "mX":
                           mrotmX(i,i+1)
                       elif wh == "pX":
                           mrotpX(i,i+1)
                       elif wh == "mY":
                           mrotmY(i,i+1)
                       elif wh == "pY":
                           mrotpY(i,i+1)
                       elif wh == "mZ":
                           mrotmZ(i,i+1)
                       elif wh == "pZ":
                           mrotpZ(i,i+1)
                   event_cb.setHandled()
               elif obname[:5] == "label":
                   if event.getState() == 1:
                       wh = obname[5:7]
                       n = Dictionary[str(App.ActiveDocument.Name)+"Size"]
                       if wh == "mX":
                           mrotmX(0,n)
                       elif wh == "pX":
                           mrotpX(0,n)
                       elif wh == "mY":
                           mrotmY(0,n)
                       elif wh == "pY":
                           mrotpY(0,n)
                       elif wh == "mZ":
                           mrotmZ(0,n)
                       elif wh == "pZ":
                           mrotpZ(0,n)
                   event_cb.setHandled()

Dictionary[str(App.ActiveDocument.Name)+"ViewObserver"] = ViewObserver()

  1. This bit of code creates the basic cube model
  2. It is composed of faces rather than cubes because I haven't found a way of making
  3. a cube with different coloured faces
  4. Each face has a name and a label
  5. The name is persistent regardless of how the face moves about the cube
  6. The label changes and tells where the face is now: this is needed for fix_reload

fcd = App.ActiveDocument n = Dictionary[str(App.ActiveDocument.Name)+"Size"] fc = [[[[None for j in range(6)] for ix in 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 = 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", "ff"+fs+"x0")
           f1.Shape = face
           f1.Label = "fs"+fs+"x0"
           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("ff"+fs+"x0"))
           face = Part.Face(Part.makePolygon([x1y0z0,x1y0z1,x1y1z1,x1y1z0,x1y0z0]))
           f2 = fcd.addObject("Part::Feature", "ff"+fs+"x1")
           f2.Shape = face
           f2.Label = "fs"+fs+"x1"
           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("ff"+fs+"x1"))
           face = Part.Face(Part.makePolygon([x0y0z0,x0y0z1,x1y0z1,x1y0z0,x0y0z0]))
           f3 = fcd.addObject("Part::Feature", "ff"+fs+"y0")
           f3.Shape = face
           f3.Label = "fs"+fs+"y0"
           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("ff"+fs+"y0"))
           face = Part.Face(Part.makePolygon([x0y1z0,x0y1z1,x1y1z1,x1y1z0,x0y1z0]))
           f4 = fcd.addObject("Part::Feature", "ff"+fs+"y1")
           f4.Shape = face
           f4.Label = "fs"+fs+"y1"
           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("ff"+fs+"y1"))
           face = Part.Face(Part.makePolygon([x0y0z0,x0y1z0,x1y1z0,x1y0z0,x0y0z0]))
           f5 = fcd.addObject("Part::Feature", "ff"+fs+"z0")
           f5.Shape = face
           f5.Label = "fs"+fs+"z0"
           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("ff"+fs+"z0"))
           face = Part.Face(Part.makePolygon([x0y0z1,x0y1z1,x1y1z1,x1y0z1,x0y0z1]))
           f6 = fcd.addObject("Part::Feature", "ff"+fs+"z1")
           f6.Shape = face
           f6.Label = "fs"+fs+"z1"
           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("ff"+fs+"z1"))
           fc[ix][iy][iz]=[f1,f2,f3,f4,f5,f6]
  1. This bit of code creates the clickable arrows
  2. Note we make them not selectable because mouse clicking on them
  3. 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
  1. This bit of code creates some labels for the arrows
  2. Clicking on one rotates the whole cube

def makeLabel(text, mat):

   n = Dictionary[str(App.ActiveDocument.Name)+"Size"]
   tx = Draft.makeText(text)
   tx.Label = "label"+text
   tx.ViewObject.TextColor = (0.0,0.0,0.0)
   tx.ViewObject.FontSize = 0.2 * n
   tx.ViewObject.Justification = 'Center'
   node = tx.ViewObject.RootNode
   node.setName(coin.SbName("label"+text))
   if node.getNumChildren() > 0:
       child = node.getChild(0)
       if child.getTypeId().getName().__str__() == "Transform":
           child.setMatrix(mat)

def doLabels():

   n = Dictionary[str(App.ActiveDocument.Name)+"Size"]
   m = coin.SbMatrix()
   m.makeIdentity()
   m.setTransform(
       coin.SbVec3f(0.0, - n / 2.0, -(1.0 + n * 0.7)),
       coin.SbRotation(coin.SbVec3f(1.0, 0.0, 0.0), 3.14159 / 2.0),
       coin.SbVec3f(1.0, 1.0, 1.0))
   makeLabel("pX", m)
   m = coin.SbMatrix()
   m.makeIdentity()
   m.setTransform(
       coin.SbVec3f(0.0, 1.1 + n / 2.0, n / 2.0),
       coin.SbRotation(coin.SbVec3f(0.0, 1.0, 0.0), 0.0),
       coin.SbVec3f(1.0, 1.0, 1.0))
   makeLabel("mX", m)
   m = coin.SbMatrix()
   m.makeIdentity()
   m.setTransform(
       coin.SbVec3f(n / 2.0, 0.0, -(1.0 + n * 0.7)),
       coin.SbRotation(coin.SbVec3f(0.0, 0.0, 1.0), 3.14159 / 2.0),
       coin.SbVec3f(1.0, 1.0, 1.0))
   m1 = coin.SbMatrix()
   m1.makeIdentity()
   m1.setTransform(
       coin.SbVec3f(0.0, 0.0, 0.0),
       coin.SbRotation(coin.SbVec3f(1.0, 0.0, 0.0), 3.14159 / 2.0),
       coin.SbVec3f(1.0, 1.0, 1.0))
   m1.multRight(m)
   makeLabel("pY", m1)
   m = coin.SbMatrix()
   m.makeIdentity()
   m.setTransform(
       coin.SbVec3f(-1.1 - n / 2.0, 0.0, n / 2.0),
       coin.SbRotation(coin.SbVec3f(0.0, 0.0, 1.0), 3.14159 / 2.0),
       coin.SbVec3f(1.0, 1.0, 1.0))
   makeLabel("mY", m)
   m = coin.SbMatrix()
   m.makeIdentity()
   m.setTransform(
       coin.SbVec3f(n / 2.0, 0.7 + n * 0.6, 0.0),
       coin.SbRotation(coin.SbVec3f(0.0, 1.0, 0.0), 3.14159 / 2.0),
       coin.SbVec3f(1.0, 1.0, 1.0))
   makeLabel("pZ", m)
   m = coin.SbMatrix()
   m.makeIdentity()
   m.setTransform(
       coin.SbVec3f(-1.1 - n / 2.0, - n / 2.0, 0.0),
       coin.SbRotation(coin.SbVec3f(1.0, 0.0, 0.0), 3.14159 / 2.0),
       coin.SbVec3f(1.0, 1.0, 1.0))
   m1 = coin.SbMatrix()
   m1.makeIdentity()
   m1.setTransform(
       coin.SbVec3f(0.0, 0.0, 0.0),
       coin.SbRotation(coin.SbVec3f(0.0, 0.0, 1.0), 3.14159 / 2.0),
       coin.SbVec3f(1.0, 1.0, 1.0))
   m1.multRight(m)
   makeLabel("mZ", m1)

doLabels()

Gui.ActiveDocument.ActiveView.viewAxonometric() Gui.SendMsgToActiveView("ViewFit")

  1. Viewfit doesn't seem to do the right thing with MultiViews
  2. so we adjust the camera height manually before creating them

def fixCamera(lift):

   # This gets FreeCAD's top level SceneGraph (including camera node),
   # not the document's SceneGraph which hangs off of it
   v = Gui.ActiveDocument.ActiveView.getViewer()
   sceneGraph = v.getSoEventManager().getSceneGraph()
   camera = sceneGraph.getChild(2)
   if camera.getTypeId().getName().__str__() == "OrthographicCamera":
       if lift:
           camera.height.setValue((2.0 + n / 20.0) * camera.height.getValue())
       return camera.orientation.getValue()

rotation = fixCamera(True)

  1. 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
  1. This bit of code creates the buttons at the top of the view window
  2. The buttons are in a frameless window to save screen space

height = 40 class ButtonRow(QtGui.QWidget):

   def __init__(self):
       super(ButtonRow, self).__init__()
       view3DWidget = findView(QtGui.qApp.activeWindow().centralWidget())
       if view3DWidget != None:
           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()

Dictionary[str(App.ActiveDocument.Name)+"buttons"] = ButtonRow()

  1. This bit of code disables the default Phong shading
  2. and avoids the face colours appearing to change during rotation

def fixLightModel():

   v = Gui.ActiveDocument.ActiveView.getViewer()
   sceneGraph = v.getSoEventManager().getSceneGraph()
   if str(sceneGraph.getChild(0).getName()) <> "LightModel":
       lm=coin.SoLightModel()
       lm.model.setValue(0)
       lm.setName("LightModel")
       sceneGraph.insertChild(lm,0)

fixLightModel()

  1. This bit of code persuades FreeCAD'a renderer to put
  2. several views of the cube into the same window

def MultiViews(parent, child, i, rotation):

   n = Dictionary[str(App.ActiveDocument.Name)+"Size"]
   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 * 0.9 + 0.4, n * 0.9 + 0.4, 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.15 - 0.7, - n * 1.15 - 0.7, 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(rotation):

   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':
               name = child.getName().__str__()[:5]
               if name != "arrow" and name != "label":
                   MultiViews(sg,child,i,rotation)
           if child.getTypeId().getName().__str__() == 'MultipleCopy':
               if child.getNumChildren() != 0:
                   name = child.getChild(0).getName().__str__()
                   if fcd.getObject(name) == None:
                       child.removeAllChildren()

createMultiViews(rotation)

  1. Restore the normal cursor now that we have done all the slow stuff

QtGui.QApplication.restoreOverrideCursor()

  1. This bit of code animates a slice rotation

def rotate(dir, rs, re, steps):

   n = Dictionary[str(App.ActiveDocument.Name)+"Size"]
   fc = Dictionary[str(App.ActiveDocument.Name)+"cubies"]
   fd = [[[[None for j in range(6)] for ix in range(n)] for iy in range(n)] for ix in range(n)]
   fp = [[[[Base.Placement() for j in range(6)] for ix in range(n)] for iy in range(n)] for ix in range(n)]
   xyz = ["x0","x1","y0","y1","z0","z1"]
   if dir.x > 0:
       # We need an explicit matrix for the final rotation step
       # to prevent rounding errors accumulating over time
       ff = Base.Matrix(1,0,0,0,0,0,-1,0,0,1,0,0,0,0,0,1)
       kk =[0,1,5,4,2,3]
       for iy in range(n):
           for iz in range(n):
               for j in range(rs, re):
                   for k in range(6):
                       c = fc[j][iy][iz][k]
                       c.Label = c.Name
                       fd[j][iy][iz][k] = c
                       fp[j][iy][iz][k] = c.Placement
       if steps > 0:
           fm = Base.Matrix()
           fm.rotateX(1.570795 / steps)
           for i in range(steps):
               for iy in range(n):
                   for iz in range(n):
                       for j in range(rs, re):
                           for k in range(6):
                               c = fc[j][iy][iz][k]
                               p = c.Placement
                               c.Placement = Base.Placement(fm).multiply(p)
               Gui.updateGui()
       for iy in range(n):
           for iz in range(n):
               for j in range(rs, re):
                   for k in range(6):
                       c = fd[j][iz][n-1-iy][kk[k]]
                       p = fp[j][iz][n-1-iy][kk[k]]
                       c.Label = "fs"+str(j)+"q"+str(iy)+"q"+str(iz)+xyz[k]
                       c.Placement = Base.Placement(ff).multiply(p)
                       fc[j][iy][iz][k] = c
   elif dir.x < 0:
       ff = Base.Matrix(1,0,0,0,0,0,1,0,0,-1,0,0,0,0,0,1)
       kk =[0,1,4,5,3,2]
       for iy in range(n):
           for iz in range(n):
               for j in range(rs, re):
                   for k in range(6):
                       c = fc[j][iy][iz][k]
                       c.Label = c.Name
                       fd[j][iy][iz][k] = c
                       fp[j][iy][iz][k] = c.Placement
       if steps > 0:
           fm = Base.Matrix()
           fm.rotateX(-1.570795 / steps)
           for i in range(steps):
               for iy in range(n):
                   for iz in range(n):
                       for j in range(rs, re):
                           for k in range(6):
                               c = fc[j][iy][iz][k]
                               p = c.Placement
                               c.Placement = Base.Placement(fm).multiply(p)
               Gui.updateGui()
       for iy in range(n):
           for iz in range(n):
               for j in range(rs, re):
                   for k in range(6):
                       c = fd[j][n-1-iz][iy][kk[k]]
                       p = fp[j][n-1-iz][iy][kk[k]]
                       c.Label = "fs"+str(j)+"q"+str(iy)+"q"+str(iz)+xyz[k]
                       c.Placement = Base.Placement(ff).multiply(p)
                       fc[j][iy][iz][k] = c
   elif dir.y > 0:
       ff = Base.Matrix(0,0,1,0,0,1,0,0,-1,0,0,0,0,0,0,1)
       kk =[4,5,2,3,1,0]
       for ix in range(n):
           for iz in range(n):
               for j in range(rs, re):
                   for k in range(6):
                       c = fc[ix][j][iz][k]
                       c.Label = c.Name
                       fd[ix][j][iz][k] = c
                       fp[ix][j][iz][k] = c.Placement
       if steps > 0:
           fm = Base.Matrix()
           fm.rotateY(1.570795 / steps)
           for i in range(steps):
               for ix in range(n):
                   for iz in range(n):
                       for j in range(rs, re):
                           for k in range(6):
                               c = fc[ix][j][iz][k]
                               p = c.Placement
                               c.Placement = Base.Placement(fm).multiply(p)
               Gui.updateGui()
       for ix in range(n):
           for iz in range(n):
               for j in range(rs, re):
                   for k in range(6):
                       c = fd[n-1-iz][j][ix][kk[k]]
                       p = fp[n-1-iz][j][ix][kk[k]]
                       c.Label = "fs"+str(ix)+"q"+str(j)+"q"+str(iz)+xyz[k]
                       c.Placement = Base.Placement(ff).multiply(p)
                       fc[ix][j][iz][k] = c
   elif dir.y < 0:
       ff = Base.Matrix(0,0,-1,0,0,1,0,0,1,0,0,0,0,0,0,1)
       kk =[5,4,2,3,0,1]
       for ix in range(n):
           for iz in range(n):
               for j in range(rs, re):
                   for k in range(6):
                       c = fc[ix][j][iz][k]
                       c.Label = c.Name
                       fd[ix][j][iz][k] = c
                       fp[ix][j][iz][k] = c.Placement
       if steps > 0:
           fm = Base.Matrix()
           fm.rotateY(-1.570795 / steps)
           for i in range(steps):
               for ix in range(n):
                   for iz in range(n):
                       for j in range(rs, re):
                           for k in range(6):
                               c = fc[ix][j][iz][k]
                               p = c.Placement
                               c.Placement = Base.Placement(fm).multiply(p)
               Gui.updateGui()
       for ix in range(n):
           for iz in range(n):
               for j in range(rs, re):
                   for k in range(6):
                       c = fd[iz][j][n-1-ix][kk[k]]
                       p = fp[iz][j][n-1-ix][kk[k]]
                       c.Label = "fs"+str(ix)+"q"+str(j)+"q"+str(iz)+xyz[k]
                       c.Placement = Base.Placement(ff).multiply(p)
                       fc[ix][j][iz][k] = c
   elif dir.z > 0:
       ff = Base.Matrix(0,-1,0,0,1,0,0,0,0,0,1,0,0,0,0,1)
       kk = [3,2,0,1,4,5]
       for ix in range(n):
           for iy in range(n):
               for j in range(rs, re):
                   for k in range(6):
                       c = fc[ix][iy][j][k]
                       c.Label = c.Name
                       fd[ix][iy][j][k] = c
                       fp[ix][iy][j][k] = c.Placement
       if steps > 0:
           fm = Base.Matrix()
           fm.rotateZ(1.570795 / steps)
           for i in range(steps):
               for ix in range(n):
                   for iy in range(n):
                       for j in range(rs, re):
                           for k in range(6):
                               c = fc[ix][iy][j][k]
                               p = c.Placement
                               c.Placement = Base.Placement(fm).multiply(p)
               Gui.updateGui()
       for ix in range(n):
           for iy in range(n):
               for j in range(rs, re):
                   for k in range(6):
                       c = fd[iy][n-1-ix][j][kk[k]]
                       p = fp[iy][n-1-ix][j][kk[k]]
                       c.Label = "fs"+str(ix)+"q"+str(iy)+"q"+str(j)+xyz[k]
                       c.Placement = Base.Placement(ff).multiply(p)
                       fc[ix][iy][j][k] = c
   elif dir.z < 0:
       ff = Base.Matrix(0,1,0,0,-1,0,0,0,0,0,1,0,0,0,0,1)
       kk = [2,3,1,0,5,4]
       for ix in range(n):
           for iy in range(n):
               for j in range(rs, re):
                   for k in range(6):
                       c = fc[ix][iy][j][k]
                       c.Label = c.Name
                       fd[ix][iy][j][k] = c
                       fp[ix][iy][j][k] = c.Placement
       if steps > 0:
           fm = Base.Matrix()
           fm.rotateZ(-1.570795 / steps)
           for i in range(steps):
               for ix in range(n):
                   for iy in range(n):
                       for j in range(rs, re):
                           for k in range(6):
                               c = fc[ix][iy][j][k]
                               p = c.Placement
                               c.Placement = Base.Placement(fm).multiply(p)
               Gui.updateGui()
       for ix in range(n):
           for iy in range(n):
               for j in range(rs, re):
                   for k in range(6):
                       c = fd[n-1-iy][ix][j][kk[k]]
                       p = fp[n-1-iy][ix][j][kk[k]]
                       c.Label = "fs"+str(ix)+"q"+str(iy)+"q"+str(j)+xyz[k]
                       c.Placement = Base.Placement(ff).multiply(p)
                       fc[ix][iy][j][k] = c

def slowrotate(dir, rs, re):

   n = Dictionary[str(App.ActiveDocument.Name)+"Size"]
   steps = slowness / (n * n)
   if re > rs:
       rotate(dir, rs, re, steps / (re - rs))
       Gui.updateGui()
  1. Quick rotation for use when randomising or modifying history

def fastrotate(dir, rs, re):

   rotate(dir, rs, re, 0)
  1. These functions manage the history
  2. Once you have created a cube, these functions will be defined and in scope
  3. and you can call them from another macro created by saving history
  4. or by hand from the python console window

history = [] Dictionary[str(App.ActiveDocument.Name)+"history"] = history def mrotpX(i,j):

   slowrotate(Base.Vector(1,0,0),i,j)
   Dictionary[str(App.ActiveDocument.Name)+"history"].append("mrotpX("+str(i)+","+str(j)+")")

def mrotpY(i,j):

   slowrotate(Base.Vector(0,1,0),i,j)
   Dictionary[str(App.ActiveDocument.Name)+"history"].append("mrotpY("+str(i)+","+str(j)+")")

def mrotpZ(i,j):

   slowrotate(Base.Vector(0,0,1),i,j)
   Dictionary[str(App.ActiveDocument.Name)+"history"].append("mrotpZ("+str(i)+","+str(j)+")")

def mrotmX(i,j):

   slowrotate(Base.Vector(-1,0,0),i,j)
   Dictionary[str(App.ActiveDocument.Name)+"history"].append("mrotmX("+str(i)+","+str(j)+")")

def mrotmY(i,j):

   slowrotate(Base.Vector(0,-1,0),i,j)
   Dictionary[str(App.ActiveDocument.Name)+"history"].append("mrotmY("+str(i)+","+str(j)+")")

def mrotmZ(i,j):

   slowrotate(Base.Vector(0,0,-1),i,j)
   Dictionary[str(App.ActiveDocument.Name)+"history"].append("mrotmZ("+str(i)+","+str(j)+")")

def undo():

   history = Dictionary[str(App.ActiveDocument.Name)+"history"]
   if len(history) > 0:
       fs = history.pop()
       wh = fs[:6]
       p = fs.index(",")
       i = int(fs[7:p])
       j = int(fs[(p+1):(len(fs)-1)])
       if wh == "mrotmX":
           slowrotate(Base.Vector(1,0,0),i,j)
       elif wh == "mrotpX":
           slowrotate(Base.Vector(-1,0,0),i,j)
       elif wh == "mrotmY":
           slowrotate(Base.Vector(0,1,0),i,j)
       elif wh == "mrotpY":
           slowrotate(Base.Vector(0,-1,0),i,j)
       elif wh == "mrotmZ":
           slowrotate(Base.Vector(0,0,1),i,j)
       elif wh == "mrotpZ":
           slowrotate(Base.Vector(0,0,-1),i,j)

def itostring(i, n):

   if 2 * i == n - 1:
   	return "n/2"
   elif i <= n / 2:
       return str(i)
   elif i == n - 1:
       return "n-1"
   else:
       return "n-" + str(n-i) 

def jtostring(j, n):

   if j <= n / 2:
      return str(j)
   elif 2 * j == n + 1:
   	return "n/2+1"
   elif j == n:
       return "n"
   elif j == n - 1:
       return "n-1"
   else:
       return "n-" + str(n-j) 

def saveHistory():

   fcd = App.ActiveDocument
   history = Dictionary[str(fcd.Name)+"history"]
   n = Dictionary[str(fcd.Name)+"Size"]
   if len(history) > 0:
       # This statement will be needed at the start of any macro
       # to get the size of the cube in the currently active document
       # in case we have several documents open at once
       fs = "n = Dictionary[str(App.ActiveDocument.Name)+'Size']\n"
       for s in history:
           p = s.index(",")
           i = int(s[7:p])
           j = int(s[(p+1):(len(s)-1)])
           fs = fs + s[:7] + itostring(i, n) + ","
           fs = fs + jtostring(j, n) + ")\n"
       clip = QtCore.QCoreApplication.instance().clipboard()
       clip.setText(fs)

def reset():

   QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
   fcd = App.ActiveDocument
   Dictionary[str(fcd.Name)+"history"] = []
   n = Dictionary[str(fcd.Name)+"Size"]
   fc = Dictionary[str(fcd.Name)+"cubies"]
   for obj in fcd.Objects:
       fs = obj.Name
       if fs[0:2] == "ff":
           obj.Label = fs
   for obj in fcd.Objects:
       fs = obj.Name
       if fs[0:2] == "ff":
           obj.Label = "fs"+fs[2:]
           obj.Placement = Base.Placement()
           l = len(fs)
           q1 = fs.find("q")
           q2 = fs.find("q",q1+1)
           ix = int(fs[2:q1])
           iy = int(fs[q1+1:q2])
           iz = int(fs[q2+1:l-2])
           k = "x0x1y0y1z0z1".find(fs[l-2:])/2
           fc[ix][iy][iz][k] = obj
   QtGui.QApplication.restoreOverrideCursor()
  1. Randomise the cube

def randomise():

   QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
   fcd = App.ActiveDocument
   history = Dictionary[str(fcd.Name)+"history"]
   n = Dictionary[str(fcd.Name)+"Size"]
   random.seed()
   i = random.randrange(0,6)
   for x in range(n*24):
       i = (i + 2 - i % 2 +random.randrange(0,4)) % 6
       j = random.randrange(0,n)
       if i == 0:
           fastrotate(Base.Vector(1,0,0),j,j+1)
           history.append("mrotpX("+str(j)+","+str(j+1)+")")
       elif i == 1:
           fastrotate(Base.Vector(-1,0,0),j,j+1)
           history.append("mrotmX("+str(j)+","+str(j+1)+")")
       elif i == 2:
           fastrotate(Base.Vector(0,1,0),j,j+1)
           history.append("mrotpY("+str(j)+","+str(j+1)+")")
       elif i == 3:
           fastrotate(Base.Vector(0,-1,0),j,j+1)
           history.append("mrotmY("+str(j)+","+str(j+1)+")")
       elif i == 4:
           fastrotate(Base.Vector(0,0,1),j,j+1)
           history.append("mrotpZ("+str(j)+","+str(j+1)+")")
       elif i == 5:
           fastrotate(Base.Vector(0,0,-1),j,j+1)
           history.append("mrotmZ("+str(j)+","+str(j+1)+")")
   QtGui.QApplication.restoreOverrideCursor()
  1. Various macros to modify the history
  2. They all read the history and undo it, and then perform some modified version
  3. of the history. The modifications can be time-reversal, reflection along an
  4. axis (as if the cube were reflected, the history replayed,
  5. and the cube reflected back again), or rotation (as if the cube were rotated,
  6. the history replayed, and the cube rotated back again).

def slow_history():

   QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
   fcd = App.ActiveDocument
   history = Dictionary[str(fcd.Name)+"history"]
   n = Dictionary[str(fcd.Name)+"Size"]
   nh = []
   while len(history) > 0:
       nh.append(history.pop())
   reset()
   Gui.updateGui()
   while len(nh) > 0:
       fs = nh.pop()
       wh = fs[:6]
       p = fs.index(",")
       i = int(fs[7:p])
       j = int(fs[p+1:len(fs)-1])
       if wh == "mrotpX":
           mrotpX(i,j)
       elif wh == "mrotmX":
           mrotmX(i,j)
       elif wh == "mrotpY":
           mrotpY(i,j)
       elif wh == "mrotmY":
           mrotmY(i,j)
       elif wh == "mrotpZ":
           mrotpZ(i,j)
       elif wh == "mrotmZ":
           mrotmZ(i,j)
   QtGui.QApplication.restoreOverrideCursor()

def reverse_history():

   QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
   fcd = App.ActiveDocument
   history = Dictionary[str(fcd.Name)+"history"]
   n = Dictionary[str(fcd.Name)+"Size"]
   nh = []
   while len(history) > 0:
       nh.append(history.pop())
   reset()
   history = Dictionary[str(fcd.Name)+"history"]
   for fs in nh:
       wh = fs[:6]
       p = fs.index(",")
       i = int(fs[7:p])
       j = int(fs[p+1:len(fs)-1])
       if wh == "mrotpX":
           fastrotate(Base.Vector(1,0,0),i,j)
           history.append("mrotpX("+str(i)+","+str(j)+")")
       elif wh == "mrotmX":
           fastrotate(Base.Vector(-1,0,0),i,j)
           history.append("mrotmX("+str(i)+","+str(j)+")")
       elif wh == "mrotpY":
           fastrotate(Base.Vector(0,1,0),i,j)
           history.append("mrotpY("+str(i)+","+str(j)+")")
       elif wh == "mrotmY":
           fastrotate(Base.Vector(0,-1,0),i,j)
           history.append("mrotmY("+str(i)+","+str(j)+")")
       elif wh == "mrotpZ":
           fastrotate(Base.Vector(0,0,1),i,j)
           history.append("mrotpZ("+str(i)+","+str(j)+")")
       elif wh == "mrotmZ":
           fastrotate(Base.Vector(0,0,-1),i,j)
           history.append("mrotmZ("+str(i)+","+str(j)+")")
   QtGui.QApplication.restoreOverrideCursor()

def undo_history():

   QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
   fcd = App.ActiveDocument
   history = Dictionary[str(fcd.Name)+"history"]
   n = Dictionary[str(fcd.Name)+"Size"]
   nh = []
   while len(history) > 0:
       nh.append(history.pop())
   reset()
   history = Dictionary[str(fcd.Name)+"history"]
   for fs in nh:
       wh = fs[:6]
       p = fs.index(",")
       i = int(fs[7:p])
       j = int(fs[p+1:len(fs)-1])
       if wh == "mrotpX":
           fastrotate(Base.Vector(-1,0,0),i,j)
           history.append("mrotmX("+str(i)+","+str(j)+")")
       elif wh == "mrotmX":
           fastrotate(Base.Vector(1,0,0),i,j)
           history.append("mrotpX("+str(i)+","+str(j)+")")
       elif wh == "mrotpY":
           fastrotate(Base.Vector(0,-1,0),i,j)
           history.append("mrotmY("+str(i)+","+str(j)+")")
       elif wh == "mrotmY":
           fastrotate(Base.Vector(0,1,0),i,j)
           history.append("mrotpY("+str(i)+","+str(j)+")")
       elif wh == "mrotpZ":
           fastrotate(Base.Vector(0,0,-1),i,j)
           history.append("mrotmZ("+str(i)+","+str(j)+")")
       elif wh == "mrotmZ":
           fastrotate(Base.Vector(0,0,1),i,j)
           history.append("mrotpZ("+str(i)+","+str(j)+")")
   QtGui.QApplication.restoreOverrideCursor()

def reflectX_history():

   QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
   fcd = App.ActiveDocument
   history = Dictionary[str(fcd.Name)+"history"]
   n = Dictionary[str(fcd.Name)+"Size"]
   nh = []
   while len(history) > 0:
       nh.append(history.pop())
   reset()
   history = Dictionary[str(fcd.Name)+"history"]
   while len(nh) > 0:
       fs = nh.pop()
       wh = fs[:6]
       p = fs.index(",")
       i = int(fs[7:p])
       j = int(fs[p+1:len(fs)-1])
       if wh == "mrotpX":
           fastrotate(Base.Vector(1,0,0),n-j,n-i)
           history.append("mrotpX("+str(n-j)+","+str(n-i)+")")
       elif wh == "mrotmX":
           fastrotate(Base.Vector(-1,0,0),n-j,n-i)
           history.append("mrotmX("+str(n-j)+","+str(n-i)+")")
       elif wh == "mrotpY":
           fastrotate(Base.Vector(0,-1,0),i,j)
           history.append("mrotmY("+str(i)+","+str(j)+")")
       elif wh == "mrotmY":
           fastrotate(Base.Vector(0,1,0),i,j)
           history.append("mrotpY("+str(i)+","+str(j)+")")
       elif wh == "mrotpZ":
           fastrotate(Base.Vector(0,0,-1),i,j)
           history.append("mrotmZ("+str(i)+","+str(j)+")")
       elif wh == "mrotmZ":
           fastrotate(Base.Vector(0,0,1),i,j)
           history.append("mrotpZ("+str(i)+","+str(j)+")")
   QtGui.QApplication.restoreOverrideCursor()

def reflectY_history():

   QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
   fcd = App.ActiveDocument
   history = Dictionary[str(fcd.Name)+"history"]
   n = Dictionary[str(fcd.Name)+"Size"]
   nh = []
   while len(history) > 0:
       nh.append(history.pop())
   reset()
   history = Dictionary[str(fcd.Name)+"history"]
   while len(nh) > 0:
       fs = nh.pop()
       wh = fs[:6]
       p = fs.index(",")
       i = int(fs[7:p])
       j = int(fs[p+1:len(fs)-1])
       if wh == "mrotpX":
           fastrotate(Base.Vector(-1,0,0),i,j)
           history.append("mrotmX("+str(i)+","+str(j)+")")
       elif wh == "mrotmX":
           fastrotate(Base.Vector(1,0,0),i,j)
           history.append("mrotpX("+str(i)+","+str(j)+")")
       elif wh == "mrotpY":
           fastrotate(Base.Vector(0,1,0),n-j,n-i)
           history.append("mrotpY("+str(n-j)+","+str(n-i)+")")
       elif wh == "mrotmY":
           fastrotate(Base.Vector(0,-1,0),n-j,n-i)
           history.append("mrotmY("+str(n-j)+","+str(n-i)+")")
       elif wh == "mrotpZ":
           fastrotate(Base.Vector(0,0,-1),i,j)
           history.append("mrotmZ("+str(i)+","+str(j)+")")
       elif wh == "mrotmZ":
           fastrotate(Base.Vector(0,0,1),i,j)
           history.append("mrotpZ("+str(i)+","+str(j)+")")
   QtGui.QApplication.restoreOverrideCursor()

def reflectZ_history():

   QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
   fcd = App.ActiveDocument
   history = Dictionary[str(fcd.Name)+"history"]
   n = Dictionary[str(fcd.Name)+"Size"]
   nh = []
   while len(history) > 0:
       nh.append(history.pop())
   reset()
   history = Dictionary[str(fcd.Name)+"history"]
   while len(nh) > 0:
       fs = nh.pop()
       wh = fs[:6]
       p = fs.index(",")
       i = int(fs[7:p])
       j = int(fs[p+1:len(fs)-1])
       if wh == "mrotpX":
           fastrotate(Base.Vector(-1,0,0),i,j)
           history.append("mrotmX("+str(i)+","+str(j)+")")
       elif wh == "mrotmX":
           fastrotate(Base.Vector(1,0,0),i,j)
           history.append("mrotpX("+str(i)+","+str(j)+")")
       elif wh == "mrotpY":
           fastrotate(Base.Vector(0,-1,0),i,j)
           history.append("mrotmY("+str(i)+","+str(j)+")")
       elif wh == "mrotmY":
           fastrotate(Base.Vector(0,1,0),i,j)
           history.append("mrotpY("+str(i)+","+str(j)+")")
       elif wh == "mrotpZ":
           fastrotate(Base.Vector(0,0,1),n-j,n-i)
           history.append("mrotpZ("+str(n-j)+","+str(n-i)+")")
       elif wh == "mrotmZ":
           fastrotate(Base.Vector(0,0,-1),n-j,n-i)
           history.append("mrotmZ("+str(n-j)+","+str(n-i)+")")
   QtGui.QApplication.restoreOverrideCursor()

def rotpX_history():

   QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
   fcd = App.ActiveDocument
   history = Dictionary[str(fcd.Name)+"history"]
   n = Dictionary[str(fcd.Name)+"Size"]
   nh = []
   while len(history) > 0:
       nh.append(history.pop())
   reset()
   history = Dictionary[str(fcd.Name)+"history"]
   while len(nh) > 0:
       fs = nh.pop()
       wh = fs[:6]
       p = fs.index(",")
       i = int(fs[7:p])
       j = int(fs[p+1:len(fs)-1])
       if wh == "mrotpX":
           fastrotate(Base.Vector(1,0,0),i,j)
           history.append("mrotpX("+str(i)+","+str(j)+")")
       elif wh == "mrotmX":
           fastrotate(Base.Vector(-1,0,0),i,j)
           history.append("mrotmX("+str(i)+","+str(j)+")")
       elif wh == "mrotpY":
           fastrotate(Base.Vector(0,0,1),i,j)
           history.append("mrotpZ("+str(i)+","+str(j)+")")
       elif wh == "mrotmY":
           fastrotate(Base.Vector(0,0,-1),i,j)
           history.append("mrotmZ("+str(i)+","+str(j)+")")
       elif wh == "mrotpZ":
           fastrotate(Base.Vector(0,-1,0),n-j,n-i)
           history.append("mrotmY("+str(n-j)+","+str(n-i)+")")
       elif wh == "mrotmZ":
           fastrotate(Base.Vector(0,1,0),n-j,n-i)
           history.append("mrotpY("+str(n-j)+","+str(n-i)+")")
   QtGui.QApplication.restoreOverrideCursor()

def rotmX_history():

   QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
   fcd = App.ActiveDocument
   history = Dictionary[str(fcd.Name)+"history"]
   n = Dictionary[str(fcd.Name)+"Size"]
   nh = []
   while len(history) > 0:
       nh.append(history.pop())
   reset()
   history = Dictionary[str(fcd.Name)+"history"]
   while len(nh) > 0:
       fs = nh.pop()
       wh = fs[:6]
       p = fs.index(",")
       i = int(fs[7:p])
       j = int(fs[p+1:len(fs)-1])
       if wh == "mrotpX":
           fastrotate(Base.Vector(1,0,0),i,j)
           history.append("mrotpX("+str(i)+","+str(j)+")")
       elif wh == "mrotmX":
           fastrotate(Base.Vector(-1,0,0),i,j)
           history.append("mrotmX("+str(i)+","+str(j)+")")
       elif wh == "mrotpY":
           fastrotate(Base.Vector(0,0,-1),n-j,n-i)
           history.append("mrotmZ("+str(n-j)+","+str(n-i)+")")
       elif wh == "mrotmY":
           fastrotate(Base.Vector(0,0,1),n-j,n-i)
           history.append("mrotpZ("+str(n-j)+","+str(n-i)+")")
       elif wh == "mrotpZ":
           fastrotate(Base.Vector(0,1,0),i,j)
           history.append("mrotpY("+str(i)+","+str(j)+")")
       elif wh == "mrotmZ":
           fastrotate(Base.Vector(0,-1,0),i,j)
           history.append("mrotmY("+str(i)+","+str(j)+")")
   QtGui.QApplication.restoreOverrideCursor()

def rotpY_history():

   QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
   fcd = App.ActiveDocument
   history = Dictionary[str(fcd.Name)+"history"]
   n = Dictionary[str(fcd.Name)+"Size"]
   nh = []
   while len(history) > 0:
       nh.append(history.pop())
   reset()
   history = Dictionary[str(fcd.Name)+"history"]
   while len(nh) > 0:
       fs = nh.pop()
       wh = fs[:6]
       p = fs.index(",")
       i = int(fs[7:p])
       j = int(fs[p+1:len(fs)-1])
       if wh == "mrotpX":
           fastrotate(Base.Vector(0,0,-1),n-j,n-i)
           history.append("mrotmZ("+str(n-j)+","+str(n-i)+")")
       elif wh == "mrotmX":
           fastrotate(Base.Vector(0,0,1),n-j,n-i)
           history.append("mrotpZ("+str(n-j)+","+str(n-i)+")")
       elif wh == "mrotpY":
           fastrotate(Base.Vector(0,1,0),i,j)
           history.append("mrotpY("+str(i)+","+str(j)+")")
       elif wh == "mrotmY":
           fastrotate(Base.Vector(0,-1,0),i,j)
           history.append("mrotmY("+str(i)+","+str(j)+")")
       elif wh == "mrotpZ":
           fastrotate(Base.Vector(1,0,0),i,j)
           history.append("mrotpX("+str(i)+","+str(j)+")")
       elif wh == "mrotmZ":
           fastrotate(Base.Vector(-1,0,0),i,j)
           history.append("mrotmX("+str(i)+","+str(j)+")")
   QtGui.QApplication.restoreOverrideCursor()

def rotmY_history():

   QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
   fcd = App.ActiveDocument
   history = Dictionary[str(fcd.Name)+"history"]
   n = Dictionary[str(fcd.Name)+"Size"]
   nh = []
   while len(history) > 0:
       nh.append(history.pop())
   reset()
   history = Dictionary[str(fcd.Name)+"history"]
   while len(nh) > 0:
       fs = nh.pop()
       wh = fs[:6]
       p = fs.index(",")
       i = int(fs[7:p])
       j = int(fs[p+1:len(fs)-1])
       if wh == "mrotpX":
           fastrotate(Base.Vector(0,0,1),i,j)
           history.append("mrotpZ("+str(i)+","+str(j)+")")
       elif wh == "mrotmX":
           fastrotate(Base.Vector(0,0,-1),i,j)
           history.append("mrotmZ("+str(i)+","+str(j)+")")
       elif wh == "mrotpY":
           fastrotate(Base.Vector(0,1,0),i,j)
           history.append("mrotpY("+str(i)+","+str(j)+")")
       elif wh == "mrotmY":
           fastrotate(Base.Vector(0,-1,0),i,j)
           history.append("mrotmY("+str(i)+","+str(j)+")")
       elif wh == "mrotpZ":
           fastrotate(Base.Vector(-1,0,0),n-j,n-i)
           history.append("mrotmX("+str(n-j)+","+str(n-i)+")")
       elif wh == "mrotmZ":
           fastrotate(Base.Vector(1,0,0),n-j,n-i)
           history.append("mrotpX("+str(n-j)+","+str(n-i)+")")
   QtGui.QApplication.restoreOverrideCursor()

def rotpZ_history():

   QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
   fcd = App.ActiveDocument
   history = Dictionary[str(fcd.Name)+"history"]
   n = Dictionary[str(fcd.Name)+"Size"]
   nh = []
   while len(history) > 0:
       nh.append(history.pop())
   reset()
   history = Dictionary[str(fcd.Name)+"history"]
   while len(nh) > 0:
       fs = nh.pop()
       wh = fs[:6]
       p = fs.index(",")
       i = int(fs[7:p])
       j = int(fs[p+1:len(fs)-1])
       if wh == "mrotpX":
           fastrotate(Base.Vector(0,1,0),i,j)
           history.append("mrotpY("+str(i)+","+str(j)+")")
       elif wh == "mrotmX":
           fastrotate(Base.Vector(0,-1,0),i,j)
           history.append("mrotmY("+str(i)+","+str(j)+")")
       elif wh == "mrotpY":
           fastrotate(Base.Vector(-1,0,0),n-j,n-i)
           history.append("mrotmX("+str(n-j)+","+str(n-i)+")")
       elif wh == "mrotmY":
           fastrotate(Base.Vector(1,0,0),n-j,n-i)
           history.append("mrotpX("+str(n-j)+","+str(n-i)+")")
       elif wh == "mrotpZ":
           fastrotate(Base.Vector(0,0,1),i,j)
           history.append("mrotpZ("+str(i)+","+str(j)+")")
       elif wh == "mrotmZ":
           fastrotate(Base.Vector(0,0,-1),i,j)
           history.append("mrotmZ("+str(i)+","+str(j)+")")
   QtGui.QApplication.restoreOverrideCursor()

def rotmZ_history():

   QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
   fcd = App.ActiveDocument
   history = Dictionary[str(fcd.Name)+"history"]
   n = Dictionary[str(fcd.Name)+"Size"]
   nh = []
   while len(history) > 0:
       nh.append(history.pop())
   reset()
   history = Dictionary[str(fcd.Name)+"history"]
   while len(nh) > 0:
       fs = nh.pop()
       wh = fs[:6]
       p = fs.index(",")
       i = int(fs[7:p])
       j = int(fs[p+1:len(fs)-1])
       if wh == "mrotpX":
           fastrotate(Base.Vector(0,-1,0),n-j,n-i)
           history.append("mrotmY("+str(n-j)+","+str(n-i)+")")
       elif wh == "mrotmX":
           fastrotate(Base.Vector(0,1,0),n-j,n-i)
           history.append("mrotpY("+str(n-j)+","+str(n-i)+")")
       elif wh == "mrotpY":
           fastrotate(Base.Vector(1,0,0),i,j)
           history.append("mrotpX("+str(i)+","+str(j)+")")
       elif wh == "mrotmY":
           fastrotate(Base.Vector(-1,0,0),i,j)
           history.append("mrotmX("+str(i)+","+str(j)+")")
       elif wh == "mrotpZ":
           fastrotate(Base.Vector(0,0,1),i,j)
           history.append("mrotpZ("+str(i)+","+str(j)+")")
       elif wh == "mrotmZ":
           fastrotate(Base.Vector(0,0,-1),i,j)
           history.append("mrotmZ("+str(i)+","+str(j)+")")
   QtGui.QApplication.restoreOverrideCursor()
  1. FreeCAD's file save only saves the state of the camera
  2. and the shapes, positions, orientations, names, and labels
  3. of the objects in the scene
  4. This function recreates all the other stuff after reloading a saved file
  5. I haven't found a way to make FreeCAD save the history, so it gets reset

def fix_reload():

   QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
   fcd = App.ActiveDocument
   # first we find how big a cube we have
   n = 0
   for obj in fcd.Objects:
       fs = obj.Name
       if fs[0:2] == "ff":
           i = int(fs[2:fs.index("q")])
           if i > n:
               n = i
       elif fs[0:4] == "Text":
           # get rid of this because it's incorrectly positioned
           # we'll create a new one later
           fcd.removeObject(fs)
       elif fs[0:5] == "arrow":
           obj.ViewObject.RootNode.setName(coin.SbName(fs))
   n = n + 1
   fc = [[[[None for j in range(6)] for ix in range(n)] for iy in range(n)] for ix in range(n)]
   Dictionary[str(fcd.Name)+"Size"] = n
   for obj in fcd.Objects:
       fs = obj.Label
       if fs[0:2] == "fs":
           l = len(fs)
           q1 = fs.find("q")
           q2 = fs.find("q",q1+1)
           ix = int(fs[2:q1])
           iy = int(fs[q1+1:q2])
           iz = int(fs[q2+1:l-2])
           k = "x0x1y0y1z0z1".find(fs[l-2:])/2
           fc[ix][iy][iz][k] = obj
   Dictionary[str(fcd.Name)+"cubies"] = fc
   Dictionary[str(fcd.Name)+"ViewObserver"] = ViewObserver()
   doLabels() # create new labels
   rotation = fixCamera(False)
   Dictionary[str(fcd.Name)+"buttons"] = ButtonRow()
   fixLightModel()
   createMultiViews(rotation)
   history = [] # according to Henry Ford
   Dictionary[str(fcd.Name)+"history"] = history
   QtGui.QApplication.restoreOverrideCursor()
   FreeCAD.Console.PrintLog("fix_reload() done\n")