diff --git a/qml/LogGraph.qml b/qml/LogGraph.qml index d2b3cc2..865c9a0 100644 --- a/qml/LogGraph.qml +++ b/qml/LogGraph.qml @@ -66,8 +66,8 @@ ApplicationWindow { anchors.left: parent.left anchors.topMargin: 5 anchors.leftMargin: 5 + anchors.bottom: parent.bottom width: parent.width - 10 - height: parent.height - sidebarContents.x; currentIndex: sidebarSelector.currentIndex z: -1 clip: true @@ -101,12 +101,10 @@ ApplicationWindow { yaxisstep: settings.yaxisstep xaxisstep: settings.xaxisstep logscalex: settings.logscalex + linewidth: settings.linewidth + textsize: settings.textsize showxgrad: settings.showxgrad showygrad: settings.showygrad - - onPaint: { - var ctx = getContext("2d"); - } } function saveDiagram(filename) { @@ -130,6 +128,8 @@ ApplicationWindow { "xaxislabel": settings.xaxislabel, "yaxislabel": settings.yaxislabel, "logscalex": settings.logscalex, + "linewidth": settings.linewidth, + "textsize": settings.textsize, "width": root.width, "height": root.height, "objects": objs, @@ -151,6 +151,10 @@ ApplicationWindow { settings.xaxislabel = data["xaxislabel"] settings.yaxislabel = data["yaxislabel"] settings.logscalex = data["logscalex"] + if("linewidth" in data) + settings.linewidth = data["linewidth"] + if("textsize" in data) + settings.textsize = data["textsize"] root.height = data["height"] root.width = data["width"] @@ -176,6 +180,8 @@ ApplicationWindow { } else { objectLists.update() } + } else { + error = "Invalid file provided." } if(error != "") { diff --git a/qml/LogGraphCanvas.qml b/qml/LogGraphCanvas.qml index fadbde3..cbad153 100644 --- a/qml/LogGraphCanvas.qml +++ b/qml/LogGraphCanvas.qml @@ -38,6 +38,8 @@ Canvas { property string xlabel: "" property string ylabel: "" property int maxgradx: 8 + property double linewidth: 1 + property double textsize: 14 property bool logscalex: false property bool showxgrad: false property bool showygrad: false @@ -56,6 +58,7 @@ Canvas { 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 @@ -63,6 +66,7 @@ Canvas { if(obj.visible) obj.draw(canvas, ctx) } } + ctx.lineWidth = 1 drawLabels(ctx) } @@ -71,7 +75,7 @@ Canvas { // Reset ctx.fillStyle = "#FFFFFF" ctx.strokeStyle = "#000000" - ctx.font = "12px sans-serif" + ctx.font = `${canvas.textsize-2}px sans-serif` ctx.fillRect(0,0,width,height) } @@ -115,12 +119,12 @@ Canvas { var axisxpx = y2px(0) // Y coordinate of X axis // Labels ctx.fillStyle = "#000000" - ctx.font = "16px sans-serif" - ctx.fillText(ylabel, axisypx+5, 24) + 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 = "12px sans-serif" + ctx.font = `${canvas.textsize-2}px sans-serif` var txtMinus = ctx.measureText('-').width if(showxgrad) { @@ -135,8 +139,8 @@ Canvas { var drawX = x*xaxisstep1 var txtX = xaxisstepExpr.simplify(x) var textSize = measureText(ctx, txtX, 6).height - drawVisibleText(ctx, txtX, x2px(drawX)-4, axisxpx+6+textSize) - drawVisibleText(ctx, '-'+txtX, x2px(-drawX)-4, axisxpx+6+textSize) + drawVisibleText(ctx, txtX, x2px(drawX)-4, axisxpx+textsize/2+textSize) + drawVisibleText(ctx, '-'+txtX, x2px(-drawX)-4, axisxpx+textsize/2+textSize) } } } @@ -165,20 +169,20 @@ Canvas { } } - function drawVisibleText(ctx, text, x, y, lineHeight = 14) { + 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+(lineHeight*i)) + ctx.fillText(txt, x, y+(canvas.textsize*i)) }) } } // Method to calculate multi-line string dimensions - function measureText(ctx, text, lineHeight=14) { + function measureText(ctx, text) { var theight = 0 var twidth = 0 text.split("\n").forEach(function(txt, i){ - theight += lineHeight + theight += canvas.textsize if(ctx.measureText(txt).width > twidth) twidth = ctx.measureText(txt).width }) return {'width': twidth, 'height': theight} @@ -217,6 +221,12 @@ Canvas { ctx.stroke(); } + function drawDashedLine2(ctx, x1, y1, x2, y2, dashPxSize = 5) { + ctx.setLineDash([dashPxSize, dashPxSize]); + drawLine(ctx, x1, y1, x2, y2) + ctx.setLineDash([]); + } + 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 diff --git a/qml/Settings.qml b/qml/Settings.qml index 358f2ab..549b9d6 100644 --- a/qml/Settings.qml +++ b/qml/Settings.qml @@ -35,15 +35,18 @@ ScrollView { property string yaxisstep: "4" property string xaxislabel: "" property string yaxislabel: "" + property double linewidth: 1 + property double textsize: 14 property bool logscalex: true property string saveFilename: "" property bool showxgrad: true property bool showygrad: true Column { - height: 30*9 //30*Math.max(1, Math.ceil(7 / columns)) + //height: 30*12 //30*Math.max(1, Math.ceil(7 / columns)) //columns: Math.floor(width / settingWidth) spacing: 10 + FileDialog { id: fdiag onAccepted: { @@ -61,7 +64,6 @@ ScrollView { } } - // Line 1 // Zoom TextSetting { id: zoomX @@ -120,6 +122,20 @@ ScrollView { } } + TextSetting { + id: xAxisStep + height: 30 + label: "X Axis Step" + icon: "icons/settings/xaxisstep.svg" + width: settings.settingWidth + defValue: settings.xaxisstep + visible: !settings.logscalex + onChanged: function(newValue) { + settings.xaxisstep = newValue + settings.changed() + } + } + TextSetting { id: yAxisStep height: 30 @@ -134,15 +150,31 @@ ScrollView { } TextSetting { - id: xAxisStep + id: lineWidth height: 30 - label: "X Axis Step" - icon: "icons/settings/xaxisstep.svg" + isDouble: true + label: "Line width" + min: 1 + icon: "icons/settings/linewidth.svg" width: settings.settingWidth - defValue: settings.xaxisstep - visible: !settings.logscalex + defValue: settings.linewidth onChanged: function(newValue) { - settings.xaxisstep = newValue + settings.linewidth = newValue + settings.changed() + } + } + + TextSetting { + id: textSize + height: 30 + isDouble: true + label: "Text size (px)" + min: 1 + icon: "icons/settings/textsize.svg" + width: settings.settingWidth + defValue: settings.textsize + onChanged: function(newValue) { + settings.textsize = newValue settings.changed() } } diff --git a/qml/icons/settings/linewidth.svg b/qml/icons/settings/linewidth.svg new file mode 100644 index 0000000..3152404 --- /dev/null +++ b/qml/icons/settings/linewidth.svg @@ -0,0 +1,80 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/qml/icons/settings/textsize.svg b/qml/icons/settings/textsize.svg new file mode 100644 index 0000000..45f0ebd --- /dev/null +++ b/qml/icons/settings/textsize.svg @@ -0,0 +1,104 @@ + + + + + + + + + + image/svg+xml + + + 2021 + + + Ad5001 + + + + + (C) Ad5001 2021 - Licensed under CC4.0-BY-NC-SA + + + + + + + + + + + + + + + + + + + + diff --git a/qml/js/mathlib.js b/qml/js/mathlib.js index 60375ea..195796e 100644 --- a/qml/js/mathlib.js +++ b/qml/js/mathlib.js @@ -42,6 +42,13 @@ parser.functions.integral = function(a, b, f, variable) { return (b-a)/6*(f(a)+4*f((a+b)/2)+f(b)) } +const DERIVATION_PRECISION = 0.1 + +parser.functions.derivative = function(f, variable, x) { + f = parser.parse(f).toJSFunction(variable, currentVars) + return (f(x+DERIVATION_PRECISION/2)-f(x-DERIVATION_PRECISION/2))/DERIVATION_PRECISION +} + class Expression { constructor(expr) { this.expr = expr diff --git a/qml/js/objects.js b/qml/js/objects.js index f5d9c24..bb9c9ec 100644 --- a/qml/js/objects.js +++ b/qml/js/objects.js @@ -163,7 +163,7 @@ class Point extends DrawableObject { break; } var text = this.getLabel() - ctx.font = "14px sans-serif" + ctx.font = `${canvas.textsize}px sans-serif` var textSize = ctx.measureText(text).width switch(this.labelPosition) { case 'top': @@ -266,8 +266,8 @@ class Function extends ExecutableObject { Function.drawFunction(canvas, ctx, this.expression, this.definitionDomain, this.destinationDomain, this.drawPoints, this.drawDashedLines) // Label var text = this.getLabel() - ctx.font = "14px sans-serif" - var textSize = canvas.measureText(ctx, text, 7) + ctx.font = `${canvas.textsize}px sans-serif` + var textSize = canvas.measureText(ctx, text) var posX = canvas.x2px(this.labelX) var posY = canvas.y2px(this.execute(this.labelX)) switch(this.labelPosition) { @@ -439,8 +439,8 @@ class GainBode extends ExecutableObject { // Label var text = this.getLabel() - ctx.font = "14px sans-serif" - var textSize = canvas.measureText(ctx, text, 7) + ctx.font = `${canvas.textsize}px sans-serif` + var textSize = canvas.measureText(ctx, text) var posX = canvas.x2px(this.labelX) var posY = canvas.y2px(this.execute(this.labelX)) switch(this.labelPosition) { @@ -588,8 +588,8 @@ class SommeGainsBode extends DrawableObject { if(inDrawDom.includes(this.labelX)) { // Label var text = this.getLabel() - ctx.font = "14px sans-serif" - var textSize = canvas.measureText(ctx, text, 7) + ctx.font = `${canvas.textsize}px sans-serif` + var textSize = canvas.measureText(ctx, text) var posX = canvas.x2px(this.labelX) var posY = canvas.y2px(dbfn.execute(this.labelX)) switch(this.labelPosition) { @@ -712,8 +712,8 @@ class PhaseBode extends ExecutableObject { // Label var text = this.getLabel() - ctx.font = "14px sans-serif" - var textSize = canvas.measureText(ctx, text, 7) + ctx.font = `${canvas.textsize}px sans-serif` + var textSize = canvas.measureText(ctx, text) var posX = canvas.x2px(this.labelX) var posY = canvas.y2px(this.execute(this.labelX)) switch(this.labelPosition) { @@ -848,8 +848,8 @@ class SommePhasesBode extends ExecutableObject { // Label var text = this.getLabel() - ctx.font = "14px sans-serif" - var textSize = canvas.measureText(ctx, text, 7) + ctx.font = `${canvas.textsize}px sans-serif` + var textSize = canvas.measureText(ctx, text) var posX = canvas.x2px(this.labelX) var posY = canvas.y2px(this.execute(this.labelX)) switch(this.labelPosition) { @@ -993,8 +993,8 @@ class CursorX extends DrawableObject { // Label var text = this.getLabel() - ctx.font = "14px sans-serif" - var textSize = canvas.measureText(ctx, text, 7) + ctx.font = `${canvas.textsize}px sans-serif` + var textSize = canvas.measureText(ctx, text) switch(this.labelPosition) { case 'left': @@ -1007,7 +1007,7 @@ class CursorX extends DrawableObject { if(this.targetValuePosition == 'Next to target' && this.getTargetElement() != null) { var text = this.getTargetValueLabel() - var textSize = canvas.measureText(ctx, text, 7) + var textSize = canvas.measureText(ctx, text) var ypox = canvas.y2px(this.getTargetElement().execute(this.x.execute())) switch(this.labelPosition) { case 'left': @@ -1103,8 +1103,8 @@ class Sequence extends ExecutableObject { // Label var text = this.getLabel() - ctx.font = "14px sans-serif" - var textSize = canvas.measureText(ctx, text, 7) + ctx.font = `${canvas.textsize}px sans-serif` + var textSize = canvas.measureText(ctx, text) var posX = canvas.x2px(this.labelX) var posY = canvas.y2px(this.execute(this.labelX)) switch(this.labelPosition) { @@ -1253,8 +1253,8 @@ class RepartitionFunction extends ExecutableObject { // Label var text = this.getLabel() - ctx.font = "14px sans-serif" - var textSize = canvas.measureText(ctx, text, 7) + ctx.font = `${canvas.textsize}px sans-serif` + var textSize = canvas.measureText(ctx, text) var posX = canvas.x2px(this.labelX) var posY = canvas.y2px(this.execute(this.labelX)) switch(this.labelPosition) { diff --git a/qml/js/utils.js b/qml/js/utils.js index d5891c8..3258b2c 100644 --- a/qml/js/utils.js +++ b/qml/js/utils.js @@ -252,20 +252,22 @@ function makeExpressionReadable(str) { [/ \* /g, '×'], [/ \^ /g, '^'], [/\^\(([^\^]+)\)/g, function(match, p1) { return textsup(p1) }], - [/\^([^ ]+)/g, function(match, p1) { return textsup(p1) }], + [/\^([^ "]+)/g, function(match, p1) { return textsup(p1) }], [/_\(([^_]+)\)/g, function(match, p1) { return textsub(p1) }], - [/_([^ ]+)/g, function(match, p1) { return textsub(p1) }], + [/_([^ "]+)/g, function(match, p1) { return textsub(p1) }], [/\[([^\[\]]+)\]/g, function(match, p1) { return textsub(p1) }], [/(\d|\))×/g, '$1'], //[/×(\d|\()/g, '$1'], [/\(([^)(+.\/-]+)\)/g, "$1"], - [/integral\((.+), ?(.+), ("|')(.+)("|'), ?("|')(.+)("|')\)/g, function(match, a, b, p1, body, p2, p3, by, p4) { - console.log('Intégrale', a, b, body, by) + [/integral\((.+), ?(.+), ?("|')(.+)("|'), ?("|')(.+)("|')\)/g, function(match, a, b, p1, body, p2, p3, by, p4) { if(a.length < b.length) { return `∫${textsub(a)}${textsup(b)} ${body} d${by}` } else { return `∫${textsup(b)}${textsub(a)} ${body} d${by}` } + }], + [/derivative\(?("|')(.+)("|'), ?("|')(.+)("|'), ?(.+)\)?/g, function(match, p1, body, p2, p3, by, p4, x) { + return `d(${body.replace(new RegExp(by, 'g'), 'x')})/dx` }] ] @@ -315,7 +317,7 @@ function parseName(str, removeUnallowed = true) { [/([^a-z]|^)gom(ega)?([^a-z]|$)/g, '$1Ω$3'], // Underscores [/_\(([^_]+)\)/g, function(match, p1) { return textsub(p1) }], - [/_([^ ]+)/g, function(match, p1) { return textsub(p1) }], + [/_([^" ]+)/g, function(match, p1) { return textsub(p1) }], // Array elements [/\[([^\]\[]+)\]/g, function(match, p1) { return textsub(p1) }], // Removing