TE2.8_Installer.exe
file. At the end of the installation process you will be prompted to select a default profile. Make sure to select the profile that correlates with the size of your Ideum touch table. (If you're using a 55 inch table, select).tangible_engine.h
file as well as a .dll
and .lib
. These can all be removed. Most importantly, is the main.cpp
file where Tangible Engine is being instantiated:
qmlRegisterSingletonType<TangibleEngine>("Qt.TangibleEngine", 1, 5, "TangibleEngine", TangibleEngine::getInstance);
TangibleEngine\
tangibleclient.cpp
tangibleclient.h
tangibledatastructures.h
tangibledatastructures.cpp
tangiblesimulator.h
tangiblesimulator.cpp
tcpthread.cpp
tcpthread.h
TangibleSimulator.qml
.pro
file and in the project file list.main.cpp
file. Here you will need to add a few lines of code to create an instance of the TangibleClient, make it a context property of the QML engine, and initialize it with the application window so that it can handle touch points.
TangibleClient* client = new TangibleClient("Localhost", 4949);
engine.rootContext()->setContextProperty("TangibleEngine", client);
QWindow *main_window = qobject_cast<QWindow*>(engine.rootObjects().first());
client->setup(main_window);
TangibleClient
needs a handle to the window so that it can receive any touch points and send those along to the service. From here, the TangibleClient
instance will handle all communication with the Service on its own, in the background.main.cpp
file for the template project for reference:
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QFileInfo>
#include <QMessageBox>
#include <QQmlContext>
#include <QQmlApplicationEngine>
#include <TangibleEngine/tangibleclient.h>
#include <TangibleEngine/tangiblesimulator.h>
#define USE_TANGIBLE_SIMULATOR
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
app.processEvents();
app.setQuitOnLastWindowClosed(true);
QQmlApplicationEngine engine;
#ifdef USE_TANGIBLE_SIMULATOR
TangibleSimulator * client = new TangibleSimulator();
#else
TangibleClient * client = new TangibleClient("Localhost", 4949);
#endif
qmlRegisterType<Tangible>("Tangibles",2,0,"Tangible");
engine.rootContext()->setContextProperty("TangibleEngine", client);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
QWindow *main_window = qobject_cast<QWindow*>(engine.rootObjects().first());
client->setup(main_window);
return app.exec();
}
TangibleClient
class exposes two lists to QML: the list of patterns that the engine is searching for, and a list of tangibles that are currently recognized. The first list serves as a means to cross-reference recognized tangibles with a list of patterns. This allows you to have complete control of how the application reacts to unique tangibles. The second list is updated with every message received from the service and takes the form of a QAbstractListModel
. In our example we use it as the model for a Repeater
component.
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
id: mainWindow
visibility: "FullScreen"
visible: true
Item
{
focus: true
Keys.onEscapePressed: Qt.quit()
}
Repeater
{
model: TangibleEngine.tangibles
TangibleVisual { tangible: model.tangible }
}
}
TangibleVisual
component is a simple visual representation of a tangible object that pulls the position, rotation, and other displayable information from the model.
import QtQuick 2.0
import Tangibles 2.0
Item {
property Tangible tangible
readonly property string name: TangibleEngine.patterns[num].name
readonly property real tangible_rotation: tangible.R * 180 / Math.PI
readonly property int num: tangible.patternId
readonly property color color: num >= 8 ? "black" : ["black","green","blue","red","magenta","darkblue","gold","gray","cyan"][num]
id: root
x: tangible.X
y: tangible.Y
////////////////////////////////////////////////////////////////////////
// Tangible Visual Elements (reticle, text)
////////////////////////////////////////////////////////////////////////
SequentialAnimation
{
running: true
loops: Animation.Infinite
NumberAnimation
{
target: root
property: "opacity"
from: 1.0
to: 0.5
easing.type: Easing.InOutQuad
duration: 1000
}
NumberAnimation
{
target: root
property: "opacity"
from: 0.5
to: 1.0
easing.type: Easing.InOutQuad
duration: 1000
}
}
Column
{
anchors.horizontalCenter: parent.horizontalCenter
y: -ring.height/2-128
Text
{
text: "ID: "+root.name
color: root.color
font.pixelSize: 32
anchors.horizontalCenter: parent.horizontalCenter
}
Item
{
height: 32
anchors.horizontalCenter: parent.horizontalCenter
width: xpos.contentWidth+ypos.contentWidth+16
Text
{
id: xpos
anchors.right: parent.horizontalCenter
anchors.rightMargin: contentHeight/4
text: "X: "+root.x.toFixed(1)+", "
color: root.color
font.pixelSize: 32
}
Text
{
id: ypos
anchors.left: parent.horizontalCenter
anchors.leftMargin: contentHeight/4
text: "Y: "+root.y.toFixed(1)
color: root.color
font.pixelSize: 32
}
}
Text
{
text: "Rotation: "+root.tangible_rotation.toFixed(0)
color: root.color
font.pixelSize: 32
anchors.horizontalCenter: parent.horizontalCenter
}
}
Item
{
rotation: root.tangible_rotation
Rectangle
{
id: ring
anchors.centerIn: parent
width: 350
height: width
radius: width/2
color: "transparent"
border.width: 2
border.color: root.color
}
Rectangle
{
anchors.horizontalCenter: ring.right
anchors.verticalCenter: ring.verticalCenter
width: 30
height: 2
color: root.color
}
Rectangle
{
anchors.horizontalCenter: ring.left
anchors.verticalCenter: ring.verticalCenter
width: 30
height: 2
color: root.color
}
Rectangle
{
anchors.horizontalCenter: ring.horizontalCenter
anchors.verticalCenter: ring.top
width: 2
height: 30
color: root.color
}
Rectangle
{
anchors.horizontalCenter: ring.horizontalCenter
anchors.verticalCenter: ring.bottom
width: 2
height: 30
color: root.color
}
}
}