X Cursor Latex implementation
Some checks reported errors
continuous-integration/drone/push Build was killed
Some checks reported errors
continuous-integration/drone/push Build was killed
Also a few bugfixes and added documentation
This commit is contained in:
parent
2691ba687f
commit
166d1a2485
7 changed files with 117 additions and 100 deletions
|
@ -157,7 +157,15 @@ Canvas {
|
||||||
*/
|
*/
|
||||||
property int drawMaxX: Math.ceil(Math.max(Math.abs(xmin), Math.abs(px2x(canvasSize.width)))/xaxisstep1)
|
property int drawMaxX: Math.ceil(Math.max(Math.abs(xmin), Math.abs(px2x(canvasSize.width)))/xaxisstep1)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\qmlproperty var LogGraphCanvas::imageLoaders
|
||||||
|
Dictionary of format {image: [callback.image data]} containing data for defered image loading.
|
||||||
|
*/
|
||||||
property var imageLoaders: {}
|
property var imageLoaders: {}
|
||||||
|
/*!
|
||||||
|
\qmlproperty var LogGraphCanvas::ctx
|
||||||
|
Cache for the 2D context so that it may be used asynchronously.
|
||||||
|
*/
|
||||||
property var ctx
|
property var ctx
|
||||||
|
|
||||||
Component.onCompleted: imageLoaders = {}
|
Component.onCompleted: imageLoaders = {}
|
||||||
|
|
|
@ -31,7 +31,7 @@ class Expression {
|
||||||
this.calc = C.parser.parse(expr).simplify()
|
this.calc = C.parser.parse(expr).simplify()
|
||||||
this.cached = this.isConstant()
|
this.cached = this.isConstant()
|
||||||
this.cachedValue = this.cached ? this.calc.evaluate(C.evalVariables) : null
|
this.cachedValue = this.cached ? this.calc.evaluate(C.evalVariables) : null
|
||||||
this.latexMarkup = Latex.expressionToLatex(this.calc.tokens)
|
this.latexMarkup = Latex.expression(this.calc.tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
isConstant() {
|
isConstant() {
|
||||||
|
|
|
@ -125,7 +125,7 @@ function variable(vari) {
|
||||||
* @param {Array} tokens - expr-eval tokens list
|
* @param {Array} tokens - expr-eval tokens list
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
function expressionToLatex(tokens) {
|
function expression(tokens) {
|
||||||
var nstack = [];
|
var nstack = [];
|
||||||
var n1, n2, n3;
|
var n1, n2, n3;
|
||||||
var f, args, argCount;
|
var f, args, argCount;
|
||||||
|
@ -239,7 +239,7 @@ function expressionToLatex(tokens) {
|
||||||
nstack.push('[' + args.join(', ') + ']');
|
nstack.push('[' + args.join(', ') + ']');
|
||||||
break;
|
break;
|
||||||
case ExprEval.IEXPR:
|
case ExprEval.IEXPR:
|
||||||
nstack.push('(' + expressionToLatex(item.value) + ')');
|
nstack.push('(' + expression(item.value) + ')');
|
||||||
break;
|
break;
|
||||||
case ExprEval.IENDSTATEMENT:
|
case ExprEval.IENDSTATEMENT:
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -38,7 +38,7 @@ class Sequence extends Expr.Expression {
|
||||||
for(var n in this.calcValues)
|
for(var n in this.calcValues)
|
||||||
if(['string', 'number'].includes(typeof this.calcValues[n])) {
|
if(['string', 'number'].includes(typeof this.calcValues[n])) {
|
||||||
let parsed = C.parser.parse(this.calcValues[n].toString()).simplify()
|
let parsed = C.parser.parse(this.calcValues[n].toString()).simplify()
|
||||||
this.latexValues[n] = Latex.expressionToLatex(parsed.tokens)
|
this.latexValues[n] = Latex.expression(parsed.tokens)
|
||||||
this.calcValues[n] = parsed.evaluate(C.evalVariables)
|
this.calcValues[n] = parsed.evaluate(C.evalVariables)
|
||||||
}
|
}
|
||||||
this.valuePlus = parseInt(valuePlus)
|
this.valuePlus = parseInt(valuePlus)
|
||||||
|
|
|
@ -209,45 +209,12 @@ class DrawableObject {
|
||||||
*/
|
*/
|
||||||
draw(canvas, ctx) {}
|
draw(canvas, ctx) {}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Automaticly draw the label of the object on the \c canvas with the 2D context \c ctx.
|
|
||||||
* This method takes into account both the \c posX and \c posY of where the label
|
|
||||||
* should be displayed, including the \c labelPosition relative to it.
|
|
||||||
* @param {Canvas} canvas
|
|
||||||
* @param {Context2D} ctx
|
|
||||||
* @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} posY - Component of the marked position on the y-axis
|
|
||||||
* @param {bool} forceText - Force the rendering of the label as text.
|
|
||||||
*/
|
|
||||||
drawLabel(canvas, ctx, labelPosition, posX, posY, forceText = false) {
|
|
||||||
let offset
|
|
||||||
if(!forceText && true) { // TODO: Check for user setting with Latex.
|
|
||||||
// With latex
|
|
||||||
let drawLblCb = function(canvas, ctx, ltxImg) {
|
|
||||||
this.drawLabelDivergence(labelPosition, 8, ltxImg, posX, posY,
|
|
||||||
(x,y) => canvas.drawVisibleImage(ctx, ltxImg.source, x, y, ltxImg.width, ltxImg.height))
|
|
||||||
}
|
|
||||||
let ltxLabel = this.getLatexLabel();
|
|
||||||
if(ltxLabel != "")
|
|
||||||
canvas.renderLatexImage(ltxLabel, this.color, drawLblCb.bind(this))
|
|
||||||
//canvas.drawVisibleImage(ctx, ltxImg.source, posX, posY)
|
|
||||||
} else {
|
|
||||||
// Without latex
|
|
||||||
let text = this.getLabel()
|
|
||||||
ctx.font = `${canvas.textsize}px sans-serif`
|
|
||||||
this.drawLabelDivergence(labelPosition, 4, canvas.measureText(ctx, text), posX, posY,
|
|
||||||
(x,y) => canvas.drawVisibleText(ctx, text, x, y))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applicates a \c drawFunction with two position arguments depending on
|
* Applicates a \c drawFunction with two position arguments depending on
|
||||||
* both the \c posX and \c posY of where the label should be displayed,
|
* both the \c posX and \c posY of where the label should be displayed,
|
||||||
* and the \c labelPosition which declares the label should be displayed
|
* and the \c labelPosition which declares the label should be displayed
|
||||||
* relatively to that position.
|
* relatively to that position.
|
||||||
|
*
|
||||||
* @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} offset - Margin between the position and the object to be drawn
|
* @param {number} offset - Margin between the position and the object to be drawn
|
||||||
* @param {Dictionary} size - Size of the label item, containing two properties, "width" and "height"
|
* @param {Dictionary} size - Size of the label item, containing two properties, "width" and "height"
|
||||||
|
@ -255,7 +222,7 @@ class DrawableObject {
|
||||||
* @param {number} posY - Component of the marked position on the y-axis
|
* @param {number} posY - Component of the marked position on the y-axis
|
||||||
* @param {function} drawFunction - Function with two arguments (x, y) that will be called to draw the label
|
* @param {function} drawFunction - Function with two arguments (x, y) that will be called to draw the label
|
||||||
*/
|
*/
|
||||||
drawLabelDivergence(labelPosition, offset, size, posX, posY, drawFunction) {
|
drawPositionDivergence(labelPosition, offset, size, posX, posY, drawFunction) {
|
||||||
switch(labelPosition) {
|
switch(labelPosition) {
|
||||||
case 'center':
|
case 'center':
|
||||||
drawFunction(posX-size.width/2, posY-size.height/2)
|
drawFunction(posX-size.width/2, posY-size.height/2)
|
||||||
|
@ -293,6 +260,56 @@ class DrawableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automaticly draw text (by default the label of the object on the \c canvas with
|
||||||
|
* the 2D context \c ctx depending on user settings.
|
||||||
|
* This method takes into account both the \c posX and \c posY of where the label
|
||||||
|
* should be displayed, including the \c labelPosition relative to it.
|
||||||
|
* The text is get both through the \c getLatexFunction and \c getTextFunction
|
||||||
|
* depending on whether to use latex.
|
||||||
|
* Then, it's displayed using the \c drawFunctionLatex (x,y,imageData) and
|
||||||
|
* \c drawFunctionText (x,y,text) depending on whether to use latex.
|
||||||
|
*
|
||||||
|
* @param {Canvas} canvas
|
||||||
|
* @param {Context2D} ctx
|
||||||
|
* @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} posY - Component of the marked position on the y-axis
|
||||||
|
* @param {bool} forceText - Force the rendering of the label as text
|
||||||
|
* @param {function|null} getLatexFunction - Function (no argument) to get the latex markup to be displayed
|
||||||
|
* @param {function|null} getTextFunction - Function (no argument) to get the text to be displayed
|
||||||
|
* @param {function|null} drawFunctionLatex - Function (x,y,imageData) to display the latex image
|
||||||
|
* @param {function|null} drawFunctionText - Function (x,y,text) to display the text
|
||||||
|
*/
|
||||||
|
drawLabel(canvas, ctx, labelPosition, posX, posY, forceText = false,
|
||||||
|
getLatexFunction = null, getTextFunction = null, drawFunctionLatex = null, drawFunctionText = null) {
|
||||||
|
// Default functions
|
||||||
|
if(getLatexFunction == null)
|
||||||
|
getLatexFunction = this.getLatexLabel.bind(this)
|
||||||
|
if(getTextFunction == null)
|
||||||
|
getTextFunction = this.getLabel.bind(this)
|
||||||
|
if(drawFunctionLatex == null)
|
||||||
|
drawFunctionLatex = (x,y,ltxImg) => canvas.drawVisibleImage(ctx, ltxImg.source, x, y, ltxImg.width, ltxImg.height)
|
||||||
|
if(drawFunctionText == null)
|
||||||
|
drawFunctionText = (x,y,text) => canvas.drawVisibleText(ctx, text, x, textSize.height+5)
|
||||||
|
// Drawing the label
|
||||||
|
let offset
|
||||||
|
if(!forceText && true) { // TODO: Check for user setting with Latex.
|
||||||
|
// With latex
|
||||||
|
let drawLblCb = function(canvas, ctx, ltxImg) {
|
||||||
|
this.drawPositionDivergence(labelPosition, 8, ltxImg, posX, posY, (x,y) => drawFunctionLatex(x,y,ltxImg))
|
||||||
|
}
|
||||||
|
let ltxLabel = getLatexFunction();
|
||||||
|
if(ltxLabel != "")
|
||||||
|
canvas.renderLatexImage(ltxLabel, this.color, drawLblCb.bind(this))
|
||||||
|
} else {
|
||||||
|
// Without latex
|
||||||
|
let text = getTextFunction()
|
||||||
|
ctx.font = `${canvas.textsize}px sans-serif`
|
||||||
|
this.drawPositionDivergence(labelPosition, 4, canvas.measureText(ctx, text), posX, posY, (x,y) => drawFunctionText(x,y,text))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ class Function extends Common.ExecutableObject {
|
||||||
return `${Latex.variable(this.name)}:\\begin{array}{llll}${this.definitionDomain.latexMarkup}\\textrm{ } & \\rightarrow & \\textrm{ }${this.destinationDomain.latexMarkup}\\\\
|
return `${Latex.variable(this.name)}:\\begin{array}{llll}${this.definitionDomain.latexMarkup}\\textrm{ } & \\rightarrow & \\textrm{ }${this.destinationDomain.latexMarkup}\\\\
|
||||||
x\\textrm{ } & \\mapsto & \\textrm{ }${this.expression.latexMarkup}\\end{array}`
|
x\\textrm{ } & \\mapsto & \\textrm{ }${this.expression.latexMarkup}\\end{array}`
|
||||||
} else {
|
} else {
|
||||||
return `\\begin{array}{l}${Latex.variable(this.name)}(x) = ${this.expression.latexMarkup}\\\\ textD_{${this.name}} = ${this.definitionDomain.latexMarkup}\\end{array}`
|
return `\\begin{array}{l}${Latex.variable(this.name)}(x) = ${this.expression.latexMarkup}\\\\ D_{${this.name}} = ${this.definitionDomain.latexMarkup}\\end{array}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,27 +22,14 @@
|
||||||
.import "../objects.js" as Objects
|
.import "../objects.js" as Objects
|
||||||
.import "../mathlib.js" as MathLib
|
.import "../mathlib.js" as MathLib
|
||||||
.import "../parameters.js" as P
|
.import "../parameters.js" as P
|
||||||
|
.import "../math/latex.js" as Latex
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class XCursor extends Common.DrawableObject {
|
class XCursor extends Common.DrawableObject {
|
||||||
static type(){return 'X Cursor'}
|
static type(){return 'X Cursor'}
|
||||||
static displayType(){return qsTr('X Cursor')}
|
static displayType(){return qsTr('X Cursor')}
|
||||||
static displayTypeMultiple(){return qsTr('X Cursors')}
|
static displayTypeMultiple(){return qsTr('X Cursors')}
|
||||||
/*static properties() {
|
|
||||||
return {
|
|
||||||
'x': 'Expression',
|
|
||||||
'targetElement': new P.ObjectType('ExecutableObject'),
|
|
||||||
'labelPosition': new P.Enum('left', 'right'),
|
|
||||||
'approximate': 'boolean',
|
|
||||||
'rounding': 'number',
|
|
||||||
'displayStyle': new P.Enum(
|
|
||||||
'— — — — — — —',
|
|
||||||
'⸺⸺⸺⸺⸺⸺',
|
|
||||||
'• • • • • • • • • •'
|
|
||||||
),
|
|
||||||
'targetValuePosition' : new P.Enum('Next to target', 'With label', 'Hidden')
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
static properties() {return {
|
static properties() {return {
|
||||||
[QT_TRANSLATE_NOOP('prop','x')]: 'Expression',
|
[QT_TRANSLATE_NOOP('prop','x')]: 'Expression',
|
||||||
[QT_TRANSLATE_NOOP('prop','targetElement')]: new P.ObjectType('ExecutableObject'),
|
[QT_TRANSLATE_NOOP('prop','targetElement')]: new P.ObjectType('ExecutableObject'),
|
||||||
|
@ -87,6 +74,13 @@ class XCursor extends Common.DrawableObject {
|
||||||
return `${this.name} = ${this.x.toString()}\n${this.getTargetValueLabel()}`
|
return `${this.name} = ${this.x.toString()}\n${this.getTargetValueLabel()}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getLatexString() {
|
||||||
|
if(this.targetElement == null) return `${Latex.variable(this.name)} = ${this.x.latexMarkup}`
|
||||||
|
return `\\begin{array}{l}
|
||||||
|
${Latex.variable(this.name)} = ${this.x.latexMarkup} \\\\
|
||||||
|
${this.getTargetValueLatexLabel()}`
|
||||||
|
}
|
||||||
|
|
||||||
getTargetValueLabel() {
|
getTargetValueLabel() {
|
||||||
var t = this.targetElement
|
var t = this.targetElement
|
||||||
var approx = ''
|
var approx = ''
|
||||||
|
@ -98,6 +92,18 @@ class XCursor extends Common.DrawableObject {
|
||||||
(this.approximate ? ' ≈ ' + approx : '')
|
(this.approximate ? ' ≈ ' + approx : '')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTargetValueLatexLabel() {
|
||||||
|
var t = this.targetElement
|
||||||
|
var approx = ''
|
||||||
|
if(this.approximate) {
|
||||||
|
approx = t.execute(this.x.execute())
|
||||||
|
approx = approx.toPrecision(this.rounding + Math.round(approx).toString().length)
|
||||||
|
}
|
||||||
|
let simpl = t.simplify(this.x.toEditableString())
|
||||||
|
return `${Latex.variable(t.name)}(${Latex.variable(this.name)}) = ${simpl.tokens ? Latex.expression(simpl.tokens) : simpl}` +
|
||||||
|
(this.approximate ? ' \\simeq ' + approx : '')
|
||||||
|
}
|
||||||
|
|
||||||
getLabel() {
|
getLabel() {
|
||||||
switch(this.labelContent) {
|
switch(this.labelContent) {
|
||||||
case 'name':
|
case 'name':
|
||||||
|
@ -118,8 +124,28 @@ class XCursor extends Common.DrawableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getLatexLabel() {
|
||||||
|
switch(this.labelContent) {
|
||||||
|
case 'name':
|
||||||
|
return Latex.variable(this.name)
|
||||||
|
break;
|
||||||
|
case 'name + value':
|
||||||
|
switch(this.targetValuePosition) {
|
||||||
|
case 'Next to target':
|
||||||
|
case 'Hidden':
|
||||||
|
return `${Latex.variable(this.name)} = ${this.x.latexMarkup}`
|
||||||
|
break;
|
||||||
|
case 'With label':
|
||||||
|
return this.getLatexString()
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'null':
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
draw(canvas, ctx) {
|
draw(canvas, ctx) {
|
||||||
var xpos = canvas.x2px(this.x.execute())
|
let xpos = canvas.x2px(this.x.execute())
|
||||||
switch(this.displayStyle) {
|
switch(this.displayStyle) {
|
||||||
case '— — — — — — —':
|
case '— — — — — — —':
|
||||||
var dashPxSize = 10
|
var dashPxSize = 10
|
||||||
|
@ -139,52 +165,18 @@ class XCursor extends Common.DrawableObject {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Label
|
// Drawing label at the top of the canvas.
|
||||||
var text = this.getLabel()
|
this.drawLabel(canvas, ctx, this.labelPosition, xpos, 0, false, null, null,
|
||||||
ctx.font = `${canvas.textsize}px sans-serif`
|
(x,y,ltxImg) => canvas.drawVisibleImage(ctx, ltxImg.source, x, 5, ltxImg.width, ltxImg.height),
|
||||||
var textSize = canvas.measureText(ctx, text)
|
(x,y,text) => canvas.drawVisibleText(ctx, text, x, textSize.height+5))
|
||||||
|
|
||||||
switch(this.labelPosition) {
|
|
||||||
case 'left':
|
|
||||||
case 'above-left':
|
|
||||||
case 'below-left':
|
|
||||||
case 'below':
|
|
||||||
case 'above':
|
|
||||||
canvas.drawVisibleText(ctx, text, xpos-textSize.width-5, textSize.height+5)
|
|
||||||
break;
|
|
||||||
case 'right':
|
|
||||||
case 'above-right':
|
|
||||||
case 'below-right':
|
|
||||||
canvas.drawVisibleText(ctx, text, xpos+5, textSize.height+5)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 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) {
|
||||||
var text = this.getTargetValueLabel()
|
let ypos = canvas.y2px(this.targetElement.execute(this.x.execute()))
|
||||||
var textSize = canvas.measureText(ctx, text)
|
this.drawLabel(canvas, ctx, this.labelPosition, xpos, ypos, false,
|
||||||
var ypox = canvas.y2px(this.targetElement.execute(this.x.execute()))
|
this.getTargetValueLatexLabel.bind(this), this.getTargetValueLabel.bind(this),
|
||||||
switch(this.labelPosition) {
|
(x,y,ltxImg) => canvas.drawVisibleImage(ctx, ltxImg.source, x, y, ltxImg.width, ltxImg.height),
|
||||||
case 'left':
|
(x,y,text) => canvas.drawVisibleText(ctx, text, x, y))
|
||||||
case 'below':
|
|
||||||
case 'above':
|
|
||||||
canvas.drawVisibleText(ctx, text, xpos-textSize.width-5, ypox+textSize.height)
|
|
||||||
break;
|
|
||||||
case 'above-left':
|
|
||||||
canvas.drawVisibleText(ctx, text, xpos-textSize.width-5, ypox+textSize.height+12)
|
|
||||||
break;
|
|
||||||
case 'below-left':
|
|
||||||
canvas.drawVisibleText(ctx, text, xpos-textSize.width-5, ypox+textSize.height-12)
|
|
||||||
break;
|
|
||||||
case 'right':
|
|
||||||
canvas.drawVisibleText(ctx, text, xpos+5, ypox+textSize.height)
|
|
||||||
break;
|
|
||||||
case 'above-right':
|
|
||||||
canvas.drawVisibleText(ctx, text, xpos+5, ypox+textSize.height+12)
|
|
||||||
break;
|
|
||||||
case 'below-right':
|
|
||||||
canvas.drawVisibleText(ctx, text, xpos+5, ypox+textSize.height-12)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue