X Cursor Latex implementation
Some checks reported errors
continuous-integration/drone/push Build was killed

Also a few bugfixes and added documentation
This commit is contained in:
Adsooi 2022-03-07 17:20:24 +01:00
parent 2691ba687f
commit 166d1a2485
Signed by: Ad5001
GPG key ID: EF45F9C6AFE20160
7 changed files with 117 additions and 100 deletions

View file

@ -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 = {}

View file

@ -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() {

View file

@ -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;

View file

@ -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)

View file

@ -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;
} }

View file

@ -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}`
} }
} }

View file

@ -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;
}
} }
} }
} }