CatmulRom::CatmulRom()
    : Object()
    , m_curve_color(QVector3D(0.957f, 0.475f, 0.0f))
    , m_draw_curve(true)
    , m_draw_solid(false)
    , m_cube(QVector3D(0, 0, 0), QMatrix3x3())
{
    m_animation_step = 0;
    m_animation_vertex = 1;
    m_cube.set_color(QVector3D(0.5f, 0.5f, 0.5f));
    c_shape_vert = -1;

    m_shape.push_back(QVector3D(0, -1, -1));
    m_shape.push_back(QVector3D(0, -1, 1));
    m_shape.push_back(QVector3D(0, 1, 1));
    m_shape.push_back(QVector3D(0, 1, -1));
}
void QOpenGLTextureBlitterPrivate::blit(GLuint texture,
                                        const QMatrix4x4 &vertexTransform,
                                        QOpenGLTextureBlitter::Origin origin)
{
    TextureBinder binder(texture);
    prepareProgram(vertexTransform);

    if (origin == QOpenGLTextureBlitter::OriginTopLeft) {
        if (textureMatrixUniformState != IdentityFlipped) {
            QMatrix3x3 flipped;
            flipped(1,1) = -1;
            flipped(1,2) = 1;
            program->setUniformValue(textureTransformUniformPos, flipped);
            textureMatrixUniformState = IdentityFlipped;
        }
    } else if (textureMatrixUniformState != Identity) {
        program->setUniformValue(textureTransformUniformPos, QMatrix3x3());
        textureMatrixUniformState = Identity;
    }

    QOpenGLContext::currentContext()->functions()->glDrawArrays(GL_TRIANGLES, 0, 6);
}
QMatrix3x3 CatmulRom::calculate_frame(QVector3D p0, QVector3D p1)
{
    QVector3D t0, t1, Q, B, N;
    t0 = p0 - p1;
    t1 = QVector3D(0, 0, 0) - p1;
    t0.normalize();
    t1.normalize();
    Q = t0 - t1;
    // This is the same frame, so we should return the previous frame translated...
    Q.normalize();
    B = QVector3D::crossProduct(Q, t0);
    N = QVector3D::crossProduct(t0, B);
    N.normalize();
    B.normalize();
    QMatrix3x3 orientation = QMatrix3x3();
    if (Q.x() == 0 && Q.y() == 0 && Q.z() == 0)
    {
        orientation.setToIdentity();
        return orientation;
    }
    float * o_mat = orientation.data();

    // Get frame orientation
    o_mat[0] = t0.x();
    o_mat[1] = B.x();
    o_mat[2] = N.x();

    o_mat[3] = t0.y();
    o_mat[4] = B.y();
    o_mat[5] = N.y();

    o_mat[6] = t0.z();
    o_mat[7] = B.z();
    o_mat[8] = N.z();
    return orientation;
}
void PointViewerMainWindow::handleMessage(QByteArray message)
{
    QList<QByteArray> commandTokens = message.split('\n');
    if (commandTokens.empty())
        return;
    if (commandTokens[0] == "OPEN_FILES")
    {
        QList<QByteArray> flags = commandTokens[1].split('\0');
        bool replaceLabel = flags.contains("REPLACE_LABEL");
        bool deleteAfterLoad = flags.contains("DELETE_AFTER_LOAD");
        bool mutateExisting = flags.contains("MUTATE_EXISTING");
        for (int i = 2; i < commandTokens.size(); ++i)
        {
            QList<QByteArray> pathAndLabel = commandTokens[i].split('\0');
            if (pathAndLabel.size() != 2)
            {
                g_logger.error("Unrecognized OPEN_FILES token: %s",
                               QString(commandTokens[i]));
                continue;
            }
            FileLoadInfo loadInfo(pathAndLabel[0], pathAndLabel[1], replaceLabel);
            loadInfo.deleteAfterLoad = deleteAfterLoad;
            loadInfo.mutateExisting = mutateExisting;
            m_fileLoader->loadFile(loadInfo);
        }
    }
    else if (commandTokens[0] == "CLEAR_FILES")
    {
        m_geometries->clear();
    }
    else if (commandTokens[0] == "UNLOAD_FILES")
    {
        QString regex_str = commandTokens[1];
        QRegExp regex(regex_str, Qt::CaseSensitive, QRegExp::WildcardUnix);
        if (!regex.isValid())
        {
            g_logger.error("Invalid pattern in -unload command: '%s': %s",
                           regex_str, regex.errorString());
            return;
        }
        m_geometries->unloadFiles(regex);
        m_pointView->removeAnnotations(regex);
    }
    else if (commandTokens[0] == "SET_VIEW_LABEL")
    {
        QString regex_str = commandTokens[1];
        QRegExp regex(regex_str, Qt::CaseSensitive, QRegExp::FixedString);
        if (!regex.isValid())
        {
            g_logger.error("Invalid pattern in -unload command: '%s': %s",
                           regex_str, regex.errorString());
            return;
        }
        QModelIndex index = m_geometries->findLabel(regex);
        if (index.isValid())
            m_pointView->centerOnGeometry(index);
    }
    else if (commandTokens[0] == "ANNOTATE")
    {
        if (commandTokens.size() - 1 != 5)
        {
            tfm::format(std::cerr, "Expected five arguments, got %d\n",
                        commandTokens.size() - 1);
            return;
        }
        QString label = commandTokens[1];
        QString text = commandTokens[2];
        bool xOk = false, yOk = false, zOk = false;
        double x = commandTokens[3].toDouble(&xOk);
        double y = commandTokens[4].toDouble(&yOk);
        double z = commandTokens[5].toDouble(&zOk);
        if (!zOk || !yOk || !zOk)
        {
            std::cerr << "Could not parse XYZ coordinates for position\n";
            return;
        }
        m_pointView->addAnnotation(label, text, Imath::V3d(x, y, z));
    }
    else if (commandTokens[0] == "SET_VIEW_POSITION")
    {
        if (commandTokens.size()-1 != 3)
        {
            tfm::format(std::cerr, "Expected three coordinates, got %d\n",
                        commandTokens.size()-1);
            return;
        }
        bool xOk = false, yOk = false, zOk = false;
        double x = commandTokens[1].toDouble(&xOk);
        double y = commandTokens[2].toDouble(&yOk);
        double z = commandTokens[3].toDouble(&zOk);
        if (!zOk || !yOk || !zOk)
        {
            std::cerr << "Could not parse XYZ coordinates for position\n";
            return;
        }
        m_pointView->setExplicitCursorPos(Imath::V3d(x, y, z));
    }
    else if (commandTokens[0] == "SET_VIEW_ANGLES")
    {
        if (commandTokens.size()-1 != 3)
        {
            tfm::format(std::cerr, "Expected three view angles, got %d\n",
                        commandTokens.size()-1);
            return;
        }
        bool yawOk = false, pitchOk = false, rollOk = false;
        double yaw   = commandTokens[1].toDouble(&yawOk);
        double pitch = commandTokens[2].toDouble(&pitchOk);
        double roll  = commandTokens[3].toDouble(&rollOk);
        if (!yawOk || !pitchOk || !rollOk)
        {
            std::cerr << "Could not parse Euler angles for view\n";
            return;
        }
        m_pointView->camera().setRotation(
            QQuaternion::fromAxisAndAngle(0,0,1, roll)  *
            QQuaternion::fromAxisAndAngle(1,0,0, pitch-90) *
            QQuaternion::fromAxisAndAngle(0,0,1, yaw)
        );
    }
    else if (commandTokens[0] == "SET_VIEW_ROTATION")
    {
        if (commandTokens.size()-1 != 9)
        {
            tfm::format(std::cerr, "Expected 9 rotation matrix components, got %d\n",
                        commandTokens.size()-1);
            return;
        }
#       ifdef DISPLAZ_USE_QT4
        qreal rot[9] = {0};
#       else
        float rot[9] = {0};
#       endif
        for (int i = 0; i < 9; ++i)
        {
            bool ok = true;
            rot[i] = commandTokens[i+1].toDouble(&ok);
            if(!ok)
            {
                tfm::format(std::cerr, "badly formatted view matrix message:\n%s", message.constData());
                return;
            }
        }
        m_pointView->camera().setRotation(QMatrix3x3(rot));
    }
    else if (commandTokens[0] == "SET_VIEW_RADIUS")
    {
        bool ok = false;
        double viewRadius = commandTokens[1].toDouble(&ok);
        if (!ok)
        {
            std::cerr << "Could not parse view radius";
            return;
        }
        m_pointView->camera().setEyeToCenterDistance(viewRadius);
    }
    else if (commandTokens[0] == "QUERY_CURSOR")
    {
        // Yuck!
        IpcChannel* channel = dynamic_cast<IpcChannel*>(sender());
        if (!channel)
        {
            qWarning() << "Signalling object not a IpcChannel!\n";
            return;
        }
        V3d p = m_pointView->cursorPos();
        std::string response = tfm::format("%.15g %.15g %.15g", p.x, p.y, p.z);
        channel->sendMessage(QByteArray(response.data(), (int)response.size()));
    }
    else if (commandTokens[0] == "QUIT")
    {
        close();
    }
    else if (commandTokens[0] == "SET_MAX_POINT_COUNT")
    {
        m_maxPointCount = commandTokens[1].toLongLong();
    }
    else if (commandTokens[0] == "OPEN_SHADER")
    {
        openShaderFile(commandTokens[1]);
    }
    else if (commandTokens[0] == "NOTIFY")
    {
        if (commandTokens.size() < 3)
        {
            g_logger.error("Could not parse NOTIFY message: %s", QString::fromUtf8(message));
            return;
        }
        QString spec = QString::fromUtf8(commandTokens[1]);
        QList<QString> specList = spec.split(':');
        if (specList[0].toLower() != "log")
        {
            g_logger.error("Could not parse NOTIFY spec: %s", spec);
            return;
        }

        Logger::LogLevel level = Logger::Info;
        if (specList.size() > 1)
            level = Logger::parseLogLevel(specList[1].toLower().toStdString());

        // Ugh, reassemble message from multiple lines.  Need a better
        // transport serialization!
        QByteArray message;
        for (int i = 2; i < commandTokens.size(); ++i)
        {
            if (i > 2)
                message += "\n";
            message += commandTokens[i];
        }

        g_logger.log(level, "%s", tfm::makeFormatList(message.constData()));
    }
    else if(commandTokens[0] == "HOOK")
    {
        IpcChannel* channel = dynamic_cast<IpcChannel*>(sender());
        if (!channel)
        {
            qWarning() << "Signalling object not a IpcChannel!\n";
            return;
        }
        for(int i=1; i<commandTokens.count(); i+=2)
        {
            HookFormatter* formatter = new HookFormatter(this, commandTokens[i], commandTokens[i+1], channel);
            m_hookManager->connectHook(commandTokens[i], formatter);
        }
    }
    else
    {
        g_logger.error("Unkown remote message:\n%s", QString::fromUtf8(message));
    }
}