Add fingerPaint script (intermediate version) to script-archive folder
Added folder fingerPaint inside script-archive. In order to run this intermediate version of the fingerPaint script, load fingerPaint/fingerPaint.js .
383
script-archive/fingerPaint/PaintWindow.qml
Normal file
|
@ -0,0 +1,383 @@
|
||||||
|
// import QtQuick 2.5
|
||||||
|
import QtQuick.Controls 1.4
|
||||||
|
import QtQuick 2.6
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
import QtQuick.Controls 1.5
|
||||||
|
import QtQuick.Dialogs 1.1
|
||||||
|
import Hifi 1.0 as Hifi
|
||||||
|
import QtQuick.Controls.Styles 1.4
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import "content"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
width: parent ? parent.width : 100
|
||||||
|
height: parent ? parent.height : 100
|
||||||
|
color: "transparent"
|
||||||
|
signal moved(vector2d position);
|
||||||
|
signal resized(size size);
|
||||||
|
|
||||||
|
signal sendToScript(var message);
|
||||||
|
|
||||||
|
property var channel;
|
||||||
|
|
||||||
|
TabView {
|
||||||
|
id: frame
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 4
|
||||||
|
Tab { title: "content/tabicons/colorpaletteBtn.png"
|
||||||
|
ColorWheel{
|
||||||
|
id: colorPicker
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
function sendToScript( message) { root.sendToScript(message)}
|
||||||
|
}
|
||||||
|
Tab { title: "content/tabicons/linewidthBtn.png"
|
||||||
|
Slider {
|
||||||
|
|
||||||
|
value: 0.25
|
||||||
|
onValueChanged: {
|
||||||
|
root.sendToScript(["width", value]);
|
||||||
|
}
|
||||||
|
|
||||||
|
style: SliderStyle {
|
||||||
|
|
||||||
|
handle: Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
|
||||||
|
implicitWidth: 34
|
||||||
|
implicitHeight: 34
|
||||||
|
border.color: "black"
|
||||||
|
border.width: 1
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent; anchors.margins: 2
|
||||||
|
border.color: "white"; border.width: 1
|
||||||
|
color: "transparent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Tab { title: "content/tabicons/brushesBtn.png"
|
||||||
|
GridLayout {
|
||||||
|
id: grid
|
||||||
|
columns: 3
|
||||||
|
|
||||||
|
Layout.maximumWidth : 100
|
||||||
|
Layout.maximumHeight : 100
|
||||||
|
Layout.fillWidth : true
|
||||||
|
Layout.fillHeight : true
|
||||||
|
Button {
|
||||||
|
//width : Layout.fillWidth
|
||||||
|
//height : Layout.fillHeight
|
||||||
|
Layout.fillWidth : true
|
||||||
|
Layout.fillHeight : true
|
||||||
|
text: ""
|
||||||
|
//iconSource: "content/brushes/paintbrush1.png"
|
||||||
|
onClicked: {
|
||||||
|
root.sendToScript(["brush", img.source]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: img
|
||||||
|
source: "content/brushes/paintbrush1.png"
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
style: ButtonStyle {
|
||||||
|
background: Rectangle {
|
||||||
|
//width : Layout.fillWidth
|
||||||
|
//height : Layout.fillHeight
|
||||||
|
border.width: control.activeFocus ? 2 : 1
|
||||||
|
border.color: "#888"
|
||||||
|
radius: 4
|
||||||
|
gradient: Gradient {
|
||||||
|
GradientStop { position: 0 ; color: control.pressed ? "#ccc" : "#eee" }
|
||||||
|
GradientStop { position: 1 ; color: control.pressed ? "#aaa" : "#ccc" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
//width : Layout.fillWidth
|
||||||
|
//height : Layout.fillHeight
|
||||||
|
Layout.fillWidth : true
|
||||||
|
Layout.fillHeight : true
|
||||||
|
text: ""
|
||||||
|
//iconSource: "content/brushes/paintbrush2.png"
|
||||||
|
//onClicked: {
|
||||||
|
// root.sendToScript(["brush", iconSource]);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//iconSource: "content/brushes/paintbrush1.png"
|
||||||
|
onClicked: {
|
||||||
|
root.sendToScript(["brush", img2.source]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: img2
|
||||||
|
source: "content/brushes/paintbrush2.png"
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
style: ButtonStyle {
|
||||||
|
background: Rectangle {
|
||||||
|
//width : Layout.fillWidth
|
||||||
|
//height : Layout.fillHeight
|
||||||
|
border.width: control.activeFocus ? 2 : 1
|
||||||
|
border.color: "#888"
|
||||||
|
radius: 4
|
||||||
|
gradient: Gradient {
|
||||||
|
GradientStop { position: 0 ; color: control.pressed ? "#ccc" : "#eee" }
|
||||||
|
GradientStop { position: 1 ; color: control.pressed ? "#aaa" : "#ccc" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
Layout.fillWidth : true
|
||||||
|
Layout.fillHeight : true
|
||||||
|
//width : Layout.fillWidth
|
||||||
|
//height : Layout.fillHeight
|
||||||
|
text: ""
|
||||||
|
//iconSource: "content/brushes/paintbrush3.png"
|
||||||
|
//onClicked: {
|
||||||
|
// root.sendToScript(["brush", iconSource]);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//iconSource: "content/brushes/paintbrush1.png"
|
||||||
|
onClicked: {
|
||||||
|
root.sendToScript(["brush", img3.source]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: img3
|
||||||
|
source: "content/brushes/paintbrush3.png"
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
style: ButtonStyle {
|
||||||
|
background: Rectangle {
|
||||||
|
//width : Layout.fillWidth
|
||||||
|
//height : Layout.fillHeight
|
||||||
|
border.width: control.activeFocus ? 2 : 1
|
||||||
|
border.color: "#888"
|
||||||
|
radius: 4
|
||||||
|
gradient: Gradient {
|
||||||
|
GradientStop { position: 0 ; color: control.pressed ? "#ccc" : "#eee" }
|
||||||
|
GradientStop { position: 1 ; color: control.pressed ? "#aaa" : "#ccc" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
Layout.fillWidth : true
|
||||||
|
Layout.fillHeight : true
|
||||||
|
//width : Layout.fillWidth
|
||||||
|
//height : Layout.fillHeight
|
||||||
|
text: ""
|
||||||
|
//iconSource: "content/brushes/paintbrush4.png"
|
||||||
|
//onClicked: {
|
||||||
|
// root.sendToScript(["brush", iconSource]);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//iconSource: "content/brushes/paintbrush1.png"
|
||||||
|
onClicked: {
|
||||||
|
root.sendToScript(["brush", img4.source]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: img4
|
||||||
|
source: "content/brushes/paintbrush4.png"
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
style: ButtonStyle {
|
||||||
|
background: Rectangle {
|
||||||
|
//width : Layout.fillWidth
|
||||||
|
//height : Layout.fillHeight
|
||||||
|
border.width: control.activeFocus ? 2 : 1
|
||||||
|
border.color: "#888"
|
||||||
|
radius: 4
|
||||||
|
gradient: Gradient {
|
||||||
|
GradientStop { position: 0 ; color: control.pressed ? "#ccc" : "#eee" }
|
||||||
|
GradientStop { position: 1 ; color: control.pressed ? "#aaa" : "#ccc" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
Layout.fillWidth : true
|
||||||
|
Layout.fillHeight : true
|
||||||
|
//width : Layout.fillWidth
|
||||||
|
//height : Layout.fillHeight
|
||||||
|
text: ""
|
||||||
|
//iconSource: "content/brushes/paintbrush5.png"
|
||||||
|
//onClicked: {
|
||||||
|
// root.sendToScript(["brush", iconSource]);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//iconSource: "content/brushes/paintbrush1.png"
|
||||||
|
onClicked: {
|
||||||
|
root.sendToScript(["brush", img5.source]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: img5
|
||||||
|
source: "content/brushes/paintbrush5.png"
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
style: ButtonStyle {
|
||||||
|
background: Rectangle {
|
||||||
|
//width : Layout.fillWidth
|
||||||
|
//height : Layout.fillHeight
|
||||||
|
border.width: control.activeFocus ? 2 : 1
|
||||||
|
border.color: "#888"
|
||||||
|
radius: 4
|
||||||
|
gradient: Gradient {
|
||||||
|
GradientStop { position: 0 ; color: control.pressed ? "#ccc" : "#eee" }
|
||||||
|
GradientStop { position: 1 ; color: control.pressed ? "#aaa" : "#ccc" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
Layout.fillWidth : true
|
||||||
|
Layout.fillHeight : true
|
||||||
|
//width : Layout.fillWidth
|
||||||
|
//height : Layout.fillHeight
|
||||||
|
text: ""
|
||||||
|
//iconSource: "content/brushes/paintbrush6.png"
|
||||||
|
//onClicked: {
|
||||||
|
// root.sendToScript(["brush", iconSource]);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//iconSource: "content/brushes/paintbrush1.png"
|
||||||
|
onClicked: {
|
||||||
|
root.sendToScript(["brush", img6.source]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: img6
|
||||||
|
source: "content/brushes/paintbrush6.png"
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
style: ButtonStyle {
|
||||||
|
background: Rectangle {
|
||||||
|
//width : Layout.fillWidth
|
||||||
|
//height : Layout.fillHeight
|
||||||
|
border.width: control.activeFocus ? 2 : 1
|
||||||
|
border.color: "#888"
|
||||||
|
radius: 4
|
||||||
|
gradient: Gradient {
|
||||||
|
GradientStop { position: 0 ; color: control.pressed ? "#ccc" : "#eee" }
|
||||||
|
GradientStop { position: 1 ; color: control.pressed ? "#aaa" : "#ccc" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Tab { title: "content/tabicons/eraser.png"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Button {
|
||||||
|
width : 200
|
||||||
|
height : 50
|
||||||
|
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: "Undo"
|
||||||
|
onClicked: {
|
||||||
|
root.sendToScript(["undo"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
style: ButtonStyle {
|
||||||
|
background: Rectangle {
|
||||||
|
//width : Layout.fillWidth
|
||||||
|
//height : Layout.fillHeight
|
||||||
|
border.width: control.activeFocus ? 2 : 1
|
||||||
|
border.color: "#888"
|
||||||
|
radius: 4
|
||||||
|
gradient: Gradient {
|
||||||
|
GradientStop { position: 0 ; color: control.pressed ? "#ccc" : "#eee" }
|
||||||
|
GradientStop { position: 1 ; color: control.pressed ? "#aaa" : "#ccc" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Tab { title: "content/tabicons/pointingfinger128px.png"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Button {
|
||||||
|
width : 200
|
||||||
|
height : 50
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: "Toggle Hand"
|
||||||
|
onClicked: {
|
||||||
|
root.sendToScript(["hand"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
style: ButtonStyle {
|
||||||
|
background: Rectangle {
|
||||||
|
|
||||||
|
border.width: control.activeFocus ? 2 : 1
|
||||||
|
border.color: "#888"
|
||||||
|
radius: 4
|
||||||
|
gradient: Gradient {
|
||||||
|
GradientStop { position: 0 ; color: control.pressed ? "#ccc" : "#eee" }
|
||||||
|
GradientStop { position: 1 ; color: control.pressed ? "#aaa" : "#ccc" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
style: TabViewStyle {
|
||||||
|
frameOverlap: 1
|
||||||
|
tab: Rectangle {
|
||||||
|
color: styleData.selected ? "steelblue" : "grey"
|
||||||
|
border.color: "grey"
|
||||||
|
implicitWidth: 100
|
||||||
|
implicitHeight: 100
|
||||||
|
radius: 2
|
||||||
|
Text {
|
||||||
|
id: text
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: ""
|
||||||
|
color: styleData.selected ? "white" : "black"
|
||||||
|
}
|
||||||
|
Image {
|
||||||
|
source: styleData.title
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frame: Rectangle { color: "grey" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
16
script-archive/fingerPaint/content/CheckerBoard.qml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
|
||||||
|
Grid {
|
||||||
|
id: root
|
||||||
|
property int cellSide: 5
|
||||||
|
anchors.fill: parent
|
||||||
|
rows: height/cellSide; columns: width/cellSide
|
||||||
|
clip: true
|
||||||
|
Repeater {
|
||||||
|
model: root.columns*root.rows
|
||||||
|
Rectangle {
|
||||||
|
width: root.cellSide; height: root.cellSide
|
||||||
|
color: (index%2 == 0) ? "gray" : "white"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
94
script-archive/fingerPaint/content/ColorUtils.js
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
// creates color value from hue, saturation, brightness, alpha
|
||||||
|
function hsba(h, s, b, a) {
|
||||||
|
var lightness = (2 - s)*b;
|
||||||
|
var satHSL = s*b/((lightness <= 1) ? lightness : 2 - lightness);
|
||||||
|
lightness /= 2;
|
||||||
|
return Qt.hsla(h, satHSL, lightness, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
function clamp(val, min, max){
|
||||||
|
return Math.max(min, Math.min(max, val)) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mix(x, y , a)
|
||||||
|
{
|
||||||
|
return x * (1 - a) + y * a ;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hsva2rgba(hsva) {
|
||||||
|
var c = hsva.z * hsva.y ;
|
||||||
|
var x = c * (1 - Math.abs( (hsva.x * 6) % 2 - 1 )) ;
|
||||||
|
var m = hsva.z - c ;
|
||||||
|
|
||||||
|
if (hsva.x < 1/6 )
|
||||||
|
return Qt.vector4d(c+m, x+m, m, hsva.w) ;
|
||||||
|
else if (hsva.x < 1/3 )
|
||||||
|
return Qt.vector4d(x+m, c+m, m, hsva.w) ;
|
||||||
|
else if (hsva.x < 0.5 )
|
||||||
|
return Qt.vector4d(m, c+m, x+m, hsva.w) ;
|
||||||
|
else if (hsva.x < 2/3 )
|
||||||
|
return Qt.vector4d(m, x+m, c+m, hsva.w) ;
|
||||||
|
else if (hsva.x < 5/6 )
|
||||||
|
return Qt.vector4d(x+m, m, c+m, hsva.w) ;
|
||||||
|
else
|
||||||
|
return Qt.vector4d(c+m, m, x+m, hsva.w) ;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function rgba2hsva(rgba)
|
||||||
|
{
|
||||||
|
var r = rgba.x;
|
||||||
|
var g = rgba.y;
|
||||||
|
var b = rgba.z;
|
||||||
|
var max = Math.max(r, g, b), min = Math.min(r, g, b);
|
||||||
|
var h, s, v = max;
|
||||||
|
|
||||||
|
var d = max - min;
|
||||||
|
s = max === 0 ? 0 : d / max;
|
||||||
|
|
||||||
|
if(max == min){
|
||||||
|
h = 0; // achromatic
|
||||||
|
} else{
|
||||||
|
switch(max){
|
||||||
|
case r:
|
||||||
|
h = (g - b) / d + (g < b ? 6 : 0);
|
||||||
|
break;
|
||||||
|
case g:
|
||||||
|
h = (b - r) / d + 2;
|
||||||
|
break;
|
||||||
|
case b:
|
||||||
|
h = (r - g) / d + 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
h /= 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Qt.vector4d(h, s, v, rgba.w);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// extracts integer color channel value [0..255] from color value
|
||||||
|
function getChannelStr(clr, channelIdx) {
|
||||||
|
return parseInt(clr.toString().substr(channelIdx*2 + 1, 2), 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
//convert to hexa with nb char
|
||||||
|
function intToHexa(val , nb)
|
||||||
|
{
|
||||||
|
var hexaTmp = val.toString(16) ;
|
||||||
|
var hexa = "";
|
||||||
|
var size = hexaTmp.length
|
||||||
|
if (size < nb )
|
||||||
|
{
|
||||||
|
for(var i = 0 ; i < nb - size ; ++i)
|
||||||
|
{
|
||||||
|
hexa += "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hexa + hexaTmp
|
||||||
|
}
|
||||||
|
|
||||||
|
function hexaFromRGBA(red, green, blue, alpha)
|
||||||
|
{
|
||||||
|
return intToHexa(Math.round(red * 255), 2)+intToHexa(Math.round(green * 255), 2)+intToHexa(Math.round(blue * 255), 2);
|
||||||
|
}
|
284
script-archive/fingerPaint/content/ColorWheel.qml
Normal file
|
@ -0,0 +1,284 @@
|
||||||
|
import QtQuick 2.2
|
||||||
|
import QtQuick.Window 2.0
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
|
||||||
|
import "ColorUtils.js" as ColorUtils
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height * 0.666
|
||||||
|
focus: true
|
||||||
|
|
||||||
|
// Color value in RGBA with floating point values between 0.0 and 1.0.
|
||||||
|
|
||||||
|
|
||||||
|
property vector4d colorHSVA: Qt.vector4d(1, 1, 1, 1)
|
||||||
|
QtObject {
|
||||||
|
id: m
|
||||||
|
// Color value in HSVA with floating point values between 0.0 and 1.0.
|
||||||
|
property vector4d colorRGBA: ColorUtils.hsva2rgba(root.colorHSVA)
|
||||||
|
}
|
||||||
|
|
||||||
|
signal accepted
|
||||||
|
|
||||||
|
onAccepted: {
|
||||||
|
var rgba = ColorUtils.hsva2rgba(root.colorHSVA)
|
||||||
|
parent.sendToScript(["color", rgba.x*255 , rgba.y*255, rgba.z*255]);
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: 20
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
Wheel {
|
||||||
|
id: wheel
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.minimumWidth: 200
|
||||||
|
Layout.minimumHeight: 200
|
||||||
|
|
||||||
|
hue: colorHSVA.x
|
||||||
|
saturation: colorHSVA.y
|
||||||
|
onUpdateHS: {
|
||||||
|
colorHSVA = Qt.vector4d(hueSignal,saturationSignal, colorHSVA.z, colorHSVA.w)
|
||||||
|
}
|
||||||
|
onAccepted: {
|
||||||
|
root.accepted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// brightness picker slider
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.minimumWidth: 20
|
||||||
|
Layout.minimumHeight: 200
|
||||||
|
|
||||||
|
//Brightness background
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
gradient: Gradient {
|
||||||
|
GradientStop {
|
||||||
|
id: brightnessBeginColor
|
||||||
|
position: 0.0
|
||||||
|
color: {
|
||||||
|
var rgba = ColorUtils.hsva2rgba(
|
||||||
|
Qt.vector4d(colorHSVA.x,
|
||||||
|
colorHSVA.y, 1, 1))
|
||||||
|
return Qt.rgba(rgba.x, rgba.y, rgba.z, rgba.w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GradientStop {
|
||||||
|
position: 1.0
|
||||||
|
color: "#000000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VerticalSlider {
|
||||||
|
id: brigthnessSlider
|
||||||
|
anchors.fill: parent
|
||||||
|
value: colorHSVA.z
|
||||||
|
onValueChanged: {
|
||||||
|
colorHSVA = Qt.vector4d(colorHSVA.x, colorHSVA.y, value, colorHSVA.w)
|
||||||
|
}
|
||||||
|
onAccepted: {
|
||||||
|
root.accepted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// text inputs
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.minimumWidth: 150
|
||||||
|
Layout.minimumHeight: 200
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
// current color display
|
||||||
|
Rectangle {
|
||||||
|
Layout.minimumWidth: 150
|
||||||
|
Layout.minimumHeight: 50
|
||||||
|
CheckerBoard {
|
||||||
|
cellSide: 5
|
||||||
|
}
|
||||||
|
Rectangle {
|
||||||
|
id: colorDisplay
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
border.width: 1
|
||||||
|
border.color: "black"
|
||||||
|
color: Qt.rgba(m.colorRGBA.x, m.colorRGBA.y, m.colorRGBA.z)
|
||||||
|
opacity: m.colorRGBA.w
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// current color value
|
||||||
|
Item {
|
||||||
|
Layout.minimumWidth: 120
|
||||||
|
Layout.minimumHeight: 25
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: captionBox
|
||||||
|
text: "#"
|
||||||
|
width: 18
|
||||||
|
height: parent.height
|
||||||
|
color: "#AAAAAA"
|
||||||
|
font.pixelSize: 16
|
||||||
|
font.bold: true
|
||||||
|
}
|
||||||
|
PanelBorder {
|
||||||
|
height: parent.height
|
||||||
|
anchors.left : captionBox.right
|
||||||
|
width: parent.width - captionBox.width
|
||||||
|
TextInput {
|
||||||
|
id: currentColor
|
||||||
|
color: "#AAAAAA"
|
||||||
|
selectionColor: "#FF7777AA"
|
||||||
|
font.pixelSize: 16
|
||||||
|
font.capitalization: "AllUppercase"
|
||||||
|
maximumLength: 9
|
||||||
|
focus: true
|
||||||
|
text: ColorUtils.hexaFromRGBA(m.colorRGBA.x, m.colorRGBA.y,
|
||||||
|
m.colorRGBA.z, m.colorRGBA.w)
|
||||||
|
font.family: "Droid Sans"
|
||||||
|
selectByMouse: true
|
||||||
|
validator: RegExpValidator {
|
||||||
|
regExp: /^([A-Fa-f0-9]{6})$/
|
||||||
|
}
|
||||||
|
onEditingFinished: {
|
||||||
|
var colorTmp = Qt.vector4d( parseInt(text.substr(0, 2), 16) / 255,
|
||||||
|
parseInt(text.substr(2, 2), 16) / 255,
|
||||||
|
parseInt(text.substr(4, 2), 16) / 255,
|
||||||
|
colorHSVA.w) ;
|
||||||
|
colorHSVA = ColorUtils.rgba2hsva(colorTmp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// H, S, B color value boxes
|
||||||
|
Column {
|
||||||
|
Layout.minimumWidth: 80
|
||||||
|
Layout.minimumHeight: 25
|
||||||
|
NumberBox {
|
||||||
|
id: hue
|
||||||
|
caption: "H"
|
||||||
|
// TODO: put in NumberBox
|
||||||
|
value: Math.round(colorHSVA.x * 100000) / 100000 // 5 Decimals
|
||||||
|
decimals: 2
|
||||||
|
max: 1
|
||||||
|
min: 0
|
||||||
|
onAccepted: {
|
||||||
|
colorHSVA = Qt.vector4d(boxValue, colorHSVA.y, colorHSVA.z, colorHSVA.w)
|
||||||
|
root.accepted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NumberBox {
|
||||||
|
id: sat
|
||||||
|
caption: "S"
|
||||||
|
value: Math.round(colorHSVA.y * 100) / 100 // 2 Decimals
|
||||||
|
decimals: 2
|
||||||
|
max: 1
|
||||||
|
min: 0
|
||||||
|
onAccepted: {
|
||||||
|
colorHSVA = Qt.vector4d(colorHSVA.x, boxValue, colorHSVA.z, colorHSVA.w)
|
||||||
|
root.accepted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NumberBox {
|
||||||
|
id: brightness
|
||||||
|
caption: "B"
|
||||||
|
value: Math.round(colorHSVA.z * 100) / 100 // 2 Decimals
|
||||||
|
decimals: 2
|
||||||
|
max: 1
|
||||||
|
min: 0
|
||||||
|
onAccepted: {
|
||||||
|
colorHSVA = Qt.vector4d(colorHSVA.x, colorHSVA.y, boxValue, colorHSVA.w)
|
||||||
|
root.accepted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NumberBox {
|
||||||
|
id: hsbAlpha
|
||||||
|
caption: "A"
|
||||||
|
value: Math.round(colorHSVA.w * 100) / 100 // 2 Decimals
|
||||||
|
decimals: 2
|
||||||
|
max: 1
|
||||||
|
min: 0
|
||||||
|
onAccepted: {
|
||||||
|
colorHSVA.w = boxValue
|
||||||
|
root.accepted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// R, G, B color values boxes
|
||||||
|
Column {
|
||||||
|
Layout.minimumWidth: 80
|
||||||
|
Layout.minimumHeight: 25
|
||||||
|
NumberBox {
|
||||||
|
id: red
|
||||||
|
caption: "R"
|
||||||
|
value: Math.round(m.colorRGBA.x * 255)
|
||||||
|
min: 0
|
||||||
|
max: 255
|
||||||
|
decimals: 0
|
||||||
|
onAccepted: {
|
||||||
|
var colorTmp = Qt.vector4d( boxValue / 255,
|
||||||
|
m.colorRGBA.y,
|
||||||
|
m.colorRGBA.z,
|
||||||
|
colorHSVA.w) ;
|
||||||
|
colorHSVA = ColorUtils.rgba2hsva(colorTmp)
|
||||||
|
root.accepted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NumberBox {
|
||||||
|
id: green
|
||||||
|
caption: "G"
|
||||||
|
value: Math.round(m.colorRGBA.y * 255)
|
||||||
|
min: 0
|
||||||
|
max: 255
|
||||||
|
decimals: 0
|
||||||
|
onAccepted: {
|
||||||
|
var colorTmp = Qt.vector4d( m.colorRGBA.x,
|
||||||
|
boxValue / 255,
|
||||||
|
m.colorRGBA.z,
|
||||||
|
colorHSVA.w) ;
|
||||||
|
colorHSVA = ColorUtils.rgba2hsva(colorTmp)
|
||||||
|
root.accepted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NumberBox {
|
||||||
|
id: blue
|
||||||
|
caption: "B"
|
||||||
|
value: Math.round(m.colorRGBA.z * 255)
|
||||||
|
min: 0
|
||||||
|
max: 255
|
||||||
|
decimals: 0
|
||||||
|
onAccepted: {
|
||||||
|
var colorTmp = Qt.vector4d( m.colorRGBA.x,
|
||||||
|
m.colorRGBA.y,
|
||||||
|
boxValue / 255,
|
||||||
|
colorHSVA.w) ;
|
||||||
|
colorHSVA = ColorUtils.rgba2hsva(colorTmp)
|
||||||
|
root.accepted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NumberBox {
|
||||||
|
id: rgbAlpha
|
||||||
|
caption: "A"
|
||||||
|
value: Math.round(m.colorRGBA.w * 255)
|
||||||
|
min: 0
|
||||||
|
max: 255
|
||||||
|
decimals: 0
|
||||||
|
onAccepted: {
|
||||||
|
root.colorHSVA.w = boxValue / 255
|
||||||
|
root.accepted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
64
script-archive/fingerPaint/content/NumberBox.qml
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import QtQuick 2.2
|
||||||
|
import "ColorUtils.js" as ColorUtils
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: root
|
||||||
|
property string caption: ""
|
||||||
|
property real value: 0
|
||||||
|
property real min: 0
|
||||||
|
property real max: 255
|
||||||
|
property int decimals: 2
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: m
|
||||||
|
// Hack: force update of the text after text validation
|
||||||
|
property int forceTextUpdate: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
onValueChanged: {
|
||||||
|
|
||||||
|
console.debug("NumberBox root.value:" + root.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
width: 60
|
||||||
|
height: 20
|
||||||
|
spacing: 0
|
||||||
|
anchors.margins: 5
|
||||||
|
|
||||||
|
signal accepted(var boxValue)
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: captionBox
|
||||||
|
text: root.caption
|
||||||
|
width: 18; height: parent.height
|
||||||
|
color: "#AAAAAA"
|
||||||
|
font.pixelSize: 16; font.bold: true
|
||||||
|
horizontalAlignment: Text.AlignRight; verticalAlignment: Text.AlignBottom
|
||||||
|
anchors.bottomMargin: 3
|
||||||
|
}
|
||||||
|
PanelBorder {
|
||||||
|
height: parent.height
|
||||||
|
anchors.leftMargin: 4;
|
||||||
|
anchors.left: captionBox.right; anchors.right: parent.right
|
||||||
|
TextInput {
|
||||||
|
id: inputBox
|
||||||
|
// Hack: force update of the text if the value is the same after the clamp.
|
||||||
|
text: m.forceTextUpdate ? root.value.toString() : root.value.toString()
|
||||||
|
anchors.leftMargin: 4; anchors.topMargin: 1; anchors.fill: parent
|
||||||
|
color: "#AAAAAA"; selectionColor: "#FF7777AA"
|
||||||
|
font.pixelSize: 14
|
||||||
|
focus: true
|
||||||
|
onEditingFinished: {
|
||||||
|
var newText = ColorUtils.clamp(parseFloat(inputBox.text), root.min, root.max).toString()
|
||||||
|
if(newText != root.value) {
|
||||||
|
root.accepted(newText)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m.forceTextUpdate = m.forceTextUpdate + 1 // Hack: force update
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
16
script-archive/fingerPaint/content/PanelBorder.qml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width : 40; height : 15; radius: 2
|
||||||
|
border.width: 1; border.color: "#FF101010"
|
||||||
|
color: "transparent"
|
||||||
|
anchors.leftMargin: 1; anchors.topMargin: 3
|
||||||
|
clip: true
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent; radius: 2
|
||||||
|
anchors.leftMargin: -1; anchors.topMargin: -1
|
||||||
|
anchors.rightMargin: 0; anchors.bottomMargin: 0
|
||||||
|
border.width: 1; border.color: "#FF525255"
|
||||||
|
color: "transparent"
|
||||||
|
}
|
||||||
|
}
|
72
script-archive/fingerPaint/content/VerticalSlider.qml
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
import "mathUtils.js" as MathUtils
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
width: 15
|
||||||
|
height: 200
|
||||||
|
|
||||||
|
|
||||||
|
property real value
|
||||||
|
signal accepted
|
||||||
|
|
||||||
|
states :
|
||||||
|
// When user is moving the slider
|
||||||
|
State {
|
||||||
|
name: "editing"
|
||||||
|
PropertyChanges {
|
||||||
|
target: root
|
||||||
|
// Initialize with the value in the default state.
|
||||||
|
// Allows to break the link in that state.
|
||||||
|
value: root.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cursor
|
||||||
|
Item {
|
||||||
|
id: pickerCursor
|
||||||
|
width: parent.width
|
||||||
|
height: 8
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: cursor
|
||||||
|
x: -4
|
||||||
|
y: MathUtils.clamp(root.height * (1 - root.value), 0.0, root.height)
|
||||||
|
width: parent.width + -2*x
|
||||||
|
height: parent.height
|
||||||
|
border.color: "black"
|
||||||
|
border.width: 1
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent; anchors.margins: 2
|
||||||
|
border.color: "white"; border.width: 1
|
||||||
|
color: "transparent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mouseAreaSlider
|
||||||
|
anchors.fill: parent
|
||||||
|
property bool isDrag : false
|
||||||
|
function sliderHandleMouse(mouse){
|
||||||
|
root.state = 'editing'
|
||||||
|
if (mouse.buttons & Qt.LeftButton | isDrag) {
|
||||||
|
root.value = MathUtils.clampAndProject(mouse.y, 0.0, height, 1.0, 0.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onPositionChanged: {
|
||||||
|
sliderHandleMouse(mouse)
|
||||||
|
}
|
||||||
|
onPressed: {
|
||||||
|
isDrag = true
|
||||||
|
sliderHandleMouse(mouse)
|
||||||
|
}
|
||||||
|
onReleased: {
|
||||||
|
isDrag = false
|
||||||
|
root.state = ''
|
||||||
|
root.accepted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
145
script-archive/fingerPaint/content/Wheel.qml
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
import QtQuick 2.0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
width: 200
|
||||||
|
height: 200
|
||||||
|
|
||||||
|
property real hue : 1
|
||||||
|
property real saturation : 1
|
||||||
|
|
||||||
|
signal accepted()
|
||||||
|
signal updateHS(var hueSignal, var saturationSignal)
|
||||||
|
|
||||||
|
states :
|
||||||
|
// When user is moving the slider
|
||||||
|
State {
|
||||||
|
name: "editing"
|
||||||
|
PropertyChanges {
|
||||||
|
target: root
|
||||||
|
// Better solution ? because the value is change in the fonction of mouse area
|
||||||
|
hue: hue
|
||||||
|
saturation: saturation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: wheel
|
||||||
|
//Keep the wheel round
|
||||||
|
width: parent.width < parent.height ? parent.width : parent.height ;
|
||||||
|
height: width ;
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
color: "transparent"
|
||||||
|
ShaderEffect {
|
||||||
|
id: shader
|
||||||
|
anchors.fill: parent
|
||||||
|
vertexShader: "
|
||||||
|
uniform highp mat4 qt_Matrix;
|
||||||
|
attribute highp vec4 qt_Vertex;
|
||||||
|
attribute highp vec2 qt_MultiTexCoord0;
|
||||||
|
varying highp vec2 coord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
coord = qt_MultiTexCoord0 - vec2(0.5, 0.5);
|
||||||
|
gl_Position = qt_Matrix * qt_Vertex;
|
||||||
|
}"
|
||||||
|
fragmentShader: "
|
||||||
|
varying highp vec2 coord;
|
||||||
|
|
||||||
|
vec3 hsv2rgb(in vec3 c){
|
||||||
|
vec4 k = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
||||||
|
vec3 p = abs(fract(c.xxx + k.xyz) * 6.0 - k.www);
|
||||||
|
return c.z * mix(k.xxx, clamp(p - k.xxx, 0.0, 1.0), c.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
const float PI = 3.14159265358979323846264;
|
||||||
|
float s = sqrt(coord.x * coord.x + coord.y * coord.y);
|
||||||
|
|
||||||
|
if( s > 0.5 ){
|
||||||
|
gl_FragColor = vec4(0, 0, 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float h = - atan( coord.y / coord.x );
|
||||||
|
s *= 2.0;
|
||||||
|
|
||||||
|
if( coord.x >= 0.0 ){
|
||||||
|
h += PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
h = h / (2.0 * PI);
|
||||||
|
vec3 hsl = vec3(h, s, 1.0);
|
||||||
|
vec3 rgb = hsv2rgb(hsl);
|
||||||
|
gl_FragColor.rgb = rgb;
|
||||||
|
gl_FragColor.a = 1.0;
|
||||||
|
}"
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: pickerCursor
|
||||||
|
x: parent.width/2 * (1 + root.saturation * Math.cos(2 * Math.PI * root.hue - Math.PI)) - r
|
||||||
|
y: parent.width/2 * (1 + root.saturation * Math.sin(-2 * Math.PI * root.hue - Math.PI)) - r
|
||||||
|
property int r : 8
|
||||||
|
Rectangle {
|
||||||
|
width: parent.r*2; height: parent.r*2
|
||||||
|
radius: parent.r
|
||||||
|
border.color: "black"; border.width: 2
|
||||||
|
color: "transparent"
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent; anchors.margins: 2;
|
||||||
|
border.color: "white"; border.width: 2
|
||||||
|
radius: width/2
|
||||||
|
color: "transparent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id : wheelArea
|
||||||
|
property bool isDrag : false
|
||||||
|
|
||||||
|
// Clamp cursor to wheel
|
||||||
|
function keepCursorInWheel(mouse, wheelArea, wheel) {
|
||||||
|
root.state = 'editing'
|
||||||
|
if (mouse.buttons & Qt.LeftButton | isDrag) {
|
||||||
|
// cartesian to polar coords
|
||||||
|
var ro = Math.sqrt(Math.pow(mouse.x-wheel.width/2,2)+Math.pow(mouse.y-wheel.height/2,2));
|
||||||
|
var theta = Math.atan2(((mouse.y-wheel.height/2)*(-1)),((mouse.x-wheel.width/2)));
|
||||||
|
|
||||||
|
// Wheel limit
|
||||||
|
if(ro > wheel.width/2)
|
||||||
|
ro = wheel.width/2;
|
||||||
|
|
||||||
|
// polar to cartesian coords
|
||||||
|
var cursor = Qt.vector2d(0, 0);
|
||||||
|
cursor.x = Math.max(-pickerCursor.r, Math.min(wheelArea.width, ro*Math.cos(theta)+wheel.width/2)-pickerCursor.r);
|
||||||
|
cursor.y = Math.max(-pickerCursor.r, Math.min(wheelArea.height, wheel.height/2-ro*Math.sin(theta)-pickerCursor.r));
|
||||||
|
|
||||||
|
hue = Math.ceil((Math.atan2(((cursor.y+pickerCursor.r-wheel.height/2)*(-1)),((cursor.x+pickerCursor.r-wheel.width/2)))/(Math.PI*2)+0.5)*100)/100
|
||||||
|
saturation = Math.ceil(Math.sqrt(Math.pow(cursor.x+pickerCursor.r-width/2,2)+Math.pow(cursor.y+pickerCursor.r-height/2,2))/wheel.height*2*100)/100;
|
||||||
|
root.updateHS(hue, saturation) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onPositionChanged: {
|
||||||
|
keepCursorInWheel(mouse, wheelArea, wheel)
|
||||||
|
|
||||||
|
}
|
||||||
|
onPressed: {
|
||||||
|
isDrag = true
|
||||||
|
keepCursorInWheel(mouse, wheelArea, wheel)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onReleased: {
|
||||||
|
isDrag = false
|
||||||
|
root.state = ''
|
||||||
|
root.accepted() ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
BIN
script-archive/fingerPaint/content/brushes/paintbrush1.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
script-archive/fingerPaint/content/brushes/paintbrush2.png
Normal file
After Width: | Height: | Size: 119 KiB |
BIN
script-archive/fingerPaint/content/brushes/paintbrush3.png
Normal file
After Width: | Height: | Size: 3 KiB |
BIN
script-archive/fingerPaint/content/brushes/paintbrush4.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
script-archive/fingerPaint/content/brushes/paintbrush5.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
script-archive/fingerPaint/content/brushes/paintbrush6.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
21
script-archive/fingerPaint/content/mathUtils.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
.pragma library
|
||||||
|
|
||||||
|
// FIXME(loicm) It would be better to have these functions available in a global
|
||||||
|
// set of common native C++ functions.
|
||||||
|
|
||||||
|
function clamp(x, min, max) {
|
||||||
|
return Math.max(min, Math.min(x, max));
|
||||||
|
}
|
||||||
|
|
||||||
|
function lerp(x, a, b) {
|
||||||
|
return ((1.0 - x) * a) + (x * b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Linearly project a value x from [xmin, xmax] into [ymin, ymax]
|
||||||
|
function projectValue(x, xmin, xmax, ymin, ymax) {
|
||||||
|
return ((x - xmin) * ymax - (x - xmax) * ymin) / (xmax - xmin)
|
||||||
|
}
|
||||||
|
|
||||||
|
function clampAndProject(x, xmin, xmax, ymin, ymax) {
|
||||||
|
return projectValue(clamp(x, xmin, xmax), xmin, xmax, ymin, ymax)
|
||||||
|
}
|
BIN
script-archive/fingerPaint/content/tabicons/brushesBtn.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
script-archive/fingerPaint/content/tabicons/colorpaletteBtn.png
Normal file
After Width: | Height: | Size: 8.5 KiB |
BIN
script-archive/fingerPaint/content/tabicons/eraser.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
script-archive/fingerPaint/content/tabicons/linewidthBtn.png
Normal file
After Width: | Height: | Size: 641 B |
After Width: | Height: | Size: 1.9 KiB |
654
script-archive/fingerPaint/fingerPaint.js
Normal file
|
@ -0,0 +1,654 @@
|
||||||
|
//
|
||||||
|
// fingerPaint.js
|
||||||
|
//
|
||||||
|
// Created by David Rowe on 15 Feb 2017
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
var tablet,
|
||||||
|
button,
|
||||||
|
BUTTON_NAME = "PAINT",
|
||||||
|
isFingerPainting = false,
|
||||||
|
leftHand = null,
|
||||||
|
rightHand = null,
|
||||||
|
leftBrush = null,
|
||||||
|
rightBrush = null,
|
||||||
|
isLeftHandDominant = false,
|
||||||
|
CONTROLLER_MAPPING_NAME = "com.highfidelity.fingerPaint",
|
||||||
|
isTabletDisplayed = false,
|
||||||
|
HIFI_POINT_INDEX_MESSAGE_CHANNEL = "Hifi-Point-Index",
|
||||||
|
HIFI_GRAB_DISABLE_MESSAGE_CHANNEL = "Hifi-Grab-Disable",
|
||||||
|
HIFI_POINTER_DISABLE_MESSAGE_CHANNEL = "Hifi-Pointer-Disable";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Set up the qml ui
|
||||||
|
var qml = Script.resolvePath('PaintWindow.qml');
|
||||||
|
var window = null;
|
||||||
|
|
||||||
|
var inkSource = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function paintBrush(name) {
|
||||||
|
// Paints in 3D.
|
||||||
|
var brushName = name,
|
||||||
|
STROKE_COLOR = { red: 250, green: 0, blue: 0 },
|
||||||
|
ERASE_SEARCH_RADIUS = 0.1, // m
|
||||||
|
STROKE_DIMENSIONS = { x: 10, y: 10, z: 10 },
|
||||||
|
isDrawingLine = false,
|
||||||
|
entityID,
|
||||||
|
basePosition,
|
||||||
|
strokePoints,
|
||||||
|
strokeNormals,
|
||||||
|
strokeWidths,
|
||||||
|
timeOfLastPoint,
|
||||||
|
texture = null ,
|
||||||
|
//'https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Caris_Tessellation.svg/1024px-Caris_Tessellation.svg.png', // Daantje
|
||||||
|
strokeWidthMultiplier = 0.6,
|
||||||
|
MIN_STROKE_LENGTH = 0.005, // m
|
||||||
|
MIN_STROKE_INTERVAL = 66, // ms
|
||||||
|
MAX_POINTS_PER_LINE = 70; // Hard-coded limit in PolyLineEntityItem.h.
|
||||||
|
|
||||||
|
var undo = null;
|
||||||
|
|
||||||
|
function strokeNormal() {
|
||||||
|
return Vec3.multiplyQbyV(Camera.getOrientation(), Vec3.UNIT_NEG_Z);
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeStrokeColor(red, green, blue) {
|
||||||
|
STROKE_COLOR.red = red;
|
||||||
|
STROKE_COLOR.green = green;
|
||||||
|
STROKE_COLOR.blue = blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStrokeColor() {
|
||||||
|
return STROKE_COLOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeStrokeWidthMultiplier(multiplier) {
|
||||||
|
strokeWidthMultiplier = multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStrokeWidth() {
|
||||||
|
return strokeWidthMultiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeTexture(textureURL) {
|
||||||
|
texture = textureURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
function undoErasing() {
|
||||||
|
if (undo) {
|
||||||
|
Entities.addEntity(undo);
|
||||||
|
undo = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startLine(position, width) {
|
||||||
|
// Start drawing a polyline.
|
||||||
|
|
||||||
|
width = width * strokeWidthMultiplier;
|
||||||
|
|
||||||
|
if (isDrawingLine) {
|
||||||
|
print("ERROR: startLine() called when already drawing line");
|
||||||
|
// Nevertheless, continue on and start a new line.
|
||||||
|
}
|
||||||
|
|
||||||
|
basePosition = position;
|
||||||
|
|
||||||
|
strokePoints = [Vec3.ZERO];
|
||||||
|
strokeNormals = [strokeNormal()];
|
||||||
|
strokeWidths = [width];
|
||||||
|
timeOfLastPoint = Date.now();
|
||||||
|
|
||||||
|
entityID = Entities.addEntity({
|
||||||
|
type: "PolyLine",
|
||||||
|
name: "fingerPainting",
|
||||||
|
color: STROKE_COLOR,
|
||||||
|
position: position,
|
||||||
|
linePoints: strokePoints,
|
||||||
|
normals: strokeNormals,
|
||||||
|
strokeWidths: strokeWidths,
|
||||||
|
textures: texture, // Daantje
|
||||||
|
dimensions: STROKE_DIMENSIONS
|
||||||
|
});
|
||||||
|
|
||||||
|
isDrawingLine = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawLine(position, width) {
|
||||||
|
// Add a stroke to the polyline if stroke is a sufficient length.
|
||||||
|
var localPosition,
|
||||||
|
distanceToPrevious,
|
||||||
|
MAX_DISTANCE_TO_PREVIOUS = 1.0;
|
||||||
|
|
||||||
|
width = width * strokeWidthMultiplier;
|
||||||
|
|
||||||
|
if (!isDrawingLine) {
|
||||||
|
print("ERROR: drawLine() called when not drawing line");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
localPosition = Vec3.subtract(position, basePosition);
|
||||||
|
distanceToPrevious = Vec3.distance(localPosition, strokePoints[strokePoints.length - 1]);
|
||||||
|
|
||||||
|
if (distanceToPrevious > MAX_DISTANCE_TO_PREVIOUS) {
|
||||||
|
// Ignore occasional spurious finger tip positions.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (distanceToPrevious >= MIN_STROKE_LENGTH
|
||||||
|
&& (Date.now() - timeOfLastPoint) >= MIN_STROKE_INTERVAL
|
||||||
|
&& strokePoints.length < MAX_POINTS_PER_LINE) {
|
||||||
|
strokePoints.push(localPosition);
|
||||||
|
strokeNormals.push(strokeNormal());
|
||||||
|
strokeWidths.push(width);
|
||||||
|
timeOfLastPoint = Date.now();
|
||||||
|
|
||||||
|
Entities.editEntity(entityID, {
|
||||||
|
linePoints: strokePoints,
|
||||||
|
normals: strokeNormals,
|
||||||
|
strokeWidths: strokeWidths
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishLine(position, width) {
|
||||||
|
// Finish drawing polyline; delete if it has only 1 point.
|
||||||
|
|
||||||
|
width = width * strokeWidthMultiplier;
|
||||||
|
|
||||||
|
if (!isDrawingLine) {
|
||||||
|
print("ERROR: finishLine() called when not drawing line");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strokePoints.length === 1) {
|
||||||
|
// Delete "empty" line.
|
||||||
|
Entities.deleteEntity(entityID);
|
||||||
|
}
|
||||||
|
|
||||||
|
isDrawingLine = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelLine() {
|
||||||
|
// Cancel any line being drawn.
|
||||||
|
if (isDrawingLine) {
|
||||||
|
Entities.deleteEntity(entityID);
|
||||||
|
isDrawingLine = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function eraseClosestLine(position) {
|
||||||
|
// Erase closest line that is within search radius of finger tip.
|
||||||
|
var entities,
|
||||||
|
entitiesLength,
|
||||||
|
properties,
|
||||||
|
i,
|
||||||
|
pointsLength,
|
||||||
|
j,
|
||||||
|
distance,
|
||||||
|
found = false,
|
||||||
|
foundID,
|
||||||
|
foundDistance = ERASE_SEARCH_RADIUS;
|
||||||
|
|
||||||
|
// Find entities with bounding box within search radius.
|
||||||
|
entities = Entities.findEntities(position, ERASE_SEARCH_RADIUS);
|
||||||
|
|
||||||
|
// Fine polyline entity with closest point within search radius.
|
||||||
|
for (i = 0, entitiesLength = entities.length; i < entitiesLength; i += 1) {
|
||||||
|
properties = Entities.getEntityProperties(entities[i], ["type", "position", "linePoints"]);
|
||||||
|
if (properties.type === "PolyLine") {
|
||||||
|
basePosition = properties.position;
|
||||||
|
for (j = 0, pointsLength = properties.linePoints.length; j < pointsLength; j += 1) {
|
||||||
|
distance = Vec3.distance(position, Vec3.sum(basePosition, properties.linePoints[j]));
|
||||||
|
if (distance <= foundDistance) {
|
||||||
|
found = true;
|
||||||
|
foundID = entities[i];
|
||||||
|
foundDistance = distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete found entity.
|
||||||
|
if (found) {
|
||||||
|
undo = Entities.getEntityProperties(foundID);
|
||||||
|
Entities.deleteEntity(foundID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tearDown() {
|
||||||
|
cancelLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
startLine: startLine,
|
||||||
|
drawLine: drawLine,
|
||||||
|
finishLine: finishLine,
|
||||||
|
cancelLine: cancelLine,
|
||||||
|
eraseClosestLine: eraseClosestLine,
|
||||||
|
tearDown: tearDown,
|
||||||
|
changeStrokeColor: changeStrokeColor,
|
||||||
|
changeStrokeWidthMultiplier: changeStrokeWidthMultiplier,
|
||||||
|
changeTexture: changeTexture,
|
||||||
|
undoErasing: undoErasing,
|
||||||
|
getStrokeColor: getStrokeColor,
|
||||||
|
getStrokeWidth: getStrokeWidth
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function handController(name) {
|
||||||
|
// Translates controller data into application events.
|
||||||
|
var handName = name,
|
||||||
|
|
||||||
|
triggerPressedCallback,
|
||||||
|
triggerPressingCallback,
|
||||||
|
triggerReleasedCallback,
|
||||||
|
gripPressedCallback,
|
||||||
|
|
||||||
|
rawTriggerValue = 0.0,
|
||||||
|
triggerValue = 0.0,
|
||||||
|
isTriggerPressed = false,
|
||||||
|
TRIGGER_SMOOTH_RATIO = 0.1,
|
||||||
|
TRIGGER_OFF = 0.05,
|
||||||
|
TRIGGER_ON = 0.1,
|
||||||
|
TRIGGER_START_WIDTH_RAMP = 0.15,
|
||||||
|
TRIGGER_FINISH_WIDTH_RAMP = 1.0,
|
||||||
|
TRIGGER_RAMP_WIDTH = TRIGGER_FINISH_WIDTH_RAMP - TRIGGER_START_WIDTH_RAMP,
|
||||||
|
MIN_LINE_WIDTH = 0.005,
|
||||||
|
MAX_LINE_WIDTH = 0.03,
|
||||||
|
RAMP_LINE_WIDTH = MAX_LINE_WIDTH - MIN_LINE_WIDTH,
|
||||||
|
|
||||||
|
rawGripValue = 0.0,
|
||||||
|
gripValue = 0.0,
|
||||||
|
isGripPressed = false,
|
||||||
|
GRIP_SMOOTH_RATIO = 0.1,
|
||||||
|
GRIP_OFF = 0.05,
|
||||||
|
GRIP_ON = 0.1;
|
||||||
|
|
||||||
|
function onTriggerPress(value) {
|
||||||
|
// Controller values are only updated when they change so store latest for use in update.
|
||||||
|
rawTriggerValue = value;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTriggerPress(value) {
|
||||||
|
|
||||||
|
var LASER_ALPHA = 0.5;
|
||||||
|
var LASER_TRIGGER_COLOR_XYZW = {x: 250 / 255, y: 10 / 255, z: 10 / 255, w: LASER_ALPHA};
|
||||||
|
var SYSTEM_LASER_DIRECTION = {x: 0, y: 0, z: -1};
|
||||||
|
var LEFT_HUD_LASER = 1;
|
||||||
|
var RIGHT_HUD_LASER = 2;
|
||||||
|
var BOTH_HUD_LASERS = LEFT_HUD_LASER + RIGHT_HUD_LASER;
|
||||||
|
if (isLeftHandDominant){
|
||||||
|
HMD.setHandLasers(RIGHT_HUD_LASER, true, LASER_TRIGGER_COLOR_XYZW, SYSTEM_LASER_DIRECTION);
|
||||||
|
|
||||||
|
HMD.disableHandLasers(LEFT_HUD_LASER);
|
||||||
|
}else{
|
||||||
|
HMD.setHandLasers(LEFT_HUD_LASER, true, LASER_TRIGGER_COLOR_XYZW, SYSTEM_LASER_DIRECTION);
|
||||||
|
HMD.disableHandLasers(RIGHT_HUD_LASER);
|
||||||
|
|
||||||
|
}
|
||||||
|
HMD.disableExtraLaser();
|
||||||
|
|
||||||
|
|
||||||
|
var wasTriggerPressed,
|
||||||
|
fingerTipPosition,
|
||||||
|
lineWidth;
|
||||||
|
|
||||||
|
triggerValue = triggerValue * TRIGGER_SMOOTH_RATIO + rawTriggerValue * (1.0 - TRIGGER_SMOOTH_RATIO);
|
||||||
|
|
||||||
|
wasTriggerPressed = isTriggerPressed;
|
||||||
|
if (isTriggerPressed) {
|
||||||
|
isTriggerPressed = triggerValue > TRIGGER_OFF;
|
||||||
|
} else {
|
||||||
|
isTriggerPressed = triggerValue > TRIGGER_ON;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wasTriggerPressed || isTriggerPressed) {
|
||||||
|
fingerTipPosition = MyAvatar.getJointPosition(handName === "left" ? "LeftHandIndex4" : "RightHandIndex4");
|
||||||
|
|
||||||
|
opositeHandPosition = MyAvatar.getJointPosition(handName === "left" ? "RightHandMiddle1" : "LeftHandMiddle1");
|
||||||
|
|
||||||
|
if (triggerValue < TRIGGER_START_WIDTH_RAMP) {
|
||||||
|
lineWidth = MIN_LINE_WIDTH;
|
||||||
|
} else {
|
||||||
|
lineWidth = MIN_LINE_WIDTH
|
||||||
|
+ (triggerValue - TRIGGER_START_WIDTH_RAMP) / TRIGGER_RAMP_WIDTH * RAMP_LINE_WIDTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((handName === "left" && isLeftHandDominant) || (handName === "right" && !isLeftHandDominant)){
|
||||||
|
if (!wasTriggerPressed && isTriggerPressed) {
|
||||||
|
|
||||||
|
// TEST DAANTJE changes to a random color everytime you start a new line
|
||||||
|
//leftBrush.changeStrokeColor(Math.random()*255, Math.random()*255, Math.random()*255);
|
||||||
|
//rightBrush.changeStrokeColor(Math.random()*255, Math.random()*255, Math.random()*255);
|
||||||
|
// TEST Stroke line width
|
||||||
|
//var dim = Math.random()*4 + +0.5;
|
||||||
|
//var dim2 = Math.floor( Math.random()*40 + 5);
|
||||||
|
//leftBrush.changeStrokeWidthMultiplier(dim);
|
||||||
|
//rightBrush.changeStrokeWidthMultiplier(dim);
|
||||||
|
|
||||||
|
triggerPressedCallback(fingerTipPosition, lineWidth);
|
||||||
|
} else if (wasTriggerPressed && isTriggerPressed) {
|
||||||
|
triggerPressingCallback(fingerTipPosition, lineWidth);
|
||||||
|
} else {
|
||||||
|
triggerReleasedCallback(fingerTipPosition, lineWidth);
|
||||||
|
|
||||||
|
// define condition to switch dominant hands
|
||||||
|
if (Vec3.length(Vec3.subtract(fingerTipPosition, opositeHandPosition)) < 0.1){
|
||||||
|
isLeftHandDominant = !isLeftHandDominant;
|
||||||
|
|
||||||
|
// Test DAANTJE changes texture
|
||||||
|
// if (Math.random() > 0.5) {
|
||||||
|
// leftBrush.changeTexture(null);
|
||||||
|
// rightBrush.changeTexture(null);
|
||||||
|
// }else {
|
||||||
|
// leftBrush.changeTexture('http://i.imgur.com/SSWDJtd.png');
|
||||||
|
// rightBrush.changeTexture('https://upload.wikimedia.org/wikipedia/commons/thumb/9/93/Caris_Tessellation.svg/1024px-Caris_Tessellation.svg.png');
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function onGripPress(value) {
|
||||||
|
// Controller values are only updated when they change so store latest for use in update.
|
||||||
|
rawGripValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateGripPress() {
|
||||||
|
var fingerTipPosition;
|
||||||
|
|
||||||
|
gripValue = gripValue * GRIP_SMOOTH_RATIO + rawGripValue * (1.0 - GRIP_SMOOTH_RATIO);
|
||||||
|
|
||||||
|
if (isGripPressed) {
|
||||||
|
isGripPressed = gripValue > GRIP_OFF;
|
||||||
|
} else {
|
||||||
|
isGripPressed = gripValue > GRIP_ON;
|
||||||
|
if (isGripPressed) {
|
||||||
|
fingerTipPosition = MyAvatar.getJointPosition(handName === "left" ? "LeftHandIndex4" : "RightHandIndex4");
|
||||||
|
|
||||||
|
if ((handName === "left" && isLeftHandDominant) || (handName === "right" && !isLeftHandDominant)){
|
||||||
|
gripPressedCallback(fingerTipPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onUpdate() {
|
||||||
|
|
||||||
|
//update ink Source
|
||||||
|
var strokeColor = leftBrush.getStrokeColor();
|
||||||
|
var strokeWidth = leftBrush.getStrokeWidth()*0.06;
|
||||||
|
|
||||||
|
var position = MyAvatar.getJointPosition(isLeftHandDominant ? "LeftHandIndex4" : "RightHandIndex4");
|
||||||
|
if (inkSource){
|
||||||
|
|
||||||
|
|
||||||
|
Entities.editEntity(inkSource, {
|
||||||
|
color : strokeColor,
|
||||||
|
position : position,
|
||||||
|
dimensions : {
|
||||||
|
x: strokeWidth,
|
||||||
|
y: strokeWidth,
|
||||||
|
z: strokeWidth}
|
||||||
|
|
||||||
|
});
|
||||||
|
} else{
|
||||||
|
var inkSourceProps = {
|
||||||
|
type: "Sphere",
|
||||||
|
name: "inkSource",
|
||||||
|
color: strokeColor,
|
||||||
|
position: position,
|
||||||
|
ignoreForCollisions: true,
|
||||||
|
|
||||||
|
dimensions: {x: strokeWidth, y:strokeWidth, z:strokeWidth}
|
||||||
|
}
|
||||||
|
inkSource = Entities.addEntity(inkSourceProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTriggerPress();
|
||||||
|
updateGripPress();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setUp(onTriggerPressed, onTriggerPressing, onTriggerReleased, onGripPressed) {
|
||||||
|
triggerPressedCallback = onTriggerPressed;
|
||||||
|
triggerPressingCallback = onTriggerPressing;
|
||||||
|
triggerReleasedCallback = onTriggerReleased;
|
||||||
|
gripPressedCallback = onGripPressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tearDown() {
|
||||||
|
// Nothing to do.
|
||||||
|
//Entities
|
||||||
|
if (inkSource){
|
||||||
|
Entities.deleteEntity(inkSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
onTriggerPress: onTriggerPress,
|
||||||
|
onGripPress: onGripPress,
|
||||||
|
onUpdate: onUpdate,
|
||||||
|
setUp: setUp,
|
||||||
|
tearDown: tearDown
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateHandFunctions() {
|
||||||
|
// Update other scripts' hand functions.
|
||||||
|
var enabled = !isFingerPainting || isTabletDisplayed;
|
||||||
|
|
||||||
|
Messages.sendMessage(HIFI_GRAB_DISABLE_MESSAGE_CHANNEL, JSON.stringify({
|
||||||
|
holdEnabled: enabled,
|
||||||
|
nearGrabEnabled: enabled,
|
||||||
|
farGrabEnabled: enabled
|
||||||
|
}), true);
|
||||||
|
|
||||||
|
|
||||||
|
Messages.sendMessage(HIFI_POINTER_DISABLE_MESSAGE_CHANNEL, JSON.stringify({
|
||||||
|
pointerEnabled: false
|
||||||
|
}), true);
|
||||||
|
|
||||||
|
// Messages.sendMessage(HIFI_POINTER_DISABLE_MESSAGE_CHANNEL, JSON.stringify({
|
||||||
|
// pointerEnabled: enabled
|
||||||
|
//}), true);
|
||||||
|
//}), true);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Messages.sendMessage(HIFI_POINT_INDEX_MESSAGE_CHANNEL, JSON.stringify({
|
||||||
|
pointIndex: !enabled
|
||||||
|
}), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableProcessing() {
|
||||||
|
// Connect controller API to handController objects.
|
||||||
|
leftHand = handController("left");
|
||||||
|
rightHand = handController("right");
|
||||||
|
var controllerMapping = Controller.newMapping(CONTROLLER_MAPPING_NAME);
|
||||||
|
controllerMapping.from(Controller.Standard.LT).to(leftHand.onTriggerPress);
|
||||||
|
controllerMapping.from(Controller.Standard.LeftGrip).to(leftHand.onGripPress);
|
||||||
|
controllerMapping.from(Controller.Standard.RT).to(rightHand.onTriggerPress);
|
||||||
|
controllerMapping.from(Controller.Standard.RightGrip).to(rightHand.onGripPress);
|
||||||
|
Controller.enableMapping(CONTROLLER_MAPPING_NAME);
|
||||||
|
|
||||||
|
// Connect handController outputs to paintBrush objects.
|
||||||
|
leftBrush = paintBrush("left");
|
||||||
|
leftHand.setUp(leftBrush.startLine, leftBrush.drawLine, leftBrush.finishLine, leftBrush.eraseClosestLine);
|
||||||
|
rightBrush = paintBrush("right");
|
||||||
|
rightHand.setUp(rightBrush.startLine, rightBrush.drawLine, rightBrush.finishLine, rightBrush.eraseClosestLine);
|
||||||
|
|
||||||
|
// Messages channels for enabling/disabling other scripts' functions.
|
||||||
|
Messages.subscribe(HIFI_POINT_INDEX_MESSAGE_CHANNEL);
|
||||||
|
Messages.subscribe(HIFI_GRAB_DISABLE_MESSAGE_CHANNEL);
|
||||||
|
Messages.subscribe(HIFI_POINTER_DISABLE_MESSAGE_CHANNEL);
|
||||||
|
|
||||||
|
// Update hand controls.
|
||||||
|
Script.update.connect(leftHand.onUpdate);
|
||||||
|
Script.update.connect(rightHand.onUpdate);
|
||||||
|
|
||||||
|
// enable window palette
|
||||||
|
window = new OverlayWindow({
|
||||||
|
title: 'Paint Window',
|
||||||
|
source: qml,
|
||||||
|
width: 600, height: 600,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 75
|
||||||
|
//50
|
||||||
|
window.setPosition(75, 100);
|
||||||
|
//window.closed.connect(function() {
|
||||||
|
//Script.stop();
|
||||||
|
//});
|
||||||
|
|
||||||
|
window.fromQml.connect(function(message){
|
||||||
|
if (message[0] === "color"){
|
||||||
|
leftBrush.changeStrokeColor(message[1], message[2], message[3]);
|
||||||
|
rightBrush.changeStrokeColor(message[1], message[2], message[3]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (message[0] === "width"){
|
||||||
|
var dim = message[1]*2 +0.1;
|
||||||
|
//var dim2 = Math.floor( Math.random()*40 + 5);
|
||||||
|
leftBrush.changeStrokeWidthMultiplier(dim);
|
||||||
|
rightBrush.changeStrokeWidthMultiplier(dim);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (message[0] === "brush"){
|
||||||
|
|
||||||
|
//var dim2 = Math.floor( Math.random()*40 + 5);
|
||||||
|
leftBrush.changeTexture(message[1]);
|
||||||
|
rightBrush.changeTexture(message[1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (message[0] === "undo"){
|
||||||
|
leftBrush.undoErasing();
|
||||||
|
rightBrush.undoErasing();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (message[0] === "hand"){
|
||||||
|
isLeftHandDominant = !isLeftHandDominant;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableProcessing() {
|
||||||
|
Script.update.disconnect(leftHand.onUpdate);
|
||||||
|
Script.update.disconnect(rightHand.onUpdate);
|
||||||
|
|
||||||
|
Controller.disableMapping(CONTROLLER_MAPPING_NAME);
|
||||||
|
|
||||||
|
leftBrush.tearDown();
|
||||||
|
leftBrush = null;
|
||||||
|
leftHand.tearDown();
|
||||||
|
leftHand = null;
|
||||||
|
|
||||||
|
rightBrush.tearDown();
|
||||||
|
rightBrush = null;
|
||||||
|
rightHand.tearDown();
|
||||||
|
rightHand = null;
|
||||||
|
|
||||||
|
Messages.unsubscribe(HIFI_POINT_INDEX_MESSAGE_CHANNEL);
|
||||||
|
Messages.unsubscribe(HIFI_GRAB_DISABLE_MESSAGE_CHANNEL);
|
||||||
|
Messages.unsubscribe(HIFI_POINTER_DISABLE_MESSAGE_CHANNEL);
|
||||||
|
|
||||||
|
// disable window palette
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onButtonClicked() {
|
||||||
|
var wasFingerPainting = isFingerPainting;
|
||||||
|
|
||||||
|
isFingerPainting = !isFingerPainting;
|
||||||
|
button.editProperties({ isActive: isFingerPainting });
|
||||||
|
|
||||||
|
print("Finger painting: " + isFingerPainting ? "on" : "off");
|
||||||
|
|
||||||
|
if (wasFingerPainting) {
|
||||||
|
leftBrush.cancelLine();
|
||||||
|
rightBrush.cancelLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFingerPainting) {
|
||||||
|
enableProcessing();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHandFunctions();
|
||||||
|
|
||||||
|
if (!isFingerPainting) {
|
||||||
|
disableProcessing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onTabletScreenChanged(type, url) {
|
||||||
|
var TABLET_SCREEN_CLOSED = "Closed";
|
||||||
|
|
||||||
|
isTabletDisplayed = type !== TABLET_SCREEN_CLOSED;
|
||||||
|
updateHandFunctions();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setUp() {
|
||||||
|
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||||
|
if (!tablet) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tablet button.
|
||||||
|
button = tablet.addButton({
|
||||||
|
icon: "icons/tablet-icons/finger-paint-i.svg",
|
||||||
|
activeIcon: "icons/tablet-icons/finger-paint-a.svg",
|
||||||
|
text: BUTTON_NAME,
|
||||||
|
isActive: isFingerPainting
|
||||||
|
});
|
||||||
|
button.clicked.connect(onButtonClicked);
|
||||||
|
|
||||||
|
// Track whether tablet is displayed or not.
|
||||||
|
tablet.screenChanged.connect(onTabletScreenChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
function tearDown() {
|
||||||
|
if (!tablet) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFingerPainting) {
|
||||||
|
isFingerPainting = false;
|
||||||
|
updateHandFunctions();
|
||||||
|
disableProcessing();
|
||||||
|
}
|
||||||
|
|
||||||
|
tablet.screenChanged.disconnect(onTabletScreenChanged);
|
||||||
|
|
||||||
|
button.clicked.disconnect(onButtonClicked);
|
||||||
|
tablet.removeButton(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
setUp();
|
||||||
|
Script.scriptEnding.connect(tearDown);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}());
|