SailTuner/qml/CircleMeter.qml

265 lines
6.3 KiB
QML
Raw Normal View History

2015-12-27 14:01:07 +03:00
import QtQuick 2.0
/**
* Meter in half circle
*/
Item {
/// current level
property double level: 0.5 
2015-12-27 14:01:07 +03:00
/// minimum level
property double min: -50
2015-12-27 14:01:07 +03:00
/// maximum level
property double max: 50
/// numbers to write on the scale
property variant marks: [-40, -20, 0, 20, 40]
2016-01-01 12:39:17 +03:00
/// interval of little marks
property double submarks_int: 5
/// marks regions colors
property variant region_color: ["#F88E48", "#F8DE48", "#99E882", "#F8DE48", "#F88E48"]
2015-12-27 14:01:07 +03:00
/// theme object
property QtObject theme
property double r_circle_min: 0.85
property double r_circle_max: 1
2016-01-01 13:35:28 +03:00
property double r_arrow_base: 0.05
2016-01-01 12:39:17 +03:00
property double amin: angle(min)
property double amax: angle(max)
/// positions helper functions
function angle(level) {
return (level - min) / (max - min) * Math.PI - Math.PI / 2
}
function getx(angle, k) {
return width * 0.5 + width * 0.5 * k * Math.sin(angle)
}
function gety(angle, k) {
// k: [0,1]
return height - height * k * Math.cos(angle)
}
2016-01-01 13:35:28 +03:00
function dist(x1, y1, x2, y2) {
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2))
}
/// objects draw
function arc(ctx, k) {
ctx.beginPath()
ctx.moveTo(getx(amin, k), gety(amin, k))
for (var i = amin + 0.01; i <= amax; i+=0.01) {
ctx.lineTo(getx(i,k), gety(i,k))
}
ctx.stroke()
}
function arc_part(ctx, k, a1, a2) {
ctx.lineTo(getx(a1,k), gety(a1,k))
var eps = 0.01
if (a2 > a1) {
for (var i = a1 + eps; i < a2; i+=eps) {
ctx.lineTo(getx(i,k), gety(i,k))
}
}
else {
for (var i = a1 - eps; i > a2; i-=eps) {
ctx.lineTo(getx(i,k), gety(i,k))
}
}
ctx.lineTo(getx(a2,k), gety(a2,k))
}
function line_mark(ctx, value, r_min, r_max) {
var a = angle(value)
ctx.beginPath()
ctx.moveTo(getx(a, r_min), gety(a, r_min))
ctx.lineTo(getx(a, r_max), gety(a, r_max))
ctx.stroke()
}
function find_region() {
var l1 = min
var l2
for (var i = 0; i < marks.length; i++) {
if (i == marks.length - 1) l2 = max
else l2 = (marks[i] + marks[i+1]) / 2
if (level <= l2) return [i, l1, l2]
l1 = l2
}
return [0,0,0]
}
/// Ellipse
2015-12-27 14:01:07 +03:00
Canvas {
id: ellipse
2015-12-27 14:01:07 +03:00
anchors.fill: parent
property double r_text: 0.92
property double l_marker: 0.035
2016-01-01 12:39:17 +03:00
property double h_marker: 6
property double l_submarker: 0.020
property double h_submarker: 2
property int font_size: 20
2015-12-27 14:01:07 +03:00
onPaint: {
var ctx = getContext('2d');
2015-12-27 14:01:07 +03:00
ctx.strokeStyle = theme.primaryColor
ctx.lineWidth = 1
arc(ctx, r_circle_min)
arc(ctx, r_circle_max)
ctx.font = font_size + "px sans-serif"
ctx.textAlign = "center"
2016-01-01 12:39:17 +03:00
//ctx.fillStyle = theme.secondaryColor
2016-01-01 13:35:28 +03:00
// first sub marks
ctx.strokeStyle = theme.primaryColor
ctx.lineWidth = h_submarker
for (var j = marks[0] - submarks_int; j > min; j -= submarks_int) {
line_mark(ctx, j, r_circle_min - l_submarker, r_circle_min + l_submarker)
}
for (var i = 0; i < marks.length; i++) {
2016-01-01 12:39:17 +03:00
ctx.strokeStyle = theme.secondaryColor
ctx.lineWidth = h_marker
line_mark(ctx, marks[i], r_circle_min - l_marker, r_circle_min + l_marker)
var a = angle(marks[i])
ctx.fillText(marks[i], getx(a, r_text), gety(a, r_text) + 4)
ctx.strokeText()
2016-01-01 12:39:17 +03:00
// sub marks
ctx.strokeStyle = theme.primaryColor
ctx.lineWidth = h_submarker
var j_max = (i == marks.length - 1 ? max : marks[i+1])
for (var j = marks[i] + submarks_int; j < j_max; j += submarks_int) {
line_mark(ctx, j, r_circle_min - l_submarker, r_circle_min + l_submarker)
}
}
// "beetween" marks
ctx.lineWidth = 1
ctx.strokeStyle = theme.primaryColor
for (var i = 0; i < marks.length - 1; i++) {
line_mark(ctx, (marks[i] + marks[i+1])/2, r_circle_min, r_circle_max)
}
2016-01-01 12:39:17 +03:00
// center: arrow base
ctx.lineWidth = 1
ctx.strokeStyle = theme.secondaryColor
arc(ctx, r_arrow_base)
}
}
Canvas {
/// level arrow
id: arrow
anchors.fill: parent
property double k: 0.82
property double angle: parent.angle(level)
2016-01-01 13:35:28 +03:00
property double r_base: 0.06
property double k_head: 0.1 // arrow width factor of base
onPaint: {
var ctx = getContext('2d');
ctx.clearRect(0,0,width,height)
2016-01-01 13:35:28 +03:00
// arrow base center
var x = getx(angle, r_arrow_base + r_base)
var y = gety(angle, r_arrow_base + r_base)
// arrow base radius
var d_base = dist(x, y, getx(angle, r_arrow_base), gety(angle, r_arrow_base))
// arrow head width
var d_head = d_base * k_head
ctx.beginPath()
2016-01-01 13:35:28 +03:00
// base
var x1 = x - d_base * Math.cos(angle)
var y1 = y - d_base * Math.sin(angle)
ctx.moveTo(x1, y1)
ctx.arc(x, y, d_base, Math.PI + angle, angle, 1)
// head
var dx = d_head * Math.cos(angle)
var dy = d_head * Math.sin(angle)
var hx = getx(angle, k)
var hy = gety(angle, k)
ctx.lineTo(hx + dx, hy + dy)
ctx.lineTo(hx - dx, hy - dy)
ctx.closePath()
ctx.stroke()
2016-01-01 13:35:28 +03:00
var grd = ctx.createLinearGradient(dx + hx, dy + hy, x1, y1)
grd.addColorStop(0, 'rgba(128,128,128,0.5)')
grd.addColorStop(1, "transparent")
ctx.fillStyle = grd
ctx.fill()
}
}
Canvas {
/// region colors
id: regions
property variant reg_drawed
anchors.fill: parent
property variant reg: find_region()
property double r_avg: (r_circle_min + r_circle_max) / 2
z: -4
onPaint: {
var ctx = getContext('2d');
ctx.clearRect(0,0,width,height)
var a0 = angle(marks[reg[0]]) // angle of mark
var a1 = angle(reg[1]) // min angle of region
var a2 = angle(reg[2]) // max angle of region
// gradient
var x_center = getx(a0, r_avg);
var y_center = gety(a0, r_avg);
var grd = ctx.createRadialGradient(x_center, y_center, 2, x_center, y_center,
(reg[2] - reg[1]) * (width + height) / 2 / (max - min))
grd.addColorStop(0, region_color[reg[0]])
grd.addColorStop(1, "transparent")
ctx.fillStyle = grd
ctx.beginPath()
ctx.moveTo(getx(a1, r_circle_min), gety(a1, r_circle_min))
arc_part(ctx, r_circle_min, a1, a2)
arc_part(ctx, r_circle_max, a2, a1)
ctx.lineTo(getx(a1, r_circle_min), gety(a1, r_circle_min))
ctx.fill()
reg_drawed = reg
}
function update_level() {
if (!reg_drawed || level > reg_drawed[2] || level < reg_drawed[1]) {
reg = find_region()
requestPaint()
}
}
}
Behavior on level {
NumberAnimation {
duration: 50
easing.amplitude: max - min
2015-12-27 14:01:07 +03:00
}
}
onLevelChanged: {
arrow.requestPaint()
regions.update_level()
}
MouseArea {
anchors.fill: parent
onClicked: {
level = Math.random() * (max - min) + min
}
}
2015-12-27 14:01:07 +03:00
}