Compare commits

...

2 commits

Author SHA1 Message Date
166d1a2485
X Cursor Latex implementation
Some checks reported errors
continuous-integration/drone/push Build was killed
Also a few bugfixes and added documentation
2022-03-07 17:20:24 +01:00
2691ba687f
Changed a few serif text to sans serif. 2022-03-07 15:06:40 +01:00
9 changed files with 122 additions and 105 deletions

View file

@ -157,7 +157,15 @@ Canvas {
*/
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: {}
/*!
\qmlproperty var LogGraphCanvas::ctx
Cache for the 2D context so that it may be used asynchronously.
*/
property var ctx
Component.onCompleted: imageLoaders = {}

View file

@ -31,7 +31,7 @@ class Expression {
this.calc = C.parser.parse(expr).simplify()
this.cached = this.isConstant()
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() {

View file

@ -125,7 +125,7 @@ function variable(vari) {
* @param {Array} tokens - expr-eval tokens list
* @returns {string}
*/
function expressionToLatex(tokens) {
function expression(tokens) {
var nstack = [];
var n1, n2, n3;
var f, args, argCount;
@ -239,7 +239,7 @@ function expressionToLatex(tokens) {
nstack.push('[' + args.join(', ') + ']');
break;
case ExprEval.IEXPR:
nstack.push('(' + expressionToLatex(item.value) + ')');
nstack.push('(' + expression(item.value) + ')');
break;
case ExprEval.IENDSTATEMENT:
break;

View file

@ -38,7 +38,7 @@ class Sequence extends Expr.Expression {
for(var n in this.calcValues)
if(['string', 'number'].includes(typeof this.calcValues[n])) {
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.valuePlus = parseInt(valuePlus)

View file

@ -209,45 +209,12 @@ class DrawableObject {
*/
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
* 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
* relatively to that 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 {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 {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) {
case 'center':
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() {
return this.name;
}

View file

@ -78,8 +78,8 @@ class GainBode extends Common.ExecutableObject {
getLatexString() {
let pass = this.pass == "low" ? qsTr("low-pass") : qsTr("high-pass");
return `\\mathrm{${Latex.variable(this.name)}:}\\begin{array}{l}
\\textrm{${pass}};${Latex.variable(this.om_0.name)} = ${this.om_0.x.latexMarkup} \\\\
${this.gain.latexMarkup}\\textrm{ dB/dec}
\\textsf{${pass}};${Latex.variable(this.om_0.name)} = ${this.om_0.x.latexMarkup} \\\\
${this.gain.latexMarkup}\\textsf{ dB/dec}
\\end{array}`
}

View file

@ -77,7 +77,7 @@ class PhaseBode extends Common.ExecutableObject {
}
getLatexString() {
return `${Latex.variable(this.name)}: ${this.phase.latexMarkup}\\textrm{${this.unit} at }${Latex.variable(this.om_0.name)} = ${this.om_0.x.latexMarkup}`
return `${Latex.variable(this.name)}: ${this.phase.latexMarkup}\\textsf{${this.unit} at }${Latex.variable(this.om_0.name)} = ${this.om_0.x.latexMarkup}`
}
execute(x=1) {

View file

@ -70,7 +70,7 @@ class Text extends Common.DrawableObject {
let i
for(i = 0; txt.includes('$$'); i++)
if(i & 0x01) // Every odd number
txt = txt.replace('$$', '\\textrm{')
txt = txt.replace('$$', '\\textsf{')
else
txt = txt.replace('$$', '}')
if(i & 0x01) // Finished by a }
@ -79,7 +79,7 @@ class Text extends Common.DrawableObject {
}
getLatexString() {
return `${Latex.variable(this.name)} = "\\textrm{${this.latexMarkupText()}}"`
return `${Latex.variable(this.name)} = "\\textsf{${this.latexMarkupText()}}"`
}
export() {
@ -91,7 +91,7 @@ class Text extends Common.DrawableObject {
}
getLatexLabel() {
return `\\textrm{${this.latexMarkupText()}}`
return `\\textsf{${this.latexMarkupText()}}`
}
draw(canvas, ctx) {

View file

@ -22,27 +22,14 @@
.import "../objects.js" as Objects
.import "../mathlib.js" as MathLib
.import "../parameters.js" as P
.import "../math/latex.js" as Latex
class XCursor extends Common.DrawableObject {
static type(){return 'X Cursor'}
static displayType(){return qsTr('X Cursor')}
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 {
[QT_TRANSLATE_NOOP('prop','x')]: 'Expression',
[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()}`
}
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() {
var t = this.targetElement
var approx = ''
@ -98,6 +92,18 @@ class XCursor extends Common.DrawableObject {
(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() {
switch(this.labelContent) {
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) {
var xpos = canvas.x2px(this.x.execute())
let xpos = canvas.x2px(this.x.execute())
switch(this.displayStyle) {
case '— — — — — — —':
var dashPxSize = 10
@ -139,52 +165,18 @@ class XCursor extends Common.DrawableObject {
break;
}
// Label
var text = this.getLabel()
ctx.font = `${canvas.textsize}px sans-serif`
var textSize = canvas.measureText(ctx, text)
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 top of the canvas.
this.drawLabel(canvas, ctx, this.labelPosition, xpos, 0, false, null, null,
(x,y,ltxImg) => canvas.drawVisibleImage(ctx, ltxImg.source, x, 5, ltxImg.width, ltxImg.height),
(x,y,text) => canvas.drawVisibleText(ctx, text, x, textSize.height+5))
// Drawing label at the position of the target element.
if(this.targetValuePosition == 'Next to target' && this.targetElement != null) {
var text = this.getTargetValueLabel()
var textSize = canvas.measureText(ctx, text)
var ypox = canvas.y2px(this.targetElement.execute(this.x.execute()))
switch(this.labelPosition) {
case 'left':
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;
}
let ypos = canvas.y2px(this.targetElement.execute(this.x.execute()))
this.drawLabel(canvas, ctx, this.labelPosition, xpos, ypos, false,
this.getTargetValueLatexLabel.bind(this), this.getTargetValueLabel.bind(this),
(x,y,ltxImg) => canvas.drawVisibleImage(ctx, ltxImg.source, x, y, ltxImg.width, ltxImg.height),
(x,y,text) => canvas.drawVisibleText(ctx, text, x, y))
}
}
}