419 lines
14 KiB
QML
419 lines
14 KiB
QML
/**
|
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and repartition functions.
|
|
* Copyright (C) 2022 Ad5001
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import QtQuick 2.12
|
|
import "js/objects.js" as Objects
|
|
import "js/utils.js" as Utils
|
|
import "js/mathlib.js" as MathLib
|
|
|
|
/*!
|
|
\qmltype LogGraphCanvas
|
|
\inqmlmodule eu.ad5001.LogarithmPlotter
|
|
\brief Canvas used to display the diagram.
|
|
|
|
Provides a customized canvas with several helper methods to be used by objects.
|
|
|
|
\sa LogarithmPlotter, PickLocationOverlay
|
|
*/
|
|
Canvas {
|
|
id: canvas
|
|
anchors.top: separator.bottom
|
|
anchors.left: parent.left
|
|
height: parent.height - 90
|
|
width: parent.width
|
|
|
|
/*!
|
|
\qmlproperty double LogGraphCanvas::xmin
|
|
Minimum x of the diagram, provided from settings.
|
|
\sa Settings
|
|
*/
|
|
property double xmin: 0
|
|
/*!
|
|
\qmlproperty double LogGraphCanvas::ymax
|
|
Maximum y of the diagram, provided from settings.
|
|
\sa Settings
|
|
*/
|
|
property double ymax: 0
|
|
/*!
|
|
\qmlproperty double LogGraphCanvas::xzoom
|
|
Zoom on the x axis of the diagram, provided from settings.
|
|
\sa Settings
|
|
*/
|
|
property double xzoom: 10
|
|
/*!
|
|
\qmlproperty double LogGraphCanvas::yzoom
|
|
Zoom on the y axis of the diagram, provided from settings.
|
|
\sa Settings
|
|
*/
|
|
property double yzoom: 10
|
|
/*!
|
|
\qmlproperty string LogGraphCanvas::xaxisstep
|
|
Step of the x axis graduation, provided from settings.
|
|
\note: Only available in non-logarithmic mode.
|
|
\sa Settings
|
|
*/
|
|
property string xaxisstep: "4"
|
|
/*!
|
|
\qmlproperty string LogGraphCanvas::yaxisstep
|
|
Step of the y axis graduation, provided from settings.
|
|
\sa Settings
|
|
*/
|
|
property string yaxisstep: "4"
|
|
/*!
|
|
\qmlproperty string LogGraphCanvas::xlabel
|
|
Label used on the x axis, provided from settings.
|
|
\sa Settings
|
|
*/
|
|
property string xlabel: ""
|
|
/*!
|
|
\qmlproperty string LogGraphCanvas::ylabel
|
|
Label used on the y axis, provided from settings.
|
|
\sa Settings
|
|
*/
|
|
property string ylabel: ""
|
|
/*!
|
|
\qmlproperty double LogGraphCanvas::linewidth
|
|
Width of lines that will be drawn into the canvas, provided from settings.
|
|
\sa Settings
|
|
*/
|
|
property double linewidth: 1
|
|
/*!
|
|
\qmlproperty double LogGraphCanvas::textsize
|
|
Font size of the text that will be drawn into the canvas, provided from settings.
|
|
\sa Settings
|
|
*/
|
|
property double textsize: 14
|
|
/*!
|
|
\qmlproperty bool LogGraphCanvas::logscalex
|
|
true if the canvas should be in logarithmic mode, false otherwise.
|
|
Provided from settings.
|
|
\sa Settings
|
|
*/
|
|
property bool logscalex: false
|
|
/*!
|
|
\qmlproperty bool LogGraphCanvas::showxgrad
|
|
true if the x graduation should be shown, false otherwise.
|
|
Provided from settings.
|
|
\sa Settings
|
|
*/
|
|
property bool showxgrad: false
|
|
/*!
|
|
\qmlproperty bool LogGraphCanvas::showygrad
|
|
true if the y graduation should be shown, false otherwise.
|
|
Provided from settings.
|
|
\sa Settings
|
|
*/
|
|
property bool showygrad: false
|
|
|
|
/*!
|
|
\qmlproperty int LogGraphCanvas::maxgradx
|
|
Max power of the logarithmic scaled on the x axis in logarithmic mode.
|
|
*/
|
|
property int maxgradx: 20
|
|
|
|
/*!
|
|
\qmlproperty var LogGraphCanvas::yaxisstepExpr
|
|
Expression for the y axis step (used to create labels).
|
|
*/
|
|
property var yaxisstepExpr: (new MathLib.Expression(`x*(${yaxisstep})`))
|
|
/*!
|
|
\qmlproperty double LogGraphCanvas::yaxisstep1
|
|
Value of the for the y axis step.
|
|
*/
|
|
property double yaxisstep1: yaxisstepExpr.execute(1)
|
|
/*!
|
|
\qmlproperty int LogGraphCanvas::drawMaxY
|
|
Minimum value of y that should be drawn onto the canvas.
|
|
*/
|
|
property int drawMaxY: Math.ceil(Math.max(Math.abs(ymax), Math.abs(px2y(canvasSize.height)))/yaxisstep1)
|
|
/*!
|
|
\qmlproperty var LogGraphCanvas::xaxisstepExpr
|
|
Expression for the x axis step (used to create labels).
|
|
*/
|
|
property var xaxisstepExpr: (new MathLib.Expression(`x*(${xaxisstep})`))
|
|
/*!
|
|
\qmlproperty double LogGraphCanvas::xaxisstep1
|
|
Value of the for the x axis step.
|
|
*/
|
|
property double xaxisstep1: xaxisstepExpr.execute(1)
|
|
/*!
|
|
\qmlproperty int LogGraphCanvas::drawMaxX
|
|
Maximum value of x that should be drawn onto the canvas.
|
|
*/
|
|
property int drawMaxX: Math.ceil(Math.max(Math.abs(xmin), Math.abs(px2x(canvasSize.width)))/xaxisstep1)
|
|
|
|
|
|
onPaint: {
|
|
//console.log('Redrawing')
|
|
var ctx = getContext("2d");
|
|
reset(ctx)
|
|
drawGrille(ctx)
|
|
drawAxises(ctx)
|
|
ctx.lineWidth = linewidth
|
|
for(var objType in Objects.currentObjects) {
|
|
for(var obj of Objects.currentObjects[objType]){
|
|
ctx.strokeStyle = obj.color
|
|
ctx.fillStyle = obj.color
|
|
if(obj.visible) obj.draw(canvas, ctx)
|
|
}
|
|
}
|
|
ctx.lineWidth = 1
|
|
drawLabels(ctx)
|
|
|
|
}
|
|
|
|
|
|
/*!
|
|
\qmlmethod void LogGraphCanvas::reset(var ctx)
|
|
Resets the canvas to a blank one with default setting using 2D \c ctx.
|
|
*/
|
|
function reset(ctx){
|
|
// Reset
|
|
ctx.fillStyle = "#FFFFFF"
|
|
ctx.strokeStyle = "#000000"
|
|
ctx.font = `${canvas.textsize-2}px sans-serif`
|
|
ctx.fillRect(0,0,width,height)
|
|
}
|
|
|
|
// Drawing the log based graph
|
|
|
|
/*!
|
|
\qmlmethod void LogGraphCanvas::drawGrille(var ctx)
|
|
Draws the grid using 2D \c ctx.
|
|
*/
|
|
function drawGrille(ctx) {
|
|
ctx.strokeStyle = "#C0C0C0"
|
|
if(logscalex) {
|
|
for(var xpow = -maxgradx; xpow <= maxgradx; xpow++) {
|
|
for(var xmulti = 1; xmulti < 10; xmulti++) {
|
|
drawXLine(ctx, Math.pow(10, xpow)*xmulti)
|
|
}
|
|
}
|
|
} else {
|
|
for(var x = 0; x < drawMaxX; x+=1) {
|
|
drawXLine(ctx, x*xaxisstep1)
|
|
drawXLine(ctx, -x*xaxisstep1)
|
|
}
|
|
}
|
|
for(var y = 0; y < drawMaxY; y+=1) {
|
|
drawYLine(ctx, y*yaxisstep1)
|
|
drawYLine(ctx, -y*yaxisstep1)
|
|
}
|
|
}
|
|
|
|
/*!
|
|
\qmlmethod void LogGraphCanvas::drawAxises(var ctx)
|
|
Draws the graph axises using 2D \c ctx.
|
|
*/
|
|
function drawAxises(ctx) {
|
|
ctx.strokeStyle = "#000000"
|
|
var axisypos = logscalex ? 1 : 0
|
|
drawXLine(ctx, axisypos)
|
|
drawYLine(ctx, 0)
|
|
var axisypx = x2px(axisypos) // X coordinate of Y axis
|
|
var axisxpx = y2px(0) // Y coordinate of X axis
|
|
// Drawing arrows
|
|
drawLine(ctx, axisypx, 0, axisypx-10, 10)
|
|
drawLine(ctx, axisypx, 0, axisypx+10, 10)
|
|
drawLine(ctx, canvasSize.width, axisxpx, canvasSize.width-10, axisxpx-10)
|
|
drawLine(ctx, canvasSize.width, axisxpx, canvasSize.width-10, axisxpx+10)
|
|
}
|
|
|
|
/*!
|
|
\qmlmethod void LogGraphCanvas::drawLabels(var ctx)
|
|
Draws all labels (graduation & axises labels) using 2D \c ctx.
|
|
*/
|
|
function drawLabels(ctx) {
|
|
var axisypx = x2px(logscalex ? 1 : 0) // X coordinate of Y axis
|
|
var axisxpx = y2px(0) // Y coordinate of X axis
|
|
// Labels
|
|
ctx.fillStyle = "#000000"
|
|
ctx.font = `${canvas.textsize+2}px sans-serif`
|
|
ctx.fillText(ylabel, axisypx+10, 24)
|
|
var textSize = ctx.measureText(xlabel).width
|
|
ctx.fillText(xlabel, canvasSize.width-14-textSize, axisxpx-5)
|
|
// Axis graduation labels
|
|
ctx.font = `${canvas.textsize-2}px sans-serif`
|
|
|
|
var txtMinus = ctx.measureText('-').width
|
|
if(showxgrad) {
|
|
if(logscalex) {
|
|
for(var xpow = -maxgradx; xpow <= maxgradx; xpow+=1) {
|
|
var textSize = ctx.measureText("10"+Utils.textsup(xpow)).width
|
|
if(xpow != 0)
|
|
drawVisibleText(ctx, "10"+Utils.textsup(xpow), x2px(Math.pow(10,xpow))-textSize/2, axisxpx+16+(6*(y==0)))
|
|
}
|
|
} else {
|
|
for(var x = 1; x < drawMaxX; x += 1) {
|
|
var drawX = x*xaxisstep1
|
|
var txtX = xaxisstepExpr.simplify(x)
|
|
var textSize = measureText(ctx, txtX, 6).height
|
|
drawVisibleText(ctx, txtX, x2px(drawX)-4, axisxpx+textsize/2+textSize)
|
|
drawVisibleText(ctx, '-'+txtX, x2px(-drawX)-4, axisxpx+textsize/2+textSize)
|
|
}
|
|
}
|
|
}
|
|
if(showygrad) {
|
|
for(var y = 0; y < drawMaxY; y += 1) {
|
|
var drawY = y*yaxisstep1
|
|
var txtY = yaxisstepExpr.simplify(y)
|
|
var textSize = ctx.measureText(txtY).width
|
|
drawVisibleText(ctx, txtY, axisypx-6-textSize, y2px(drawY)+4+(10*(y==0)))
|
|
if(y != 0)
|
|
drawVisibleText(ctx, '-'+txtY, axisypx-6-textSize-txtMinus, y2px(-drawY)+4)
|
|
}
|
|
}
|
|
ctx.fillStyle = "#FFFFFF"
|
|
}
|
|
|
|
/*!
|
|
\qmlmethod void LogGraphCanvas::drawXLine(var ctx, double x)
|
|
Draws an horizontal line at \c x plot coordinate using 2D \c ctx.
|
|
*/
|
|
function drawXLine(ctx, x) {
|
|
if(visible(x, ymax)) {
|
|
drawLine(ctx, x2px(x), 0, x2px(x), canvasSize.height)
|
|
}
|
|
}
|
|
|
|
/*!
|
|
\qmlmethod void LogGraphCanvas::drawXLine(var ctx, double x)
|
|
Draws an vertical line at \c y plot coordinate using 2D \c ctx.
|
|
*/
|
|
function drawYLine(ctx, y) {
|
|
if(visible(xmin, y)) {
|
|
drawLine(ctx, 0, y2px(y), canvasSize.width, y2px(y))
|
|
}
|
|
}
|
|
|
|
/*!
|
|
\qmlmethod void LogGraphCanvas::drawVisibleText(var ctx, string text, double x, double y)
|
|
Writes multline \c text onto the canvas using 2D \c ctx.
|
|
\note The \c x and \c y properties here are relative to the canvas, not the plot.
|
|
*/
|
|
function drawVisibleText(ctx, text, x, y) {
|
|
if(x > 0 && x < canvasSize.width && y > 0 && y < canvasSize.height) {
|
|
text.toString().split("\n").forEach(function(txt, i){
|
|
ctx.fillText(txt, x, y+(canvas.textsize*i))
|
|
})
|
|
}
|
|
}
|
|
|
|
/*!
|
|
\qmlmethod var LogGraphCanvas::measureText(var ctx, string text)
|
|
Measures the wicth and height of a multiline \c text that would be drawn onto the canvas using 2D \c ctx.
|
|
Return format: dictionary {"width": width, "height": height}
|
|
*/
|
|
function measureText(ctx, text) {
|
|
var theight = 0
|
|
var twidth = 0
|
|
text.split("\n").forEach(function(txt, i){
|
|
theight += canvas.textsize
|
|
if(ctx.measureText(txt).width > twidth) twidth = ctx.measureText(txt).width
|
|
})
|
|
return {'width': twidth, 'height': theight}
|
|
}
|
|
|
|
/*!
|
|
\qmlmethod double LogGraphCanvas::x2px(double x)
|
|
Converts an \c x coordinate to it's relative position on the canvas.
|
|
It supports both logarithmic and non logarithmic scale depending on the currently selected mode.
|
|
*/
|
|
function x2px(x) {
|
|
if(logscalex) {
|
|
var logxmin = Math.log(xmin)
|
|
return (Math.log(x)-logxmin)*xzoom
|
|
} else return (x - xmin)*xzoom
|
|
}
|
|
|
|
/*!
|
|
\qmlmethod double LogGraphCanvas::y2px(double y)
|
|
Converts an \c y coordinate to it's relative position on the canvas.
|
|
The y axis not supporting logarithmic scale, it only support linear convertion.
|
|
*/
|
|
function y2px(y) {
|
|
return (ymax-y)*yzoom
|
|
}
|
|
|
|
/*!
|
|
\qmlmethod double LogGraphCanvas::px2x(double px)
|
|
Converts an x \c px position on the canvas to it's corresponding coordinate on the plot.
|
|
It supports both logarithmic and non logarithmic scale depending on the currently selected mode.
|
|
*/
|
|
function px2x(px) {
|
|
if(logscalex) {
|
|
return Math.exp(px/xzoom+Math.log(xmin))
|
|
} else return (px/xzoom+xmin)
|
|
}
|
|
|
|
/*!
|
|
\qmlmethod double LogGraphCanvas::px2x(double px)
|
|
Converts an x \c px position on the canvas to it's corresponding coordinate on the plot.
|
|
It supports both logarithmic and non logarithmic scale depending on the currently selected mode.
|
|
*/
|
|
function px2y(px) {
|
|
return -(px/yzoom-ymax)
|
|
}
|
|
|
|
/*!
|
|
\qmlmethod bool LogGraphCanvas::visible(double x, double y)
|
|
Checks whether a plot point (\c x, \c y) is visible or not on the canvas.
|
|
*/
|
|
function visible(x, y) {
|
|
return (x2px(x) >= 0 && x2px(x) <= canvasSize.width) && (y2px(y) >= 0 && y2px(y) <= canvasSize.height)
|
|
}
|
|
|
|
/*!
|
|
\qmlmethod bool LogGraphCanvas::drawLine(var ctx, double x1, double y1, double x2, double y2)
|
|
Draws a line from plot point (\c x1, \c y1) to plot point (\c x2, \¢ y2) using 2D \c ctx.
|
|
*/
|
|
function drawLine(ctx, x1, y1, x2, y2) {
|
|
ctx.beginPath();
|
|
ctx.moveTo(x1, y1);
|
|
ctx.lineTo(x2, y2);
|
|
ctx.stroke();
|
|
}
|
|
|
|
/*!
|
|
\qmlmethod bool LogGraphCanvas::drawDashedLine2(var ctx, double x1, double y1, double x2, double y2)
|
|
Draws a dashed line from plot point (\c x1, \c y1) to plot point (\c x2, \¢ y2) using 2D \c ctx.
|
|
*/
|
|
function drawDashedLine2(ctx, x1, y1, x2, y2, dashPxSize = 5) {
|
|
ctx.setLineDash([dashPxSize, dashPxSize]);
|
|
drawLine(ctx, x1, y1, x2, y2)
|
|
ctx.setLineDash([]);
|
|
}
|
|
|
|
/*!
|
|
\qmlmethod bool LogGraphCanvas::drawDashedLine(var ctx, double x1, double y1, double x2, double y2)
|
|
Draws a dashed line from plot point (\c x1, \c y1) to plot point (\c x2, \¢ y2) using 2D \c ctx.
|
|
(Legacy slower method)
|
|
*/
|
|
function drawDashedLine(ctx, x1, y1, x2, y2, dashPxSize = 10) {
|
|
var distance = Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))
|
|
var progPerc = dashPxSize/distance
|
|
ctx.beginPath();
|
|
ctx.moveTo(x1, y1);
|
|
for(var i = 0; i < 1; i += progPerc) {
|
|
ctx.lineTo(x1-(x1-x2)*i, y1-(y1-y2)*i)
|
|
ctx.moveTo(x1-(x1-x2)*(i+progPerc/2), y1-(y1-y2)*(i+progPerc/2))
|
|
}
|
|
ctx.stroke();
|
|
}
|
|
}
|