FreeCAD Logo FreeCAD 1.0
  • Englisch Afrikaans Arabisch Weißrussisch Katalanisch Tschechisch Deutsch Griechisch Spanisch Spanisch Baskisch Finnisch Philippinisch Französisch Galicisch Kroatisch Ungarisch Indonesisch Italienisch Japanisch Kabylisch Koreanisch Litauisch Niederländisch Norwegisches Bokmål Polnisch Portugiesisch Portugiesisch Rumänisch Russisch Slowakisch Slowenisch Serbisch Schwedisch Türkisch Ukrainisch Valencianisch Vietnamesisch Chinesisch Chinesisch
  • Funktionen
  • Herunterladen
  • Blog
  • Dokumentation
    Inhaltsverzeichnis Erste Schritte Benutzerdokumentation Das FreeCAD-Handbuch Dokumentation der Arbeitsbereiche Python-Dokumentation Dokumentation der C++ Programmierung Anleitungen Häufig gestellte Fragen Datenschutzrichtlinie Über FreeCAD
  • Mitwirken
    Wie kann ich helfen Sponsor Einen Fehler melden Einen Pull Request durchführen Jobs und Finanzierung Richtlinien für Beiträge Entwicklerhandbuch Übersetzungen
  • Community
    Verhaltenskodex Forum The FPA GitHub GitLab Codeberg Mastodon Matrix IRC IRC via Webchat Gitter Discord Reddit Twitter Facebook LinkedIn Kalender
  • ♥ Donate

Donate

$
SEPA-Informationen
Bitte richten Sie Ihre SEPA-Überweisung an:
Beneficiary: The FreeCAD project association
IBAN: BE04 0019 2896 4531
BIC/SWIFT: GEBABEBBXXX
Kreditinstitut: BNP Paribas Fortis
Adresse: Rue de la Station 64, 1360 Perwez, Belgium

While Stripe doesn't support monthly donations, you can still become a sponsor! Simply make a one-time donation equivalent to 12 months of support, and you'll gain access to the corresponding sponsoring tier. It's an easy and flexible way to contribute.

If you are not sure or not able to commit to a regular donation, but still want to help the project, you can do a one-time donation, of any amount.

Choose freely the amount you wish to donate one time only.

You can support FreeCAD by sponsoring it as an individual or organization through various platforms. Sponsorship provides a steady income for developers, allowing the FPA to plan ahead and enabling greater investment in FreeCAD. To encourage sponsorship, we offer different tiers, and unless you choose to remain anonymous, your name or company logo will be featured on our website accordingly.

from 1 USD / 1 EUR per month. You will not have your name displayed here, but you will have helped the project a lot anyway. Together, normal sponsors maintain the project on its feet as much as the bigger sponsors.

from 25 USD / 25 EUR per month. Your name or company name is displayed on this page.

from 100 USD / 100 EUR per month. Your name or company name is displayed on this page, with a link to your website, and a one-line description text.

from 200 USD / 200 EUR per month. Your name or company name and logo displayed on this page, with a link to your website and a custom description text. Companies that have helped FreeCAD early on also appear under Gold sponsors.

Instead of donating each month, you might find it more comfortable to make a one-time donation that, when divided by twelve, would give you right to enter a sponsoring tier. Don't hesitate to do so!

Choose freely the amount you wish to donate each month.

Please inform your forum name or twitter handle as a notein your transfer, or reach to us, so we can give you proper credits!

Rubik’s Cube

Beschreibung
Makro zum Anzeigen eines Rubik’s Cube und zum interaktiven Durchführen von Scheiben-Rotationen.

Versionsmakro : 00.05
Datum der letzten Änderung : 2018-12-15
FreeCAD version : <= 0.17
Herunterladen : Werkzeugleisten-Symbol
Autor: Aleph0
Autor
Aleph0
Herunterladen
Werkzeugleisten-Symbol
Links
Makros Rezepte
Wie man Makros installiert
Symbolleisten anpassen
Macro-Version
00.05
Datum der letzten Änderung
2018-12-15
FreeCAD-Version(s)
<= 0.17
Standardverknüpfung
None
Siehe auch
Makro Megaminx

Beschreibung

Makro zum Anzeigen eines Rubik’s Cube und zum interaktiven Durchführen von Scheiben-Rotationen.

Skript

ToolBar Icon

Macro_Rubik_Cube.FCMacro

# -*- 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.  *
*   step_history() replays the history, popping a dialog box before each  *
*   rotation - this is useful if you have developed an operator using the *
*   model and you want to do the same rotations on a real cube.           *
*                                                                         *
***************************************************************************
*   Copyright © 2018 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.05"
__date__    = "15/12/2018"
__Comment__ = "Virtual Rubik Cube"
__Wiki__ = "https://wiki.freecad.org/index.php?title=Macro_Rubik_Cube"
__Help__ = "see first few lines of macro text"
__Status__ = "stable"
__Requires__ = "freecad 0.16"

#OS: Ubuntu 14.04.5 LTS
#Word size of OS: 64-bit
#Word size of FreeCAD: 64-bit
#Version: 0.16.6703 (Git)
#Build type: None
#Branch: releases/FreeCAD-0-16
#Hash: 2ce5c8d2e3020d05005ed71f710e09e9aa561f40
#Python version: 2.7.6
#Qt version: 4.8.6
#Coin version: 4.0.0a
#OCC version: 6.8.0.oce-0.17

# This parameter determines the speed at which slice rotations are animated
# If you have a faster computer you can increase the value
# This will make the animation smoother
# If you have a slow computer you can decrease the value
# This will make the animation faster but more jerky for large cubes
slowness = 500

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

# This horrible hack is needed because some versions of FreeCAD seem to have
# been built with a mixture of Qt4 and Qt5. In those versions, the Qt5 version
# of setOverrideCursor() doesn't work, but the Qt4 version of MainWindow
# doesn't seem to have a centralWidget(), which we need.
try:
    import PyQt4.QtGui
    def busyCursor():
        PyQt4.QtGui.QApplication.setOverrideCursor(
            PyQt4.QtGui.QCursor(PyQt4.QtCore.Qt.WaitCursor))
    def restoreCursor():
        PyQt4.QtGui.QApplication.restoreOverrideCursor()
except Exception:
    def busyCursor():
        QtGui.QApplication.setOverrideCursor(
            QtGui.QCursor(QtCore.Qt.WaitCursor))
    def restoreCursor():
        QtGui.QApplication.restoreOverrideCursor()

# If this is the first time this macro has been run in this invocation of
# FreeCAD, create our dictionary of document-specific data structures
if not hasattr(FreeCAD, "Rubik_Cube_executed"):
    FreeCAD.Rubik_Cube_executed = 1
    Dictionary = {}

# Create a new document and make it current
App.ActiveDocument = App.newDocument("Rubik_Cube")
Gui.ActiveDocument = Gui.getDocument(str(App.ActiveDocument.Name))

# for debugging
def showMessage(s):
    diag = QtGui.QMessageBox(QtGui.QMessageBox.NoIcon, '', s)
    diag.setWindowModality(QtCore.Qt.NonModal)
    diag.exec_()

# This bit of code pops up the dialog to ask for the size and whether to orient
# inner faces (useful if you are simulating a cube which has non-symmetrical
# patterns instead of plain colours).
defaultSize = 3
class AskSizeWindow(QtGui.QDialog):
    # automagically called when a class instance 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()
        width = 300
        height = 200
        margin = 10
        xpos = geom.center().x() - width / 2
        ypos = geom.center().y() - height / 2
        self.setGeometry(xpos, ypos, width, height)
        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.checkBox = QtGui.QCheckBox("Oriented inner faces", self)
        self.checkBox.setGeometry(QtCore.QRect(width/2 - 100, ypos, 200, 30))
        Dictionary[str(App.ActiveDocument.Name)+"Oriented"] = QtCore.Qt.Unchecked
        self.checkBox.setCheckState(QtCore.Qt.Unchecked)
        self.checkBox.setObjectName("checkBox")
        self.checkBox.stateChanged.connect(self.on_checkBox_stateChanged)
        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 on_checkBox_stateChanged(self, val):
        Dictionary[str(App.ActiveDocument.Name)+"Oriented"] = val
    def onOK(self):
        self.close()
        self.destroy()
# now create the class instance which shows the dialog
AskSizeWindow().exec_()

# Display a wait cursor while we are making the cube
busyCursor()

# Dialog box shown on each step by step_history()
class StepWindow(QtGui.QDialog):
    # automagically called when a class instance is created
    def __init__(self):
        super(StepWindow, self).__init__()
        # Lay out the interactive elements
        mw = Gui.getMainWindow()
        cw = mw.centralWidget()
        ww = 220
        wh = 100
        margin = 10
        self.setWindowTitle("Next Step")
        geom1 = mw.geometry()
        geom2 = cw.geometry()
        xpos = geom1.left() + geom2.center().x() - ww / 2
        buttons = Dictionary[str(App.ActiveDocument.Name)+"buttons"]
        buttonsheight = buttons.geometry().height()
        # The 25 is a bit magic to get the Dialog box just below the buttons
        ypos = geom1.top() + geom2.top() + buttonsheight + 25
        self.setGeometry(xpos, ypos, ww, wh)
        ypos = margin
        self.label_1 = QtGui.QLabel(self)
        self.label_1.setGeometry(QtCore.QRect(ww/2 - 100, ypos, 200, 25)) 
        self.label_1.setObjectName("label_1")
        ypos = ypos + 40
        self.OKbutton = QtGui.QPushButton(self)
        self.OKbutton.setGeometry(QtCore.QRect(margin, ypos, 80, 40))
        self.OKbutton.setText("OK")
        self.OKbutton.clicked.connect(self.onOK)
        self.Quitbutton = QtGui.QPushButton(self)
        xpos = ww - 80 - margin
        self.Quitbutton.setGeometry(QtCore.QRect(xpos, ypos, 80, 40))
        self.Quitbutton.setText("Stop")
        self.Quitbutton.clicked.connect(self.onQuit)
    def onOK(self):
        self.close()
        self.destroy()
    def onQuit(self):
        Dictionary[str(App.ActiveDocument.Name)+"Stepping"] = 0
        self.close()
        self.destroy()

# 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()

# Create a ViewObserver and save it in the dictionary so that it does not get
# garbage-collected
Dictionary[str(App.ActiveDocument.Name)+"ViewObserver"] = ViewObserver()

# This bit of code creates the basic cube model
# It is composed of faces rather than cubes because I haven't found a way of
# making a cube with different coloured faces
# 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()
# I don't make the interior cubies because you can never see them
fcd = App.ActiveDocument
n = Dictionary[str(App.ActiveDocument.Name)+"Size"]
# Make array of 6 faces for each of n x n x n cubies
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
oriented = Dictionary[str(App.ActiveDocument.Name)+"Oriented"]
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):
            if ix != 0 and ix != n-1 and (
              iy != 0 and iy != n-1 and
              iz != 0 and iz != n-1):
                continue
            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)
            if oriented != QtCore.Qt.Unchecked and (
              ix == 0 and iy != 0 and iy != n-1 and iz != 0 and iz != n-1):
                x0y2z1 = Base.Vector(fx-0.5,fy,fz)
                face = Part.Face(Part.makePolygon(
                    [x0y0z0,x0y0z1,x0y2z1,x0y1z1,x0y1z0,x0y0z0]))
            else:
                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"))
            if oriented != QtCore.Qt.Unchecked and (
              ix == n - 1 and iy != 0 and iy != n-1 and iz != 0 and iz != n-1):
                x1y2z1 = Base.Vector(fx+0.5,fy,fz)
                face = Part.Face(Part.makePolygon(
                    [x1y0z0,x1y0z1,x1y2z1,x1y1z1,x1y1z0,x1y0z0]))
            else:
                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"))
            if oriented != QtCore.Qt.Unchecked and (
              ix != 0 and ix != n-1 and iy == 0 and iz != 0 and iz != n-1):
                x1y0z2 = Base.Vector(fx,fy-0.5,fz)
                face = Part.Face(Part.makePolygon(
                    [x0y0z0,x0y0z1,x1y0z1,x1y0z2,x1y0z0,x0y0z0]))
            else:
                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"))
            if oriented != QtCore.Qt.Unchecked and (
              ix != 0 and ix != n-1 and iy == n - 1 and iz != 0 and iz != n-1):
                x1y1z2 = Base.Vector(fx,fy+0.5,fz)
                face = Part.Face(Part.makePolygon(
                    [x0y1z0,x0y1z1,x1y1z1,x1y1z2,x1y1z0,x0y1z0]))
            else:
                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"))
            if oriented != QtCore.Qt.Unchecked and (
              ix != 0 and ix != n-1 and iy != 0 and iy != n-1 and iz == 0):
                x2y0z0 = Base.Vector(fx,fy,fz-0.5)
                face = Part.Face(Part.makePolygon(
                    [x0y0z0,x0y1z0,x1y1z0,x1y0z0,x2y0z0,x0y0z0]))
            else:
                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"))
            if oriented != QtCore.Qt.Unchecked and (
              ix != 0 and ix != n-1 and iy != 0 and iy != n-1 and iz == n - 1):
                x2y0z1 = Base.Vector(fx,fy,fz+0.5)
                face = Part.Face(Part.makePolygon(
                    [x0y0z1,x0y1z1,x1y1z1,x1y0z1,x2y0z1,x0y0z1]))
            else:
                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]

# This bit of code creates the clickable arrows
# Note we make them not selectable because mouse clicking on them
# does a slice rotation instead of selecting the arrow.
for i in range(n):
    fx = i - (n - 1) / 2.0
    fy = -(n / 2.0)
    fz = -(0.2 + n / 2.0)
    fs = "arrowpX"+str(i)
    v0 = Base.Vector(fx-0.1,fy,fz)
    v1 = Base.Vector(fx-0.1,fy,fz-0.5)
    v2 = Base.Vector(fx-0.2,fy,fz-0.5)
    v3 = Base.Vector(fx,fy,fz-0.7)
    v4 = Base.Vector(fx+0.2,fy,fz-0.5)
    v5 = Base.Vector(fx+0.1,fy,fz-0.5)
    v6 = Base.Vector(fx+0.1,fy,fz)
    arrow = fcd.addObject("Part::Feature", fs)
    arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0]))
    arrow.ViewObject.DiffuseColor = [(0.0,0.0,0.0)]
    arrow.ViewObject.RootNode.setName(coin.SbName(fs))
    arrow.ViewObject.Selectable = False
    fy = 0.2 + n / 2.0
    fz = n / 2.0
    fs = "arrowmX"+str(i)
    v0 = Base.Vector(fx-0.1,fy,fz)
    v1 = Base.Vector(fx-0.1,fy+0.5,fz)
    v2 = Base.Vector(fx-0.2,fy+0.5,fz)
    v3 = Base.Vector(fx,fy+0.7,fz)
    v4 = Base.Vector(fx+0.2,fy+0.5,fz)
    v5 = Base.Vector(fx+0.1,fy+0.5,fz)
    v6 = Base.Vector(fx+0.1,fy,fz)
    arrow = fcd.addObject("Part::Feature", fs)
    arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0]))
    arrow.ViewObject.DiffuseColor = [(0.0,0.0,0.0)]
    arrow.ViewObject.RootNode.setName(coin.SbName(fs))
    arrow.ViewObject.Selectable = False
    fx = n / 2.0
    fy = i - (n - 1) / 2.0
    fz = -(0.2 + n / 2.0)
    fs = "arrowpY"+str(i)
    v0 = Base.Vector(fx,fy-0.1,fz)
    v1 = Base.Vector(fx,fy-0.1,fz-0.5)
    v2 = Base.Vector(fx,fy-0.2,fz-0.5)
    v3 = Base.Vector(fx,fy,fz-0.7)
    v4 = Base.Vector(fx,fy+0.2,fz-0.5)
    v5 = Base.Vector(fx,fy+0.1,fz-0.5)
    v6 = Base.Vector(fx,fy+0.1,fz)
    arrow = fcd.addObject("Part::Feature", fs)
    arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0]))
    arrow.ViewObject.DiffuseColor = [(0.0,0.0,0.0)]
    arrow.ViewObject.RootNode.setName(coin.SbName(fs))
    arrow.ViewObject.Selectable = False
    fx = -(0.2 + n / 2.0)
    fy = i - (n - 1) / 2.0
    fz = n / 2.0
    fs = "arrowmY"+str(i)
    v0 = Base.Vector(fx,fy-0.1,fz)
    v1 = Base.Vector(fx-0.5,fy-0.1,fz)
    v2 = Base.Vector(fx-0.5,fy-0.2,fz)
    v3 = Base.Vector(fx-0.7,fy,fz)
    v4 = Base.Vector(fx-0.5,fy+0.2,fz)
    v5 = Base.Vector(fx-0.5,fy+0.1,fz)
    v6 = Base.Vector(fx,fy+0.1,fz)
    arrow = fcd.addObject("Part::Feature", fs)
    arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0]))
    arrow.ViewObject.DiffuseColor = [(0.0,0.0,0.0)]
    arrow.ViewObject.RootNode.setName(coin.SbName(fs))
    arrow.ViewObject.Selectable = False
    fx = n / 2.0
    fy = 0.2 + n / 2.0
    fz = i - (n - 1) / 2.0
    fs = "arrowpZ"+str(i)
    v0 = Base.Vector(fx,fy,fz-0.1)
    v1 = Base.Vector(fx,fy+0.5,fz-0.1)
    v2 = Base.Vector(fx,fy+0.5,fz-0.2)
    v3 = Base.Vector(fx,fy+0.7,fz)
    v4 = Base.Vector(fx,fy+0.5,fz+0.2)
    v5 = Base.Vector(fx,fy+0.5,fz+0.1)
    v6 = Base.Vector(fx,fy,fz+0.1)
    arrow = fcd.addObject("Part::Feature", fs)
    arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0]))
    arrow.ViewObject.DiffuseColor = [(0.0,0.0,0.0)]
    arrow.ViewObject.RootNode.setName(coin.SbName(fs))
    arrow.ViewObject.Selectable = False
    fx = -(0.2 + n / 2.0)
    fy = -(n / 2.0)
    fz = i - (n - 1) / 2.0
    fs = "arrowmZ"+str(i)
    v0 = Base.Vector(fx,fy,fz-0.1)
    v1 = Base.Vector(fx-0.5,fy,fz-0.1)
    v2 = Base.Vector(fx-0.5,fy,fz-0.2)
    v3 = Base.Vector(fx-0.7,fy,fz)
    v4 = Base.Vector(fx-0.5,fy,fz+0.2)
    v5 = Base.Vector(fx-0.5,fy,fz+0.1)
    v6 = Base.Vector(fx,fy,fz+0.1)
    arrow = fcd.addObject("Part::Feature", fs)
    arrow.Shape = Part.Face(Part.makePolygon([v0,v1,v2,v3,v4,v5,v6,v0]))
    arrow.ViewObject.DiffuseColor = [(0.0,0.0,0.0)]
    arrow.ViewObject.RootNode.setName(coin.SbName(fs))
    arrow.ViewObject.Selectable = False

# Utility routine for doLabels() to make a single label
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)

# This bit of code creates some labels for the arrows
# Clicking on one rotates the whole cube
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)

# now call it to make them
doLabels()

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

# Viewfit doesn't seem to do the right thing with MultiViews
# 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)

# 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

# This bit of code disables the default Phong shading
# 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()

# This bit of code persuades FreeCAD'a renderer to put
# 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)

# 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):
                    if fc[j][iy][iz][0] <> None:
                        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):
                            if fc[j][iy][iz][0] <> None:
                                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):
                    if fc[j][iy][iz][0] <> None:
                        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):
                    if fc[j][iy][iz][0] <> None:
                        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):
                            if fc[j][iy][iz][0] <> None:
                                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):
                    if fc[j][iy][iz][0] <> None:
                        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):
                    if fc[ix][j][iz][0] <> None:
                        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):
                            if fc[ix][j][iz][0] <> None:
                                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):
                    if fc[ix][j][iz][0] <> None:
                        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):
                    if fc[ix][j][iz][0] <> None:
                        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):
                            if fc[ix][j][iz][0] <> None:
                                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):
                    if fc[ix][j][iz][0] <> None:
                        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):
                    if fc[ix][iy][j][0] <> None:
                        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):
                            if fc[ix][iy][j][0] <> None:
                                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):
                    if fc[ix][iy][j][0] <> None:
                        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):
                    if fc[ix][iy][j][0] <> None:
                        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):
                          if fc[ix][iy][j][0] <> None:
                            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):
                    if fc[ix][iy][j][0] <> None:
                        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()

# 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
# and you can call them from another macro created by saving history
# 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():
    busyCursor()
    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
    restoreCursor()

# Randomise the cube
def randomise():
    busyCursor()
    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)+")")
    restoreCursor()

# 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():
    busyCursor()
    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)
    restoreCursor()
def step_history():
    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()
    Dictionary[str(fcd.Name)+"Stepping"] = 1
    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])
        w = StepWindow()
        w.label_1.setText("Doing "+wh+"("+str(i)+","+str(j)+")")
        w.label_1.setAlignment(QtCore.Qt.AlignCenter)
        w.exec_()
        if Dictionary[str(fcd.Name)+"Stepping"] == 0:
            break
        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)
def reverse_history():
    busyCursor()
    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)+")")
    restoreCursor()
def undo_history():
    busyCursor()
    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)+")")
    restoreCursor()
def reflectX_history():
    busyCursor()
    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)+")")
    restoreCursor()
def reflectY_history():
    busyCursor()
    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)+")")
    restoreCursor()
def reflectZ_history():
    busyCursor()
    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)+")")
    restoreCursor()
def rotpX_history():
    busyCursor()
    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)+")")
    restoreCursor()
def rotmX_history():
    busyCursor()
    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)+")")
    restoreCursor()
def rotpY_history():
    busyCursor()
    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)+")")
    restoreCursor()
def rotmY_history():
    busyCursor()
    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)+")")
    restoreCursor()
def rotpZ_history():
    busyCursor()
    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)+")")
    restoreCursor()
def rotmZ_history():
    busyCursor()
    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)+")")
    restoreCursor()

# This bit of code creates the buttons at the top of the view window
# The buttons are in a frameless window to save screen space
class ButtonRow(QtGui.QWidget):
    def __init__(self):
        super(ButtonRow, self).__init__()
        mw = Gui.getMainWindow()
        view3DWidget = findView(mw.centralWidget())
        if view3DWidget != None:
            self.setParent(view3DWidget)
        self.setAutoFillBackground(True)
        self.undoButton = QtGui.QPushButton(self)
        self.undoButton.setText("Undo")
        self.undoButton.clicked.connect(self.onUndo)
        self.saveButton = QtGui.QPushButton(self)
        self.saveButton.setText("Copy history to clipboard")
        self.saveButton.clicked.connect(self.onSave)
        self.resetButton = QtGui.QPushButton(self)
        self.resetButton.setText("Reset")
        self.resetButton.clicked.connect(self.onReset)
        self.show()
    def onUndo(self):
        undo()
    def onReset(self):
        reset()
    def onSave(self):
        saveHistory()
    def paintEvent(self, event):
        # we position the buttons every paint
        # in case the window size has changed
        bh = 30
        bw = 80
        mw = Gui.getMainWindow()
        view3DWidget = findView(mw.centralWidget())
        if view3DWidget != None:
            geom = view3DWidget.geometry()
            xpos = 0
            self.setGeometry(xpos, 0, geom.width(), bh + 10)
            gap = (geom.width() - 5 * bw) / 4
            if gap < 0:
                gap = 0
            xpos = gap
            self.undoButton.setGeometry(xpos, 0, bw, bh)
            xpos = xpos + gap + bw
            self.saveButton.setGeometry(xpos, 0, 3 * bw, bh)
            xpos = xpos + 3 * bw + gap
            self.resetButton.setGeometry(xpos, 0, bw, bh)
        # now do the paint, which will paint the buttons as well since they
        # are this widget's children
        super(ButtonRow, self).paintEvent(event)
Dictionary[str(App.ActiveDocument.Name)+"buttons"] = ButtonRow()

# 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():
    busyCursor()
    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)
    # create the buttons at the top of the window
    Dictionary[str(App.ActiveDocument.Name)+"buttons"] = ButtonRow()
    fixLightModel()
    createMultiViews(rotation)
    history = [] # according to Henry Ford
    Dictionary[str(fcd.Name)+"history"] = history
    restoreCursor()
    FreeCAD.Console.PrintLog("fix_reload() done\n")

restoreCursor()

Diese Seite wird abgerufen von https://wiki.freecad.org/Macro_Rubik_Cube

Kontaktiere uns!
Forum GitHub Mastodon Matrix IRC Gitter.im Discord Reddit Twitter Facebook LinkedIn

© Das FreeCAD-Team. Bildnachweis (von oben nach unten): ppemawm, r-frank, epileftric, regis, rider_mortagnais, bejant.

Dieses Projekt wird unterstützt von: , KiCad Services Corp. und andere Sponsoren

GitHubDiese Seite auf GitHub verbessern