Creating Canvas JS Module.
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
861bb001c9
commit
73cba85592
36 changed files with 797 additions and 590 deletions
|
@ -113,7 +113,7 @@ def run():
|
||||||
helper = Helper(pwd, tmpfile)
|
helper = Helper(pwd, tmpfile)
|
||||||
latex = Latex(tempdir)
|
latex = Latex(tempdir)
|
||||||
modules = engine.newObject()
|
modules = engine.newObject()
|
||||||
engine.globalObject().setProperty('Runtime', modules)
|
engine.globalObject().setProperty('Modules', modules)
|
||||||
engine.globalObject().setProperty('Helper', engine.newQObject(helper))
|
engine.globalObject().setProperty('Helper', engine.newQObject(helper))
|
||||||
engine.globalObject().setProperty("Latex", engine.newQObject(latex))
|
engine.globalObject().setProperty("Latex", engine.newQObject(latex))
|
||||||
# engine.rootContext().setContextProperty("Helper", helper)
|
# engine.rootContext().setContextProperty("Helper", helper)
|
||||||
|
|
|
@ -103,17 +103,17 @@ MenuBar {
|
||||||
title: qsTr("&Create")
|
title: qsTr("&Create")
|
||||||
// Services repeater
|
// Services repeater
|
||||||
Repeater {
|
Repeater {
|
||||||
model: Object.keys(Runtime.Objects.types)
|
model: Object.keys(Modules.Objects.types)
|
||||||
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: Runtime.Objects.types[modelData].displayType()
|
text: Modules.Objects.types[modelData].displayType()
|
||||||
visible: Runtime.Objects.types[modelData].createable()
|
visible: Modules.Objects.types[modelData].createable()
|
||||||
height: visible ? implicitHeight : 0
|
height: visible ? implicitHeight : 0
|
||||||
icon.name: modelData
|
icon.name: modelData
|
||||||
icon.source: './icons/objects/' + modelData + '.svg'
|
icon.source: './icons/objects/' + modelData + '.svg'
|
||||||
icon.color: sysPalette.buttonText
|
icon.color: sysPalette.buttonText
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
var newObj = Runtime.Objects.createNewRegisteredObject(modelData)
|
var newObj = Modules.Objects.createNewRegisteredObject(modelData)
|
||||||
history.addToHistory(new HistoryLib.CreateNewObject(newObj.name, modelData, newObj.export()))
|
history.addToHistory(new HistoryLib.CreateNewObject(newObj.name, modelData, newObj.export()))
|
||||||
objectLists.update()
|
objectLists.update()
|
||||||
}
|
}
|
||||||
|
@ -151,7 +151,7 @@ MenuBar {
|
||||||
checked: Helper.getSettingBool("enable_latex")
|
checked: Helper.getSettingBool("enable_latex")
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
Helper.setSettingBool("enable_latex", checked)
|
Helper.setSettingBool("enable_latex", checked)
|
||||||
Runtime.Latex.enabled = checked
|
Modules.Latex.enabled = checked
|
||||||
drawCanvas.requestPaint()
|
drawCanvas.requestPaint()
|
||||||
}
|
}
|
||||||
icon.name: 'Expression'
|
icon.name: 'Expression'
|
||||||
|
|
|
@ -212,8 +212,8 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
Runtime.History.history = historyObj
|
Modules.History.history = historyObj
|
||||||
Runtime.History.themeTextColor = sysPalette.windowText
|
Modules.History.themeTextColor = sysPalette.windowText
|
||||||
Runtime.History.imageDepth = Screen.devicePixelRatio
|
Modules.History.imageDepth = Screen.devicePixelRatio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,37 +126,6 @@ Canvas {
|
||||||
*/
|
*/
|
||||||
property int maxgradx: 20
|
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)
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty var LogGraphCanvas::imageLoaders
|
\qmlproperty var LogGraphCanvas::imageLoaders
|
||||||
Dictionary of format {image: [callback.image data]} containing data for defered image loading.
|
Dictionary of format {image: [callback.image data]} containing data for defered image loading.
|
||||||
|
@ -168,7 +137,10 @@ Canvas {
|
||||||
*/
|
*/
|
||||||
property var ctx
|
property var ctx
|
||||||
|
|
||||||
Component.onCompleted: imageLoaders = {}
|
Component.onCompleted: {
|
||||||
|
imageLoaders = {}
|
||||||
|
Modules.Canvas.initialize(canvas, drawingErrorDialog)
|
||||||
|
}
|
||||||
|
|
||||||
Native.MessageDialog {
|
Native.MessageDialog {
|
||||||
id: drawingErrorDialog
|
id: drawingErrorDialog
|
||||||
|
@ -183,27 +155,7 @@ Canvas {
|
||||||
onPaint: function(rect) {
|
onPaint: function(rect) {
|
||||||
//console.log('Redrawing')
|
//console.log('Redrawing')
|
||||||
if(rect.width == canvas.width) { // Redraw full canvas
|
if(rect.width == canvas.width) { // Redraw full canvas
|
||||||
ctx = getContext("2d");
|
Modules.Canvas.redraw()
|
||||||
reset(ctx)
|
|
||||||
drawGrille(ctx)
|
|
||||||
drawAxises(ctx)
|
|
||||||
drawLabels(ctx)
|
|
||||||
ctx.lineWidth = linewidth
|
|
||||||
for(var objType in Runtime.Objects.currentObjects) {
|
|
||||||
for(var obj of Runtime.Objects.currentObjects[objType]){
|
|
||||||
ctx.strokeStyle = obj.color
|
|
||||||
ctx.fillStyle = obj.color
|
|
||||||
if(obj.visible)
|
|
||||||
try {
|
|
||||||
obj.draw(canvas, ctx)
|
|
||||||
} catch(e) {
|
|
||||||
// Drawing throws an error. Generally, it's due to a new modification (or the opening of a file)
|
|
||||||
drawingErrorDialog.showDialog(objType, obj.name, e.message)
|
|
||||||
history.undo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctx.lineWidth = 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,277 +168,4 @@ Canvas {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
\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}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}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-4}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*(xpow==1)))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for(var x = 1; x < drawMaxX; x += 1) {
|
|
||||||
var drawX = x*xaxisstep1
|
|
||||||
var txtX = xaxisstepExpr.simplify(x).replace(/^\((.+)\)$/, '$1')
|
|
||||||
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).replace(/^\((.+)\)$/, '$1')
|
|
||||||
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(isVisible(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(isVisible(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 void LogGraphCanvas::drawVisibleImage(var ctx, var image, double x, double y)
|
|
||||||
Draws an \c image onto the canvas using 2D \c ctx.
|
|
||||||
\note The \c x, \c y \c width and \c height properties here are relative to the canvas, not the plot.
|
|
||||||
*/
|
|
||||||
function drawVisibleImage(ctx, image, x, y, width, height) {
|
|
||||||
//console.log("Drawing image", isImageLoaded(image), isImageError(image))
|
|
||||||
markDirty(Qt.rect(x, y, width, height));
|
|
||||||
ctx.drawImage(image, x, y, width, height)
|
|
||||||
/*if(true || (x > 0 && x < canvasSize.width && y > 0 && y < canvasSize.height)) {
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\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) {
|
|
||||||
let theight = 0
|
|
||||||
let twidth = 0
|
|
||||||
let defaultHeight = ctx.measureText("M").width // Approximate but good enough!
|
|
||||||
text.split("\n").forEach(function(txt, i){
|
|
||||||
theight += defaultHeight
|
|
||||||
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::isVisible(double x, double y)
|
|
||||||
Checks whether a plot point (\c x, \c y) is visible or not on the canvas.
|
|
||||||
*/
|
|
||||||
function isVisible(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();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
\qmlmethod var LogGraphCanvas::renderLatexImage(string ltxText, color)
|
|
||||||
Renders latex markup \c ltxText to an image and loads it. Returns a dictionary with three values: source, width and height.
|
|
||||||
*/
|
|
||||||
function renderLatexImage(ltxText, color, callback) {
|
|
||||||
let [ltxSrc, ltxWidth, ltxHeight] = Latex.render(ltxText, textsize, color).split(",")
|
|
||||||
let imgData = {
|
|
||||||
"source": ltxSrc,
|
|
||||||
"width": parseFloat(ltxWidth),
|
|
||||||
"height": parseFloat(ltxHeight)
|
|
||||||
};
|
|
||||||
if(!isImageLoaded(ltxSrc) && !isImageLoading(ltxSrc)){
|
|
||||||
// Wait until the image is loaded to callback.
|
|
||||||
loadImage(ltxSrc)
|
|
||||||
imageLoaders[ltxSrc] = [callback, imgData]
|
|
||||||
} else {
|
|
||||||
// Callback directly
|
|
||||||
callback(canvas, ctx, imgData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ import QtQuick.Layouts 1.12
|
||||||
import QtQuick
|
import QtQuick
|
||||||
|
|
||||||
// Auto loading all modules.
|
// Auto loading all modules.
|
||||||
import "js/modules.js" as Modules
|
import "js/autoload.js" as ModulesAutoload
|
||||||
|
|
||||||
import eu.ad5001.LogarithmPlotter.History 1.0
|
import eu.ad5001.LogarithmPlotter.History 1.0
|
||||||
import eu.ad5001.LogarithmPlotter.ObjectLists 1.0
|
import eu.ad5001.LogarithmPlotter.ObjectLists 1.0
|
||||||
|
@ -191,9 +191,9 @@ ApplicationWindow {
|
||||||
filename += '.lpf'
|
filename += '.lpf'
|
||||||
settings.saveFilename = filename
|
settings.saveFilename = filename
|
||||||
var objs = {}
|
var objs = {}
|
||||||
for(var objType in Runtime.Objects.currentObjects){
|
for(var objType in Modules.Objects.currentObjects){
|
||||||
objs[objType] = []
|
objs[objType] = []
|
||||||
for(var obj of Runtime.Objects.currentObjects[objType]) {
|
for(var obj of Modules.Objects.currentObjects[objType]) {
|
||||||
objs[objType].push(obj.export())
|
objs[objType].push(obj.export())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -255,19 +255,19 @@ ApplicationWindow {
|
||||||
root.width = data["width"]
|
root.width = data["width"]
|
||||||
|
|
||||||
// Importing objects
|
// Importing objects
|
||||||
Runtime.Objects.currentObjects = {}
|
Modules.Objects.currentObjects = {}
|
||||||
Runtime.Object.keys(Objects.currentObjectsByName).forEach(key => {
|
Modules.Object.keys(Objects.currentObjectsByName).forEach(key => {
|
||||||
delete Runtime.Objects.currentObjectsByName[key];
|
delete Modules.Objects.currentObjectsByName[key];
|
||||||
// Required to keep the same reference for the copy of the object used in expression variable detection.
|
// Required to keep the same reference for the copy of the object used in expression variable detection.
|
||||||
// Another way would be to change the reference as well, but I feel like the code would be less clean.
|
// Another way would be to change the reference as well, but I feel like the code would be less clean.
|
||||||
})
|
})
|
||||||
for(let objType in data['objects']) {
|
for(let objType in data['objects']) {
|
||||||
if(Object.keys(Runtime.Objects.types).indexOf(objType) > -1) {
|
if(Object.keys(Modules.Objects.types).indexOf(objType) > -1) {
|
||||||
Runtime.Objects.currentObjects[objType] = []
|
Modules.Objects.currentObjects[objType] = []
|
||||||
for(let objData of data['objects'][objType]) {
|
for(let objData of data['objects'][objType]) {
|
||||||
let obj = new Runtime.Objects.types[objType](...objData)
|
let obj = new Modules.Objects.types[objType](...objData)
|
||||||
Runtime.Objects.currentObjects[objType].push(obj)
|
Modules.Objects.currentObjects[objType].push(obj)
|
||||||
Runtime.Objects.currentObjectsByName[obj.name] = obj
|
Modules.Objects.currentObjectsByName[obj.name] = obj
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error += qsTr("Unknown object type: %1.").arg(objType) + "\n";
|
error += qsTr("Unknown object type: %1.").arg(objType) + "\n";
|
||||||
|
@ -275,8 +275,8 @@ ApplicationWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updating object dependencies.
|
// Updating object dependencies.
|
||||||
for(let objName in Runtime.Objects.currentObjectsByName)
|
for(let objName in Modules.Objects.currentObjectsByName)
|
||||||
Runtime.Objects.currentObjectsByName[objName].update()
|
Modules.Objects.currentObjectsByName[objName].update()
|
||||||
|
|
||||||
// Importing history
|
// Importing history
|
||||||
if("history" in data)
|
if("history" in data)
|
||||||
|
|
|
@ -130,6 +130,7 @@ Repeater {
|
||||||
}
|
}
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
// Error in expression or domain
|
// Error in expression or domain
|
||||||
|
console.trace()
|
||||||
parsingErrorDialog.showDialog(propertyName, newValue, e.message)
|
parsingErrorDialog.showDialog(propertyName, newValue, e.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -187,8 +188,8 @@ Repeater {
|
||||||
|
|
||||||
// Base, untranslated version of the model.
|
// Base, untranslated version of the model.
|
||||||
property var baseModel: selectObjMode ?
|
property var baseModel: selectObjMode ?
|
||||||
Runtime.Objects.getObjectsName(propertyType.objType).concat(
|
Modules.Objects.getObjectsName(propertyType.objType).concat(
|
||||||
isRealObject ? [qsTr("+ Create new %1").arg(Runtime.Objects.types[propertyType.objType].displayType())] : [])
|
isRealObject ? [qsTr("+ Create new %1").arg(Modules.Objects.types[propertyType.objType].displayType())] : [])
|
||||||
: propertyType.values
|
: propertyType.values
|
||||||
// Translated version of the model.
|
// Translated version of the model.
|
||||||
model: selectObjMode ? baseModel : propertyType.translatedValues
|
model: selectObjMode ? baseModel : propertyType.translatedValues
|
||||||
|
@ -198,20 +199,20 @@ Repeater {
|
||||||
if(selectObjMode) {
|
if(selectObjMode) {
|
||||||
// This is only done when what we're selecting are Objects.
|
// This is only done when what we're selecting are Objects.
|
||||||
// Setting object property.
|
// Setting object property.
|
||||||
var selectedObj = Runtime.Objects.currentObjectsByName[baseModel[newIndex]]
|
var selectedObj = Modules.Objects.currentObjectsByName[baseModel[newIndex]]
|
||||||
if(newIndex != 0) {
|
if(newIndex != 0) {
|
||||||
// Make sure we don't set the object to null.
|
// Make sure we don't set the object to null.
|
||||||
if(selectedObj == null) {
|
if(selectedObj == null) {
|
||||||
// Creating new object.
|
// Creating new object.
|
||||||
selectedObj = Runtime.Objects.createNewRegisteredObject(propertyType.objType)
|
selectedObj = Modules.Objects.createNewRegisteredObject(propertyType.objType)
|
||||||
history.addToHistory(new HistoryLib.CreateNewObject(selectedObj.name, propertyType.objType, selectedObj.export()))
|
history.addToHistory(new HistoryLib.CreateNewObject(selectedObj.name, propertyType.objType, selectedObj.export()))
|
||||||
baseModel = Runtime.Objects.getObjectsName(propertyType.objType).concat(
|
baseModel = Modules.Objects.getObjectsName(propertyType.objType).concat(
|
||||||
isRealObject ? [qsTr("+ Create new %1").arg(Runtime.Objects.types[propertyType.objType].displayType())] :
|
isRealObject ? [qsTr("+ Create new %1").arg(Modules.Objects.types[propertyType.objType].displayType())] :
|
||||||
[])
|
[])
|
||||||
currentIndex = baseModel.indexOf(selectedObj.name)
|
currentIndex = baseModel.indexOf(selectedObj.name)
|
||||||
}
|
}
|
||||||
selectedObj.requiredBy.push(Runtime.Objects.currentObjects[objType][objIndex])
|
selectedObj.requiredBy.push(Modules.Objects.currentObjects[objType][objIndex])
|
||||||
//Runtime.Objects.currentObjects[objType][objIndex].requiredBy = obj[propertyName].filter((obj) => obj.name != obj.name)
|
//Modules.Objects.currentObjects[objType][objIndex].requiredBy = obj[propertyName].filter((obj) => obj.name != obj.name)
|
||||||
}
|
}
|
||||||
obj.requiredBy = obj.requiredBy.filter((obj) => obj.name != obj.name)
|
obj.requiredBy = obj.requiredBy.filter((obj) => obj.name != obj.name)
|
||||||
history.addToHistory(new HistoryLib.EditedProperty(
|
history.addToHistory(new HistoryLib.EditedProperty(
|
||||||
|
@ -255,7 +256,7 @@ Repeater {
|
||||||
obj.name, objType, propertyName,
|
obj.name, objType, propertyName,
|
||||||
obj[propertyName], exported
|
obj[propertyName], exported
|
||||||
))
|
))
|
||||||
//Runtime.Objects.currentObjects[objType][objIndex][propertyName] = exported
|
//Modules.Objects.currentObjects[objType][objIndex][propertyName] = exported
|
||||||
obj[propertyName] = exported
|
obj[propertyName] = exported
|
||||||
root.changed()
|
root.changed()
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ Popup.BaseDialog {
|
||||||
\qmlproperty var EditorDialog::obj
|
\qmlproperty var EditorDialog::obj
|
||||||
Instance of the object being edited.
|
Instance of the object being edited.
|
||||||
*/
|
*/
|
||||||
property var obj: Runtime.Objects.currentObjects[objType][objIndex]
|
property var obj: Modules.Objects.currentObjects[objType][objIndex]
|
||||||
/*!
|
/*!
|
||||||
\qmlproperty var EditorDialog::posPicker
|
\qmlproperty var EditorDialog::posPicker
|
||||||
Reference to the global PositionPicker QML object.
|
Reference to the global PositionPicker QML object.
|
||||||
|
@ -85,7 +85,7 @@ Popup.BaseDialog {
|
||||||
Label {
|
Label {
|
||||||
id: dlgTitle
|
id: dlgTitle
|
||||||
verticalAlignment: TextInput.AlignVCenter
|
verticalAlignment: TextInput.AlignVCenter
|
||||||
text: qsTr("Edit properties of %1 %2").arg(Runtime.Objects.types[objEditor.objType].displayType()).arg(objEditor.obj.name)
|
text: qsTr("Edit properties of %1 %2").arg(Modules.Objects.types[objEditor.objType].displayType()).arg(objEditor.obj.name)
|
||||||
font.pixelSize: 20
|
font.pixelSize: 20
|
||||||
color: sysPalette.windowText
|
color: sysPalette.windowText
|
||||||
}
|
}
|
||||||
|
@ -111,14 +111,14 @@ Popup.BaseDialog {
|
||||||
onChanged: function(newValue) {
|
onChanged: function(newValue) {
|
||||||
let newName = Utils.parseName(newValue)
|
let newName = Utils.parseName(newValue)
|
||||||
if(newName != '' && objEditor.obj.name != newName) {
|
if(newName != '' && objEditor.obj.name != newName) {
|
||||||
if(newName in Runtime.Objects.currentObjectsByName) {
|
if(newName in Modules.Objects.currentObjectsByName) {
|
||||||
invalidNameDialog.showDialog(newName)
|
invalidNameDialog.showDialog(newName)
|
||||||
} else {
|
} else {
|
||||||
history.addToHistory(new HistoryLib.NameChanged(
|
history.addToHistory(new HistoryLib.NameChanged(
|
||||||
objEditor.obj.name, objEditor.objType, newName
|
objEditor.obj.name, objEditor.objType, newName
|
||||||
))
|
))
|
||||||
Runtime.Objects.renameObject(obj.name, newName)
|
Modules.Objects.renameObject(obj.name, newName)
|
||||||
objEditor.obj = Runtime.Objects.currentObjects[objEditor.objType][objEditor.objIndex]
|
objEditor.obj = Modules.Objects.currentObjects[objEditor.objType][objEditor.objIndex]
|
||||||
objectListList.update()
|
objectListList.update()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,7 +163,7 @@ Popup.BaseDialog {
|
||||||
*/
|
*/
|
||||||
function open() {
|
function open() {
|
||||||
dlgCustomProperties.model = [] // Reset
|
dlgCustomProperties.model = [] // Reset
|
||||||
let objProps = Runtime.Objects.types[objEditor.objType].properties()
|
let objProps = Modules.Objects.types[objEditor.objType].properties()
|
||||||
dlgCustomProperties.model = Object.keys(objProps).map(prop => [prop, objProps[prop]]) // Converted to 2-dimentional array.
|
dlgCustomProperties.model = Object.keys(objProps).map(prop => [prop, objProps[prop]]) // Converted to 2-dimentional array.
|
||||||
objEditor.show()
|
objEditor.show()
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ Column {
|
||||||
// Open editor
|
// Open editor
|
||||||
objectEditor.obj = obj
|
objectEditor.obj = obj
|
||||||
objectEditor.objType = obj.type
|
objectEditor.objType = obj.type
|
||||||
objectEditor.objIndex = Runtime.Objects.currentObjects[obj.type].indexOf(obj)
|
objectEditor.objIndex = Modules.Objects.currentObjects[obj.type].indexOf(obj)
|
||||||
objectEditor.open()
|
objectEditor.open()
|
||||||
// Disconnect potential link
|
// Disconnect potential link
|
||||||
posPicker.picked.disconnect(openEditorDialog)
|
posPicker.picked.disconnect(openEditorDialog)
|
||||||
|
@ -60,12 +60,12 @@ Column {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
columns: 3
|
columns: 3
|
||||||
Repeater {
|
Repeater {
|
||||||
model: Object.keys(Runtime.Objects.types)
|
model: Object.keys(Modules.Objects.types)
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: createBtn
|
id: createBtn
|
||||||
width: 96
|
width: 96
|
||||||
visible: Runtime.Objects.types[modelData].createable()
|
visible: Modules.Objects.types[modelData].createable()
|
||||||
height: visible ? width*0.8 : 0
|
height: visible ? width*0.8 : 0
|
||||||
// The KDE SDK is kinda buggy, so it respects neither specified color nor display propreties.
|
// The KDE SDK is kinda buggy, so it respects neither specified color nor display propreties.
|
||||||
//display: AbstractButton.TextUnderIcon
|
//display: AbstractButton.TextUnderIcon
|
||||||
|
@ -93,7 +93,7 @@ Column {
|
||||||
anchors.rightMargin: 4
|
anchors.rightMargin: 4
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
font.pixelSize: 14
|
font.pixelSize: 14
|
||||||
text: Runtime.Objects.types[modelData].displayType()
|
text: Modules.Objects.types[modelData].displayType()
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
clip: true
|
clip: true
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ Column {
|
||||||
ToolTip.text: label.text
|
ToolTip.text: label.text
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
let newObj = Runtime.Objects.createNewRegisteredObject(modelData)
|
let newObj = Modules.Objects.createNewRegisteredObject(modelData)
|
||||||
history.addToHistory(new HistoryLib.CreateNewObject(newObj.name, modelData, newObj.export()))
|
history.addToHistory(new HistoryLib.CreateNewObject(newObj.name, modelData, newObj.export()))
|
||||||
objectLists.update()
|
objectLists.update()
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ ScrollView {
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: objectsListView
|
id: objectsListView
|
||||||
model: Object.keys(Runtime.Objects.types)
|
model: Object.keys(Modules.Objects.types)
|
||||||
//width: implicitWidth //objectListList.width - (implicitHeight > objectListList.parent.height ? 20 : 0)
|
//width: implicitWidth //objectListList.width - (implicitHeight > objectListList.parent.height ? 20 : 0)
|
||||||
implicitHeight: contentItem.childrenRect.height + footerItem.height + 10
|
implicitHeight: contentItem.childrenRect.height + footerItem.height + 10
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ ScrollView {
|
||||||
id: objTypeList
|
id: objTypeList
|
||||||
property string objType: objectsListView.model[index]
|
property string objType: objectsListView.model[index]
|
||||||
property var editingRows: []
|
property var editingRows: []
|
||||||
model: Runtime.Objects.currentObjects[objType]
|
model: Modules.Objects.currentObjects[objType]
|
||||||
width: objectsListView.width
|
width: objectsListView.width
|
||||||
implicitHeight: contentItem.childrenRect.height
|
implicitHeight: contentItem.childrenRect.height
|
||||||
visible: model != undefined && model.length > 0
|
visible: model != undefined && model.length > 0
|
||||||
|
@ -69,23 +69,23 @@ ScrollView {
|
||||||
|
|
||||||
CheckBox {
|
CheckBox {
|
||||||
id: typeVisibilityCheckBox
|
id: typeVisibilityCheckBox
|
||||||
checked: Runtime.Objects.currentObjects[objType] != undefined ? Runtime.Objects.currentObjects[objType].every(obj => obj.visible) : true
|
checked: Modules.Objects.currentObjects[objType] != undefined ? Modules.Objects.currentObjects[objType].every(obj => obj.visible) : true
|
||||||
onClicked: {
|
onClicked: {
|
||||||
for(var obj of Runtime.Objects.currentObjects[objType]) obj.visible = this.checked
|
for(var obj of Modules.Objects.currentObjects[objType]) obj.visible = this.checked
|
||||||
for(var obj of objTypeList.editingRows) obj.objVisible = this.checked
|
for(var obj of objTypeList.editingRows) obj.objVisible = this.checked
|
||||||
objectListList.changed()
|
objectListList.changed()
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolTip.visible: hovered
|
ToolTip.visible: hovered
|
||||||
ToolTip.text: checked ?
|
ToolTip.text: checked ?
|
||||||
qsTr("Hide all %1").arg(Runtime.Objects.types[objType].displayTypeMultiple()) :
|
qsTr("Hide all %1").arg(Modules.Objects.types[objType].displayTypeMultiple()) :
|
||||||
qsTr("Show all %1").arg(Runtime.Objects.types[objType].displayTypeMultiple())
|
qsTr("Show all %1").arg(Modules.Objects.types[objType].displayTypeMultiple())
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: typeHeaderText
|
id: typeHeaderText
|
||||||
verticalAlignment: TextInput.AlignVCenter
|
verticalAlignment: TextInput.AlignVCenter
|
||||||
text: qsTranslate("control", "%1: ").arg(Runtime.Objects.types[objType].displayTypeMultiple())
|
text: qsTranslate("control", "%1: ").arg(Modules.Objects.types[objType].displayTypeMultiple())
|
||||||
font.pixelSize: 20
|
font.pixelSize: 20
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,11 +93,11 @@ ScrollView {
|
||||||
delegate: ObjectRow {
|
delegate: ObjectRow {
|
||||||
id: controlRow
|
id: controlRow
|
||||||
width: objTypeList.width
|
width: objTypeList.width
|
||||||
obj: Runtime.Objects.currentObjects[objType][index]
|
obj: Modules.Objects.currentObjects[objType][index]
|
||||||
posPicker: positionPicker
|
posPicker: positionPicker
|
||||||
|
|
||||||
onChanged: {
|
onChanged: {
|
||||||
obj = Runtime.Objects.currentObjects[objType][index]
|
obj = Modules.Objects.currentObjects[objType][index]
|
||||||
objectListList.update()
|
objectListList.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ ScrollView {
|
||||||
function update() {
|
function update() {
|
||||||
objectListList.changed()
|
objectListList.changed()
|
||||||
for(var objType in objectListList.listViews) {
|
for(var objType in objectListList.listViews) {
|
||||||
objectListList.listViews[objType].model = Runtime.Objects.currentObjects[objType]
|
objectListList.listViews[objType].model = Modules.Objects.currentObjects[objType]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,16 +89,16 @@ Item {
|
||||||
id: objDescription
|
id: objDescription
|
||||||
anchors.left: objVisibilityCheckBox.right
|
anchors.left: objVisibilityCheckBox.right
|
||||||
anchors.right: deleteButton.left
|
anchors.right: deleteButton.left
|
||||||
height: Runtime.Latex.enabled ? Math.max(parent.minHeight, latexDescription.height+4) : parent.minHeight
|
height: Modules.Latex.enabled ? Math.max(parent.minHeight, latexDescription.height+4) : parent.minHeight
|
||||||
verticalAlignment: TextInput.AlignVCenter
|
verticalAlignment: TextInput.AlignVCenter
|
||||||
text: Runtime.Latex.enabled ? "" : obj.getReadableString()
|
text: Modules.Latex.enabled ? "" : obj.getReadableString()
|
||||||
font.pixelSize: 14
|
font.pixelSize: 14
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
id: latexDescription
|
id: latexDescription
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
visible: Runtime.Latex.enabled
|
visible: Modules.Latex.enabled
|
||||||
property double depth: Screen.devicePixelRatio
|
property double depth: Screen.devicePixelRatio
|
||||||
property var ltxInfo: visible ? Latex.render(obj.getLatexString(), depth*(parent.font.pixelSize+2), parent.color).split(",") : ["","0","0"]
|
property var ltxInfo: visible ? Latex.render(obj.getLatexString(), depth*(parent.font.pixelSize+2), parent.color).split(",") : ["","0","0"]
|
||||||
source: visible ? ltxInfo[0] : ""
|
source: visible ? ltxInfo[0] : ""
|
||||||
|
@ -109,7 +109,7 @@ Item {
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
objEditor.obj = Runtime.Objects.currentObjects[obj.type][index]
|
objEditor.obj = Modules.Objects.currentObjects[obj.type][index]
|
||||||
objEditor.objType = obj.type
|
objEditor.objType = obj.type
|
||||||
objEditor.objIndex = index
|
objEditor.objIndex = index
|
||||||
//objEditor.editingRow = objectRow
|
//objEditor.editingRow = objectRow
|
||||||
|
@ -211,14 +211,14 @@ Item {
|
||||||
function deleteRecursively(object) {
|
function deleteRecursively(object) {
|
||||||
for(let toRemove of object.requiredBy)
|
for(let toRemove of object.requiredBy)
|
||||||
deleteRecursively(toRemove)
|
deleteRecursively(toRemove)
|
||||||
if(Runtime.Objects.currentObjectsByName[object.name] != undefined) {
|
if(Modules.Objects.currentObjectsByName[object.name] != undefined) {
|
||||||
// Object still exists
|
// Object still exists
|
||||||
// Temporary fix for objects require not being propertly updated.
|
// Temporary fix for objects require not being propertly updated.
|
||||||
object.requiredBy = []
|
object.requiredBy = []
|
||||||
history.addToHistory(new HistoryLib.DeleteObject(
|
history.addToHistory(new HistoryLib.DeleteObject(
|
||||||
object.name, object.type, object.export()
|
object.name, object.type, object.export()
|
||||||
))
|
))
|
||||||
Runtime.Objects.deleteObject(object.name)
|
Modules.Objects.deleteObject(object.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,7 +113,7 @@ Item {
|
||||||
if(mouse.button == Qt.LeftButton) { // Validate
|
if(mouse.button == Qt.LeftButton) { // Validate
|
||||||
let newValueX = !parent.userPickX ? null : parseValue(picked.mouseX.toString(), objType, propertyX)
|
let newValueX = !parent.userPickX ? null : parseValue(picked.mouseX.toString(), objType, propertyX)
|
||||||
let newValueY = !parent.userPickY ? null : parseValue(picked.mouseY.toString(), objType, propertyY)
|
let newValueY = !parent.userPickY ? null : parseValue(picked.mouseY.toString(), objType, propertyY)
|
||||||
let obj = Runtime.Objects.currentObjectsByName[objName]
|
let obj = Modules.Objects.currentObjectsByName[objName]
|
||||||
// Set values
|
// Set values
|
||||||
if(parent.userPickX && parent.userPickY) {
|
if(parent.userPickX && parent.userPickY) {
|
||||||
history.addToHistory(new HistoryLib.EditedPosition(
|
history.addToHistory(new HistoryLib.EditedPosition(
|
||||||
|
@ -262,7 +262,7 @@ Item {
|
||||||
color: 'black'
|
color: 'black'
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: canvas.x2px(picked.mouseX)
|
anchors.leftMargin: Modules.Canvas.x2px(picked.mouseX)
|
||||||
visible: parent.userPickX
|
visible: parent.userPickX
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,7 +273,7 @@ Item {
|
||||||
color: 'black'
|
color: 'black'
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.topMargin: canvas.y2px(picked.mouseY)
|
anchors.topMargin: Modules.Canvas.y2px(picked.mouseY)
|
||||||
visible: parent.userPickY
|
visible: parent.userPickY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,25 +282,26 @@ Item {
|
||||||
x: picker.mouseX - width - 5
|
x: picker.mouseX - width - 5
|
||||||
y: picker.mouseY - height - 5
|
y: picker.mouseY - height - 5
|
||||||
color: 'black'
|
color: 'black'
|
||||||
property double axisX: canvas.xaxisstep1
|
property double axisX: Modules.Canvas.axesStep.x.value
|
||||||
|
property double axisY: Modules.Canvas.axesStep.y.value
|
||||||
property double mouseX: {
|
property double mouseX: {
|
||||||
let xpos = canvas.px2x(picker.mouseX)
|
let xpos = Modules.Canvas.px2x(picker.mouseX)
|
||||||
if(snapToGridCheckbox.checked) {
|
if(snapToGridCheckbox.checked) {
|
||||||
if(canvas.logscalex) {
|
if(canvas.logscalex) {
|
||||||
// Calculate the logged power
|
// Calculate the logged power
|
||||||
let pow = Math.pow(10, Math.floor(Math.log10(xpos)))
|
let pow = Math.pow(10, Math.floor(Math.log10(xpos)))
|
||||||
return pow*Math.round(xpos/pow)
|
return pow*Math.round(xpos/pow)
|
||||||
} else {
|
} else {
|
||||||
return canvas.xaxisstep1*Math.round(xpos/canvas.xaxisstep1)
|
return axisX*Math.round(xpos/axisX)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return xpos.toFixed(parent.precision)
|
return xpos.toFixed(parent.precision)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
property double mouseY: {
|
property double mouseY: {
|
||||||
let ypos = canvas.px2y(picker.mouseY)
|
let ypos = Modules.Canvas.px2y(picker.mouseY)
|
||||||
if(snapToGridCheckbox.checked) {
|
if(snapToGridCheckbox.checked) {
|
||||||
return canvas.yaxisstep1*Math.round(ypos/canvas.yaxisstep1)
|
return axisY*Math.round(ypos/axisY)
|
||||||
} else {
|
} else {
|
||||||
return ypos.toFixed(parent.precision)
|
return ypos.toFixed(parent.precision)
|
||||||
}
|
}
|
||||||
|
@ -323,7 +324,7 @@ Item {
|
||||||
Parses a given \c value as an expression or a number depending on the type of \c propertyName of all \c objType.
|
Parses a given \c value as an expression or a number depending on the type of \c propertyName of all \c objType.
|
||||||
*/
|
*/
|
||||||
function parseValue(value, objType, propertyName) {
|
function parseValue(value, objType, propertyName) {
|
||||||
if(Runtime.Objects.types[objType].properties()[propertyName] == 'number')
|
if(Modules.Objects.types[objType].properties()[propertyName] == 'number')
|
||||||
return parseFloat(value)
|
return parseFloat(value)
|
||||||
else
|
else
|
||||||
return new MathLib.Expression(value)
|
return new MathLib.Expression(value)
|
||||||
|
|
|
@ -391,9 +391,9 @@ Item {
|
||||||
property string objectName: isEnteringProperty ?
|
property string objectName: isEnteringProperty ?
|
||||||
(parent.currentToken.dot ? parent.previousToken.value : parent.previousToken2.value)
|
(parent.currentToken.dot ? parent.previousToken.value : parent.previousToken2.value)
|
||||||
: ""
|
: ""
|
||||||
property bool doesObjectExist: isEnteringProperty && (objectName in Runtime.Objects.currentObjectsByName)
|
property bool doesObjectExist: isEnteringProperty && (objectName in Modules.Objects.currentObjectsByName)
|
||||||
property var objectProperties: doesObjectExist ?
|
property var objectProperties: doesObjectExist ?
|
||||||
Runtime.Objects.currentObjectsByName[objectName].constructor.properties() :
|
Modules.Objects.currentObjectsByName[objectName].constructor.properties() :
|
||||||
{}
|
{}
|
||||||
categoryItems: Object.keys(objectProperties)
|
categoryItems: Object.keys(objectProperties)
|
||||||
autocompleteGenerator: (item) => {
|
autocompleteGenerator: (item) => {
|
||||||
|
@ -460,9 +460,9 @@ Item {
|
||||||
visbilityCondition: parent.currentToken.identifier && !parent.previousToken.dot
|
visbilityCondition: parent.currentToken.identifier && !parent.previousToken.dot
|
||||||
itemStartIndex: functionsList.itemStartIndex + functionsList.model.length
|
itemStartIndex: functionsList.itemStartIndex + functionsList.model.length
|
||||||
itemSelected: parent.itemSelected
|
itemSelected: parent.itemSelected
|
||||||
categoryItems: Runtime.Objects.getObjectsName("ExecutableObject").filter(obj => obj != self)
|
categoryItems: Modules.Objects.getObjectsName("ExecutableObject").filter(obj => obj != self)
|
||||||
autocompleteGenerator: (item) => {return {
|
autocompleteGenerator: (item) => {return {
|
||||||
'text': item, 'annotation': Runtime.Objects.currentObjectsByName[item] == null ? '' : Objects.currentObjectsByName[item].constructor.displayType(),
|
'text': item, 'annotation': Modules.Objects.currentObjectsByName[item] == null ? '' : Objects.currentObjectsByName[item].constructor.displayType(),
|
||||||
'autocomplete': item+'()', 'cursorFinalOffset': -1
|
'autocomplete': item+'()', 'cursorFinalOffset': -1
|
||||||
}}
|
}}
|
||||||
baseText: parent.visible ? parent.currentToken.value : ""
|
baseText: parent.visible ? parent.currentToken.value : ""
|
||||||
|
@ -475,9 +475,9 @@ Item {
|
||||||
visbilityCondition: parent.currentToken.identifier && !parent.previousToken.dot
|
visbilityCondition: parent.currentToken.identifier && !parent.previousToken.dot
|
||||||
itemStartIndex: executableObjectsList.itemStartIndex + executableObjectsList.model.length
|
itemStartIndex: executableObjectsList.itemStartIndex + executableObjectsList.model.length
|
||||||
itemSelected: parent.itemSelected
|
itemSelected: parent.itemSelected
|
||||||
categoryItems: Object.keys(Runtime.Objects.currentObjectsByName).filter(obj => obj != self)
|
categoryItems: Object.keys(Modules.Objects.currentObjectsByName).filter(obj => obj != self)
|
||||||
autocompleteGenerator: (item) => {return {
|
autocompleteGenerator: (item) => {return {
|
||||||
'text': item, 'annotation': `${Runtime.Objects.currentObjectsByName[item].constructor.displayType()}`,
|
'text': item, 'annotation': `${Modules.Objects.currentObjectsByName[item].constructor.displayType()}`,
|
||||||
'autocomplete': item+'.', 'cursorFinalOffset': 0
|
'autocomplete': item+'.', 'cursorFinalOffset': 0
|
||||||
}}
|
}}
|
||||||
baseText: parent.visible ? parent.currentToken.value : ""
|
baseText: parent.visible ? parent.currentToken.value : ""
|
||||||
|
@ -537,8 +537,8 @@ Item {
|
||||||
throw new Error(qsTranslate('error', 'Object cannot be dependent on itself.'))
|
throw new Error(qsTranslate('error', 'Object cannot be dependent on itself.'))
|
||||||
// Recursive dependencies
|
// Recursive dependencies
|
||||||
let dependentOnSelfObjects = expr.requiredObjects().filter(
|
let dependentOnSelfObjects = expr.requiredObjects().filter(
|
||||||
(obj) => Runtime.Objects.currentObjectsByName[obj].getDependenciesList()
|
(obj) => Modules.Objects.currentObjectsByName[obj].getDependenciesList()
|
||||||
.includes(Runtime.Objects.currentObjectsByName[control.self])
|
.includes(Modules.Objects.currentObjectsByName[control.self])
|
||||||
)
|
)
|
||||||
if(dependentOnSelfObjects.length == 1)
|
if(dependentOnSelfObjects.length == 1)
|
||||||
throw new Error(qsTranslate('error', 'Circular dependency detected. Object %1 depends on %2.').arg(dependentOnSelfObjects[0].toString()).arg(control.self))
|
throw new Error(qsTranslate('error', 'Circular dependency detected. Object %1 depends on %2.').arg(dependentOnSelfObjects[0].toString()).arg(control.self))
|
||||||
|
|
|
@ -222,10 +222,10 @@ ScrollView {
|
||||||
label: qsTr("Max X")
|
label: qsTr("Max X")
|
||||||
icon: "settings/xmax.svg"
|
icon: "settings/xmax.svg"
|
||||||
width: settings.settingWidth
|
width: settings.settingWidth
|
||||||
value: canvas.px2x(canvas.canvasSize.width).toFixed(2)
|
value: Modules.Canvas.px2x(canvas.width).toFixed(2)
|
||||||
onChanged: function(xvaluemax) {
|
onChanged: function(xvaluemax) {
|
||||||
if(xvaluemax > settings.xmin) {
|
if(xvaluemax > settings.xmin) {
|
||||||
settings.xzoom = settings.xzoom * canvas.canvasSize.width/(canvas.x2px(xvaluemax)) // Adjusting zoom to fit. = (end)/(px of current point)
|
settings.xzoom = settings.xzoom * canvas.width/(Modules.Canvas.x2px(xvaluemax)) // Adjusting zoom to fit. = (end)/(px of current point)
|
||||||
settings.changed()
|
settings.changed()
|
||||||
} else {
|
} else {
|
||||||
alert.show("Maximum x value must be superior to minimum.")
|
alert.show("Maximum x value must be superior to minimum.")
|
||||||
|
@ -241,10 +241,10 @@ ScrollView {
|
||||||
label: qsTr("Min Y")
|
label: qsTr("Min Y")
|
||||||
icon: "settings/ymin.svg"
|
icon: "settings/ymin.svg"
|
||||||
width: settings.settingWidth
|
width: settings.settingWidth
|
||||||
defValue: canvas.px2y(canvas.canvasSize.height).toFixed(2)
|
defValue: Modules.Canvas.px2y(canvas.height).toFixed(2)
|
||||||
onChanged: function(yvaluemin) {
|
onChanged: function(yvaluemin) {
|
||||||
if(yvaluemin < settings.ymax) {
|
if(yvaluemin < settings.ymax) {
|
||||||
settings.yzoom = settings.yzoom * canvas.canvasSize.height/(canvas.y2px(yvaluemin)) // Adjusting zoom to fit. = (end)/(px of current point)
|
settings.yzoom = settings.yzoom * canvas.height/(Modules.Canvas.y2px(yvaluemin)) // Adjusting zoom to fit. = (end)/(px of current point)
|
||||||
settings.changed()
|
settings.changed()
|
||||||
} else {
|
} else {
|
||||||
alert.show("Minimum y value must be inferior to maximum.")
|
alert.show("Minimum y value must be inferior to maximum.")
|
||||||
|
|
|
@ -94,7 +94,7 @@ Item {
|
||||||
property int positionChangeTimer: 0
|
property int positionChangeTimer: 0
|
||||||
|
|
||||||
function updatePosition(deltaX, deltaY) {
|
function updatePosition(deltaX, deltaY) {
|
||||||
settingsInstance.xmin = (canvas.px2x(canvas.x2px(settingsInstance.xmin)-deltaX))
|
settingsInstance.xmin = (Modules.Canvas.px2x(Modules.Canvas.x2px(settingsInstance.xmin)-deltaX))
|
||||||
settingsInstance.ymax += deltaY/canvas.yzoom
|
settingsInstance.ymax += deltaY/canvas.yzoom
|
||||||
settingsInstance.ymax = settingsInstance.ymax.toFixed(4)
|
settingsInstance.ymax = settingsInstance.ymax.toFixed(4)
|
||||||
settingsInstance.changed()
|
settingsInstance.changed()
|
||||||
|
@ -107,6 +107,7 @@ Item {
|
||||||
prevY = mouse.y
|
prevY = mouse.y
|
||||||
parent.beginPositionChange()
|
parent.beginPositionChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
onPositionChanged: function(mouse) {
|
onPositionChanged: function(mouse) {
|
||||||
positionChangeTimer++
|
positionChangeTimer++
|
||||||
if(positionChangeTimer == 3) {
|
if(positionChangeTimer == 3) {
|
||||||
|
@ -118,12 +119,14 @@ Item {
|
||||||
positionChangeTimer = 0
|
positionChangeTimer = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onReleased: function(mouse) {
|
onReleased: function(mouse) {
|
||||||
let deltaX = mouse.x - prevX
|
let deltaX = mouse.x - prevX
|
||||||
let deltaY = mouse.y - prevY
|
let deltaY = mouse.y - prevY
|
||||||
updatePosition(deltaX, deltaY)
|
updatePosition(deltaX, deltaY)
|
||||||
parent.endPositionChange(deltaX, deltaY)
|
parent.endPositionChange(deltaX, deltaY)
|
||||||
}
|
}
|
||||||
|
|
||||||
onWheel: function(wheel) {
|
onWheel: function(wheel) {
|
||||||
// Scrolling
|
// Scrolling
|
||||||
let scrollSteps = Math.round(wheel.angleDelta.y / 120)
|
let scrollSteps = Math.round(wheel.angleDelta.y / 120)
|
||||||
|
|
|
@ -5,3 +5,4 @@
|
||||||
.import "objs/autoload.mjs" as Autoload
|
.import "objs/autoload.mjs" as Autoload
|
||||||
.import "math/latex.mjs" as Latex
|
.import "math/latex.mjs" as Latex
|
||||||
.import "history/common.mjs" as HistoryCommon
|
.import "history/common.mjs" as HistoryCommon
|
||||||
|
.import "canvas.mjs" as CanvasAPI
|
523
LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/canvas.mjs
Normal file
523
LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/canvas.mjs
Normal file
|
@ -0,0 +1,523 @@
|
||||||
|
/**
|
||||||
|
* LogarithmPlotter - 2D plotter software to make BODE plots, sequences and distribution functions.
|
||||||
|
* Copyright (C) 2021-2024 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 {Module} from "./modules.mjs"
|
||||||
|
import {textsup} from "./utils.mjs"
|
||||||
|
import {Expression} from "./mathlib.mjs"
|
||||||
|
|
||||||
|
|
||||||
|
class CanvasAPI extends Module {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('Canvas', [
|
||||||
|
Modules.Objects,
|
||||||
|
Modules.History
|
||||||
|
])
|
||||||
|
|
||||||
|
/** @type {HTMLCanvasElement} */
|
||||||
|
this._canvas = null
|
||||||
|
|
||||||
|
/** @type {CanvasRenderingContext2D} */
|
||||||
|
this._ctx = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Object}
|
||||||
|
* @property {function(string, string, string)} showDialog
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this._drawingErrorDialog = null
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {Object.<string, {expression: Expression, value: number, maxDraw: number}>}
|
||||||
|
*/
|
||||||
|
this.axesSteps = {
|
||||||
|
x: {
|
||||||
|
expression: null,
|
||||||
|
value: -1,
|
||||||
|
maxDraw: -1
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
expression: null,
|
||||||
|
value: -1,
|
||||||
|
maxDraw: -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize(canvasObject, drawingErrorDialog) {
|
||||||
|
this._canvas = canvasObject
|
||||||
|
this._drawingErrorDialog = drawingErrorDialog
|
||||||
|
}
|
||||||
|
|
||||||
|
get width() { return this._canvas.width }
|
||||||
|
|
||||||
|
get height() { return this._canvas.height }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimum x of the diagram, provided from settings.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get xmin() { return this._canvas.xmin }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zoom on the x-axis of the diagram, provided from settings.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get xzoom() { return this._canvas.xzoom }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum y of the diagram, provided from settings.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get ymax() { return this._canvas.ymax }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zoom on the y-axis of the diagram, provided from settings.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get yzoom() { return this._canvas.yzoom }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label used on the x-axis, provided from settings.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
get xlabel() { return this._canvas.xlabel }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label used on the y-axis, provided from settings.
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
get ylabel() { return this._canvas.ylabel }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Width of lines that will be drawn into the canvas, provided from settings.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get linewidth() { return this._canvas.linewidth }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Font size of the text that will be drawn into the canvas, provided from settings.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get textsize() { return this._canvas.textsize }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if the canvas should be in logarithmic mode, false otherwise.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
get logscalex() { return this._canvas.logscalex }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if the x graduation should be shown, false otherwise.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
get showxgrad() { return this._canvas.showxgrad }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if the y graduation should be shown, false otherwise.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
get showygrad() { return this._canvas.showygrad }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Max power of the logarithmic scaled on the x axis in logarithmic mode.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get maxgradx() { return this._canvas.maxgradx }
|
||||||
|
|
||||||
|
//
|
||||||
|
// Methods to draw the canvas
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redraws the entire canvas
|
||||||
|
*/
|
||||||
|
redraw() {
|
||||||
|
this._ctx = this._canvas.getContext("2d")
|
||||||
|
this._computeAxes()
|
||||||
|
this._reset()
|
||||||
|
this._drawGrid()
|
||||||
|
this._drawAxes()
|
||||||
|
this._drawLabels()
|
||||||
|
this._ctx.lineWidth = this.linewidth
|
||||||
|
for(let objType in Modules.Objects.currentObjects) {
|
||||||
|
for(let obj of Modules.Objects.currentObjects[objType]){
|
||||||
|
this._ctx.strokeStyle = obj.color
|
||||||
|
this._ctx.fillStyle = obj.color
|
||||||
|
if(obj.visible)
|
||||||
|
try {
|
||||||
|
obj.draw(this)
|
||||||
|
} catch(e) {
|
||||||
|
// Drawing throws an error. Generally, it's due to a new modification (or the opening of a file)
|
||||||
|
this._drawingErrorDialog.showDialog(objType, obj.name, e.message)
|
||||||
|
Modules.History.undo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._ctx.lineWidth = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates informations for drawing gradations for axes.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_computeAxes() {
|
||||||
|
let exprY = new Expression(`x*(${this._canvas.yaxisstep})`)
|
||||||
|
let y1 = exprY.execute(1)
|
||||||
|
let exprX = new Expression(`x*(${this._canvas.xaxisstep})`)
|
||||||
|
let x1 = exprX.execute(1)
|
||||||
|
this.axesSteps = {
|
||||||
|
x: {
|
||||||
|
expression: exprX,
|
||||||
|
value: x1,
|
||||||
|
maxDraw: Math.ceil(Math.max(Math.abs(this.xmin), Math.abs(this.px2x(this.width)))/x1)
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
expression: exprY,
|
||||||
|
value: y1,
|
||||||
|
maxDraw: Math.ceil(Math.max(Math.abs(this.ymax), Math.abs(this.px2y(this.height)))/y1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the canvas to a blank one with default setting.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_reset(){
|
||||||
|
// Reset
|
||||||
|
this._ctx.fillStyle = "#FFFFFF"
|
||||||
|
this._ctx.strokeStyle = "#000000"
|
||||||
|
this._ctx.font = `${this.textsize}px sans-serif`
|
||||||
|
this._ctx.fillRect(0,0,this.width,this.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the grid.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_drawGrid() {
|
||||||
|
this._ctx.strokeStyle = "#C0C0C0"
|
||||||
|
if(this.logscalex) {
|
||||||
|
for(let xpow = -this.maxgradx; xpow <= this.maxgradx; xpow++) {
|
||||||
|
for(let xmulti = 1; xmulti < 10; xmulti++) {
|
||||||
|
this.drawXLine(Math.pow(10, xpow)*xmulti)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for(let x = 0; x < this.axesSteps.x.maxDraw; x+=1) {
|
||||||
|
this.drawXLine(x*this.axesSteps.x.value)
|
||||||
|
this.drawXLine(-x*this.axesSteps.x.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(let y = 0; y < this.axesSteps.y.maxDraw; y+=1) {
|
||||||
|
this.drawYLine(y*this.axesSteps.y.value)
|
||||||
|
this.drawYLine(-y*this.axesSteps.y.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the graph axes.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_drawAxes() {
|
||||||
|
this._ctx.strokeStyle = "#000000"
|
||||||
|
let axisypos = this.logscalex ? 1 : 0
|
||||||
|
this.drawXLine(axisypos)
|
||||||
|
this.drawYLine(0)
|
||||||
|
let axisypx = this.x2px(axisypos) // X coordinate of Y axis
|
||||||
|
let axisxpx = this.y2px(0) // Y coordinate of X axis
|
||||||
|
// Drawing arrows
|
||||||
|
this.drawLine(axisypx, 0, axisypx-10, 10)
|
||||||
|
this.drawLine(axisypx, 0, axisypx+10, 10)
|
||||||
|
this.drawLine(this.width, axisxpx, this.width-10, axisxpx-10)
|
||||||
|
this.drawLine(this.width, axisxpx, this.width-10, axisxpx+10)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the canvas to a blank one with default setting.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_drawLabels() {
|
||||||
|
let axisypx = this.x2px(this.logscalex ? 1 : 0) // X coordinate of Y axis
|
||||||
|
let axisxpx = this.y2px(0) // Y coordinate of X axis
|
||||||
|
// Labels
|
||||||
|
this._ctx.fillStyle = "#000000"
|
||||||
|
this._ctx.font = `${this.textsize}px sans-serif`
|
||||||
|
this._ctx.fillText(this.ylabel, axisypx+10, 24)
|
||||||
|
let textWidth = this._ctx.measureText(this.xlabel).width
|
||||||
|
this._ctx.fillText(this.xlabel, this.width-14-textWidth, axisxpx-5)
|
||||||
|
// Axis graduation labels
|
||||||
|
this._ctx.font = `${this.textsize-4}px sans-serif`
|
||||||
|
|
||||||
|
let txtMinus = this._ctx.measureText('-').width
|
||||||
|
if(this.showxgrad) {
|
||||||
|
if(this.logscalex) {
|
||||||
|
for(let xpow = -this.maxgradx; xpow <= this.maxgradx; xpow+=1) {
|
||||||
|
textWidth = this._ctx.measureText("10"+textsup(xpow)).width
|
||||||
|
if(xpow !== 0)
|
||||||
|
this.drawVisibleText("10"+textsup(xpow), this.x2px(Math.pow(10,xpow))-textWidth/2, axisxpx+16+(6*(xpow===1)))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for(let x = 1; x < this.axesSteps.x.maxDraw; x += 1) {
|
||||||
|
let drawX = x*this.axesSteps.x.value
|
||||||
|
let txtX = this.axesSteps.x.expression.simplify(x).replace(/^\((.+)\)$/, '$1')
|
||||||
|
let textHeight = this.measureText(txtX).height
|
||||||
|
this.drawVisibleText(txtX, this.x2px(drawX)-4, axisxpx+this.textsize/2+textHeight)
|
||||||
|
this.drawVisibleText('-'+txtX, this.x2px(-drawX)-4, axisxpx+this.textsize/2+textHeight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(this.showygrad) {
|
||||||
|
for(let y = 0; y < this.axesSteps.y.maxDraw; y += 1) {
|
||||||
|
let drawY = y*this.axesSteps.y.value
|
||||||
|
let txtY = this.axesSteps.y.expression.simplify(y).replace(/^\((.+)\)$/, '$1')
|
||||||
|
textWidth = this._ctx.measureText(txtY).width
|
||||||
|
this.drawVisibleText(txtY, axisypx-6-textWidth, this.y2px(drawY)+4+(10*(y===0)))
|
||||||
|
if(y !== 0)
|
||||||
|
this.drawVisibleText('-'+txtY, axisypx-6-textWidth-txtMinus, this.y2px(-drawY)+4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._ctx.fillStyle = "#FFFFFF"
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Public functions
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws an horizontal line at x plot coordinate.
|
||||||
|
* @param {number} x
|
||||||
|
*/
|
||||||
|
drawXLine(x) {
|
||||||
|
if(this.isVisible(x, this.ymax)) {
|
||||||
|
this.drawLine(this.x2px(x), 0, this.x2px(x), this.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws an vertical line at y plot coordinate
|
||||||
|
* @param {number} y
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
drawYLine(y) {
|
||||||
|
if(this.isVisible(this.xmin, y)) {
|
||||||
|
this.drawLine(0, this.y2px(y), this.width, this.y2px(y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes multiline text onto the canvas.
|
||||||
|
* NOTE: The x and y properties here are relative to the canvas, not the plot.
|
||||||
|
* @param {string} text
|
||||||
|
* @param {number} x
|
||||||
|
* @param {number} y
|
||||||
|
*/
|
||||||
|
drawVisibleText(text, x, y) {
|
||||||
|
if(x > 0 && x < this.width && y > 0 && y < this.height) {
|
||||||
|
text.toString().split("\n").forEach((txt, i) => {
|
||||||
|
this._ctx.fillText(txt, x, y+(this.textsize*i))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws an image onto the canvas.
|
||||||
|
* NOTE: The x, y width and height properties here are relative to the canvas, not the plot.
|
||||||
|
* @param {CanvasImageSource} image
|
||||||
|
* @param {number} x
|
||||||
|
* @param {number} y
|
||||||
|
* @param {number} width
|
||||||
|
* @param {number} height
|
||||||
|
*/
|
||||||
|
drawVisibleImage(image, x, y, width, height) {
|
||||||
|
this._canvas.markDirty(Qt.rect(x, y, width, height));
|
||||||
|
this._ctx.drawImage(image, x, y, width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Measures the width and height of a multiline text that would be drawn onto the canvas.
|
||||||
|
* @param {string} text
|
||||||
|
* @returns {{width: number, height: number}}
|
||||||
|
*/
|
||||||
|
measureText(text) {
|
||||||
|
let theight = 0
|
||||||
|
let twidth = 0
|
||||||
|
let defaultHeight = this.textsize * 1.2 // Approximate but good enough!
|
||||||
|
for(let txt of text.split("\n")) {
|
||||||
|
theight += defaultHeight
|
||||||
|
if(this._ctx.measureText(txt).width > twidth) twidth = this._ctx.measureText(txt).width
|
||||||
|
}
|
||||||
|
return {'width': twidth, 'height': theight}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an x coordinate to its relative position on the canvas.
|
||||||
|
* It supports both logarithmic and non-logarithmic scale depending on the currently selected mode.
|
||||||
|
* @param {number} x
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
x2px(x) {
|
||||||
|
if(this.logscalex) {
|
||||||
|
let logxmin = Math.log(this.xmin)
|
||||||
|
return (Math.log(x)-logxmin)*this.xzoom
|
||||||
|
} else return (x - this.xmin)*this.xzoom
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an y coordinate to it's relative position on the canvas.
|
||||||
|
* The y-axis not supporting logarithmic scale, it only supports linear conversion.
|
||||||
|
* @param {number} y
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
y2px(y) {
|
||||||
|
return (this.ymax-y)*this.yzoom
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an x 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.
|
||||||
|
* @param {number} px
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
px2x(px) {
|
||||||
|
if(this.logscalex) {
|
||||||
|
return Math.exp(px/this.xzoom+Math.log(this.xmin))
|
||||||
|
} else return (px/this.xzoom+this.xmin)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an x 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.
|
||||||
|
* @param {number} px
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
px2y(px) {
|
||||||
|
return -(px/this.yzoom-this.ymax)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a plot point (x, y) is visible or not on the canvas.
|
||||||
|
* @param {number} x
|
||||||
|
* @param {number} y
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
isVisible(x, y) {
|
||||||
|
return (this.x2px(x) >= 0 && this.x2px(x) <= this.width) && (this.y2px(y) >= 0 && this.y2px(y) <= this.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a line from plot point (x1, y1) to plot point (x2, y2).
|
||||||
|
* @param {number} x1
|
||||||
|
* @param {number} y1
|
||||||
|
* @param {number} x2
|
||||||
|
* @param {number} y2
|
||||||
|
*/
|
||||||
|
drawLine(x1, y1, x2, y2) {
|
||||||
|
this._ctx.beginPath();
|
||||||
|
this._ctx.moveTo(x1, y1);
|
||||||
|
this._ctx.lineTo(x2, y2);
|
||||||
|
this._ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a dashed line from plot point (x1, y1) to plot point (x2, y2).
|
||||||
|
* @param {number} x1
|
||||||
|
* @param {number} y1
|
||||||
|
* @param {number} x2
|
||||||
|
* @param {number} y2
|
||||||
|
* @param {number} dashPxSize
|
||||||
|
*/
|
||||||
|
drawDashedLine(x1, y1, x2, y2, dashPxSize = 6) {
|
||||||
|
this._ctx.setLineDash([dashPxSize/2, dashPxSize]);
|
||||||
|
this.drawLine(x1, y1, x2, y2)
|
||||||
|
this._ctx.setLineDash([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders latex markup ltxText to an image and loads it. Returns a dictionary with three values: source, width and height.
|
||||||
|
* @param {string} ltxText
|
||||||
|
* @param {string} color
|
||||||
|
* @param {function({width: number, height: number, source: string})} callback
|
||||||
|
*/
|
||||||
|
renderLatexImage(ltxText, color, callback) {
|
||||||
|
let [ltxSrc, ltxWidth, ltxHeight] = Latex.render(ltxText, this.textsize, color).split(",")
|
||||||
|
let imgData = {
|
||||||
|
"source": ltxSrc,
|
||||||
|
"width": parseFloat(ltxWidth),
|
||||||
|
"height": parseFloat(ltxHeight)
|
||||||
|
};
|
||||||
|
if(!this._canvas.isImageLoaded(ltxSrc) && !this._canvas.isImageLoading(ltxSrc)){
|
||||||
|
// Wait until the image is loaded to callback.
|
||||||
|
this._canvas.loadImage(ltxSrc)
|
||||||
|
this._canvas.imageLoaders[ltxSrc] = [callback, imgData]
|
||||||
|
} else {
|
||||||
|
// Callback directly
|
||||||
|
callback(imgData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Context methods
|
||||||
|
//
|
||||||
|
|
||||||
|
get font() { return this._ctx.font }
|
||||||
|
set font(value) { return this._ctx.font = value }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws an act on the canvas centered on a point.
|
||||||
|
* @param {number} x
|
||||||
|
* @param {number} y
|
||||||
|
* @param {number} radius
|
||||||
|
* @param {number} startAngle
|
||||||
|
* @param {number} endAngle
|
||||||
|
* @param {boolean} counterclockwise
|
||||||
|
*/
|
||||||
|
arc(x, y, radius, startAngle, endAngle, counterclockwise=false) {
|
||||||
|
this._ctx.beginPath()
|
||||||
|
this._ctx.arc(x, y, radius, startAngle, endAngle, counterclockwise)
|
||||||
|
this._ctx.stroke()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a filled circle centered on a point.
|
||||||
|
* @param {number} x
|
||||||
|
* @param {number} y
|
||||||
|
* @param {number} radius
|
||||||
|
*/
|
||||||
|
disc(x, y, radius) {
|
||||||
|
this._ctx.beginPath();
|
||||||
|
this._ctx.arc(x, y, radius, 0, 2 * Math.PI)
|
||||||
|
this._ctx.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a filled rectangle onto the canvas.
|
||||||
|
* @param {number} x
|
||||||
|
* @param {number} y
|
||||||
|
* @param {number} w
|
||||||
|
* @param {number} h
|
||||||
|
*/
|
||||||
|
fillRect(x, y, w, h) {
|
||||||
|
this._ctx.fillRect(x, y, w, h)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {CanvasAPI} */
|
||||||
|
Modules.Canvas = Modules.Canvas || new CanvasAPI()
|
||||||
|
export const API = Modules.Canvas
|
|
@ -16,14 +16,14 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { RuntimeAPI } from "../runtime.mjs"
|
import { Module } from "../modules.mjs"
|
||||||
import Latex from "../math/latex.mjs"
|
import Latex from "../math/latex.mjs"
|
||||||
|
|
||||||
|
|
||||||
class HistoryCommonAPI extends RuntimeAPI {
|
class HistoryCommonAPI extends Module {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('History', [
|
super('History', [
|
||||||
Runtime.Latex
|
Modules.Latex
|
||||||
])
|
])
|
||||||
// History QML object
|
// History QML object
|
||||||
this.history = null;
|
this.history = null;
|
||||||
|
@ -31,12 +31,16 @@ class HistoryCommonAPI extends RuntimeAPI {
|
||||||
this.imageDepth = 2;
|
this.imageDepth = 2;
|
||||||
this.fontSize = 14;
|
this.fontSize = 14;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
undo() { this.history.undo() }
|
||||||
|
redo() { this.history.redo() }
|
||||||
|
addToHistory(action) { this.history.addToHistory(action) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {HistoryCommonAPI} */
|
/** @type {HistoryCommonAPI} */
|
||||||
Runtime.History = Runtime.History || new HistoryCommonAPI()
|
Modules.History = Modules.History || new HistoryCommonAPI()
|
||||||
|
|
||||||
export const API = Runtime.History
|
export const API = Modules.History
|
||||||
|
|
||||||
export class Action {
|
export class Action {
|
||||||
/**
|
/**
|
||||||
|
@ -95,7 +99,7 @@ export class Action {
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
getIconRichText(type) {
|
getIconRichText(type) {
|
||||||
return `<img source="../icons/objects/${type}.svg" style="color: ${Runtime.History.themeTextColor};" width=18 height=18></img>`
|
return `<img source="../icons/objects/${type}.svg" style="color: ${Modules.History.themeTextColor};" width=18 height=18></img>`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -107,11 +111,11 @@ export class Action {
|
||||||
renderLatexAsHtml(latexString) {
|
renderLatexAsHtml(latexString) {
|
||||||
if(!Latex.enabled)
|
if(!Latex.enabled)
|
||||||
throw new Error("Cannot render an item as LaTeX when LaTeX is disabled.")
|
throw new Error("Cannot render an item as LaTeX when LaTeX is disabled.")
|
||||||
let imgDepth = Runtime.History.imageDepth
|
let imgDepth = Modules.History.imageDepth
|
||||||
let [src, width, height] = Latex.render(
|
let [src, width, height] = Latex.render(
|
||||||
latexString,
|
latexString,
|
||||||
imgDepth * (Runtime.History.fontSize + 2),
|
imgDepth * (Modules.History.fontSize + 2),
|
||||||
Runtime.History.themeTextColor
|
Modules.History.themeTextColor
|
||||||
).split(",")
|
).split(",")
|
||||||
return `<img src="${src}" width="${parseInt(width)/imgDepth}" height="${parseInt(height)/imgDepth}" style="vertical-align: middle"/>`
|
return `<img src="${src}" width="${parseInt(width)/imgDepth}" height="${parseInt(height)/imgDepth}" style="vertical-align: middle"/>`
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,7 @@
|
||||||
.pragma library
|
.pragma library
|
||||||
|
|
||||||
.import "expr-eval.js" as ExprEval
|
.import "expr-eval.js" as ExprEval
|
||||||
.import "../../runtime.mjs" as R
|
.import "../../modules.mjs" as M
|
||||||
|
|
||||||
let RuntimeAPI = R.RuntimeAPI
|
|
||||||
|
|
||||||
const evalVariables = {
|
const evalVariables = {
|
||||||
// Variables not provided by expr-eval.js, needs to be provided manually
|
// Variables not provided by expr-eval.js, needs to be provided manually
|
||||||
|
@ -38,11 +36,11 @@ const evalVariables = {
|
||||||
"false": false
|
"false": false
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExprParserAPI extends RuntimeAPI {
|
class ExprParserAPI extends M.Module {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('ExprParser', [
|
super('ExprParser', [
|
||||||
/** @type {ObjectsAPI} */
|
/** @type {ObjectsAPI} */
|
||||||
Runtime.Objects
|
Modules.Objects
|
||||||
])
|
])
|
||||||
this.currentVars = {}
|
this.currentVars = {}
|
||||||
this.Internals = ExprEval
|
this.Internals = ExprEval
|
||||||
|
@ -115,7 +113,7 @@ class ExprParserAPI extends RuntimeAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {ExprParserAPI} */
|
/** @type {ExprParserAPI} */
|
||||||
Runtime.ExprParser = Runtime.ExprParser || new ExprParserAPI()
|
Modules.ExprParser = Modules.ExprParser || new ExprParserAPI()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
// Type polyfills for IDEs.
|
// Type polyfills for IDEs.
|
||||||
// Never directly imported.
|
// Never directly imported.
|
||||||
|
|
||||||
Runtime = Runtime || {}
|
Modules = Modules || {}
|
||||||
/** @type {function(string, string): string} */
|
/** @type {function(string, string): string} */
|
||||||
qsTranslate = qsTranslate || function(category, string) { throw new Error('qsTranslate not implemented.'); }
|
qsTranslate = qsTranslate || function(category, string) { throw new Error('qsTranslate not implemented.'); }
|
||||||
/** @type {function(string): string} */
|
/** @type {function(string): string} */
|
||||||
|
@ -29,6 +29,19 @@ QT_TRANSLATE_NOOP = QT_TRANSLATE_NOOP || function(string, string) { throw new Er
|
||||||
/** @type {function(string|boolean|int): string} */
|
/** @type {function(string|boolean|int): string} */
|
||||||
String.prototype.arg = String.prototype.arg || function(parameter) { throw new Error('arg not implemented.'); }
|
String.prototype.arg = String.prototype.arg || function(parameter) { throw new Error('arg not implemented.'); }
|
||||||
|
|
||||||
|
const Qt = {
|
||||||
|
/**
|
||||||
|
* @param {number} x
|
||||||
|
* @param {number} y
|
||||||
|
* @param {number} width
|
||||||
|
* @param {number} height
|
||||||
|
* @returns {{x, width, y, height}}
|
||||||
|
*/
|
||||||
|
rect: function(x, y, width, height) {
|
||||||
|
return {x: x, y: y, width: width, height: height};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Typehints for Helper. */
|
/** Typehints for Helper. */
|
||||||
const Helper = {
|
const Helper = {
|
||||||
/** @type {function(string): boolean} */
|
/** @type {function(string): boolean} */
|
||||||
|
|
|
@ -25,16 +25,16 @@ import * as Utils from "../utils.mjs"
|
||||||
*/
|
*/
|
||||||
export class Expression {
|
export class Expression {
|
||||||
constructor(expr) {
|
constructor(expr) {
|
||||||
if(!Runtime.ExprParser)
|
if(!Modules.ExprParser)
|
||||||
throw new Error('Expression parser not initialized.')
|
throw new Error('Expression parser not initialized.')
|
||||||
if(!Runtime.Objects)
|
if(!Modules.Objects)
|
||||||
throw new Error('Objects API not initialized.')
|
throw new Error('Objects API not initialized.')
|
||||||
this.expr = Utils.exponentsToExpression(expr)
|
this.expr = Utils.exponentsToExpression(expr)
|
||||||
this.calc = Runtime.ExprParser.parse(this.expr).simplify()
|
this.calc = Modules.ExprParser.parse(this.expr).simplify()
|
||||||
this.cached = this.isConstant()
|
this.cached = this.isConstant()
|
||||||
this.cachedValue = null
|
this.cachedValue = null
|
||||||
if(this.cached && this.allRequirementsFullfilled())
|
if(this.cached && this.allRequirementsFullfilled())
|
||||||
this.cachedValue = this.calc.evaluate(Runtime.Objects.currentObjectsByName)
|
this.cachedValue = this.calc.evaluate(Modules.Objects.currentObjectsByName)
|
||||||
this.latexMarkup = Latex.expression(this.calc.tokens)
|
this.latexMarkup = Latex.expression(this.calc.tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,26 +48,26 @@ export class Expression {
|
||||||
}
|
}
|
||||||
|
|
||||||
allRequirementsFullfilled() {
|
allRequirementsFullfilled() {
|
||||||
return this.requiredObjects().every(objName => objName in Runtime.Objects.currentObjectsByName)
|
return this.requiredObjects().every(objName => objName in Modules.Objects.currentObjectsByName)
|
||||||
}
|
}
|
||||||
|
|
||||||
undefinedVariables() {
|
undefinedVariables() {
|
||||||
return this.requiredObjects().filter(objName => !(objName in Runtime.Objects.currentObjectsByName))
|
return this.requiredObjects().filter(objName => !(objName in Modules.Objects.currentObjectsByName))
|
||||||
}
|
}
|
||||||
|
|
||||||
recache() {
|
recache() {
|
||||||
if(this.cached)
|
if(this.cached)
|
||||||
this.cachedValue = this.calc.evaluate(Runtime.Objects.currentObjectsByName)
|
this.cachedValue = this.calc.evaluate(Modules.Objects.currentObjectsByName)
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(x = 1) {
|
execute(x = 1) {
|
||||||
if(this.cached) {
|
if(this.cached) {
|
||||||
if(this.cachedValue == null)
|
if(this.cachedValue == null)
|
||||||
this.cachedValue = this.calc.evaluate(Runtime.Objects.currentObjectsByName)
|
this.cachedValue = this.calc.evaluate(Modules.Objects.currentObjectsByName)
|
||||||
return this.cachedValue
|
return this.cachedValue
|
||||||
}
|
}
|
||||||
Runtime.ExprParser.currentVars = Object.assign({'x': x}, Runtime.Objects.currentObjectsByName)
|
Modules.ExprParser.currentVars = Object.assign({'x': x}, Modules.Objects.currentObjectsByName)
|
||||||
return this.calc.evaluate(Runtime.ExprParser.currentVars)
|
return this.calc.evaluate(Modules.ExprParser.currentVars)
|
||||||
}
|
}
|
||||||
|
|
||||||
simplify(x) {
|
simplify(x) {
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { RuntimeAPI } from '../runtime.mjs'
|
import { Module } from '../modules.mjs'
|
||||||
|
|
||||||
const unicodechars = ["α","β","γ","δ","ε","ζ","η",
|
const unicodechars = ["α","β","γ","δ","ε","ζ","η",
|
||||||
"π","θ","κ","λ","μ","ξ","ρ",
|
"π","θ","κ","λ","μ","ξ","ρ",
|
||||||
|
@ -39,13 +39,12 @@ const equivalchars = ["\\alpha","\\beta","\\gamma","\\delta","\\epsilon","\\zeta
|
||||||
"{}_{4}","{}_{5}","{}_{6}","{}_{7}","{}_{8}","{}_{9}","{}_{0}",
|
"{}_{4}","{}_{5}","{}_{6}","{}_{7}","{}_{8}","{}_{9}","{}_{0}",
|
||||||
"\\pi", "\\infty"]
|
"\\pi", "\\infty"]
|
||||||
|
|
||||||
console.log(Runtime.ExprParser)
|
|
||||||
|
class LatexAPI extends Module {
|
||||||
class LatexAPI extends RuntimeAPI {
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('Latex', [
|
super('Latex', [
|
||||||
/** @type {ExprParserAPI} */
|
/** @type {ExprParserAPI} */
|
||||||
Runtime.ExprParser
|
Modules.ExprParser
|
||||||
])
|
])
|
||||||
/**
|
/**
|
||||||
* true if latex has been enabled by the user, false otherwise.
|
* true if latex has been enabled by the user, false otherwise.
|
||||||
|
@ -161,18 +160,18 @@ class LatexAPI extends RuntimeAPI {
|
||||||
let type = item.type
|
let type = item.type
|
||||||
|
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case Runtime.ExprParser.Internals.INUMBER:
|
case Modules.ExprParser.Internals.INUMBER:
|
||||||
if(item.value === Infinity) {
|
if(item.value === Infinity) {
|
||||||
nstack.push("\\infty")
|
nstack.push("\\infty")
|
||||||
} else if(typeof item.value === 'number' && item.value < 0) {
|
} else if(typeof item.value === 'number' && item.value < 0) {
|
||||||
nstack.push(this.par(item.value));
|
nstack.push(this.par(item.value));
|
||||||
} else if(Array.isArray(item.value)) {
|
} else if(Array.isArray(item.value)) {
|
||||||
nstack.push('[' + item.value.map(Runtime.ExprParser.Internals.escapeValue).join(', ') + ']');
|
nstack.push('[' + item.value.map(Modules.ExprParser.Internals.escapeValue).join(', ') + ']');
|
||||||
} else {
|
} else {
|
||||||
nstack.push(Runtime.ExprParser.Internals.escapeValue(item.value));
|
nstack.push(Modules.ExprParser.Internals.escapeValue(item.value));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Runtime.ExprParser.Internals.IOP2:
|
case Modules.ExprParser.Internals.IOP2:
|
||||||
n2 = nstack.pop();
|
n2 = nstack.pop();
|
||||||
n1 = nstack.pop();
|
n1 = nstack.pop();
|
||||||
f = item.value;
|
f = item.value;
|
||||||
|
@ -211,7 +210,7 @@ class LatexAPI extends RuntimeAPI {
|
||||||
throw new EvalError("Unknown operator " + ope + ".");
|
throw new EvalError("Unknown operator " + ope + ".");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Runtime.ExprParser.Internals.IOP3: // Thirdiary operator
|
case Modules.ExprParser.Internals.IOP3: // Thirdiary operator
|
||||||
n3 = nstack.pop();
|
n3 = nstack.pop();
|
||||||
n2 = nstack.pop();
|
n2 = nstack.pop();
|
||||||
n1 = nstack.pop();
|
n1 = nstack.pop();
|
||||||
|
@ -222,11 +221,11 @@ class LatexAPI extends RuntimeAPI {
|
||||||
throw new EvalError('Unknown operator ' + ope + '.');
|
throw new EvalError('Unknown operator ' + ope + '.');
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Runtime.ExprParser.Internals.IVAR:
|
case Modules.ExprParser.Internals.IVAR:
|
||||||
case Runtime.ExprParser.Internals.IVARNAME:
|
case Modules.ExprParser.Internals.IVARNAME:
|
||||||
nstack.push(this.variable(item.value.toString()));
|
nstack.push(this.variable(item.value.toString()));
|
||||||
break;
|
break;
|
||||||
case Runtime.ExprParser.Internals.IOP1: // Unary operator
|
case Modules.ExprParser.Internals.IOP1: // Unary operator
|
||||||
n1 = nstack.pop();
|
n1 = nstack.pop();
|
||||||
f = item.value;
|
f = item.value;
|
||||||
switch(f) {
|
switch(f) {
|
||||||
|
@ -242,7 +241,7 @@ class LatexAPI extends RuntimeAPI {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Runtime.ExprParser.Internals.IFUNCALL:
|
case Modules.ExprParser.Internals.IFUNCALL:
|
||||||
argCount = item.value;
|
argCount = item.value;
|
||||||
args = [];
|
args = [];
|
||||||
while (argCount-- > 0) {
|
while (argCount-- > 0) {
|
||||||
|
@ -252,14 +251,14 @@ class LatexAPI extends RuntimeAPI {
|
||||||
// Handling various functions
|
// Handling various functions
|
||||||
nstack.push(this.functionToLatex(f, args))
|
nstack.push(this.functionToLatex(f, args))
|
||||||
break;
|
break;
|
||||||
case Runtime.ExprParser.Internals.IFUNDEF:
|
case Modules.ExprParser.Internals.IFUNDEF:
|
||||||
nstack.push(this.par(n1 + '(' + args.join(', ') + ') = ' + n2));
|
nstack.push(this.par(n1 + '(' + args.join(', ') + ') = ' + n2));
|
||||||
break;
|
break;
|
||||||
case Runtime.ExprParser.Internals.IMEMBER:
|
case Modules.ExprParser.Internals.IMEMBER:
|
||||||
n1 = nstack.pop();
|
n1 = nstack.pop();
|
||||||
nstack.push(n1 + '.' + item.value);
|
nstack.push(n1 + '.' + item.value);
|
||||||
break;
|
break;
|
||||||
case Runtime.ExprParser.Internals.IARRAY:
|
case Modules.ExprParser.Internals.IARRAY:
|
||||||
argCount = item.value;
|
argCount = item.value;
|
||||||
args = [];
|
args = [];
|
||||||
while (argCount-- > 0) {
|
while (argCount-- > 0) {
|
||||||
|
@ -267,10 +266,10 @@ class LatexAPI extends RuntimeAPI {
|
||||||
}
|
}
|
||||||
nstack.push('[' + args.join(', ') + ']');
|
nstack.push('[' + args.join(', ') + ']');
|
||||||
break;
|
break;
|
||||||
case Runtime.ExprParser.Internals.IEXPR:
|
case Modules.ExprParser.Internals.IEXPR:
|
||||||
nstack.push('(' + this.expression(item.value) + ')');
|
nstack.push('(' + this.expression(item.value) + ')');
|
||||||
break;
|
break;
|
||||||
case Runtime.ExprParser.Internals.IENDSTATEMENT:
|
case Modules.ExprParser.Internals.IENDSTATEMENT:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new EvalError('invalid Expression');
|
throw new EvalError('invalid Expression');
|
||||||
|
@ -284,6 +283,6 @@ class LatexAPI extends RuntimeAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {LatexAPI} */
|
/** @type {LatexAPI} */
|
||||||
Runtime.Latex = Runtime.Latex || new LatexAPI()
|
Modules.Latex = Modules.Latex || new LatexAPI()
|
||||||
|
|
||||||
export default Runtime.Latex
|
export default Modules.Latex
|
||||||
|
|
|
@ -33,7 +33,7 @@ export class Sequence extends Expr.Expression {
|
||||||
this.latexValues = Object.assign({}, baseValues)
|
this.latexValues = Object.assign({}, baseValues)
|
||||||
for(let n in this.calcValues)
|
for(let n in this.calcValues)
|
||||||
if(['string', 'number'].includes(typeof this.calcValues[n])) {
|
if(['string', 'number'].includes(typeof this.calcValues[n])) {
|
||||||
let parsed = Runtime.ExprParser.parse(this.calcValues[n].toString()).simplify()
|
let parsed = Modules.ExprParser.parse(this.calcValues[n].toString()).simplify()
|
||||||
this.latexValues[n] = Latex.expression(parsed.tokens)
|
this.latexValues[n] = Latex.expression(parsed.tokens)
|
||||||
this.calcValues[n] = parsed.evaluate()
|
this.calcValues[n] = parsed.evaluate()
|
||||||
}
|
}
|
||||||
|
@ -60,17 +60,17 @@ export class Sequence extends Expr.Expression {
|
||||||
|
|
||||||
cache(n = 1) {
|
cache(n = 1) {
|
||||||
let str = Utils.simplifyExpression(this.calc.substitute('n', n-this.valuePlus).toString())
|
let str = Utils.simplifyExpression(this.calc.substitute('n', n-this.valuePlus).toString())
|
||||||
let expr = Runtime.ExprParser.parse(str).simplify()
|
let expr = Modules.ExprParser.parse(str).simplify()
|
||||||
// Cache values required for this one.
|
// Cache values required for this one.
|
||||||
if(!this.calcValues[n-this.valuePlus] && n-this.valuePlus > 0)
|
if(!this.calcValues[n-this.valuePlus] && n-this.valuePlus > 0)
|
||||||
this.cache(n-this.valuePlus)
|
this.cache(n-this.valuePlus)
|
||||||
// Setting current variables
|
// Setting current variables
|
||||||
Runtime.ExprParser.currentVars = Object.assign(
|
Modules.ExprParser.currentVars = Object.assign(
|
||||||
{'n': n-this.valuePlus}, // Just in case, add n (for custom functions)
|
{'n': n-this.valuePlus}, // Just in case, add n (for custom functions)
|
||||||
Runtime.Objects.currentObjectsByName,
|
Modules.Objects.currentObjectsByName,
|
||||||
{[this.name]: this.calcValues}
|
{[this.name]: this.calcValues}
|
||||||
)
|
)
|
||||||
this.calcValues[n] = expr.evaluate(Runtime.ExprParser.currentVars)
|
this.calcValues[n] = expr.evaluate(Modules.ExprParser.currentVars)
|
||||||
}
|
}
|
||||||
|
|
||||||
toString(forceSign=false) {
|
toString(forceSign=false) {
|
||||||
|
|
|
@ -19,12 +19,12 @@
|
||||||
/**
|
/**
|
||||||
* Base class for global APIs in runtime.
|
* Base class for global APIs in runtime.
|
||||||
*/
|
*/
|
||||||
export class RuntimeAPI {
|
export class Module {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {string} name - Name of the API
|
* @param {string} name - Name of the API
|
||||||
* @param {(RuntimeAPI|undefined)[]} requires - List of APIs required to initialize this one.
|
* @param {(Module|undefined)[]} requires - List of APIs required to initialize this one.
|
||||||
*/
|
*/
|
||||||
constructor(name, requires = []) {
|
constructor(name, requires = []) {
|
||||||
console.log(`Loading module ${name}...`)
|
console.log(`Loading module ${name}...`)
|
||||||
|
@ -33,7 +33,7 @@ export class RuntimeAPI {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if all requirements are defined.
|
* Checks if all requirements are defined.
|
||||||
* @param {(RuntimeAPI|undefined)[]} requires
|
* @param {(Module|undefined)[]} requires
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
*/
|
*/
|
||||||
__check_requirements(requires, name) {
|
__check_requirements(requires, name) {
|
|
@ -16,9 +16,9 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { RuntimeAPI } from './runtime.mjs'
|
import { Module } from './modules.mjs'
|
||||||
|
|
||||||
class ObjectsAPI extends RuntimeAPI {
|
class ObjectsAPI extends Module {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('Objects')
|
super('Objects')
|
||||||
|
@ -107,6 +107,6 @@ class ObjectsAPI extends RuntimeAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {ObjectsAPI} */
|
/** @type {ObjectsAPI} */
|
||||||
Runtime.Objects = Runtime.Objects || new ObjectsAPI()
|
Modules.Objects = Modules.Objects || new ObjectsAPI()
|
||||||
|
|
||||||
export default Runtime.Objects
|
export default Modules.Objects
|
||||||
|
|
|
@ -28,7 +28,7 @@ import XCursor from "xcursor.mjs"
|
||||||
import Sequence from "sequence.mjs"
|
import Sequence from "sequence.mjs"
|
||||||
import RepartitionFunction from "repartition.mjs"
|
import RepartitionFunction from "repartition.mjs"
|
||||||
|
|
||||||
if(Object.keys(Runtime.Objects.types).length === 0) {
|
if(Object.keys(Modules.Objects.types).length === 0) {
|
||||||
ObjectsCommonAPI.registerObject(Point)
|
ObjectsCommonAPI.registerObject(Point)
|
||||||
ObjectsCommonAPI.registerObject(Text)
|
ObjectsCommonAPI.registerObject(Text)
|
||||||
ObjectsCommonAPI.registerObject(Function)
|
ObjectsCommonAPI.registerObject(Function)
|
||||||
|
|
|
@ -19,17 +19,17 @@
|
||||||
import { getRandomColor, textsub } from "../utils.mjs"
|
import { getRandomColor, textsub } from "../utils.mjs"
|
||||||
import Objects from "../objects.mjs"
|
import Objects from "../objects.mjs"
|
||||||
import Latex from "../math/latex.mjs"
|
import Latex from "../math/latex.mjs"
|
||||||
import {RuntimeAPI} from "../runtime.mjs"
|
import {Module} from "../modules.mjs"
|
||||||
|
|
||||||
// This file contains the default data to be imported from all other objects
|
// This file contains the default data to be imported from all other objects
|
||||||
|
|
||||||
class ObjectsCommonAPI extends RuntimeAPI {
|
class ObjectsCommonAPI extends Module {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super('ObjectsCommon', [
|
super('ObjectsCommon', [
|
||||||
Runtime.Objects,
|
Modules.Objects,
|
||||||
Runtime.ExprParser,
|
Modules.ExprParser,
|
||||||
Runtime.Latex
|
Modules.Latex
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,9 +72,9 @@ class ObjectsCommonAPI extends RuntimeAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {ObjectsCommonAPI} */
|
/** @type {ObjectsCommonAPI} */
|
||||||
Runtime.ObjectsCommon = Runtime.ObjectsCommon || new ObjectsCommonAPI()
|
Modules.ObjectsCommon = Modules.ObjectsCommon || new ObjectsCommonAPI()
|
||||||
|
|
||||||
export const API = Runtime.ObjectsCommon
|
export const API = Modules.ObjectsCommon
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to extend for every type of object that
|
* Class to extend for every type of object that
|
||||||
|
@ -269,18 +269,17 @@ export class DrawableObject {
|
||||||
for(let toRemove of this.requiredBy) { // Normally, there should be none here, but better leave nothing just in case.
|
for(let toRemove of this.requiredBy) { // Normally, there should be none here, but better leave nothing just in case.
|
||||||
Objects.deleteObject(toRemove.name)
|
Objects.deleteObject(toRemove.name)
|
||||||
}
|
}
|
||||||
console.log(this.requires)
|
|
||||||
for(let toRemoveFrom of this.requires) {
|
for(let toRemoveFrom of this.requires) {
|
||||||
toRemoveFrom.requiredBy = toRemoveFrom.requiredBy.filter(o => o !== this)
|
toRemoveFrom.requiredBy = toRemoveFrom.requiredBy.filter(o => o !== this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract method. Draw the object onto the \c canvas with the 2D context \c ctx.
|
* Abstract method. Draw the object onto the \c canvas with the.
|
||||||
* @param {Canvas} canvas
|
* @param {CanvasAPI} canvas
|
||||||
* @param {CanvasRenderingContext2D} ctx
|
* @param {CanvasRenderingContext2D} ctx
|
||||||
*/
|
*/
|
||||||
draw(canvas, ctx) {}
|
draw(canvas) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applicates a \c drawFunction with two position arguments depending on
|
* Applicates a \c drawFunction with two position arguments depending on
|
||||||
|
@ -343,7 +342,7 @@ export class DrawableObject {
|
||||||
* Then, it's displayed using the \c drawFunctionLatex (x,y,imageData) and
|
* Then, it's displayed using the \c drawFunctionLatex (x,y,imageData) and
|
||||||
* \c drawFunctionText (x,y,text) depending on whether to use latex.
|
* \c drawFunctionText (x,y,text) depending on whether to use latex.
|
||||||
*
|
*
|
||||||
* @param {Canvas} canvas
|
* @param {CanvasAPI} canvas
|
||||||
* @param {CanvasRenderingContext2D} ctx
|
* @param {CanvasRenderingContext2D} ctx
|
||||||
* @param {string|Enum} labelPosition - Position of the label relative to the marked position
|
* @param {string|Enum} labelPosition - Position of the label relative to the marked position
|
||||||
* @param {number} posX - Component of the marked position on the x-axis
|
* @param {number} posX - Component of the marked position on the x-axis
|
||||||
|
@ -354,7 +353,7 @@ export class DrawableObject {
|
||||||
* @param {function|null} drawFunctionLatex - Function (x,y,imageData) to display the latex image
|
* @param {function|null} drawFunctionLatex - Function (x,y,imageData) to display the latex image
|
||||||
* @param {function|null} drawFunctionText - Function (x,y,text,textSize) to display the text
|
* @param {function|null} drawFunctionText - Function (x,y,text,textSize) to display the text
|
||||||
*/
|
*/
|
||||||
drawLabel(canvas, ctx, labelPosition, posX, posY,forceText = false,
|
drawLabel(canvas, labelPosition, posX, posY,forceText = false,
|
||||||
getLatexFunction = null, getTextFunction = null, drawFunctionLatex = null, drawFunctionText = null) {
|
getLatexFunction = null, getTextFunction = null, drawFunctionLatex = null, drawFunctionText = null) {
|
||||||
// Default functions
|
// Default functions
|
||||||
if(getLatexFunction == null)
|
if(getLatexFunction == null)
|
||||||
|
@ -362,25 +361,25 @@ export class DrawableObject {
|
||||||
if(getTextFunction == null)
|
if(getTextFunction == null)
|
||||||
getTextFunction = this.getLabel.bind(this)
|
getTextFunction = this.getLabel.bind(this)
|
||||||
if(drawFunctionLatex == null)
|
if(drawFunctionLatex == null)
|
||||||
drawFunctionLatex = (x,y,ltxImg) => canvas.drawVisibleImage(ctx, ltxImg.source, x, y, ltxImg.width, ltxImg.height)
|
drawFunctionLatex = (x,y,ltxImg) => canvas.drawVisibleImage(ltxImg.source, x, y, ltxImg.width, ltxImg.height)
|
||||||
if(drawFunctionText == null)
|
if(drawFunctionText == null)
|
||||||
drawFunctionText = (x,y,text,textSize) => canvas.drawVisibleText(ctx, text, x, y+textSize.height) // Positioned from left bottom
|
drawFunctionText = (x,y,text,textSize) => canvas.drawVisibleText(text, x, y+textSize.height) // Positioned from left bottom
|
||||||
// Drawing the label
|
// Drawing the label
|
||||||
let offset
|
let offset
|
||||||
if(!forceText && Latex.enabled) {
|
if(!forceText && Latex.enabled) {
|
||||||
// With latex
|
// With latex
|
||||||
let drawLblCb = function(canvas, ctx, ltxImg) {
|
let drawLblCb = ((ltxImg) => {
|
||||||
this.drawPositionDivergence(labelPosition, 8+ctx.lineWidth/2, ltxImg, posX, posY, (x,y) => drawFunctionLatex(x,y,ltxImg))
|
this.drawPositionDivergence(labelPosition, 8+canvas.linewidth/2, ltxImg, posX, posY, (x,y) => drawFunctionLatex(x,y,ltxImg))
|
||||||
}
|
}).bind(this)
|
||||||
let ltxLabel = getLatexFunction();
|
let ltxLabel = getLatexFunction();
|
||||||
if(ltxLabel != "")
|
if(ltxLabel !== "")
|
||||||
canvas.renderLatexImage(ltxLabel, this.color, drawLblCb.bind(this))
|
canvas.renderLatexImage(ltxLabel, this.color, drawLblCb.bind(this))
|
||||||
} else {
|
} else {
|
||||||
// Without latex
|
// Without latex
|
||||||
let text = getTextFunction()
|
let text = getTextFunction()
|
||||||
ctx.font = `${canvas.textsize}px sans-serif`
|
canvas.font = `${canvas.textsize}px sans-serif`
|
||||||
let textSize = canvas.measureText(ctx, text)
|
let textSize = canvas.measureText(text)
|
||||||
this.drawPositionDivergence(labelPosition, 8+ctx.lineWidth/2, textSize, posX, posY, (x,y) => drawFunctionText(x,y,text,textSize))
|
this.drawPositionDivergence(labelPosition, 8+canvas.linewidth/2, textSize, posX, posY, (x,y) => drawFunctionText(x,y,text,textSize))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -104,17 +104,17 @@ export default class Function extends ExecutableObject {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(canvas, ctx) {
|
draw(canvas) {
|
||||||
Function.drawFunction(canvas, ctx, this.expression, this.definitionDomain, this.destinationDomain, this.drawPoints, this.drawDashedLines)
|
Function.drawFunction(canvas, this.expression, this.definitionDomain, this.destinationDomain, this.drawPoints, this.drawDashedLines)
|
||||||
// Label
|
// Label
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reusable in other objects.
|
* Reusable in other objects.
|
||||||
* Drawing small traits every few pixels
|
* Drawing small traits every few pixels
|
||||||
*/
|
*/
|
||||||
static drawFunction(canvas, ctx, expr, definitionDomain, destinationDomain, drawPoints = true, drawDash = true) {
|
static drawFunction(canvas, expr, definitionDomain, destinationDomain, drawPoints = true, drawDash = true) {
|
||||||
let pxprecision = 10
|
let pxprecision = 10
|
||||||
let previousX = canvas.px2x(0)
|
let previousX = canvas.px2x(0)
|
||||||
let previousY = null;
|
let previousY = null;
|
||||||
|
@ -124,7 +124,7 @@ export default class Function extends ExecutableObject {
|
||||||
if(previousX === null) previousX = definitionDomain.next(canvas.px2x(0))
|
if(previousX === null) previousX = definitionDomain.next(canvas.px2x(0))
|
||||||
previousY = expr.execute(previousX)
|
previousY = expr.execute(previousX)
|
||||||
if(!drawPoints && !drawDash) return
|
if(!drawPoints && !drawDash) return
|
||||||
while(previousX !== null && canvas.x2px(previousX) < canvas.canvasSize.width) {
|
while(previousX !== null && canvas.x2px(previousX) < canvas.width) {
|
||||||
// Reconverted for canvas to fix for logarithmic scales.
|
// Reconverted for canvas to fix for logarithmic scales.
|
||||||
let currentX = definitionDomain.next(canvas.px2x(canvas.x2px(previousX)+pxprecision));
|
let currentX = definitionDomain.next(canvas.px2x(canvas.x2px(previousX)+pxprecision));
|
||||||
let currentY = expr.execute(currentX)
|
let currentY = expr.execute(currentX)
|
||||||
|
@ -132,10 +132,10 @@ export default class Function extends ExecutableObject {
|
||||||
if((definitionDomain.includes(currentX) || definitionDomain.includes(previousX)) &&
|
if((definitionDomain.includes(currentX) || definitionDomain.includes(previousX)) &&
|
||||||
(destinationDomain.includes(currentY) || destinationDomain.includes(previousY))) {
|
(destinationDomain.includes(currentY) || destinationDomain.includes(previousY))) {
|
||||||
if(drawDash)
|
if(drawDash)
|
||||||
canvas.drawDashedLine(ctx, canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY))
|
canvas.drawDashedLine(canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY))
|
||||||
if(drawPoints) {
|
if(drawPoints) {
|
||||||
ctx.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2)
|
canvas.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2)
|
||||||
ctx.fillRect(canvas.x2px(previousX)-1, canvas.y2px(previousY)-5, 2, 10)
|
canvas.fillRect(canvas.x2px(previousX)-1, canvas.y2px(previousY)-5, 2, 10)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
previousX = currentX
|
previousX = currentX
|
||||||
|
@ -143,8 +143,8 @@ export default class Function extends ExecutableObject {
|
||||||
}
|
}
|
||||||
if(drawPoints) {
|
if(drawPoints) {
|
||||||
// Drawing the last cross
|
// Drawing the last cross
|
||||||
ctx.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2)
|
canvas.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2)
|
||||||
ctx.fillRect(canvas.x2px(previousX)-1, canvas.y2px(previousY)-5, 2, 10)
|
canvas.fillRect(canvas.x2px(previousX)-1, canvas.y2px(previousY)-5, 2, 10)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Use max precision if function is trigonometrical on log scale.
|
// Use max precision if function is trigonometrical on log scale.
|
||||||
|
@ -154,7 +154,7 @@ export default class Function extends ExecutableObject {
|
||||||
// Calculate the previousY at the start of the canvas
|
// Calculate the previousY at the start of the canvas
|
||||||
if(definitionDomain.includes(previousX))
|
if(definitionDomain.includes(previousX))
|
||||||
previousY = expr.execute(previousX)
|
previousY = expr.execute(previousX)
|
||||||
for(let px = pxprecision; px < canvas.canvasSize.width; px += pxprecision) {
|
for(let px = pxprecision; px < canvas.width; px += pxprecision) {
|
||||||
let currentX = canvas.px2x(px)
|
let currentX = canvas.px2x(px)
|
||||||
if(!definitionDomain.includes(previousX) && definitionDomain.includes(currentX)) {
|
if(!definitionDomain.includes(previousX) && definitionDomain.includes(currentX)) {
|
||||||
// Should draw up to currentX, but NOT at previousX.
|
// Should draw up to currentX, but NOT at previousX.
|
||||||
|
@ -176,12 +176,12 @@ export default class Function extends ExecutableObject {
|
||||||
} while(!definitionDomain.includes(currentX) && currentX !== previousX)
|
} while(!definitionDomain.includes(currentX) && currentX !== previousX)
|
||||||
}
|
}
|
||||||
// This max variation is needed for functions with asymptotical vertical lines (e.g. 1/x, tan x...)
|
// This max variation is needed for functions with asymptotical vertical lines (e.g. 1/x, tan x...)
|
||||||
let maxvariation = (canvas.px2y(0)-canvas.px2y(canvas.canvasSize.height))
|
let maxvariation = (canvas.px2y(0)-canvas.px2y(canvas.height))
|
||||||
if(definitionDomain.includes(previousX) && definitionDomain.includes(currentX)) {
|
if(definitionDomain.includes(previousX) && definitionDomain.includes(currentX)) {
|
||||||
let currentY = expr.execute(currentX)
|
let currentY = expr.execute(currentX)
|
||||||
if(destinationDomain.includes(currentY)) {
|
if(destinationDomain.includes(currentY)) {
|
||||||
if(previousY != null && destinationDomain.includes(previousY) && Math.abs(previousY-currentY) < maxvariation) {
|
if(previousY != null && destinationDomain.includes(previousY) && Math.abs(previousY-currentY) < maxvariation) {
|
||||||
canvas.drawLine(ctx, canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY))
|
canvas.drawLine(canvas.x2px(previousX), canvas.y2px(previousY), canvas.x2px(currentX), canvas.y2px(currentY))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
previousY = currentY
|
previousY = currentY
|
||||||
|
|
|
@ -40,7 +40,7 @@ export default class GainBode extends ExecutableObject {
|
||||||
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
||||||
[QT_TRANSLATE_NOOP('prop','omGraduation')]: 'boolean'
|
[QT_TRANSLATE_NOOP('prop','omGraduation')]: 'boolean'
|
||||||
}}
|
}}
|
||||||
|
|
||||||
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
||||||
om_0 = '', pass = 'high', gain = '20', labelPosition = 'above', labelX = 1, omGraduation = false) {
|
om_0 = '', pass = 'high', gain = '20', labelPosition = 'above', labelX = 1, omGraduation = false) {
|
||||||
if(name == null) name = Common.getNewName('G')
|
if(name == null) name = Common.getNewName('G')
|
||||||
|
@ -52,12 +52,13 @@ export default class GainBode extends ExecutableObject {
|
||||||
if(om_0 == null) {
|
if(om_0 == null) {
|
||||||
// Create new point
|
// Create new point
|
||||||
om_0 = Objects.createNewRegisteredObject('Point', [Common.getNewName('ω'), true, this.color, 'name'])
|
om_0 = Objects.createNewRegisteredObject('Point', [Common.getNewName('ω'), true, this.color, 'name'])
|
||||||
HistoryAPI.history.addToHistory(new CreateNewObject(om_0.name, 'Point', om_0.export()))
|
HistoryAPI.addToHistory(new CreateNewObject(om_0.name, 'Point', om_0.export()))
|
||||||
om_0.update()
|
om_0.update()
|
||||||
labelPosition = 'below'
|
labelPosition = 'below'
|
||||||
}
|
}
|
||||||
om_0.requiredBy.push(this)
|
om_0.requiredBy.push(this)
|
||||||
}
|
}
|
||||||
|
/** @type {Point} */
|
||||||
this.om_0 = om_0
|
this.om_0 = om_0
|
||||||
this.pass = pass
|
this.pass = pass
|
||||||
if(typeof gain == 'number' || typeof gain == 'string') gain = new Expression(gain.toString())
|
if(typeof gain == 'number' || typeof gain == 'string') gain = new Expression(gain.toString())
|
||||||
|
@ -98,7 +99,7 @@ export default class GainBode extends ExecutableObject {
|
||||||
simplify(x = 1) {
|
simplify(x = 1) {
|
||||||
let xval = x
|
let xval = x
|
||||||
if(typeof x == 'string') xval = executeExpression(x)
|
if(typeof x == 'string') xval = executeExpression(x)
|
||||||
if((this.pass === 'high' && xval < this.om_0.x) || (this.pass === 'low' && xval > this.om_0.x)) {
|
if((this.pass === 'high' && xval < this.om_0.x.execute()) || (this.pass === 'low' && xval > this.om_0.x.execute())) {
|
||||||
let dbfn = new Expression(`${this.gain.execute()}*(ln(x)-ln(${this.om_0.x}))/ln(10)+${this.om_0.y}`)
|
let dbfn = new Expression(`${this.gain.execute()}*(ln(x)-ln(${this.om_0.x}))/ln(10)+${this.om_0.y}`)
|
||||||
return dbfn.simplify(x)
|
return dbfn.simplify(x)
|
||||||
} else {
|
} else {
|
||||||
|
@ -110,29 +111,29 @@ export default class GainBode extends ExecutableObject {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(canvas, ctx) {
|
draw(canvas) {
|
||||||
let base = [canvas.x2px(this.om_0.x), canvas.y2px(this.om_0.y)]
|
let base = [canvas.x2px(this.om_0.x.execute()), canvas.y2px(this.om_0.y.execute())]
|
||||||
let dbfn = new Expression(`${this.gain.execute()}*(log10(x)-log10(${this.om_0.x}))+${this.om_0.y}`)
|
let dbfn = new Expression(`${this.gain.execute()}*(log10(x)-log10(${this.om_0.x}))+${this.om_0.y}`)
|
||||||
let inDrawDom = new EmptySet()
|
let inDrawDom = new EmptySet()
|
||||||
|
|
||||||
if(this.pass === 'high') {
|
if(this.pass === 'high') {
|
||||||
// High pass, linear line from beginning, then constant to the end.
|
// High pass, linear line from beginning, then constant to the end.
|
||||||
canvas.drawLine(ctx, base[0], base[1], canvas.canvasSize.width, base[1])
|
canvas.drawLine(base[0], base[1], canvas.width, base[1])
|
||||||
inDrawDom = parseDomain(`]-inf;${this.om_0.x}[`)
|
inDrawDom = parseDomain(`]-inf;${this.om_0.x}[`)
|
||||||
} else {
|
} else {
|
||||||
// Low pass, constant from the beginning, linear line to the end.
|
// Low pass, constant from the beginning, linear line to the end.
|
||||||
canvas.drawLine(ctx, base[0], base[1], 0, base[1])
|
canvas.drawLine(base[0], base[1], 0, base[1])
|
||||||
inDrawDom = parseDomain(`]${this.om_0.x};+inf[`)
|
inDrawDom = parseDomain(`]${this.om_0.x};+inf[`)
|
||||||
}
|
}
|
||||||
Function.drawFunction(canvas, ctx, dbfn, inDrawDom, Domain.R)
|
Function.drawFunction(canvas, dbfn, inDrawDom, Domain.R)
|
||||||
// Dashed line representing break in function
|
// Dashed line representing break in function
|
||||||
var xpos = canvas.x2px(this.om_0.x.execute())
|
let xpos = canvas.x2px(this.om_0.x.execute())
|
||||||
var dashPxSize = 10
|
let dashPxSize = 10
|
||||||
for(var i = 0; i < canvas.canvasSize.height && this.omGraduation; i += dashPxSize*2)
|
for(let i = 0; i < canvas.height && this.omGraduation; i += dashPxSize*2)
|
||||||
canvas.drawLine(ctx, xpos, i, xpos, i+dashPxSize)
|
canvas.drawLine(xpos, i, xpos, i+dashPxSize)
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
|
|
|
@ -57,6 +57,7 @@ export default class PhaseBode extends ExecutableObject {
|
||||||
}
|
}
|
||||||
om_0.requiredBy.push(this)
|
om_0.requiredBy.push(this)
|
||||||
}
|
}
|
||||||
|
/** @type {Point} */
|
||||||
this.om_0 = om_0
|
this.om_0 = om_0
|
||||||
this.unit = unit
|
this.unit = unit
|
||||||
this.labelPosition = labelPosition
|
this.labelPosition = labelPosition
|
||||||
|
@ -100,21 +101,21 @@ export default class PhaseBode extends ExecutableObject {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(canvas, ctx) {
|
draw(canvas) {
|
||||||
let baseX = canvas.x2px(this.om_0.x.execute())
|
let baseX = canvas.x2px(this.om_0.x.execute())
|
||||||
let omy = this.om_0.y.execute()
|
let omy = this.om_0.y.execute()
|
||||||
let augmt = this.phase.execute()
|
let augmt = this.phase.execute()
|
||||||
let baseY = canvas.y2px(omy)
|
let baseY = canvas.y2px(omy)
|
||||||
let augmtY = canvas.y2px(omy+augmt)
|
let augmtY = canvas.y2px(omy+augmt)
|
||||||
// Before change line.
|
// Before change line.
|
||||||
canvas.drawLine(ctx, 0, baseY, Math.min(baseX, canvas.canvasSize.height), baseY)
|
canvas.drawLine(0, baseY, Math.min(baseX, canvas.height), baseY)
|
||||||
// Transition line.
|
// Transition line.
|
||||||
canvas.drawLine(ctx, baseX, baseY, baseX, augmtY)
|
canvas.drawLine(baseX, baseY, baseX, augmtY)
|
||||||
// After change line
|
// After change line
|
||||||
canvas.drawLine(ctx, Math.max(0, baseX), augmtY, canvas.canvasSize.width, augmtY)
|
canvas.drawLine(Math.max(0, baseX), augmtY, canvas.width, augmtY)
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
|
|
|
@ -58,24 +58,22 @@ export default class Point extends DrawableObject {
|
||||||
return [this.name, this.visible, this.color.toString(), this.labelContent, this.x.toEditableString(), this.y.toEditableString(), this.labelPosition, this.pointStyle]
|
return [this.name, this.visible, this.color.toString(), this.labelContent, this.x.toEditableString(), this.y.toEditableString(), this.labelPosition, this.pointStyle]
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(canvas, ctx) {
|
draw(canvas) {
|
||||||
var [canvasX, canvasY] = [canvas.x2px(this.x.execute()), canvas.y2px(this.y.execute())]
|
let [canvasX, canvasY] = [canvas.x2px(this.x.execute()), canvas.y2px(this.y.execute())]
|
||||||
var pointSize = 8+(ctx.lineWidth*2)
|
let pointSize = 8+(canvas.linewidth*2)
|
||||||
switch(this.pointStyle) {
|
switch(this.pointStyle) {
|
||||||
case '●':
|
case '●':
|
||||||
ctx.beginPath();
|
canvas.disc(canvasX, canvasY, pointSize/2)
|
||||||
ctx.ellipse(canvasX-pointSize/2, canvasY-pointSize/2, pointSize, pointSize)
|
|
||||||
ctx.fill();
|
|
||||||
break;
|
break;
|
||||||
case '✕':
|
case '✕':
|
||||||
canvas.drawLine(ctx, canvasX-pointSize/2, canvasY-pointSize/2, canvasX+pointSize/2, canvasY+pointSize/2)
|
canvas.drawLine(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)
|
canvas.drawLine(canvasX-pointSize/2, canvasY+pointSize/2, canvasX+pointSize/2, canvasY-pointSize/2)
|
||||||
break;
|
break;
|
||||||
case '+':
|
case '+':
|
||||||
ctx.fillRect(canvasX-pointSize/2, canvasY-1, pointSize, 2)
|
canvas.fillRect(canvasX-pointSize/2, canvasY-1, pointSize, 2)
|
||||||
ctx.fillRect(canvasX-1, canvasY-pointSize/2, 2, pointSize)
|
canvas.fillRect(canvasX-1, canvasY-pointSize/2, 2, pointSize)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, canvasX, canvasY)
|
this.drawLabel(canvas, this.labelPosition, canvasX, canvasY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,61 +92,51 @@ export default class RepartitionFunction extends ExecutableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(canvas, ctx) {
|
draw(canvas) {
|
||||||
let currentY = 0;
|
let currentY = 0;
|
||||||
let keys = Object.keys(this.probabilities).map(idx => parseInt(idx)).sort((a,b) => a-b)
|
let keys = Object.keys(this.probabilities).map(idx => parseInt(idx)).sort((a,b) => a-b)
|
||||||
if(canvas.isVisible(keys[0],this.probabilities[keys[0]].replace(/,/g, '.'))) {
|
if(canvas.isVisible(keys[0],this.probabilities[keys[0]].replace(/,/g, '.'))) {
|
||||||
canvas.drawLine(ctx,
|
canvas.drawLine(0, canvas.y2px(0), canvas.x2px(keys[0]), canvas.y2px(0))
|
||||||
0,
|
|
||||||
canvas.y2px(0),
|
|
||||||
canvas.x2px(keys[0]),
|
|
||||||
canvas.y2px(0)
|
|
||||||
)
|
|
||||||
if(canvas.isVisible(keys[0],0)) {
|
if(canvas.isVisible(keys[0],0)) {
|
||||||
ctx.beginPath();
|
canvas.arc(canvas.x2px(keys[0])+4,canvas.y2px(0), 4, Math.PI / 2, 3 * Math.PI / 2);
|
||||||
ctx.arc(canvas.x2px(keys[0])+4,canvas.y2px(0), 4, Math.PI / 2, 3 * Math.PI / 2);
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(let i = 0; i < keys.length-1; i++) {
|
for(let i = 0; i < keys.length-1; i++) {
|
||||||
let idx = keys[i];
|
let idx = keys[i];
|
||||||
currentY += parseFloat(this.probabilities[idx].replace(/,/g, '.'));
|
currentY += parseFloat(this.probabilities[idx].replace(/,/g, '.'));
|
||||||
if(canvas.isVisible(idx,currentY) || canvas.isVisible(keys[i+1],currentY)) {
|
if(canvas.isVisible(idx,currentY) || canvas.isVisible(keys[i+1],currentY)) {
|
||||||
canvas.drawLine(ctx,
|
canvas.drawLine(
|
||||||
Math.max(0,canvas.x2px(idx)),
|
Math.max(0,canvas.x2px(idx)),
|
||||||
canvas.y2px(currentY),
|
canvas.y2px(currentY),
|
||||||
Math.min(canvas.canvasSize.width,canvas.x2px(keys[i+1])),
|
Math.min(canvas.width,canvas.x2px(keys[i+1])),
|
||||||
canvas.y2px(currentY)
|
canvas.y2px(currentY)
|
||||||
)
|
)
|
||||||
if(canvas.isVisible(idx,currentY)) {
|
if(canvas.isVisible(idx,currentY)) {
|
||||||
ctx.beginPath();
|
canvas.disc(canvas.x2px(idx), canvas.y2px(currentY), 4)
|
||||||
ctx.arc(canvas.x2px(idx),canvas.y2px(currentY), 4, 0, 2 * Math.PI);
|
|
||||||
ctx.fill();
|
|
||||||
}
|
}
|
||||||
if(canvas.isVisible(keys[i+1],currentY)) {
|
if(canvas.isVisible(keys[i+1],currentY)) {
|
||||||
ctx.beginPath();
|
canvas.arc(canvas.x2px(keys[i+1])+4,canvas.y2px(currentY), 4, Math.PI / 2, 3 * Math.PI / 2);
|
||||||
ctx.arc(canvas.x2px(keys[i+1])+4,canvas.y2px(currentY), 4, Math.PI / 2, 3 * Math.PI / 2);
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(canvas.isVisible(keys[keys.length-1],currentY+parseFloat(this.probabilities[keys[keys.length-1]]))) {
|
if(canvas.isVisible(keys[keys.length-1],currentY+parseFloat(this.probabilities[keys[keys.length-1]]))) {
|
||||||
canvas.drawLine(ctx,
|
canvas.drawLine(
|
||||||
Math.max(0,canvas.x2px(keys[keys.length-1])),
|
Math.max(0,canvas.x2px(keys[keys.length-1])),
|
||||||
canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.'))),
|
canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.'))),
|
||||||
canvas.canvasSize.width,
|
canvas.width,
|
||||||
canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.')))
|
canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.')))
|
||||||
)
|
)
|
||||||
ctx.beginPath();
|
canvas.disc(
|
||||||
ctx.arc(
|
|
||||||
canvas.x2px(keys[keys.length-1]),
|
canvas.x2px(keys[keys.length-1]),
|
||||||
canvas.y2px(currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.'))),
|
canvas.y2px(
|
||||||
4, 0, 2 * Math.PI);
|
currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.'))
|
||||||
ctx.fill();
|
),
|
||||||
|
4
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -123,11 +123,11 @@ export default class Sequence extends ExecutableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(canvas, ctx) {
|
draw(canvas) {
|
||||||
Function.drawFunction(canvas, ctx, this.sequence, canvas.logscalex ? Domain.NE : Domain.N, Domain.R, this.drawPoints, this.drawDashedLines)
|
Function.drawFunction(canvas, this.sequence, canvas.logscalex ? Domain.NE : Domain.N, Domain.R, this.drawPoints, this.drawDashedLines)
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ export default class SommeGainsBode extends ExecutableObject {
|
||||||
let baseY = 0
|
let baseY = 0
|
||||||
let om0xGains = {1000000000: 0} // To draw the last part
|
let om0xGains = {1000000000: 0} // To draw the last part
|
||||||
let om0xPass = {1000000000: 'high'} // To draw the last part
|
let om0xPass = {1000000000: 'high'} // To draw the last part
|
||||||
Objects.currentObjects['Gain Bode'].forEach(function(gainObj) { // Sorting by their om_0 position.
|
for(/** @type {GainBode} */ let gainObj of Objects.currentObjects['Gain Bode']) { // Sorting by their om_0 position.
|
||||||
let om0x = gainObj.om_0.x.execute()
|
let om0x = gainObj.om_0.x.execute()
|
||||||
if(om0xGains[om0x] === undefined) {
|
if(om0xGains[om0x] === undefined) {
|
||||||
om0xGains[om0x] = gainObj.gain.execute()
|
om0xGains[om0x] = gainObj.gain.execute()
|
||||||
|
@ -99,7 +99,7 @@ export default class SommeGainsBode extends ExecutableObject {
|
||||||
om0xPass[om0x+0.001] = gainObj.pass === 'high'
|
om0xPass[om0x+0.001] = gainObj.pass === 'high'
|
||||||
}
|
}
|
||||||
baseY += gainObj.execute(drawMin)
|
baseY += gainObj.execute(drawMin)
|
||||||
})
|
}
|
||||||
// Sorting the om_0x
|
// Sorting the om_0x
|
||||||
let om0xList = Object.keys(om0xGains).map(x => parseFloat(x)) // THEY WERE CONVERTED TO STRINGS...
|
let om0xList = Object.keys(om0xGains).map(x => parseFloat(x)) // THEY WERE CONVERTED TO STRINGS...
|
||||||
om0xList.sort((a,b) => a - b)
|
om0xList.sort((a,b) => a - b)
|
||||||
|
@ -130,13 +130,13 @@ export default class SommeGainsBode extends ExecutableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(canvas, ctx) {
|
draw(canvas) {
|
||||||
if(this.cachedParts.length > 0) {
|
if(this.cachedParts.length > 0) {
|
||||||
for(let [dbfn, inDrawDom] of this.cachedParts) {
|
for(let [dbfn, inDrawDom] of this.cachedParts) {
|
||||||
Function.drawFunction(canvas, ctx, dbfn, inDrawDom, Domain.R)
|
Function.drawFunction(canvas, dbfn, inDrawDom, Domain.R)
|
||||||
if(inDrawDom.includes(this.labelX)) {
|
if(inDrawDom.includes(this.labelX)) {
|
||||||
// Label
|
// Label
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ export default class SommePhasesBode extends ExecutableObject {
|
||||||
|
|
||||||
if(Objects.currentObjects['Phase Bode'] !== undefined) {
|
if(Objects.currentObjects['Phase Bode'] !== undefined) {
|
||||||
console.log('Recalculating cache phase')
|
console.log('Recalculating cache phase')
|
||||||
for(let obj of Objects.currentObjects['Phase Bode']) {
|
for(/** @type {PhaseBode} */ let obj of Objects.currentObjects['Phase Bode']) {
|
||||||
this.om0xList.push(obj.om_0.x.execute())
|
this.om0xList.push(obj.om_0.x.execute())
|
||||||
if(phasesDict[obj.om_0.x.execute()] === undefined) {
|
if(phasesDict[obj.om_0.x.execute()] === undefined) {
|
||||||
phasesDict[obj.om_0.x.execute()] = obj.phase.execute()
|
phasesDict[obj.om_0.x.execute()] = obj.phase.execute()
|
||||||
|
@ -110,18 +110,18 @@ export default class SommePhasesBode extends ExecutableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(canvas, ctx) {
|
draw(canvas) {
|
||||||
for(let i = 0; i < this.om0xList.length-1; i++) {
|
for(let i = 0; i < this.om0xList.length-1; i++) {
|
||||||
let om0xBegin = canvas.x2px(this.om0xList[i])
|
let om0xBegin = canvas.x2px(this.om0xList[i])
|
||||||
let om0xEnd = canvas.x2px(this.om0xList[i+1])
|
let om0xEnd = canvas.x2px(this.om0xList[i+1])
|
||||||
let baseY = canvas.y2px(this.phasesList[i])
|
let baseY = canvas.y2px(this.phasesList[i])
|
||||||
let nextY = canvas.y2px(this.phasesList[i+1])
|
let nextY = canvas.y2px(this.phasesList[i+1])
|
||||||
canvas.drawLine(ctx, om0xBegin, baseY, om0xEnd, baseY)
|
canvas.drawLine(om0xBegin, baseY, om0xEnd, baseY)
|
||||||
canvas.drawLine(ctx, om0xEnd, baseY, om0xEnd, nextY)
|
canvas.drawLine(om0xEnd, baseY, om0xEnd, nextY)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.labelX), canvas.y2px(this.execute(this.labelX)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,9 +90,9 @@ export default class Text extends DrawableObject {
|
||||||
return `\\textsf{${this.latexMarkupText()}}`
|
return `\\textsf{${this.latexMarkupText()}}`
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(canvas, ctx) {
|
draw(canvas) {
|
||||||
let yOffset = this.disableLatex ? canvas.textsize-4 : 0
|
let yOffset = this.disableLatex ? canvas.textsize-4 : 0
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, canvas.x2px(this.x.execute()), canvas.y2px(this.y.execute())+yOffset, this.disableLatex)
|
this.drawLabel(canvas, this.labelPosition, canvas.x2px(this.x.execute()), canvas.y2px(this.y.execute())+yOffset, this.disableLatex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,8 +91,8 @@ export default class XCursor extends DrawableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
getTargetValueLatexLabel() {
|
getTargetValueLatexLabel() {
|
||||||
var t = this.targetElement
|
let t = this.targetElement
|
||||||
var approx = ''
|
let approx = ''
|
||||||
if(this.approximate) {
|
if(this.approximate) {
|
||||||
approx = t.execute(this.x.execute())
|
approx = t.execute(this.x.execute())
|
||||||
approx = approx.toPrecision(this.rounding + Math.round(approx).toString().length)
|
approx = approx.toPrecision(this.rounding + Math.round(approx).toString().length)
|
||||||
|
@ -142,39 +142,35 @@ export default class XCursor extends DrawableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(canvas, ctx) {
|
draw(canvas) {
|
||||||
let xpos = canvas.x2px(this.x.execute())
|
let xpos = canvas.x2px(this.x.execute())
|
||||||
switch(this.displayStyle) {
|
switch(this.displayStyle) {
|
||||||
case '— — — — — — —':
|
case '— — — — — — —':
|
||||||
var dashPxSize = 10
|
canvas.drawDashedLine(xpos, 0, xpos, canvas.height, 20)
|
||||||
for(var i = 0; i < canvas.canvasSize.height; i += dashPxSize*2)
|
|
||||||
canvas.drawLine(ctx, xpos, i, xpos, i+dashPxSize)
|
|
||||||
break;
|
break;
|
||||||
case '⸺⸺⸺⸺⸺⸺':
|
case '⸺⸺⸺⸺⸺⸺':
|
||||||
canvas.drawXLine(ctx, this.x.execute())
|
canvas.drawXLine(this.x.execute())
|
||||||
break;
|
break;
|
||||||
case '• • • • • • • • • •':
|
case '• • • • • • • • • •':
|
||||||
var pointDistancePx = 10
|
let pointDistancePx = 10
|
||||||
var pointSize = 2
|
let pointSize = 2
|
||||||
ctx.beginPath();
|
for(let i = 0; i < canvas.height; i += pointDistancePx)
|
||||||
for(var i = 0; i < canvas.canvasSize.height; i += pointDistancePx)
|
canvas.disc(xpos, i, pointSize)
|
||||||
ctx.ellipse(xpos-pointSize/2, i-pointSize/2, pointSize, pointSize)
|
|
||||||
ctx.fill();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drawing label at the top of the canvas.
|
// Drawing label at the top of the canvas.
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, xpos, 0, false, null, null,
|
this.drawLabel(canvas, this.labelPosition, xpos, 0, false, null, null,
|
||||||
(x,y,ltxImg) => canvas.drawVisibleImage(ctx, ltxImg.source, x, 5, ltxImg.width, ltxImg.height),
|
(x,y,ltxImg) => canvas.drawVisibleImage(ltxImg.source, x, 5, ltxImg.width, ltxImg.height),
|
||||||
(x,y,text,textSize) => canvas.drawVisibleText(ctx, text, x, textSize.height+5))
|
(x,y,text,textSize) => canvas.drawVisibleText(text, x, textSize.height+5))
|
||||||
|
|
||||||
// Drawing label at the position of the target element.
|
// Drawing label at the position of the target element.
|
||||||
if(this.targetValuePosition === 'Next to target' && this.targetElement != null) {
|
if(this.targetValuePosition === 'Next to target' && this.targetElement != null) {
|
||||||
let ypos = canvas.y2px(this.targetElement.execute(this.x.execute()))
|
let ypos = canvas.y2px(this.targetElement.execute(this.x.execute()))
|
||||||
this.drawLabel(canvas, ctx, this.labelPosition, xpos, ypos, false,
|
this.drawLabel(canvas, this.labelPosition, xpos, ypos, false,
|
||||||
this.getTargetValueLatexLabel.bind(this), this.getTargetValueLabel.bind(this),
|
this.getTargetValueLatexLabel.bind(this), this.getTargetValueLabel.bind(this),
|
||||||
(x,y,ltxImg) => canvas.drawVisibleImage(ctx, ltxImg.source, x, y, ltxImg.width, ltxImg.height),
|
(x,y,ltxImg) => canvas.drawVisibleImage(ltxImg.source, x, y, ltxImg.width, ltxImg.height),
|
||||||
(x,y,text,textSize) => canvas.drawVisibleText(ctx, text, x, y+textSize.height))
|
(x,y,text,textSize) => canvas.drawVisibleText(text, x, y+textSize.height))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue