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 .
This commit is contained in:
Delanir 2017-06-15 15:04:39 +01:00 committed by Artur Gomes
parent 36ff82db6b
commit 5acc7ae4c9
21 changed files with 1749 additions and 0 deletions

View 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" }
}
}
}

View 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"
}
}
}

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

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

View 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
}
}
}
}
}

View 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"
}
}

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

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View 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)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

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