#@name tesseract
@inputs 
@outputs 
@persist Vertex:array RotationOrder:array Rotation:table N
@trigger none

if (first()) {
function resetVertices() {
    Vertex = array(
    			vec4(1,   1,  1,  1),
    			vec4(1,   1,  1, -1),
    			vec4(1,   1, -1,  1),
    			vec4(1,   1, -1, -1),
    			vec4(1,  -1,  1,  1),
    			vec4(1,  -1,  1, -1),
    			vec4(1,  -1, -1,  1),
    			vec4(1,  -1, -1, -1),
    			vec4(-1,  1,  1,  1),
    			vec4(-1,  1,  1, -1),
    			vec4(-1,  1, -1,  1),
    			vec4(-1,  1, -1, -1),
    			vec4(-1, -1,  1,  1),
    			vec4(-1, -1,  1, -1),
    			vec4(-1, -1, -1,  1),
    			vec4(-1, -1, -1, -1) 
    )
}

function resetRotation() {
#[    RotationOrder:pushString( "YZ" )
    RotationOrder:pushString( "XW" )
    RotationOrder:pushString( "YW" )
    RotationOrder:pushString( "ZW" )
    RotationOrder:pushString( "XY" )
    RotationOrder:pushString( "XZ" )]#

    RotationOrder = array(
        "YZ",
        "XW",
        "YW",
        "ZW",
        "XY",
        "XZ"
    )

    Rotation["XY", number] = 0
    Rotation["XZ", number] = 0
    Rotation["XW", number] = 0
    Rotation["YZ", number] = 0
    Rotation["YW", number] = 0
    Rotation["ZW", number] = 0
}

function resetCube() {
    resetVertices()
    resetRotation()    
}

function vector4 rotateAroundXY( Vert:vector4, S, C ) {
    local TempX = C * Vert:x() + S * Vert:y()
    local TempY = -S * Vert:x() + C * Vert:y()
    return vec4(TempX, TempY, Vert:z(), Vert:w())
}

function vector4 rotateAroundXZ( Vert:vector4, S, C ) {
    local TempX = C * Vert:x() + S * Vert:z()
    local TempZ = -S * Vert:x() + C * Vert:z()
    return vec4(TempX, Vert:y(), TempZ, Vert:w())    
}

function vector4 rotateAroundXW( Vert:vector4, S, C ) {
    local TempX = C * Vert:x() + S * Vert:w()
    local TempW = -S * Vert:x() + C * Vert:w()
    return vec4(TempX, Vert:y(), Vert:z(), TempW)
}

function vector4 rotateAroundYZ( Vert:vector4, S, C ) {
    local TempY = C * Vert:y() + S * Vert:z()
    local TempZ = -S * Vert:y() + C * Vert:z()
    return vec4(Vert:x(), TempY, TempZ, Vert:w())
}

function vector4 rotateAroundYW( Vert:vector4, S, C ) {
    local TempY = C * Vert:y() - S * Vert:w()
    local TempW = S * Vert:y() + C * Vert:w()
    return vec4(Vert:x(), TempY, Vert:z(), TempW)
}

function vector4 rotateAroundZW( Vert:vector4, S, C ) {
    local TempZ = C * Vert:z() - S * Vert:w()
    local TempW = S * Vert:z() + C * Vert:w()
    return vec4(Vert:x(), Vert:y(), TempZ, TempW)
}

function vector4 getRotatedVertex( Axis:string, Vert:vector4, S, C ) {
    switch (Axis) {
        case "XY",
            return rotateAroundXY( Vert, S, C )
        break
        
        case "XZ",
            return rotateAroundXZ( Vert, S, C )
        break
        
        case "XW",
            return rotateAroundXW( Vert, S, C )
        break
        
        case "YZ",
            return rotateAroundYZ( Vert, S, C )
        break
        
        case "YW",
            return rotateAroundYW( Vert, S, C )
        break
        
        case "ZW",
            return rotateAroundZW( Vert, S, C )
        break   
    }    
    
    return vec4()
}

function applyRotation() {
   # resetVertices()
    
    local Count = Vertex:count()

    foreach (K, Axis:string = RotationOrder) {
        local S = sinr(toRad(Rotation[Axis, number]))
        local C = cosr(toRad(Rotation[Axis, number]))
        
        for (I = 1, Count) {
            Vertex[I, vector4] = getRotatedVertex(Axis, Vertex[I, vector4], S, C)
        }
    }
}

function addRotation(Axis:string, T) {
    Rotation[Axis, number] = T #Rotation[Axis, number] + T
}

function rotateAxis(Axis:string, T) {
    addRotation(Axis, T)    
    applyRotation()
}

function createLine( N, A:vector, B:vector ) {
    if (!holoEntity(N)) {
        holoCreate(N)
        holoParent(N, entity())
    }
    else {
        holoPos(N, entity():toWorld((A + B)/2))
        holoAng(N, (A - B):toAngle())
        holoScaleUnits(N, vec((A - B):length(), 1, 1))
    }
}

function renderCubeVerts(Scalar) {
    foreach (K, V:vector4 = Vertex) {
        if (!holoEntity(K)) { 
            holoCreate(K)
            holoParent(K, entity())
            holoPos(K, entity():toWorld( vec(V)*Scalar ))
            holoScaleUnits(K, vec(3,3,3))
        }
        else {
            holoPos(K, entity():toWorld( vec(V)*Scalar ))
        }
    }    
}

resetCube()


}

interval(60)

while (perf(95)) {
    N++
    if (N == 1)     { rotateAxis("XY", 0.1)  }
    elseif (N == 2) { rotateAxis("YZ", 0.15) }
    elseif (N == 3) { rotateAxis("XW", 0.6)  }
    elseif (N == 4) { rotateAxis("YW", 0.3)  }
    elseif (N == 5) { rotateAxis("YZ", 0.45) }
    elseif (N == 6) { rotateAxis("ZW", 0.5)  }
    else { N = 0 }
    
    local Scale = 24
    
    renderCubeVerts(Scale)
    
    createLine(1000, vec(Vertex[1, vector4])*Scale, vec(Vertex[2, vector4])*Scale)
    createLine(1001, vec(Vertex[1, vector4])*Scale, vec(Vertex[5, vector4])*Scale)
    createLine(1002, vec(Vertex[1, vector4])*Scale, vec(Vertex[4, vector4])*Scale)
    
    createLine(1003, vec(Vertex[2, vector4])*Scale, vec(Vertex[5, vector4])*Scale)
    createLine(1004, vec(Vertex[2, vector4])*Scale, vec(Vertex[4, vector4])*Scale)
}

#CreatePlane(rotatedVerts[0],rotatedVerts[1],rotatedVerts[5],rotatedVerts[4])
