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);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}());
|