diff --git a/qml/LogGraph.qml b/qml/LogGraph.qml index 67dd27b..5d2c91c 100644 --- a/qml/LogGraph.qml +++ b/qml/LogGraph.qml @@ -29,7 +29,7 @@ ApplicationWindow { width: 1000 height: 500 color: sysPalette.window - title: "Logarithmic Graph Creator " + (settings.saveFilename != "" ? " - " + settings.saveFilename : "") + title: "Logarithmic Plotter " + (settings.saveFilename != "" ? " - " + settings.saveFilename : "") SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active } SystemPalette { id: sysPaletteIn; colorGroup: SystemPalette.Disabled } @@ -75,11 +75,7 @@ ApplicationWindow { Settings { id: settings - onChanged: drawCanvas.requestPaint() - onCopyToClipboard: root.copyDiagramToClipboard() - onSaveDiagram: root.saveDiagram(filename) - onLoadDiagram: root.loadDiagram(filename) } ObjectLists { @@ -124,32 +120,32 @@ ApplicationWindow { "xmin": settings.xmin, "ymax": settings.ymax, "yaxisstep": settings.yaxisstep, - "xlabel": settings.xaxislabel, - "ylabel": settings.yaxislabel, + "xaxislabel": settings.xaxislabel, + "yaxislabel": settings.yaxislabel, "width": root.width, "height": root.height, "objects": objs, - "type": "bodediagramv1" + "type": "logplotv1" })) } function loadDiagram(filename) { var data = JSON.parse(Helper.load(filename)) - if(Object.keys(data).indexOf("type") != -1 && data["type"] == "bodediagramv1") { + if(Object.keys(data).indexOf("type") != -1 && data["type"] == "logplotv1") { settings.xzoom = data["xzoom"] settings.yzoom = data["yzoom"] settings.xmin = data["xmin"] settings.ymax = data["ymax"] settings.yaxisstep = data["yaxisstep"] - settings.xaxislabel = data["xlabel"] - settings.yaxislabel = data["ylabel"] + settings.xaxislabel = data["xaxislabel"] + settings.yaxislabel = data["yaxislabel"] root.height = data["height"] root.width = data["width"] Object.keys(data['objects']).forEach(function(objType){ Objects.currentObjects[objType] = [] data['objects'][objType].forEach(function(objData){ - var obj = new Objects.drawableTypes[objType](...objData) + var obj = new Objects.types[objType](...objData) Objects.currentObjects[objType].push(obj) }) }) @@ -166,4 +162,6 @@ ApplicationWindow { drawCanvas.save(file) Helper.copyImageToClipboard() } + + } diff --git a/qml/LogGraphCanvas.qml b/qml/LogGraphCanvas.qml index 78a47fa..bcfce5a 100644 --- a/qml/LogGraphCanvas.qml +++ b/qml/LogGraphCanvas.qml @@ -44,6 +44,8 @@ Canvas { drawAxises(ctx) Object.keys(Objects.currentObjects).forEach(function(objType){ Objects.currentObjects[objType].forEach(function(obj){ + ctx.strokeStyle = obj.color + ctx.fillStyle = obj.color if(obj.visible) obj.draw(canvas, ctx) }) }) @@ -61,7 +63,7 @@ Canvas { // Drawing the log based graph function drawGrille(ctx) { - ctx.strokeStyle = "#AAAAAA" + ctx.strokeStyle = "#C0C0C0" for(var xpow = -10; xpow <= 10; xpow++) { for(var xmulti = 1; xmulti < 10; xmulti++) { drawXLine(ctx, Math.pow(10, xpow)*xmulti) diff --git a/qml/ObjectLists.qml b/qml/ObjectLists.qml index 016888d..059ceff 100644 --- a/qml/ObjectLists.qml +++ b/qml/ObjectLists.qml @@ -21,6 +21,7 @@ import QtQuick.Dialogs 1.3 as D import QtQuick.Controls 2.12 import "js/objects.js" as Objects import "js/mathlib.js" as MathLib +import "js/utils.js" as Utils ListView { @@ -30,7 +31,7 @@ ListView { property var listViews: {'':''} // Needs to be initialized or will be undefined -_- - model: Object.keys(Objects.drawableTypes) + model: Object.keys(Objects.types) implicitHeight: contentItem.childrenRect.height delegate: ListView { @@ -112,9 +113,9 @@ ListView { color: obj.color title: `Pick new color for ${objType} ${obj.name}` onAccepted: { - Objects.currentObjects[objType][index].color = color - objectListList.changed() + Objects.currentObjects[objType][index].color = color.toString() controlRow.obj = Objects.currentObjects[objType][index] + objectListList.update() } } } @@ -127,7 +128,7 @@ ListView { property int objIndex: 0 property var editingRow: QtObject{} property var obj: Objects.currentObjects[objType][objIndex] - title: `Logarithmic Graph Creator` + title: `Logarithmic Plotter` width: 300 height: 400 @@ -157,11 +158,13 @@ ListView { width: dlgProperties.width defValue: objEditor.obj.name onChanged: function(newValue) { - Objects.currentObjects[objEditor.objType][objEditor.objIndex].name = newValue - // TODO Resolve dependencies - objEditor.obj = Objects.currentObjects[objEditor.objType][objEditor.objIndex] - objEditor.editingRow.obj = Objects.currentObjects[objEditor.objType][objEditor.objIndex] - objectListList.changed() + if(Utils.parseName(newValue) != '') { + Objects.currentObjects[objEditor.objType][objEditor.objIndex].name = Utils.parseName(newValue) + // TODO Resolve dependencies + objEditor.obj = Objects.currentObjects[objEditor.objType][objEditor.objIndex] + //objEditor.editingRow.obj = Objects.currentObjects[objEditor.objType][objEditor.objIndex] + objectListList.update() + } } } @@ -176,19 +179,19 @@ ListView { Objects.currentObjects[objEditor.objType][objEditor.objIndex].labelContent = model[newIndex] objEditor.obj = Objects.currentObjects[objEditor.objType][objEditor.objIndex] objEditor.editingRow.obj = Objects.currentObjects[objEditor.objType][objEditor.objIndex] - objectListList.changed() + objectListList.update() } } // Dynamic properties Repeater { - property var objProps: Objects.drawableTypes[objEditor.objType].properties() + property var objProps: Objects.types[objEditor.objType].properties() model: Array.from(Object.keys(objProps), prop => [prop, objProps[prop]]) // Converted to 2-dimentional array. Item { height: 30 width: dlgProperties.width - property string label: modelData[0].charAt(0).toUpperCase() + modelData[0].slice(1).replace(/([A-Z])/g," $1"); + property string label: Utils.camelCase2readable(modelData[0]) TextSetting { id: customPropText @@ -211,10 +214,7 @@ ListView { 'string': function(){return newValue}, 'number': function(){return parseFloat(newValue)} }[modelData[1]]() - // TODO Resolve dependencies - objEditor.obj = Objects.currentObjects[objEditor.objType][objEditor.objIndex] - objEditor.editingRow.obj = Objects.currentObjects[objEditor.objType][objEditor.objIndex] - objectListList.changed() + objectListList.update() } Component.onCompleted: { //console.log(modelData[0], objEditor.obj[modelData[0]],modelData[1], defValue) @@ -226,17 +226,29 @@ ListView { height: 30 width: dlgProperties.width label: parent.label - model: visible ? modelData[1] : [] - visible: Array.isArray(modelData[1]) - currentIndex: model.indexOf(objEditor.obj[modelData[0]]) + // True to select an object of type, false for enums. + property bool selectObjMode: (typeof modelData[1] == "string" && Object.keys(Objects.types).indexOf(modelData[1]) >= 0) + model: visible ? + (selectObjMode ? Objects.getObjectsName(modelData[1]).concat(['+ Create new ' + modelData[1]]) : modelData[1]) + : [] + visible: Array.isArray(modelData[1]) || selectObjMode + currentIndex: model.indexOf(selectObjMode ? objEditor.obj[modelData[0]].name : objEditor.obj[modelData[0]]) onActivated: function(newIndex) { // Setting object property. - Objects.currentObjects[objEditor.objType][objEditor.objIndex][modelData[0]] = model[newIndex] + if(selectObjMode) { + var selectedObj = Objects.getObjectByName(model[newIndex]) + if(selectedObj == null) { + selectedObj = Objects.createNewRegisteredObject(modelData[1]) + model = Objects.getObjectsName(modelData[1]).concat(['+ Create new ' + modelData[1]]) + currentIndex = model.indexOf(selectedObj.name) + } + Objects.currentObjects[objEditor.objType][objEditor.objIndex][modelData[0]] = selectedObj + } else { + Objects.currentObjects[objEditor.objType][objEditor.objIndex][modelData[0]] = model[newIndex] + } // Refreshing - objEditor.obj = Objects.currentObjects[objEditor.objType][objEditor.objIndex] - objEditor.editingRow.obj = Objects.currentObjects[objEditor.objType][objEditor.objIndex] - objectListList.changed() + objectListList.update() } } } @@ -257,7 +269,7 @@ ListView { } Repeater { - model: Object.keys(Objects.drawableTypes) + model: Object.keys(Objects.types) Button { id: createBtn @@ -276,15 +288,17 @@ ListView { } onClicked: { - var newobj = new Objects.drawableTypes[modelData]() - if(Object.keys(Objects.currentObjects).indexOf(modelData) == -1) - Objects.currentObjects[modelData] = [] - Objects.currentObjects[modelData].push(newobj) - objectListList.changed() - console.log(objectListList, objectListList.listViews) - objectListList.listViews[modelData].model = Objects.currentObjects[modelData] + Objects.createNewRegisteredObject(modelData) + objectListList.update() } } } } + + function update() { + objectListList.changed() + objectListList.model.forEach(function(objType){ + objectListList.listViews[objType].model = Objects.currentObjects[objType] + }) + } } diff --git a/qml/Settings.qml b/qml/Settings.qml index 7a8bc0a..0b09541 100644 --- a/qml/Settings.qml +++ b/qml/Settings.qml @@ -20,15 +20,12 @@ import QtQuick.Controls 2.12 import QtQuick 2.12 Grid { - id: root + id: settings height: 30*Math.max(1, Math.ceil(7 / columns)) columns: Math.floor(width / settingWidth) spacing: 10 signal changed() - signal copyToClipboard() - signal saveDiagram(string filename) - signal loadDiagram(string filename) property int settingWidth: 135 @@ -38,15 +35,14 @@ Grid { property double ymax: 25 property int yaxisstep: 4 property string xaxislabel: "ω (rad/s)" - property string yaxislabel: "Gain G (dB)" + property string yaxislabel: "G (dB)" property string saveFilename: "" FileDialog { id: fdiag onAccepted: { var filePath = fileUrl.toString().substr(7) - root.saveFilename = filePath - console.log(filePath) + settings.saveFilename = filePath if(exportMode) { root.saveDiagram(filePath) } else { @@ -63,11 +59,11 @@ Grid { isInt: true label: "X Zoom" min: 1 - width: root.settingWidth - defValue: root.xzoom + width: settings.settingWidth + defValue: settings.xzoom onChanged: function(newValue) { - root.xzoom = newValue - root.changed() + settings.xzoom = newValue + settings.changed() } } TextSetting { @@ -75,11 +71,11 @@ Grid { height: 30 isInt: true label: "Y Zoom" - width: root.settingWidth - defValue: root.yzoom + width: settings.settingWidth + defValue: settings.yzoom onChanged: function(newValue) { - root.yzoom = newValue - root.changed() + settings.yzoom = newValue + settings.changed() } } // Positioning the graph @@ -89,11 +85,11 @@ Grid { isDouble: true min: 0 label: "Min X" - width: root.settingWidth - defValue: root.xmin + width: settings.settingWidth + defValue: settings.xmin onChanged: function(newValue) { - root.xmin = newValue - root.changed() + settings.xmin = newValue + settings.changed() } } TextSetting { @@ -101,11 +97,11 @@ Grid { height: 30 isDouble: true label: "Max Y" - width: root.settingWidth - defValue: root.ymax + width: settings.settingWidth + defValue: settings.ymax onChanged: function(newValue) { - root.ymax = newValue - root.changed() + settings.ymax = newValue + settings.changed() } } TextSetting { @@ -113,32 +109,32 @@ Grid { height: 30 isInt: true label: "Y Axis Step" - width: root.settingWidth - defValue: root.yaxisstep + width: settings.settingWidth + defValue: settings.yaxisstep onChanged: function(newValue) { - root.yaxisstep = newValue - root.changed() + settings.yaxisstep = newValue + settings.changed() } } Button { id: copyToClipboard height: 30 - width: root.settingWidth + width: settings.settingWidth text: "Copy to clipboard" icon.name: 'editcopy' - onClicked: root.copyToClipboard() + onClicked: root.copyDiagramToClipboard() } TextSetting { id: xAxisLabel height: 30 label: "X Label" - width: root.settingWidth - defValue: root.xaxislabel + width: settings.settingWidth + defValue: settings.xaxislabel onChanged: function(newValue) { - root.xaxislabel = newValue - root.changed() + settings.xaxislabel = newValue + settings.changed() } } @@ -146,18 +142,18 @@ Grid { id: yAxisLabel height: 30 label: "Y Label" - width: root.settingWidth - defValue: root.yaxislabel + width: settings.settingWidth + defValue: settings.yaxislabel onChanged: function(newValue) { - root.yaxislabel = newValue - root.changed() + settings.yaxislabel = newValue + settings.changed() } } Button { id: saveDiagram height: 30 - width: root.settingWidth + width: settings.settingWidth text: "Save diagram" icon.name: 'filesave' onClicked: save() @@ -166,7 +162,7 @@ Grid { Button { id: saveDiagramAs height: 30 - width: root.settingWidth + width: settings.settingWidth text: "Save diagram as" icon.name: 'filesaveas' onClicked: saveAs() @@ -175,7 +171,7 @@ Grid { Button { id: loadDiagram height: 30 - width: root.settingWidth + width: settings.settingWidth text: "Load diagram" icon.name: 'fileopen' onClicked: load() @@ -183,17 +179,17 @@ Grid { CheckBox { id: modePhaseCheck height: 30 - width: root.settingWidth + width: settings.settingWidth text: "Mode phase" - property var refresh: checked ? root.changed() : root.changed() + property var refresh: checked ? settings.changed() : settings.changed() } function save() { - if(root.saveFilename == "") { + if(settings.saveFilename == "") { fdiag.exportMode = true fdiag.open() } else { - root.saveDiagram(root.saveFilename) + root.saveDiagram(settings.saveFilename) } } diff --git a/qml/js/mathlib.js b/qml/js/mathlib.js index 7bae694..d5d8e14 100644 --- a/qml/js/mathlib.js +++ b/qml/js/mathlib.js @@ -19,6 +19,7 @@ .pragma library .import "expr-eval.js" as ExprEval +.import "utils.js" as Utils const parser = new ExprEval.Parser() @@ -26,24 +27,6 @@ class Expression { constructor(expr) { this.expr = expr this.calc = parser.parse(expr).simplify() - this.replacements = [ - ['pi', 'π'], - ['inf', '∞'], - ['Infinity', '∞'], - [' * ', '×'], - ['0×', '0'], - ['1×', '1'], - ['2×', '2'], - ['3×', '3'], - ['4×', '4'], - ['5×', '5'], - ['6×', '6'], - ['7×', '7'], - ['8×', '8'], - ['9×', '9'], - [')×', ')'], - ['×(', '('], - ] } isConstant() { @@ -66,13 +49,12 @@ class Expression { return this.calc.toString() } - toString() { + toString(forceSign=false) { var str = this.calc.toString() if(str[0] == "(") str = str.substr(1) if(str[str.length - 1] == ")") str = str.substr(0, str.length - 1) - this.replacements.forEach(function(replacement){ - str = str.replace(replacement[0], replacement[1]) - }) + str = Utils.makeExpressionReadable(str) + if(str[0] != '-' && forceSign) str = '+' + str return str } } diff --git a/qml/js/objects.js b/qml/js/objects.js index ecb61c4..fdd909c 100644 --- a/qml/js/objects.js +++ b/qml/js/objects.js @@ -112,18 +112,15 @@ class Point extends DrawableObject { var pointSize = 8 switch(this.pointStyle) { case 'dot': - ctx.fillStyle = this.color ctx.beginPath(); ctx.ellipse(canvasX-pointSize/2, canvasY-pointSize/2, pointSize, pointSize) ctx.fill(); break; case 'diagonal cross': - ctx.strokeStyle = this.color canvas.drawLine(ctx, canvasX-pointSize/2, canvasY-pointSize/2, canvasX+pointSize/2, canvasY+pointSize/2) canvas.drawLine(ctx, canvasX-pointSize/2, canvasY+pointSize/2, canvasX-pointSize/2, canvasY+pointSize/2) break; case 'vertical cross': - ctx.strokeStyle = this.color canvas.drawLine(ctx, canvasX, canvasY-pointSize/2, canvasX, canvasY+pointSize/2) canvas.drawLine(ctx, canvasX-pointSize/2, canvasY, canvasX+pointSize/2, canvasY) break; @@ -131,7 +128,6 @@ class Point extends DrawableObject { var text = this.getLabel() ctx.font = "14px sans-serif" var textSize = ctx.measureText(text).width - ctx.fillStyle = this.color switch(this.labelPos) { case 'top': canvas.drawVisibleText(ctx, text, canvasX-textSize/2, canvasY-16) @@ -178,7 +174,7 @@ class Function extends DrawableObject { getReadableString() { if(this.displayMode == 'application') { - return `${this.name}: ${this.inDomain} ⸺˃ ${this.outDomain}\n x ⸺˃ ${this.expression.toString()}` + return `${this.name}: ${this.inDomain} ⸺˃ ${this.outDomain}\n ${' '.repeat(this.name.length)}x ⸺˃ ${this.expression.toString()}` } else { return `${this.name}(x) = ${this.expression.toString()}` } @@ -191,28 +187,11 @@ class Function extends DrawableObject { } draw(canvas, ctx) { - ctx.strokeStyle = this.color - ctx.fillStyle = this.color - // Drawing small traits every 2px - var pxprecision = 2 - var previousX = canvas.px2x(0) - var previousY = this.expression.evaluate(previousX) - for(var px = pxprecision; px < canvas.canvasSize.width; px += pxprecision) { - var currentX = canvas.px2x(px) - var currentY = this.expression.evaluate(currentX) - if(this.inDomain.includes(currentX) && this.inDomain.includes(previousX) && - this.outDomain.includes(currentY) && this.outDomain.includes(previousY) && - Math.abs(previousY-currentY)<100) { // 100 per 2px is a lot (probably inf to inf issue) - canvas.drawLine(ctx, canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY)) - } - previousX = currentX - previousY = currentY - } + Function.drawFunction(canvas, ctx, this.expression, this.inDomain, this.outDomain) // Label var text = this.getLabel() ctx.font = "14px sans-serif" var textSize = canvas.measureText(ctx, text) - ctx.fillStyle = this.color var posX = canvas.x2px(this.labelX) var posY = canvas.y2px(this.expression.evaluate(this.labelX)) switch(this.labelPos) { @@ -225,11 +204,125 @@ class Function extends DrawableObject { } } + + static drawFunction(canvas, ctx, expr, inDomain, outDomain) { + // Reusable in other objects. + // Drawing small traits every 2px + var pxprecision = 2 + var previousX = canvas.px2x(0) + var previousY = expr.evaluate(previousX) + for(var px = pxprecision; px < canvas.canvasSize.width; px += pxprecision) { + var currentX = canvas.px2x(px) + var currentY = expr.evaluate(currentX) + if(inDomain.includes(currentX) && inDomain.includes(previousX) && + outDomain.includes(currentY) && outDomain.includes(previousY) && + Math.abs(previousY-currentY)<100) { // 100 per 2px is a lot (probably inf to inf issue) + canvas.drawLine(ctx, canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY)) + } + previousX = currentX + previousY = currentY + } + } } -const drawableTypes = { +class GainBode extends DrawableObject { + static type(){return 'Gain Bode'} + static properties() {return { + 'ω_0': 'Point', + 'pass': ['high', 'low'], + 'gain': 'Expression', + 'labelPos': ['above', 'below'], + 'labelX': 'number' + }} + + constructor(name = null, visible = true, color = null, labelContent = 'name + value', + ω_0 = '', pass = 'high', gain = '20', labelPos = 'above', labelX = 1) { + if(name == null) name = getNewName('G', 'Gain Bode') + if(name == 'G') name = 'G₀' // G is reserved for sum of BODE gains. + super(name, visible, color, labelContent) + if(typeof ω_0 == "string") { + // Point name or create one + ω_0 = getObjectByName('Point', ω_0) + if(ω_0 == null) { + // Create new point + ω_0 = createNewRegisteredObject('Point') + ω_0.name = getNewName('ω', 'Gain Bode') + } + } + console.log(this, ω_0) + this.ω_0 = ω_0 + this.pass = pass + if(typeof gain == 'number' || typeof gain == 'string') gain = new MathLib.Expression(gain.toString()) + this.gain = gain + this.labelPos = labelPos + this.labelX = labelX + } + + getReadableString() { + return `${this.name}: ${this.pass}-pass, ω₀=${this.ω_0.x}, ${this.gain.toString(true)} dB/dec.` + } + + export() { + return [this.name, this.visible, this.color.toString(), this.labelContent, + this.ω_0.name, this.pass.toString(), this.gain.toEditableString(), this.labelPos, this.labelX] + } + + draw(canvas, ctx) { + var base = [canvas.x2px(this.ω_0.x), canvas.y2px(this.ω_0.y)] + var dbfn = new MathLib.Expression(`${this.gain.evaluate()}*(ln(x)-ln(${this.ω_0.x}))/ln(10)+${this.ω_0.y}`) + var inDrawDom = new MathLib.EmptySet() + + if(this.pass == 'high') { + // High pass, linear line from begining, then constant to the end. + canvas.drawLine(ctx, base[0], base[1], canvas.canvasSize.width, base[1]) + inDrawDom = MathLib.parseDomain(`]-inf;${this.ω_0.x}[`) + } else { + // Low pass, constant from the beginning, linear line to the end. + canvas.drawLine(ctx, base[0], base[1], 0, base[1]) + inDrawDom = MathLib.parseDomain(`]${this.ω_0.x};+inf[`) + } + Function.drawFunction(canvas, ctx, dbfn, inDrawDom, MathLib.Domain.R) + } +} + +const types = { 'Point': Point, - 'Function': Function + 'Function': Function, + 'Gain Bode': GainBode, } var currentObjects = {} + +function getObjectByName(objType, objName) { + if(currentObjects[objType] == undefined) return null + var retObj = null + currentObjects[objType].forEach(function(obj){ + if(obj.name == objName) retObj = obj + }) + return retObj +} + +function getObjectsName(objType) { + if(currentObjects[objType] == undefined) return [] + return currentObjects[objType].map(function(obj) { + return obj.name + }) +} + +function createNewRegisteredObject(objType) { + if(Object.keys(types).indexOf(objType) == -1) return null // Object type does not exist. + var newobj = new types[objType]() + if(Object.keys(currentObjects).indexOf(objType) == -1) { + currentObjects[objType] = [] + } + currentObjects[objType].push(newobj) + return newobj +} + +var points = [new Point()] +var f = new Function(); +f.point = points[0] +points[0].name = 'B' +points[0].x = 2 +f.point.x = 4 +console.log(points[0].name, f.point.name, points[0].x, f.point.x) diff --git a/qml/js/utils.js b/qml/js/utils.js index c463dfd..cd7b51d 100644 --- a/qml/js/utils.js +++ b/qml/js/utils.js @@ -16,6 +16,8 @@ * along with this program. If not, see . */ +.pragma library + var powerpos = { "-": "⁻", "0": "⁰", @@ -116,3 +118,87 @@ function textsub(text) { } return ret } + +function makeExpressionReadable(str) { + var replacements = [ + [/pi/g, 'π'], + [/Infinity/g, '∞'], + [/inf/g, '∞'], + [/ \* /g, '×'], + [/ \^ /g, '^'], + [/\^\(([^\^]+)\)/g, function(match, p1) { return textsup(p1) }], + [/\^([^ ]+)/g, function(match, p1) { return textsup(p1) }], + [/(\d|\))×/g, '$1'], + [/×(\d|\()/g, '$1'], + [/\(([^)(+.-]+)\)/g, "$1"], + [/\(([^)(+.-]+)\)/g, "$1"], + [/\(([^)(+.-]+)\)/g, "$1"], + [/\(([^)(+.-]+)\)/g, "$1"], + [/\(([^)(+.-]+)\)/g, "$1"], + // Doing it 4 times to be recursive until better implementation + ] + // Replacements + replacements.forEach(function(replacement){ + str = str.replace(replacement[0], replacement[1]) + }) + return str +} + +function parseName(str, removeUnallowed = true) { + var replacements = [ + // Greek letters + [/(\s|^)al(pha)?[^a-z]/g, 'α'], + [/(\s|^)be(ta)?[^a-z]/g, 'β'], + [/(\s|^)ga(mma)?[^a-z]/g, 'γ'], + [/(\s|^)de(lta)?[^a-z]/g, 'δ'], + [/(\s|^)ep(silon)?[^a-z]/g, 'ε'], + [/(\s|^)ze(ta)?[^a-z]/g, 'ζ'], + [/(\s|^)et(a)?[^a-z]/g, 'η'], + [/(\s|^)th(eta)?[^a-z]/g, 'θ'], + [/(\s|^)io(ta)?[^a-z]/g, 'ι'], + [/(\s|^)ka(ppa)[^a-z]?/g, 'κ'], + [/(\s|^)la(mbda)?[^a-z]/g, 'λ'], + [/(\s|^)mu[^a-z]/g, 'μ'], + [/(\s|^)nu[^a-z]/g, 'ν'], + [/(\s|^)xi[^a-z]/g, 'ξ'], + [/(\s|^)rh(o)?[^a-z]/g, 'ρ'], + [/(\s|^)si(gma)?[^a-z]/g, 'σ'], + [/(\s|^)ta(u)?[^a-z]/g, 'τ'], + [/(\s|^)up(silon)?[^a-z]/g, 'υ'], + [/(\s|^)ph(i)?[^a-z]/g, 'φ'], + [/(\s|^)ch(i)?[^a-z]/g, 'χ'], + [/(\s|^)ps(i)?[^a-z]/g, 'ψ'], + [/(\s|^)om(ega)?[^a-z]/g, 'ω'], + // Capital greek letters + [/(\s|^)gga(mma)?[^a-z]/g, 'Γ'], + [/(\s|^)gde(lta)?[^a-z]/g, 'Δ'], + [/(\s|^)gth(eta)?[^a-z]/g, 'Θ'], + [/(\s|^)gla(mbda)?[^a-z]/g, 'Λ'], + [/(\s|^)gxi[^a-z]/g, 'Ξ'], + [/(\s|^)gpi[^a-z]/g, 'Π'], + [/(\s|^)gsi(gma)[^a-z]?/g, 'Σ'], + [/(\s|^)gph(i)?[^a-z]/g, 'Φ'], + [/(\s|^)gps(i)?[^a-z]/g, 'Ψ'], + [/(\s|^)gom(ega)?[^a-z]/g, 'Ω'], + // Underscores + [/_\(([^\^]+)\)/g, function(match, p1) { return textsub(p1) }], + [/_([^ ]+)/g, function(match, p1) { return textsub(p1) }], + // Removing + [/[xπℝℕ\\∪∩\]\[ ()^/÷*×+=\d-]/g , function(match){console.log('removing', match); return ''}], + ] + if(!removeUnallowed) replacements.pop() + // Replacements + replacements.forEach(function(replacement){ + str = str.replace(replacement[0], replacement[1]) + }) + return str +} + +String.prototype.toLatinUppercase = function() { + return this.replace(/[a-z]/g, function(match){return match.toUpperCase()}) +} + +function camelCase2readable(label) { + var parsed = parseName(label, false) + return parsed.charAt(0).toLatinUppercase() + parsed.slice(1).replace(/([A-Z])/g," $1") +} diff --git a/run.py b/run.py index a280138..4a28c91 100644 --- a/run.py +++ b/run.py @@ -77,7 +77,7 @@ class Helper(QObject): os.system("xclip -selection clipboard -t image/png -i " + tempfile) app = QApplication([]) -app.setApplicationName("Logarithmic Graph Creator") +app.setApplicationName("Logarithmic Plotter") app.setOrganizationName("Ad5001") engine = QQmlApplicationEngine() helper = Helper()