Compare commits
No commits in common. "c32d70e9ed3651cfa32be6754f6da2d47c1453d5" and "95b47effdf14c62aaa61c2897f5304f61431773e" have entirely different histories.
c32d70e9ed
...
95b47effdf
49 changed files with 1434 additions and 1582 deletions
|
@ -538,7 +538,7 @@ Item {
|
||||||
try {
|
try {
|
||||||
expr = new JS.MathLib.Expression(value.toString())
|
expr = new JS.MathLib.Expression(value.toString())
|
||||||
// Check if the expression is valid, throws error otherwise.
|
// Check if the expression is valid, throws error otherwise.
|
||||||
if(!expr.allRequirementsFulfilled()) {
|
if(!expr.allRequirementsFullfilled()) {
|
||||||
let undefVars = expr.undefinedVariables()
|
let undefVars = expr.undefinedVariables()
|
||||||
if(undefVars.length > 1)
|
if(undefVars.length > 1)
|
||||||
throw new Error(qsTranslate('error', 'No object found with names %1.').arg(undefVars.join(', ')))
|
throw new Error(qsTranslate('error', 'No object found with names %1.').arg(undefVars.join(', ')))
|
||||||
|
|
|
@ -16,8 +16,6 @@
|
||||||
* 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 js from "./lib/polyfills/js.mjs"
|
|
||||||
|
|
||||||
// Loading modules in order
|
// Loading modules in order
|
||||||
import * as Objects from "./module/objects.mjs"
|
import * as Objects from "./module/objects.mjs"
|
||||||
import * as ExprParser from "./module/expreval.mjs"
|
import * as ExprParser from "./module/expreval.mjs"
|
||||||
|
|
|
@ -21,13 +21,10 @@ import Objects from "../module/objects.mjs"
|
||||||
|
|
||||||
export default class ColorChanged extends EditedProperty {
|
export default class ColorChanged extends EditedProperty {
|
||||||
// Action used everytime when an object's color is changed
|
// Action used everytime when an object's color is changed
|
||||||
type() {
|
type(){return 'ColorChanged'}
|
||||||
return "ColorChanged"
|
|
||||||
}
|
icon(){return 'appearance'}
|
||||||
|
|
||||||
icon() {
|
|
||||||
return "appearance"
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(targetName = "", targetType = "Point", oldColor = "black", newColor = "white") {
|
constructor(targetName = "", targetType = "Point", oldColor = "black", newColor = "white") {
|
||||||
super(targetName, targetType, "color", oldColor, newColor)
|
super(targetName, targetType, "color", oldColor, newColor)
|
||||||
|
@ -37,14 +34,12 @@ export default class ColorChanged extends EditedProperty {
|
||||||
return [this.targetName, this.targetType, this.previousValue, this.newValue]
|
return [this.targetName, this.targetType, this.previousValue, this.newValue]
|
||||||
}
|
}
|
||||||
|
|
||||||
color(darkVer = false) {
|
color(darkVer=false){return darkVer ? 'purple' : 'plum'}
|
||||||
return darkVer ? "purple" : "plum"
|
|
||||||
}
|
|
||||||
|
|
||||||
getReadableString() {
|
getReadableString() {
|
||||||
return qsTranslate("color", "%1 %2's color changed from %3 to %4.")
|
return qsTranslate("color", "%1 %2's color changed from %3 to %4.")
|
||||||
.arg(Objects.types[this.targetType].displayType()).arg(this.targetName)
|
.arg(Objects.types[this.targetType].displayType()).arg(this.targetName)
|
||||||
.arg(this.previousValue).arg(this.newValue)
|
.arg(this.previousValue).arg(this.newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
formatColor(color) {
|
formatColor(color) {
|
||||||
|
@ -53,9 +48,9 @@ export default class ColorChanged extends EditedProperty {
|
||||||
|
|
||||||
getHTMLString() {
|
getHTMLString() {
|
||||||
return qsTranslate("color", "%1 %2's color changed from %3 to %4.")
|
return qsTranslate("color", "%1 %2's color changed from %3 to %4.")
|
||||||
.arg(Objects.types[this.targetType].displayType())
|
.arg(Objects.types[this.targetType].displayType())
|
||||||
.arg("<b style=\"font-size: 15px;\"> " + this.targetName + " </b>")
|
.arg('<b style="font-size: 15px;"> ' + this.targetName + " </b>")
|
||||||
.arg(this.formatColor(this.previousValue)).arg(this.formatColor(this.newValue))
|
.arg(this.formatColor(this.previousValue)).arg(this.formatColor(this.newValue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,18 +25,14 @@ export class Action {
|
||||||
*
|
*
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
type() {
|
type(){return 'Unknown'}
|
||||||
return "Unknown"
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Icon associated with the action.
|
* Icon associated with the action.
|
||||||
*
|
*
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
icon() {
|
icon(){return 'position'}
|
||||||
return "position"
|
|
||||||
}
|
|
||||||
|
|
||||||
// TargetName is the name of the object that's targeted by the event.
|
// TargetName is the name of the object that's targeted by the event.
|
||||||
constructor(targetName = "", targetType = "Point") {
|
constructor(targetName = "", targetType = "Point") {
|
||||||
|
@ -47,14 +43,12 @@ export class Action {
|
||||||
/**
|
/**
|
||||||
* Undoes the action.
|
* Undoes the action.
|
||||||
*/
|
*/
|
||||||
undo() {
|
undo() {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Redoes the action.
|
* Redoes the action.
|
||||||
*/
|
*/
|
||||||
redo() {
|
redo() {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export the action to a serializable format.
|
* Export the action to a serializable format.
|
||||||
|
@ -67,12 +61,12 @@ export class Action {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a string with the human-readable description of the action.
|
* Returns a string with the human readable description of the action.
|
||||||
*
|
*
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
getReadableString() {
|
getReadableString() {
|
||||||
return "Unknown action"
|
return 'Unknown action'
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -82,7 +76,7 @@ export class Action {
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
getIconRichText(type) {
|
getIconRichText(type) {
|
||||||
return `<img src="../icons/objects/${type}.svg" style="color: ${History.themeTextColor};" width=18 height=18></img>`
|
return `<img source="../icons/objects/${type}.svg" style="color: ${History.themeTextColor};" width=18 height=18></img>`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -91,16 +85,20 @@ export class Action {
|
||||||
* @param {string} latexString - Source string of the latex.
|
* @param {string} latexString - Source string of the latex.
|
||||||
* @returns {Promise<string>}
|
* @returns {Promise<string>}
|
||||||
*/
|
*/
|
||||||
async 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.")
|
||||||
const imgDepth = History.imageDepth
|
return new Promise(resolve => {
|
||||||
const { source, width, height } = await Latex.requestAsyncRender(
|
let imgDepth = History.imageDepth
|
||||||
latexString,
|
Latex.requestAsyncRender(
|
||||||
imgDepth * (History.fontSize + 2),
|
latexString,
|
||||||
History.themeTextColor
|
imgDepth * (History.fontSize + 2),
|
||||||
)
|
History.themeTextColor
|
||||||
return `<img src="${source}" width="${width / imgDepth}" height="${height / imgDepth}" style="vertical-align: middle"/>`
|
).then((imgData) => {
|
||||||
|
const { source, width, height } = imgData
|
||||||
|
resolve(`<img src="${source}" width="${width/imgDepth}" height="${height/imgDepth}" style="vertical-align: middle"/>`)
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -19,21 +19,13 @@
|
||||||
import Objects from "../module/objects.mjs"
|
import Objects from "../module/objects.mjs"
|
||||||
import { Action } from "./common.mjs"
|
import { Action } from "./common.mjs"
|
||||||
|
|
||||||
/**
|
|
||||||
* Action used for the creation of an object.
|
|
||||||
*/
|
|
||||||
export default class CreateNewObject extends Action {
|
export default class CreateNewObject extends Action {
|
||||||
type() {
|
// Action used for the creation of an object
|
||||||
return "CreateNewObject"
|
type(){return 'CreateNewObject'}
|
||||||
}
|
|
||||||
|
|
||||||
icon() {
|
icon(){return 'create'}
|
||||||
return "create"
|
|
||||||
}
|
|
||||||
|
|
||||||
color(darkVer = false) {
|
color(darkVer=false){return darkVer ? 'green' : 'lime'}
|
||||||
return darkVer ? "green" : "lime"
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(targetName = "", targetType = "Point", properties = []) {
|
constructor(targetName = "", targetType = "Point", properties = []) {
|
||||||
super(targetName, targetType)
|
super(targetName, targetType)
|
||||||
|
@ -62,8 +54,8 @@ export default class CreateNewObject extends Action {
|
||||||
|
|
||||||
getHTMLString() {
|
getHTMLString() {
|
||||||
return qsTranslate("create", "New %1 %2 created.")
|
return qsTranslate("create", "New %1 %2 created.")
|
||||||
.arg(Objects.types[this.targetType].displayType())
|
.arg(Objects.types[this.targetType].displayType())
|
||||||
.arg("<b style=\"font-size: 15px;\">" + this.targetName + "</b>")
|
.arg('<b style="font-size: 15px;">' + this.targetName + "</b>")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,10 +20,10 @@ import Objects from "../module/objects.mjs"
|
||||||
import CreateNewObject from "./create.mjs"
|
import CreateNewObject from "./create.mjs"
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Action used at the deletion of an object. Basically the same thing as creating a new object, except Redo & Undo are reversed.
|
|
||||||
*/
|
|
||||||
export default class DeleteObject extends CreateNewObject {
|
export default class DeleteObject extends CreateNewObject {
|
||||||
|
/**
|
||||||
|
* Action used at the deletion of an object. Basically the same thing as creating a new object, except Redo & Undo are reversed.
|
||||||
|
*/
|
||||||
type(){return 'DeleteObject'}
|
type(){return 'DeleteObject'}
|
||||||
|
|
||||||
icon(){return 'delete'}
|
icon(){return 'delete'}
|
||||||
|
|
|
@ -22,20 +22,14 @@ import * as MathLib from "../math/index.mjs"
|
||||||
import { Action } from "./common.mjs"
|
import { Action } from "./common.mjs"
|
||||||
import { DrawableObject } from "../objs/common.mjs"
|
import { DrawableObject } from "../objs/common.mjs"
|
||||||
|
|
||||||
/**
|
|
||||||
* Action used everytime an object's property has been changed.
|
|
||||||
*/
|
|
||||||
export default class EditedProperty extends Action {
|
export default class EditedProperty extends Action {
|
||||||
type() {
|
// Action used everytime an object's property has been changed
|
||||||
return "EditedProperty"
|
type(){return 'EditedProperty'}
|
||||||
}
|
|
||||||
|
|
||||||
icon() {
|
icon(){return 'modify'}
|
||||||
return "modify"
|
|
||||||
}
|
|
||||||
|
|
||||||
color(darkVer = false) {
|
color(darkVer=false){
|
||||||
return darkVer ? "darkslateblue" : "cyan"
|
return darkVer ? 'darkslateblue' : 'cyan';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,12 +49,12 @@ export default class EditedProperty extends Action {
|
||||||
this.newValue = newValue
|
this.newValue = newValue
|
||||||
this.propertyType = Objects.types[targetType].properties()[targetProperty]
|
this.propertyType = Objects.types[targetType].properties()[targetProperty]
|
||||||
if(valueIsExpressionNeedingImport) {
|
if(valueIsExpressionNeedingImport) {
|
||||||
if(typeof this.propertyType == "object" && this.propertyType.type === "Expression") {
|
if(typeof this.propertyType == 'object' && this.propertyType.type === "Expression") {
|
||||||
this.previousValue = new MathLib.Expression(this.previousValue)
|
this.previousValue = new MathLib.Expression(this.previousValue);
|
||||||
this.newValue = new MathLib.Expression(this.newValue)
|
this.newValue = new MathLib.Expression(this.newValue);
|
||||||
} else if(this.propertyType === "Domain") {
|
} else if(this.propertyType === "Domain") {
|
||||||
this.previousValue = MathLib.parseDomain(this.previousValue)
|
this.previousValue = MathLib.parseDomain(this.previousValue);
|
||||||
this.newValue = MathLib.parseDomain(this.newValue)
|
this.newValue = MathLib.parseDomain(this.newValue);
|
||||||
} else {
|
} else {
|
||||||
// Objects
|
// Objects
|
||||||
this.previousValue = Objects.currentObjectsByName[this.previousValue] // Objects.getObjectByName(this.previousValue);
|
this.previousValue = Objects.currentObjectsByName[this.previousValue] // Objects.getObjectByName(this.previousValue);
|
||||||
|
@ -99,61 +93,67 @@ export default class EditedProperty extends Action {
|
||||||
case "Enum":
|
case "Enum":
|
||||||
this.prevString = this.propertyType.translatedValues[this.propertyType.values.indexOf(this.previousValue)]
|
this.prevString = this.propertyType.translatedValues[this.propertyType.values.indexOf(this.previousValue)]
|
||||||
this.nextString = this.propertyType.translatedValues[this.propertyType.values.indexOf(this.newValue)]
|
this.nextString = this.propertyType.translatedValues[this.propertyType.values.indexOf(this.newValue)]
|
||||||
break
|
break;
|
||||||
case "ObjectType":
|
case "ObjectType":
|
||||||
this.prevString = this.previousValue == null ? "null" : this.previousValue.name
|
this.prevString = this.previousValue == null ? "null" : this.previousValue.name
|
||||||
this.nextString = this.newValue == null ? "null" : this.newValue.name
|
this.nextString = this.newValue == null ? "null" : this.newValue.name
|
||||||
break
|
break;
|
||||||
case "List":
|
case "List":
|
||||||
this.prevString = this.previousValue.join(",")
|
this.prevString = this.previousValue.join(",")
|
||||||
this.nextString = this.newValue.name.join(",")
|
this.nextString = this.newValue.name.join(",")
|
||||||
break
|
break;
|
||||||
case "Dict":
|
case "Dict":
|
||||||
this.prevString = JSON.stringify(this.previousValue)
|
this.prevString = JSON.stringify(this.previousValue)
|
||||||
this.nextString = JSON.stringify(this.newValue)
|
this.nextString = JSON.stringify(this.newValue)
|
||||||
break
|
break;
|
||||||
case "Expression":
|
case "Expression":
|
||||||
this.prevString = this.previousValue == null ? "null" : this.previousValue.toString()
|
this.prevString = this.previousValue == null ? "null" : this.previousValue.toString()
|
||||||
this.nextString = this.newValue == null ? "null" : this.newValue.toString()
|
this.nextString = this.newValue == null ? "null" : this.newValue.toString()
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.prevString = this.previousValue == null ? "null" : this.previousValue.toString()
|
this.prevString = this.previousValue == null ? "null" : this.previousValue.toString()
|
||||||
this.nextString = this.newValue == null ? "null" : this.newValue.toString()
|
this.nextString = this.newValue == null ? "null" : this.newValue.toString()
|
||||||
}
|
}
|
||||||
// HTML
|
// HTML
|
||||||
this.prevHTML = "<tt style=\"background: rgba(128,128,128,0.1);\"> " + this.prevString + " </tt>"
|
this.prevHTML = '<tt style="background: rgba(128,128,128,0.1);"> '+this.prevString+' </tt>'
|
||||||
this.nextHTML = "<tt style=\"background: rgba(128,128,128,0.1);\"> " + this.nextString + " </tt>"
|
this.nextHTML = '<tt style="background: rgba(128,128,128,0.1);"> '+this.nextString+' </tt>'
|
||||||
if(Latex.enabled && typeof this.propertyType == "object" && this.propertyType.type === "Expression") {
|
if(Latex.enabled && typeof this.propertyType == 'object' && this.propertyType.type === "Expression") {
|
||||||
// Store promises so that querying can wait for them to finish.
|
// Store promises so that querying can wait for them to finish.
|
||||||
this._renderPromises = [
|
this._renderPromises = [
|
||||||
this.renderLatexAsHtml(this.previousValue.latexMarkup).then(prev => this.prevHTML = prev),
|
this.renderLatexAsHtml(this.previousValue.latexMarkup).then(prev => this.prevHTML = prev),
|
||||||
this.renderLatexAsHtml(this.newValue.latexMarkup).then(next => this.nextHTML = next)
|
this.renderLatexAsHtml(this.newValue.latexMarkup).then(next => this.nextHTML = prev)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getReadableString() {
|
getReadableString() {
|
||||||
return qsTranslate("editproperty", "%1 of %2 %3 changed from \"%4\" to \"%5\".")
|
return qsTranslate("editproperty", '%1 of %2 %3 changed from "%4" to "%5".')
|
||||||
.arg(this.targetPropertyReadable)
|
.arg(this.targetPropertyReadable)
|
||||||
.arg(Objects.types[this.targetType].displayType())
|
.arg(Objects.types[this.targetType].displayType())
|
||||||
.arg(this.targetName).arg(this.prevString).arg(this.nextString)
|
.arg(this.targetName).arg(this.prevString).arg(this.nextString)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @return {Promise<string>|string}
|
* @return {Promise<string>|string}
|
||||||
*/
|
*/
|
||||||
async getHTMLString() {
|
getHTMLString() {
|
||||||
const translation = qsTranslate("editproperty", "%1 of %2 changed from %3 to %4.")
|
return new Promise(resolve => {
|
||||||
.arg(this.targetPropertyReadable)
|
const translation = qsTranslate("editproperty", '%1 of %2 changed from %3 to %4.')
|
||||||
.arg("<b style=\"font-size: 15px;\"> " + this.targetName + " </b>")
|
.arg(this.targetPropertyReadable)
|
||||||
// Check if we need to wait for LaTeX HTML to be rendered.
|
.arg('<b style="font-size: 15px;"> ' + this.targetName + ' </b>')
|
||||||
if(this.prevHTML === undefined || this.nextHTML === undefined) {
|
// Check if we need to wait for LaTeX HTML to be rendered.
|
||||||
const [prevHTML, nextHTML] = await Promise.all(this._renderPromises)
|
if(this.prevHTML !== undefined && this.nextHTML !== undefined)
|
||||||
this.prevHTML = this.prevHTML ?? prevHTML
|
resolve(translation.arg(this.prevHTML).arg(this.nextHTML))
|
||||||
this.nextHTML = this.nextHTML ?? nextHTML
|
else
|
||||||
}
|
Promise.all(this._renderPromises).then((rendered) => {
|
||||||
return translation.arg(this.prevHTML).arg(this.nextHTML)
|
// Rendered are (potentially) two HTML strings which are defined during rendering
|
||||||
|
this.prevHTML = this.prevHTML ?? rendered[0]
|
||||||
|
this.nextHTML = this.prevHTML ?? rendered[1]
|
||||||
|
resolve(translation.arg(this.prevHTML).arg(this.nextHTML))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,9 @@
|
||||||
import EditedProperty from "./editproperty.mjs"
|
import EditedProperty from "./editproperty.mjs"
|
||||||
import Objects from "../module/objects.mjs"
|
import Objects from "../module/objects.mjs"
|
||||||
|
|
||||||
/**
|
|
||||||
* Action used everytime an object's name has been changed.
|
|
||||||
*/
|
|
||||||
export default class NameChanged extends EditedProperty {
|
export default class NameChanged extends EditedProperty {
|
||||||
|
// Action used everytime an object's property has been changed
|
||||||
type(){return 'NameChanged'}
|
type(){return 'NameChanged'}
|
||||||
|
|
||||||
icon(){return 'name'}
|
icon(){return 'name'}
|
||||||
|
|
|
@ -21,33 +21,28 @@ import Latex from "../module/latex.mjs"
|
||||||
import * as MathLib from "../math/index.mjs"
|
import * as MathLib from "../math/index.mjs"
|
||||||
import { escapeHTML } from "../utils.mjs"
|
import { escapeHTML } from "../utils.mjs"
|
||||||
import { Action } from "./common.mjs"
|
import { Action } from "./common.mjs"
|
||||||
|
import { DrawableObject } from "../objs/common.mjs"
|
||||||
|
|
||||||
/**
|
|
||||||
* Action used for objects that have a X and Y expression properties (points, texts...)
|
|
||||||
*/
|
|
||||||
export default class EditedPosition extends Action {
|
export default class EditedPosition extends Action {
|
||||||
type() {
|
// Action used for objects that have a X and Y expression properties (points, texts...)
|
||||||
return "EditedPosition"
|
type(){return 'EditedPosition'}
|
||||||
}
|
|
||||||
|
|
||||||
icon() {
|
icon(){return 'position'}
|
||||||
return "position"
|
|
||||||
}
|
|
||||||
|
|
||||||
color(darkVer = false) {
|
color(darkVer=false){
|
||||||
return darkVer ? "seagreen" : "lightseagreen"
|
return darkVer ? 'seagreen' : 'lightseagreen';
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(targetName = "", targetType = "Point", previousXValue = "", newXValue = "", previousYValue = "", newYValue = "") {
|
constructor(targetName = "", targetType = "Point", previousXValue = "", newXValue = "", previousYValue = "", newYValue = "") {
|
||||||
super(targetName, targetType)
|
super(targetName, targetType)
|
||||||
let imports = {
|
let imports = {
|
||||||
"previousXValue": previousXValue,
|
'previousXValue': previousXValue,
|
||||||
"previousYValue": previousYValue,
|
'previousYValue': previousYValue,
|
||||||
"newXValue": newXValue,
|
'newXValue': newXValue,
|
||||||
"newYValue": newYValue
|
'newYValue': newYValue
|
||||||
}
|
}
|
||||||
for(let name in imports)
|
for(let name in imports)
|
||||||
this[name] = (typeof imports[name]) == "string" ? new MathLib.Expression(imports[name]) : imports[name]
|
this[name] = (typeof imports[name]) == 'string' ? new MathLib.Expression(imports[name]) : imports[name]
|
||||||
this.setReadableValues()
|
this.setReadableValues()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,34 +71,39 @@ export default class EditedPosition extends Action {
|
||||||
this.renderLatexAsHtml(nextMarkup)
|
this.renderLatexAsHtml(nextMarkup)
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
this.prevHTML = "<tt style=\"background: rgba(128,128,128,0.1);\"> " + escapeHTML(this.prevString) + " </tt>"
|
this.prevHTML = '<tt style="background: rgba(128,128,128,0.1);"> '+escapeHTML(this.prevString)+' </tt>'
|
||||||
this.nextHTML = "<tt style=\"background: rgba(128,128,128,0.1);\"> " + escapeHTML(this.nextString) + " </tt>"
|
this.nextHTML = '<tt style="background: rgba(128,128,128,0.1);"> '+escapeHTML(this.nextString)+' </tt>'
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export() {
|
export() {
|
||||||
return [this.targetName, this.targetType,
|
return [this.targetName, this.targetType,
|
||||||
this.previousXValue.toEditableString(), this.newXValue.toEditableString(),
|
this.previousXValue.toEditableString(), this.newXValue.toEditableString(),
|
||||||
this.previousYValue.toEditableString(), this.newYValue.toEditableString()]
|
this.previousYValue.toEditableString(), this.newYValue.toEditableString()]
|
||||||
}
|
}
|
||||||
|
|
||||||
getReadableString() {
|
getReadableString() {
|
||||||
return qsTranslate("position", "Position of %1 %2 set from \"%3\" to \"%4\".")
|
return qsTranslate("position", 'Position of %1 %2 set from "%3" to "%4".')
|
||||||
.arg(Objects.types[this.targetType].displayType())
|
.arg(Objects.types[this.targetType].displayType())
|
||||||
.arg(this.targetName).arg(this.prevString).arg(this.nextString)
|
.arg(this.targetName).arg(this.prevString).arg(this.nextString)
|
||||||
}
|
}
|
||||||
|
|
||||||
async getHTMLString() {
|
getHTMLString() {
|
||||||
const translation = qsTranslate("position", "Position of %1 set from %2 to %3.")
|
return new Promise(resolve => {
|
||||||
.arg("<b style=\"font-size: 15px;\"> " + this.targetName + " </b>")
|
const translation = qsTranslate("position", 'Position of %1 set from %2 to %3.')
|
||||||
// Check if we need to wait for LaTeX HTML to be rendered.
|
.arg('<b style="font-size: 15px;"> ' + this.targetName + ' </b>')
|
||||||
if(this.prevHTML === undefined || this.nextHTML === undefined) {
|
// Check if we need to wait for LaTeX HTML to be rendered.
|
||||||
const [prevHTML, nextHTML] = await Promise.all(this._renderPromises)
|
if(this.prevHTML !== undefined && this.nextHTML !== undefined)
|
||||||
this.prevHTML = this.prevHTML ?? prevHTML
|
resolve(translation.arg(this.prevHTML).arg(this.nextHTML))
|
||||||
this.nextHTML = this.nextHTML ?? nextHTML
|
else
|
||||||
}
|
Promise.all(this._renderPromises).then((rendered) => {
|
||||||
return translation.arg(this.prevHTML).arg(this.nextHTML)
|
// Rendered are (potentially) two HTML strings which are defined during rendering
|
||||||
|
this.prevHTML = this.prevHTML ?? rendered[0]
|
||||||
|
this.nextHTML = this.nextHTML ?? rendered[1]
|
||||||
|
resolve(translation.arg(this.prevHTML).arg(this.nextHTML))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,22 +20,16 @@ import EditedProperty from "./editproperty.mjs"
|
||||||
import Objects from "../module/objects.mjs"
|
import Objects from "../module/objects.mjs"
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Action used when an object's shown or hidden.
|
|
||||||
*/
|
|
||||||
export default class EditedVisibility extends EditedProperty {
|
export default class EditedVisibility extends EditedProperty {
|
||||||
type() {
|
// Action used when an object's shown or hidden.
|
||||||
return "EditedVisibility"
|
type(){return 'EditedVisibility'}
|
||||||
}
|
|
||||||
|
|
||||||
icon() {
|
icon(){return 'visibility'}
|
||||||
return "visibility"
|
|
||||||
}
|
|
||||||
|
|
||||||
color(darkVer = false) {
|
color(darkVer=false){
|
||||||
return this.newValue ?
|
return this.newValue ?
|
||||||
(darkVer ? "darkgray" : "whitesmoke") :
|
(darkVer ? 'darkgray' : 'whitesmoke') :
|
||||||
(darkVer ? "dimgray" : "lightgray")
|
(darkVer ? 'dimgray' : 'lightgray')
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(targetName = "", targetType = "Point", newValue = true) {
|
constructor(targetName = "", targetType = "Point", newValue = true) {
|
||||||
|
@ -47,15 +41,15 @@ export default class EditedVisibility extends EditedProperty {
|
||||||
}
|
}
|
||||||
|
|
||||||
getReadableString() {
|
getReadableString() {
|
||||||
return (this.newValue ? qsTranslate("visibility", "%1 %2 shown.") : qsTranslate("visibility", "%1 %2 hidden."))
|
return (this.newValue ? qsTranslate('visibility', '%1 %2 shown.') : qsTranslate('visibility', '%1 %2 hidden.'))
|
||||||
.arg(Objects.types[this.targetType].displayType())
|
.arg(Objects.types[this.targetType].displayType())
|
||||||
.arg(this.targetName)
|
.arg(this.targetName)
|
||||||
}
|
}
|
||||||
|
|
||||||
getHTMLString() {
|
getHTMLString() {
|
||||||
return (this.newValue ? qsTranslate("visibility", "%1 %2 shown.") : qsTranslate("visibility", "%1 %2 hidden."))
|
return (this.newValue ? qsTranslate('visibility', '%1 %2 shown.') : qsTranslate('visibility', '%1 %2 hidden.'))
|
||||||
.arg(Objects.types[this.targetType].displayType())
|
.arg(Objects.types[this.targetType].displayType())
|
||||||
.arg("<b style=\"font-size: 15px;\">" + this.targetName + "</b>")
|
.arg('<b style="font-size: 15px;">' + this.targetName + "</b>")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -482,7 +482,7 @@ export class ExprEvalExpression {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the value of the expression by giving all variables and their corresponding values.
|
* Calculates the value of the expression by giving all variables and their corresponding values.
|
||||||
* @param {Object<string, number|DrawableObject>} values
|
* @param {Object<string, number>} values
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
*/
|
*/
|
||||||
evaluate(values) {
|
evaluate(values) {
|
||||||
|
@ -512,7 +512,8 @@ export class ExprEvalExpression {
|
||||||
* as constants or functions.
|
* as constants or functions.
|
||||||
* @returns {string[]}
|
* @returns {string[]}
|
||||||
*/
|
*/
|
||||||
variables(options = {}) {
|
variables(options) {
|
||||||
|
options = options || {}
|
||||||
const vars = []
|
const vars = []
|
||||||
getSymbols(this.tokens, vars, options)
|
getSymbols(this.tokens, vars, options)
|
||||||
const functions = this.functions
|
const functions = this.functions
|
||||||
|
|
|
@ -1,126 +0,0 @@
|
||||||
/**
|
|
||||||
* 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// JS polyfills to add because they're not implemented in the QML Scripting engine.
|
|
||||||
// CoreJS does not work well with it (as well as doubles the compiled size), so this is preferable.
|
|
||||||
function notPolyfilled(name) {
|
|
||||||
return function() { throw new Error(`${name} not polyfilled`) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {number} depth
|
|
||||||
* @this {Array}
|
|
||||||
* @returns {Array}
|
|
||||||
*/
|
|
||||||
function arrayFlat(depth = 1) {
|
|
||||||
const newArray = []
|
|
||||||
for(const element of this) {
|
|
||||||
if(element instanceof Array)
|
|
||||||
newArray.push(...(depth > 1 ? element.flat(depth - 1) : element))
|
|
||||||
else
|
|
||||||
newArray.push(element)
|
|
||||||
}
|
|
||||||
return newArray
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {function(any, number, Array): any} callbackFn
|
|
||||||
* @param {object} thisArg
|
|
||||||
* @this {Array}
|
|
||||||
* @returns {Array}
|
|
||||||
*/
|
|
||||||
function arrayFlatMap(callbackFn, thisArg) {
|
|
||||||
const newArray = []
|
|
||||||
for(let i = 0; i < this.length; i++) {
|
|
||||||
const value = callbackFn.call(thisArg ?? this, this[i], i, this)
|
|
||||||
if(value instanceof Array)
|
|
||||||
newArray.push(...value)
|
|
||||||
else
|
|
||||||
newArray.push(value)
|
|
||||||
}
|
|
||||||
return newArray
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replaces all instances of from by to.
|
|
||||||
* @param {string} from
|
|
||||||
* @param {string} to
|
|
||||||
* @this {string}
|
|
||||||
* @return {String}
|
|
||||||
*/
|
|
||||||
function stringReplaceAll(from, to) {
|
|
||||||
let str = this
|
|
||||||
while(str.includes(from))
|
|
||||||
str = str.replace(from, to)
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const polyfills = {
|
|
||||||
2017: [
|
|
||||||
[Object, "entries", notPolyfilled("Object.entries")],
|
|
||||||
[Object, "values", notPolyfilled("Object.values")],
|
|
||||||
[Object, "getOwnPropertyDescriptors", notPolyfilled("Object.getOwnPropertyDescriptors")],
|
|
||||||
[String.prototype, "padStart", notPolyfilled("String.prototype.padStart")],
|
|
||||||
[String.prototype, "padEnd", notPolyfilled("String.prototype.padEnd")]
|
|
||||||
],
|
|
||||||
2018: [
|
|
||||||
[Promise.prototype, "finally", notPolyfilled("Object.entries")]
|
|
||||||
],
|
|
||||||
2019: [
|
|
||||||
[String.prototype, "trimStart", notPolyfilled("String.prototype.trimStart")],
|
|
||||||
[String.prototype, "trimEnd", notPolyfilled("String.prototype.trimEnd")],
|
|
||||||
[Object, "fromEntries", notPolyfilled("Object.fromEntries")],
|
|
||||||
[Array.prototype, "flat", arrayFlat],
|
|
||||||
[Array.prototype, "flatMap", arrayFlatMap]
|
|
||||||
],
|
|
||||||
2020: [
|
|
||||||
[String.prototype, "matchAll", notPolyfilled("String.prototype.matchAll")],
|
|
||||||
[Promise, "allSettled", notPolyfilled("Promise.allSettled")]
|
|
||||||
],
|
|
||||||
2021: [
|
|
||||||
[Promise, "any", notPolyfilled("Promise.any")],
|
|
||||||
[String.prototype, "replaceAll", stringReplaceAll]
|
|
||||||
],
|
|
||||||
2022: [
|
|
||||||
[Array.prototype, "at", notPolyfilled("Array.prototype.at")],
|
|
||||||
[String.prototype, "at", notPolyfilled("String.prototype.at")],
|
|
||||||
[Object, "hasOwn", notPolyfilled("Object.hasOwn")]
|
|
||||||
],
|
|
||||||
2023: [
|
|
||||||
[Array.prototype, "findLast", notPolyfilled("Array.prototype.findLast")],
|
|
||||||
[Array.prototype, "toReversed", notPolyfilled("Array.prototype.toReversed")],
|
|
||||||
[Array.prototype, "toSorted", notPolyfilled("Array.prototype.toSorted")],
|
|
||||||
[Array.prototype, "toSpliced", notPolyfilled("Array.prototype.toSpliced")],
|
|
||||||
[Array.prototype, "with", notPolyfilled("Array.prototype.with")]
|
|
||||||
],
|
|
||||||
2024: [
|
|
||||||
[Object, "groupBy", notPolyfilled("Object.groupBy")],
|
|
||||||
[Map, "groupBy", notPolyfilled("Map.groupBy")]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fulfill polyfill.
|
|
||||||
for(const [year, entries] of Object.entries(polyfills)) {
|
|
||||||
const defined = entries.filter(x => x[0][x[1]] !== undefined)
|
|
||||||
console.info(`ES${year} support: ${defined.length === entries.length} (${defined.length}/${entries.length})`)
|
|
||||||
// Apply polyfills
|
|
||||||
for(const [context, functionName, polyfill] of entries.filter(x => x[0][x[1]] === undefined)) {
|
|
||||||
context[functionName] = polyfill
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -18,7 +18,6 @@
|
||||||
|
|
||||||
// Type polyfills for IDEs.
|
// Type polyfills for IDEs.
|
||||||
// Never directly imported.
|
// Never directly imported.
|
||||||
// Might need to be reimplemented in other implemententations.
|
|
||||||
|
|
||||||
Modules = Modules || {}
|
Modules = Modules || {}
|
||||||
/** @type {function(string, string): string} */
|
/** @type {function(string, string): string} */
|
||||||
|
@ -26,7 +25,7 @@ qsTranslate = qsTranslate || function(category, string) { throw new Error('qsTra
|
||||||
/** @type {function(string): string} */
|
/** @type {function(string): string} */
|
||||||
qsTr = qsTr || function(string) { throw new Error('qsTr not implemented.'); }
|
qsTr = qsTr || function(string) { throw new Error('qsTr not implemented.'); }
|
||||||
/** @type {function(string, string): string} */
|
/** @type {function(string, string): string} */
|
||||||
QT_TRANSLATE_NOOP = QT_TRANSLATE_NOOP || function(category, string) { throw new Error('QT_TRANSLATE_NOOP not implemented.'); }
|
QT_TRANSLATE_NOOP = QT_TRANSLATE_NOOP || function(string, string) { throw new Error('QT_TRANSLATE_NOOP not implemented.'); }
|
||||||
/** @type {function(string): string} */
|
/** @type {function(string): string} */
|
||||||
QT_TR_NOOP = QT_TR_NOOP || function(string) { throw new Error('QT_TR_NOOP not implemented.'); }
|
QT_TR_NOOP = QT_TR_NOOP || function(string) { throw new Error('QT_TR_NOOP not implemented.'); }
|
||||||
/** @type {function(string|boolean|int): string} */
|
/** @type {function(string|boolean|int): string} */
|
|
@ -23,43 +23,34 @@ import { Expression, executeExpression } from "./expression.mjs"
|
||||||
* It doesn't represent any kind of domain and is meant to be extended.
|
* It doesn't represent any kind of domain and is meant to be extended.
|
||||||
*/
|
*/
|
||||||
export class Domain {
|
export class Domain {
|
||||||
constructor() {
|
constructor() {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether x is included in the domain.
|
* Checks whether x is included in the domain.
|
||||||
* @param {number} x - The x value.
|
* @param {number} x - The x value.
|
||||||
* @return {boolean} true if included, false otherwise.
|
* @return {boolean} true if included, false otherwise.
|
||||||
*/
|
*/
|
||||||
includes(x) {
|
includes(x) { return false }
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a string representation of the domain.
|
* Returns a string representation of the domain.
|
||||||
* @return {string} String representation of the domain.
|
* @return {string} String representation of the domain.
|
||||||
*/
|
*/
|
||||||
toString() {
|
toString() { return '???' }
|
||||||
return "???"
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new domain that is the union between this domain and another.
|
* Returns a new domain that is the union between this domain and another.
|
||||||
* @param {Domain} domain - Domain to unionise with this.
|
* @param {Domain} domain - Domain to unionise with this.
|
||||||
* @return {Domain} newly created domain.
|
* @return {Domain} newly created domain.
|
||||||
*/
|
*/
|
||||||
union(domain) {
|
union(domain) { return domain }
|
||||||
return domain
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new domain that is the intersection between this domain and another.
|
* Returns a new domain that is the intersection between this domain and another.
|
||||||
* @param {Domain} domain - Domain to get the interscection with this.
|
* @param {Domain} domain - Domain to get the interscection with this.
|
||||||
* @return {Domain} newly created domain.
|
* @return {Domain} newly created domain.
|
||||||
*/
|
*/
|
||||||
intersection(domain) {
|
intersection(domain) { return this }
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports a domain from a string.
|
* Imports a domain from a string.
|
||||||
|
@ -70,20 +61,24 @@ export class Domain {
|
||||||
case "R":
|
case "R":
|
||||||
case "ℝ":
|
case "ℝ":
|
||||||
return Domain.R
|
return Domain.R
|
||||||
|
break;
|
||||||
case "RE":
|
case "RE":
|
||||||
case "R*":
|
case "R*":
|
||||||
case "ℝ*":
|
case "ℝ*":
|
||||||
return Domain.RE
|
return Domain.RE
|
||||||
|
break;
|
||||||
case "RP":
|
case "RP":
|
||||||
case "R+":
|
case "R+":
|
||||||
case "ℝ⁺":
|
case "ℝ⁺":
|
||||||
case "ℝ+":
|
case "ℝ+":
|
||||||
return Domain.RP
|
return Domain.RP
|
||||||
|
break;
|
||||||
case "RM":
|
case "RM":
|
||||||
case "R-":
|
case "R-":
|
||||||
case "ℝ⁻":
|
case "ℝ⁻":
|
||||||
case "ℝ-":
|
case "ℝ-":
|
||||||
return Domain.RM
|
return Domain.RM
|
||||||
|
break;
|
||||||
case "RPE":
|
case "RPE":
|
||||||
case "REP":
|
case "REP":
|
||||||
case "R+*":
|
case "R+*":
|
||||||
|
@ -93,6 +88,7 @@ export class Domain {
|
||||||
case "ℝ*+":
|
case "ℝ*+":
|
||||||
case "ℝ+*":
|
case "ℝ+*":
|
||||||
return Domain.RPE
|
return Domain.RPE
|
||||||
|
break;
|
||||||
case "RME":
|
case "RME":
|
||||||
case "REM":
|
case "REM":
|
||||||
case "R-*":
|
case "R-*":
|
||||||
|
@ -102,6 +98,7 @@ export class Domain {
|
||||||
case "ℝ-*":
|
case "ℝ-*":
|
||||||
case "ℝ*-":
|
case "ℝ*-":
|
||||||
return Domain.RME
|
return Domain.RME
|
||||||
|
break;
|
||||||
case "ℕ":
|
case "ℕ":
|
||||||
case "N":
|
case "N":
|
||||||
case "ZP":
|
case "ZP":
|
||||||
|
@ -109,10 +106,12 @@ export class Domain {
|
||||||
case "ℤ⁺":
|
case "ℤ⁺":
|
||||||
case "ℤ+":
|
case "ℤ+":
|
||||||
return Domain.N
|
return Domain.N
|
||||||
|
break;
|
||||||
case "NLOG":
|
case "NLOG":
|
||||||
case "ℕˡᵒᵍ":
|
case "ℕˡᵒᵍ":
|
||||||
case "ℕLOG":
|
case "ℕLOG":
|
||||||
return Domain.NLog
|
return Domain.NLog
|
||||||
|
break;
|
||||||
case "NE":
|
case "NE":
|
||||||
case "NP":
|
case "NP":
|
||||||
case "N*":
|
case "N*":
|
||||||
|
@ -129,14 +128,17 @@ export class Domain {
|
||||||
case "ℤ+*":
|
case "ℤ+*":
|
||||||
case "ℤ*+":
|
case "ℤ*+":
|
||||||
return Domain.NE
|
return Domain.NE
|
||||||
|
break;
|
||||||
case "Z":
|
case "Z":
|
||||||
case "ℤ":
|
case "ℤ":
|
||||||
return Domain.Z
|
return Domain.Z
|
||||||
|
break;
|
||||||
case "ZM":
|
case "ZM":
|
||||||
case "Z-":
|
case "Z-":
|
||||||
case "ℤ⁻":
|
case "ℤ⁻":
|
||||||
case "ℤ-":
|
case "ℤ-":
|
||||||
return Domain.ZM
|
return Domain.ZM
|
||||||
|
break;
|
||||||
case "ZME":
|
case "ZME":
|
||||||
case "ZEM":
|
case "ZEM":
|
||||||
case "Z-*":
|
case "Z-*":
|
||||||
|
@ -146,12 +148,15 @@ export class Domain {
|
||||||
case "ℤ-*":
|
case "ℤ-*":
|
||||||
case "ℤ*-":
|
case "ℤ*-":
|
||||||
return Domain.ZME
|
return Domain.ZME
|
||||||
|
break;
|
||||||
case "ZE":
|
case "ZE":
|
||||||
case "Z*":
|
case "Z*":
|
||||||
case "ℤ*":
|
case "ℤ*":
|
||||||
return Domain.ZE
|
return Domain.ZE
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return Domain.EmptySet
|
return Domain.EmptySet
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,25 +171,15 @@ export class EmptySet extends Domain {
|
||||||
this.latexMarkup = "\\emptyset"
|
this.latexMarkup = "\\emptyset"
|
||||||
}
|
}
|
||||||
|
|
||||||
includes(x) {
|
includes(x) { return false }
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
toString() {
|
toString() { return this.displayName }
|
||||||
return this.displayName
|
|
||||||
}
|
|
||||||
|
|
||||||
union(domain) {
|
union(domain) { return domain }
|
||||||
return domain
|
|
||||||
}
|
|
||||||
|
|
||||||
intersection(domain) {
|
intersection(domain) { return this }
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
static import(frm) {
|
static import(frm) { return new EmptySet() }
|
||||||
return new EmptySet()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Domain.EmptySet = new EmptySet() // To prevent use prior to declaration.
|
Domain.EmptySet = new EmptySet() // To prevent use prior to declaration.
|
||||||
|
@ -195,9 +190,9 @@ Domain.EmptySet = new EmptySet() // To prevent use prior to declaration.
|
||||||
export class Range extends Domain {
|
export class Range extends Domain {
|
||||||
constructor(begin, end, openBegin, openEnd) {
|
constructor(begin, end, openBegin, openEnd) {
|
||||||
super()
|
super()
|
||||||
if(typeof begin == "number" || typeof begin == "string") begin = new Expression(begin.toString())
|
if(typeof begin == 'number' || typeof begin == 'string') begin = new Expression(begin.toString())
|
||||||
this.begin = begin
|
this.begin = begin
|
||||||
if(typeof end == "number" || typeof end == "string") end = new Expression(end.toString())
|
if(typeof end == 'number' || typeof end == 'string') end = new Expression(end.toString())
|
||||||
this.end = end
|
this.end = end
|
||||||
this.openBegin = openBegin
|
this.openBegin = openBegin
|
||||||
this.openEnd = openEnd
|
this.openEnd = openEnd
|
||||||
|
@ -207,7 +202,7 @@ export class Range extends Domain {
|
||||||
|
|
||||||
includes(x) {
|
includes(x) {
|
||||||
if(x instanceof Expression) x = x.execute()
|
if(x instanceof Expression) x = x.execute()
|
||||||
if(typeof x == "string") x = executeExpression(x)
|
if(typeof x == 'string') x = executeExpression(x)
|
||||||
return ((this.openBegin && x > this.begin.execute()) || (!this.openBegin && x >= this.begin.execute())) &&
|
return ((this.openBegin && x > this.begin.execute()) || (!this.openBegin && x >= this.begin.execute())) &&
|
||||||
((this.openEnd && x < this.end.execute()) || (!this.openEnd && x <= this.end.execute()))
|
((this.openEnd && x < this.end.execute()) || (!this.openEnd && x <= this.end.execute()))
|
||||||
}
|
}
|
||||||
|
@ -236,8 +231,8 @@ export class Range extends Domain {
|
||||||
|
|
||||||
static import(frm) {
|
static import(frm) {
|
||||||
let openBegin = frm.trim().charAt(0) === "]"
|
let openBegin = frm.trim().charAt(0) === "]"
|
||||||
let openEnd = frm.trim().charAt(frm.length - 1) === "["
|
let openEnd = frm.trim().charAt(frm.length -1) === "["
|
||||||
let [begin, end] = frm.substring(1, frm.length - 1).split(";")
|
let [begin, end] = frm.substr(1, frm.length-2).split(";")
|
||||||
return new Range(begin.trim(), end.trim(), openBegin, openEnd)
|
return new Range(begin.trim(), end.trim(), openBegin, openEnd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -255,7 +250,7 @@ export class SpecialDomain extends Domain {
|
||||||
* @param {boolean} moveSupported - Only true if next and previous functions are valid.
|
* @param {boolean} moveSupported - Only true if next and previous functions are valid.
|
||||||
*/
|
*/
|
||||||
constructor(displayName, isValid, next = () => true, previous = () => true,
|
constructor(displayName, isValid, next = () => true, previous = () => true,
|
||||||
moveSupported = true) {
|
moveSupported = true) {
|
||||||
super()
|
super()
|
||||||
this.displayName = displayName
|
this.displayName = displayName
|
||||||
this.isValid = isValid
|
this.isValid = isValid
|
||||||
|
@ -266,19 +261,19 @@ export class SpecialDomain extends Domain {
|
||||||
|
|
||||||
includes(x) {
|
includes(x) {
|
||||||
if(x instanceof Expression) x = x.execute()
|
if(x instanceof Expression) x = x.execute()
|
||||||
if(typeof x == "string") x = executeExpression(x)
|
if(typeof x == 'string') x = executeExpression(x)
|
||||||
return this.isValid(x)
|
return this.isValid(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
next(x) {
|
next(x) {
|
||||||
if(x instanceof Expression) x = x.execute()
|
if(x instanceof Expression) x = x.execute()
|
||||||
if(typeof x == "string") x = executeExpression(x)
|
if(typeof x == 'string') x = executeExpression(x)
|
||||||
return this.nextValue(x)
|
return this.nextValue(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
previous(x) {
|
previous(x) {
|
||||||
if(x instanceof Expression) x = x.execute()
|
if(x instanceof Expression) x = x.execute()
|
||||||
if(typeof x == "string") x = executeExpression(x)
|
if(typeof x == 'string') x = executeExpression(x)
|
||||||
return this.prevValue(x)
|
return this.prevValue(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,7 +305,7 @@ export class SpecialDomain extends Domain {
|
||||||
*/
|
*/
|
||||||
export class DomainSet extends SpecialDomain {
|
export class DomainSet extends SpecialDomain {
|
||||||
constructor(values) {
|
constructor(values) {
|
||||||
super("", () => true, x => x, true)
|
super('', x => true, x => x, true)
|
||||||
let newVals = {}
|
let newVals = {}
|
||||||
this.executedValues = []
|
this.executedValues = []
|
||||||
for(let value of values) {
|
for(let value of values) {
|
||||||
|
@ -319,7 +314,7 @@ export class DomainSet extends SpecialDomain {
|
||||||
newVals[ex] = expr
|
newVals[ex] = expr
|
||||||
this.executedValues.push(ex)
|
this.executedValues.push(ex)
|
||||||
}
|
}
|
||||||
this.executedValues.sort((a, b) => a - b)
|
this.executedValues.sort((a,b) => a-b)
|
||||||
this.values = this.executedValues.map(val => newVals[val])
|
this.values = this.executedValues.map(val => newVals[val])
|
||||||
this.displayName = "{" + this.values.join(";") + "}"
|
this.displayName = "{" + this.values.join(";") + "}"
|
||||||
this.latexMarkup = `\\{${this.values.join(";")}\\}`
|
this.latexMarkup = `\\{${this.values.join(";")}\\}`
|
||||||
|
@ -327,7 +322,7 @@ export class DomainSet extends SpecialDomain {
|
||||||
|
|
||||||
includes(x) {
|
includes(x) {
|
||||||
if(x instanceof Expression) x = x.execute()
|
if(x instanceof Expression) x = x.execute()
|
||||||
if(typeof x == "string") x = executeExpression(x)
|
if(typeof x == 'string') x = executeExpression(x)
|
||||||
for(let value of this.values)
|
for(let value of this.values)
|
||||||
if(x === value.execute()) return true
|
if(x === value.execute()) return true
|
||||||
return false
|
return false
|
||||||
|
@ -335,10 +330,10 @@ export class DomainSet extends SpecialDomain {
|
||||||
|
|
||||||
next(x) {
|
next(x) {
|
||||||
if(x instanceof Expression) x = x.execute()
|
if(x instanceof Expression) x = x.execute()
|
||||||
if(typeof x == "string") x = executeExpression(x)
|
if(typeof x == 'string') x = executeExpression(x)
|
||||||
if(x < this.executedValues[0]) return this.executedValues[0]
|
if(x < this.executedValues[0]) return this.executedValues[0]
|
||||||
for(let i = 1; i < this.values.length; i++) {
|
for(let i = 1; i < this.values.length; i++) {
|
||||||
let prevValue = this.executedValues[i - 1]
|
let prevValue = this.executedValues[i-1]
|
||||||
let value = this.executedValues[i]
|
let value = this.executedValues[i]
|
||||||
if(x >= prevValue && x < value) return value
|
if(x >= prevValue && x < value) return value
|
||||||
}
|
}
|
||||||
|
@ -347,11 +342,11 @@ export class DomainSet extends SpecialDomain {
|
||||||
|
|
||||||
previous(x) {
|
previous(x) {
|
||||||
if(x instanceof Expression) x = x.execute()
|
if(x instanceof Expression) x = x.execute()
|
||||||
if(typeof x == "string") x = executeExpression(x)
|
if(typeof x == 'string') x = executeExpression(x)
|
||||||
if(x > this.executedValues[this.executedValues.length - 1])
|
if(x > this.executedValues[this.executedValues.length-1])
|
||||||
return this.executedValues[this.executedValues.length - 1]
|
return this.executedValues[this.executedValues.length-1]
|
||||||
for(let i = 1; i < this.values.length; i++) {
|
for(let i = 1; i < this.values.length; i++) {
|
||||||
let prevValue = this.executedValues[i - 1]
|
let prevValue = this.executedValues[i-1]
|
||||||
let value = this.executedValues[i]
|
let value = this.executedValues[i]
|
||||||
if(x > prevValue && x <= value) return prevValue
|
if(x > prevValue && x <= value) return prevValue
|
||||||
}
|
}
|
||||||
|
@ -366,7 +361,7 @@ export class DomainSet extends SpecialDomain {
|
||||||
if(domain instanceof EmptySet) return this
|
if(domain instanceof EmptySet) return this
|
||||||
if(domain instanceof DomainSet) {
|
if(domain instanceof DomainSet) {
|
||||||
let newValues = []
|
let newValues = []
|
||||||
let values = this.values.concat(domain.values).filter(function(val) {
|
let values = this.values.concat(domain.values).filter(function(val){
|
||||||
newValues.push(val.execute())
|
newValues.push(val.execute())
|
||||||
return newValues.indexOf(val.execute()) === newValues.length - 1
|
return newValues.indexOf(val.execute()) === newValues.length - 1
|
||||||
})
|
})
|
||||||
|
@ -394,7 +389,7 @@ export class DomainSet extends SpecialDomain {
|
||||||
if(domain instanceof EmptySet) return domain
|
if(domain instanceof EmptySet) return domain
|
||||||
if(domain instanceof DomainSet) {
|
if(domain instanceof DomainSet) {
|
||||||
let domValues = domain.values.map(expr => expr.execute())
|
let domValues = domain.values.map(expr => expr.execute())
|
||||||
this.values = this.values.filter(function(val) {
|
this.values = this.values.filter(function(val){
|
||||||
return domValues.indexOf(val.execute()) >= 0
|
return domValues.indexOf(val.execute()) >= 0
|
||||||
})
|
})
|
||||||
return this
|
return this
|
||||||
|
@ -419,7 +414,7 @@ export class DomainSet extends SpecialDomain {
|
||||||
}
|
}
|
||||||
|
|
||||||
static import(frm) {
|
static import(frm) {
|
||||||
return new DomainSet(frm.substring(1, frm.length - 1).split(";"))
|
return new DomainSet(frm.substr(1, frm.length-2).split(";"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,7 +459,7 @@ export class UnionDomain extends Domain {
|
||||||
let domains = frm.trim().split("∪")
|
let domains = frm.trim().split("∪")
|
||||||
if(domains.length === 1) domains = frm.trim().split("U") // Fallback
|
if(domains.length === 1) domains = frm.trim().split("U") // Fallback
|
||||||
let dom2 = parseDomain(domains.pop())
|
let dom2 = parseDomain(domains.pop())
|
||||||
let dom1 = parseDomain(domains.join("∪"))
|
let dom1 = parseDomain(domains.join('∪'))
|
||||||
return dom1.union(dom2)
|
return dom1.union(dom2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -493,7 +488,7 @@ export class IntersectionDomain extends Domain {
|
||||||
if(domain instanceof EmptySet) return this
|
if(domain instanceof EmptySet) return this
|
||||||
if(domain instanceof DomainSet) return domain.union(this)
|
if(domain instanceof DomainSet) return domain.union(this)
|
||||||
if(domain instanceof Range) return domain.union(this)
|
if(domain instanceof Range) return domain.union(this)
|
||||||
if(domain instanceof UnionDomain) return this.dom1.union(domain.dom1).union(this.dom2).union(domain.dom2)
|
if(domain instanceof UnionDomain) return this.dom1.union(domain.dom1).union(this.dom2).union(domain.dom2)
|
||||||
if(domain instanceof IntersectionDomain) return new UnionDomain(this, domain)
|
if(domain instanceof IntersectionDomain) return new UnionDomain(this, domain)
|
||||||
if(domain instanceof MinusDomain) return new MinusDomain(this, domain)
|
if(domain instanceof MinusDomain) return new MinusDomain(this, domain)
|
||||||
}
|
}
|
||||||
|
@ -509,7 +504,7 @@ export class IntersectionDomain extends Domain {
|
||||||
static import(frm) {
|
static import(frm) {
|
||||||
let domains = frm.trim().split("∩")
|
let domains = frm.trim().split("∩")
|
||||||
let dom1 = parseDomain(domains.pop())
|
let dom1 = parseDomain(domains.pop())
|
||||||
let dom2 = parseDomain(domains.join("∩"))
|
let dom2 = parseDomain(domains.join('∩'))
|
||||||
return dom1.intersection(dom2)
|
return dom1.intersection(dom2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -538,7 +533,7 @@ export class MinusDomain extends Domain {
|
||||||
let domains = frm.trim().split("∖")
|
let domains = frm.trim().split("∖")
|
||||||
if(domains.length === 1) domains = frm.trim().split("\\") // Fallback
|
if(domains.length === 1) domains = frm.trim().split("\\") // Fallback
|
||||||
let dom1 = parseDomain(domains.shift())
|
let dom1 = parseDomain(domains.shift())
|
||||||
let dom2 = parseDomain(domains.join("∪"))
|
let dom2 = parseDomain(domains.join('∪'))
|
||||||
return new MinusDomain(dom1, dom2)
|
return new MinusDomain(dom1, dom2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -547,53 +542,53 @@ Domain.RE = new MinusDomain("R", "{0}")
|
||||||
Domain.RE.displayName = "ℝ*"
|
Domain.RE.displayName = "ℝ*"
|
||||||
Domain.RE.latexMarkup = "\\mathbb{R}^{*}"
|
Domain.RE.latexMarkup = "\\mathbb{R}^{*}"
|
||||||
|
|
||||||
Domain.R = new Range(-Infinity, Infinity, true, true)
|
Domain.R = new Range(-Infinity,Infinity,true,true)
|
||||||
Domain.R.displayName = "ℝ"
|
Domain.R.displayName = "ℝ"
|
||||||
Domain.R.latexMarkup = "\\mathbb{R}"
|
Domain.R.latexMarkup = "\\mathbb{R}"
|
||||||
Domain.RP = new Range(0, Infinity, true, false)
|
Domain.RP = new Range(0,Infinity,true,false)
|
||||||
Domain.RP.displayName = "ℝ⁺"
|
Domain.RP.displayName = "ℝ⁺"
|
||||||
Domain.RP.latexMarkup = "\\mathbb{R}^{+}"
|
Domain.RP.latexMarkup = "\\mathbb{R}^{+}"
|
||||||
Domain.RM = new Range(-Infinity, 0, true, false)
|
Domain.RM = new Range(-Infinity,0,true,false)
|
||||||
Domain.RM.displayName = "ℝ⁻"
|
Domain.RM.displayName = "ℝ⁻"
|
||||||
Domain.RM.latexMarkup = "\\mathbb{R}^{-}"
|
Domain.RM.latexMarkup = "\\mathbb{R}^{-}"
|
||||||
Domain.RPE = new Range(0, Infinity, true, true)
|
Domain.RPE = new Range(0,Infinity,true,true)
|
||||||
Domain.RPE.displayName = "ℝ⁺*"
|
Domain.RPE.displayName = "ℝ⁺*"
|
||||||
Domain.RPE.latexMarkup = "\\mathbb{R}^{+*}"
|
Domain.RPE.latexMarkup = "\\mathbb{R}^{+*}"
|
||||||
Domain.RME = new Range(-Infinity, 0, true, true)
|
Domain.RME = new Range(-Infinity,0,true,true)
|
||||||
Domain.RME.displayName = "ℝ⁻*"
|
Domain.RME.displayName = "ℝ⁻*"
|
||||||
Domain.RME.latexMarkup = "\\mathbb{R}^{+*}"
|
Domain.RME.latexMarkup = "\\mathbb{R}^{+*}"
|
||||||
Domain.N = new SpecialDomain("ℕ", x => x % 1 === 0 && x >= 0,
|
Domain.N = new SpecialDomain('ℕ', x => x%1===0 && x >= 0,
|
||||||
x => Math.max(Math.floor(x) + 1, 0),
|
x => Math.max(Math.floor(x)+1, 0),
|
||||||
x => Math.max(Math.ceil(x) - 1, 0))
|
x => Math.max(Math.ceil(x)-1, 0))
|
||||||
Domain.N.latexMarkup = "\\mathbb{N}"
|
Domain.N.latexMarkup = "\\mathbb{N}"
|
||||||
Domain.NE = new SpecialDomain("ℕ*", x => x % 1 === 0 && x > 0,
|
Domain.NE = new SpecialDomain('ℕ*', x => x%1===0 && x > 0,
|
||||||
x => Math.max(Math.floor(x) + 1, 1),
|
x => Math.max(Math.floor(x)+1, 1),
|
||||||
x => Math.max(Math.ceil(x) - 1, 1))
|
x => Math.max(Math.ceil(x)-1, 1))
|
||||||
Domain.NE.latexMarkup = "\\mathbb{N}^{*}"
|
Domain.NE.latexMarkup = "\\mathbb{N}^{*}"
|
||||||
Domain.Z = new SpecialDomain("ℤ", x => x % 1 === 0, x => Math.floor(x) + 1, x => Math.ceil(x) - 1)
|
Domain.Z = new SpecialDomain('ℤ', x => x%1===0, x => Math.floor(x)+1, x => Math.ceil(x)-1)
|
||||||
Domain.Z.latexMarkup = "\\mathbb{Z}"
|
Domain.Z.latexMarkup = "\\mathbb{Z}"
|
||||||
Domain.ZE = new SpecialDomain("ℤ*", x => x % 1 === 0 && x !== 0,
|
Domain.ZE = new SpecialDomain('ℤ*', x => x%1===0 && x !== 0,
|
||||||
x => Math.floor(x) + 1 === 0 ? Math.floor(x) + 2 : Math.floor(x) + 1,
|
x => Math.floor(x)+1 === 0 ? Math.floor(x)+2 : Math.floor(x)+1,
|
||||||
x => Math.ceil(x) - 1 === 0 ? Math.ceil(x) - 2 : Math.ceil(x) - 1)
|
x => Math.ceil(x)-1 === 0 ? Math.ceil(x)-2 : Math.ceil(x)-1)
|
||||||
Domain.ZE.latexMarkup = "\\mathbb{Z}^{*}"
|
Domain.ZE.latexMarkup = "\\mathbb{Z}^{*}"
|
||||||
Domain.ZM = new SpecialDomain("ℤ⁻", x => x % 1 === 0 && x <= 0,
|
Domain.ZM = new SpecialDomain('ℤ⁻', x => x%1===0 && x <= 0,
|
||||||
x => Math.min(Math.floor(x) + 1, 0),
|
x => Math.min(Math.floor(x)+1, 0),
|
||||||
x => Math.min(Math.ceil(x) - 1, 0))
|
x => Math.min(Math.ceil(x)-1, 0))
|
||||||
Domain.ZM.latexMarkup = "\\mathbb{Z}^{-}"
|
Domain.ZM.latexMarkup = "\\mathbb{Z}^{-}"
|
||||||
Domain.ZME = new SpecialDomain("ℤ⁻*", x => x % 1 === 0 && x < 0,
|
Domain.ZME = new SpecialDomain('ℤ⁻*', x => x%1===0 && x < 0,
|
||||||
x => Math.min(Math.floor(x) + 1, -1),
|
x => Math.min(Math.floor(x)+1, -1),
|
||||||
x => Math.min(Math.ceil(x) - 1, -1))
|
x => Math.min(Math.ceil(x)-1, -1))
|
||||||
Domain.ZME.latexMarkup = "\\mathbb{Z}^{-*}"
|
Domain.ZME.latexMarkup = "\\mathbb{Z}^{-*}"
|
||||||
Domain.NLog = new SpecialDomain("ℕˡᵒᵍ",
|
Domain.NLog = new SpecialDomain('ℕˡᵒᵍ',
|
||||||
x => x / Math.pow(10, x.toString().length - 1) % 1 === 0 && x > 0,
|
x => x/Math.pow(10, x.toString().length-1) % 1 === 0 && x > 0,
|
||||||
function(x) {
|
function(x) {
|
||||||
let x10pow = Math.pow(10, x.toString().length - 1)
|
let x10pow = Math.pow(10, x.toString().length-1)
|
||||||
return Math.max(1, (Math.floor(x / x10pow) + 1) * x10pow)
|
return Math.max(1, (Math.floor(x/x10pow)+1)*x10pow)
|
||||||
},
|
},
|
||||||
function(x) {
|
function(x) {
|
||||||
let x10pow = Math.pow(10, x.toString().length - 1)
|
let x10pow = Math.pow(10, x.toString().length-1)
|
||||||
return Math.max(1, (Math.ceil(x / x10pow) - 1) * x10pow)
|
return Math.max(1, (Math.ceil(x/x10pow)-1)*x10pow)
|
||||||
})
|
})
|
||||||
Domain.NLog.latexMarkup = "\\mathbb{N}^{log}"
|
Domain.NLog.latexMarkup = "\\mathbb{N}^{log}"
|
||||||
|
|
||||||
let refedDomains = []
|
let refedDomains = []
|
||||||
|
@ -605,11 +600,11 @@ let refedDomains = []
|
||||||
* @returns {Domain} Parsed domain.
|
* @returns {Domain} Parsed domain.
|
||||||
*/
|
*/
|
||||||
export function parseDomain(domain) {
|
export function parseDomain(domain) {
|
||||||
if(!domain.includes(")") && !domain.includes("(")) return parseDomainSimple(domain)
|
if(!domain.includes(')') && !domain.includes('(')) return parseDomainSimple(domain)
|
||||||
let domStr
|
let domStr
|
||||||
while((domStr = /\(([^)(]+)\)/.exec(domain)) !== null) {
|
while((domStr = /\(([^)(]+)\)/.exec(domain)) !== null) {
|
||||||
let dom = parseDomainSimple(domStr[1].trim())
|
let dom = parseDomainSimple(domStr[1].trim());
|
||||||
domain = domain.replace(domStr[0], "D" + refedDomains.length)
|
domain = domain.replace(domStr[0], 'D' + refedDomains.length)
|
||||||
refedDomains.push(dom)
|
refedDomains.push(dom)
|
||||||
}
|
}
|
||||||
return parseDomainSimple(domain)
|
return parseDomainSimple(domain)
|
||||||
|
@ -626,10 +621,10 @@ export function parseDomainSimple(domain) {
|
||||||
if(domain.includes("U") || domain.includes("∪")) return UnionDomain.import(domain)
|
if(domain.includes("U") || domain.includes("∪")) return UnionDomain.import(domain)
|
||||||
if(domain.includes("∩")) return IntersectionDomain.import(domain)
|
if(domain.includes("∩")) return IntersectionDomain.import(domain)
|
||||||
if(domain.includes("∖") || domain.includes("\\")) return MinusDomain.import(domain)
|
if(domain.includes("∖") || domain.includes("\\")) return MinusDomain.import(domain)
|
||||||
if(domain.charAt(0) === "{" && domain.charAt(domain.length - 1) === "}") return DomainSet.import(domain)
|
if(domain.charAt(0) === "{" && domain.charAt(domain.length -1) === "}") return DomainSet.import(domain)
|
||||||
if(domain.includes("]") || domain.includes("[")) return Range.import(domain)
|
if(domain.includes("]") || domain.includes("[")) return Range.import(domain)
|
||||||
if(["R", "ℝ", "N", "ℕ", "Z", "ℤ"].some(str => domain.toUpperCase().includes(str)))
|
if(["R", "ℝ", "N", "ℕ", "Z", "ℤ"].some(str => domain.toUpperCase().includes(str)))
|
||||||
return Domain.import(domain)
|
return Domain.import(domain)
|
||||||
if(domain[0] === "D") return refedDomains[parseInt(domain.substring(1))]
|
if(domain[0] === 'D') return refedDomains[parseInt(domain.substr(1))]
|
||||||
return new EmptySet()
|
return new EmptySet()
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,48 +37,28 @@ export class Expression {
|
||||||
}
|
}
|
||||||
this.cached = this.isConstant()
|
this.cached = this.isConstant()
|
||||||
this.cachedValue = null
|
this.cachedValue = null
|
||||||
if(this.cached && this.allRequirementsFulfilled())
|
if(this.cached && this.allRequirementsFullfilled())
|
||||||
this.cachedValue = this.calc.evaluate(Objects.currentObjectsByName)
|
this.cachedValue = this.calc.evaluate(Objects.currentObjectsByName)
|
||||||
this.latexMarkup = Latex.expression(this.calc.tokens)
|
this.latexMarkup = Latex.expression(this.calc.tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return all the variables used in calc
|
|
||||||
* @return {string[]}
|
|
||||||
*/
|
|
||||||
variables() {
|
variables() {
|
||||||
return this.calc.variables()
|
return this.calc.variables()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the current expression is constant (does not depend on a variable, be it x or n).
|
|
||||||
* @return {boolean}
|
|
||||||
*/
|
|
||||||
isConstant() {
|
isConstant() {
|
||||||
let vars = this.calc.variables()
|
let vars = this.calc.variables()
|
||||||
return !vars.includes("x") && !vars.includes("n")
|
return !vars.includes("x") && !vars.includes("n")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the list of object names this expression is dependant on.
|
|
||||||
* @return {string[]}
|
|
||||||
*/
|
|
||||||
requiredObjects() {
|
requiredObjects() {
|
||||||
return this.calc.variables().filter(objName => objName !== "x" && objName !== "n")
|
return this.calc.variables().filter(objName => objName !== "x" && objName !== "n")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
allRequirementsFullfilled() {
|
||||||
* Checks if all the objects required for this expression are defined.
|
|
||||||
* @return {boolean}
|
|
||||||
*/
|
|
||||||
allRequirementsFulfilled() {
|
|
||||||
return this.requiredObjects().every(objName => objName in Objects.currentObjectsByName)
|
return this.requiredObjects().every(objName => objName in Objects.currentObjectsByName)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of names whose corresponding objects this expression is dependant on and are missing.
|
|
||||||
* @return {boolean}
|
|
||||||
*/
|
|
||||||
undefinedVariables() {
|
undefinedVariables() {
|
||||||
return this.requiredObjects().filter(objName => !(objName in Objects.currentObjectsByName))
|
return this.requiredObjects().filter(objName => !(objName in Objects.currentObjectsByName))
|
||||||
}
|
}
|
||||||
|
@ -94,13 +74,13 @@ export class Expression {
|
||||||
this.cachedValue = this.calc.evaluate(Objects.currentObjectsByName)
|
this.cachedValue = this.calc.evaluate(Objects.currentObjectsByName)
|
||||||
return this.cachedValue
|
return this.cachedValue
|
||||||
}
|
}
|
||||||
ExprParser.currentVars = Object.assign({ "x": x }, Objects.currentObjectsByName)
|
ExprParser.currentVars = Object.assign({'x': x}, Objects.currentObjectsByName)
|
||||||
return this.calc.evaluate(ExprParser.currentVars)
|
return this.calc.evaluate(ExprParser.currentVars)
|
||||||
}
|
}
|
||||||
|
|
||||||
simplify(x) {
|
simplify(x) {
|
||||||
let expr = this.calc.substitute("x", x).simplify()
|
let expr = this.calc.substitute('x', x).simplify()
|
||||||
if(expr.evaluate() === 0) expr = "0"
|
if(expr.evaluate() === 0) expr = '0'
|
||||||
return new Expression(expr)
|
return new Expression(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,19 +88,19 @@ export class Expression {
|
||||||
return this.calc.toString()
|
return this.calc.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
toString(forceSign = false) {
|
toString(forceSign=false) {
|
||||||
let str = Utils.makeExpressionReadable(this.calc.toString())
|
let str = Utils.makeExpressionReadable(this.calc.toString())
|
||||||
if(str !== undefined && str.match(/^\d*\.\d+$/)) {
|
if(str !== undefined && str.match(/^\d*\.\d+$/)) {
|
||||||
if(str.split(".")[1].split("0").length > 7) {
|
if(str.split('.')[1].split('0').length > 7) {
|
||||||
// Likely rounding error
|
// Likely rounding error
|
||||||
str = parseFloat(str.substring(0, str.length - 1)).toString()
|
str = parseFloat(str.substring(0, str.length-1)).toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(str[0] !== "-" && forceSign) str = "+" + str
|
if(str[0] !== '-' && forceSign) str = '+' + str
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function executeExpression(expr) {
|
export function executeExpression(expr){
|
||||||
return (new Expression(expr.toString())).execute()
|
return (new Expression(expr.toString())).execute()
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Module } from "./common.mjs"
|
import { Module } from "./common.mjs"
|
||||||
import { CanvasInterface, DialogInterface } from "./interface.mjs"
|
import { FUNCTION, Interface, CanvasInterface, DialogInterface } from "./interface.mjs"
|
||||||
import { textsup } from "../utils.mjs"
|
import { textsup } from "../utils.mjs"
|
||||||
import { Expression } from "../math/index.mjs"
|
import { Expression } from "../math/index.mjs"
|
||||||
import Latex from "./latex.mjs"
|
import Latex from "./latex.mjs"
|
||||||
|
@ -227,7 +227,7 @@ class CanvasAPI extends Module {
|
||||||
// Drawing throws an error. Generally, it's due to a new modification (or the opening of a file)
|
// Drawing throws an error. Generally, it's due to a new modification (or the opening of a file)
|
||||||
console.error(e)
|
console.error(e)
|
||||||
console.log(e.stack)
|
console.log(e.stack)
|
||||||
this._drawingErrorDialog.show(objType, obj.name, e.message)
|
this._drawingErrorDialog.showDialog(objType, obj.name, e.message)
|
||||||
History.undo()
|
History.undo()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ export class Module {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {string} name - Name of the API
|
* @param {string} name - Name of the API
|
||||||
* @param {Object.<string, (Interface|string|number|boolean)>} initializationParameters - List of parameters for the initialize function.
|
* @param {Object.<string, (Interface|string)>} initializationParameters - List of parameters for the initialize function.
|
||||||
*/
|
*/
|
||||||
constructor(name, initializationParameters = {}) {
|
constructor(name, initializationParameters = {}) {
|
||||||
console.log(`Loading module ${name}...`)
|
console.log(`Loading module ${name}...`)
|
||||||
|
|
|
@ -55,7 +55,7 @@ class ExprParserAPI extends Module {
|
||||||
* @return {function} JS function to call.
|
* @return {function} JS function to call.
|
||||||
*/
|
*/
|
||||||
parseArgumentsForFunction(args, usage1, usage2) {
|
parseArgumentsForFunction(args, usage1, usage2) {
|
||||||
let f, variable
|
let f, target, variable
|
||||||
if(args.length === 1) {
|
if(args.length === 1) {
|
||||||
// Parse object
|
// Parse object
|
||||||
f = args[0]
|
f = args[0]
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Module } from "./common.mjs"
|
import { Module } from "./common.mjs"
|
||||||
|
import Latex from "./latex.mjs"
|
||||||
import { HistoryInterface, NUMBER, STRING } from "./interface.mjs"
|
import { HistoryInterface, NUMBER, STRING } from "./interface.mjs"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -69,54 +69,65 @@ export class Interface {
|
||||||
|
|
||||||
|
|
||||||
export class SettingsInterface extends Interface {
|
export class SettingsInterface extends Interface {
|
||||||
width = NUMBER
|
constructor() {
|
||||||
height = NUMBER
|
super()
|
||||||
xmin = NUMBER
|
this.width = NUMBER
|
||||||
ymax = NUMBER
|
this.height = NUMBER
|
||||||
xzoom = NUMBER
|
this.xmin = NUMBER
|
||||||
yzoom = NUMBER
|
this.ymax = NUMBER
|
||||||
xaxisstep = STRING
|
this.xzoom = NUMBER
|
||||||
yaxisstep = STRING
|
this.yzoom = NUMBER
|
||||||
xlabel = STRING
|
this.xaxisstep = STRING
|
||||||
ylabel = STRING
|
this.yaxisstep = STRING
|
||||||
linewidth = NUMBER
|
this.xlabel = STRING
|
||||||
textsize = NUMBER
|
this.ylabel = STRING
|
||||||
logscalex = BOOLEAN
|
this.linewidth = NUMBER
|
||||||
showxgrad = BOOLEAN
|
this.textsize = NUMBER
|
||||||
showygrad = BOOLEAN
|
this.logscalex = BOOLEAN
|
||||||
|
this.showxgrad = BOOLEAN
|
||||||
|
this.showygrad = BOOLEAN
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CanvasInterface extends SettingsInterface {
|
export class CanvasInterface extends SettingsInterface {
|
||||||
imageLoaders = OBJECT
|
constructor() {
|
||||||
/** @type {function(string): CanvasRenderingContext2D} */
|
super()
|
||||||
getContext = FUNCTION
|
this.imageLoaders = OBJECT
|
||||||
/** @type {function(rect)} */
|
/** @type {function(string): CanvasRenderingContext2D} */
|
||||||
markDirty = FUNCTION
|
this.getContext = FUNCTION
|
||||||
/** @type {function(string)} */
|
/** @type {function(rect)} */
|
||||||
loadImage = FUNCTION
|
this.markDirty = FUNCTION
|
||||||
/** @type {function(string)} */
|
/** @type {function(string)} */
|
||||||
isImageLoading = FUNCTION
|
this.loadImage = FUNCTION
|
||||||
/** @type {function(string)} */
|
/** @type {function()} */
|
||||||
isImageLoaded = FUNCTION
|
this.requestPaint = FUNCTION
|
||||||
/** @type {function()} */
|
}
|
||||||
requestPaint = FUNCTION
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RootInterface extends Interface {
|
export class RootInterface extends Interface {
|
||||||
width = NUMBER
|
constructor() {
|
||||||
height = NUMBER
|
super()
|
||||||
updateObjectsLists = FUNCTION
|
this.width = NUMBER
|
||||||
|
this.height = NUMBER
|
||||||
|
this.updateObjectsLists = FUNCTION
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DialogInterface extends Interface {
|
export class DialogInterface extends Interface {
|
||||||
show = FUNCTION
|
constructor() {
|
||||||
|
super()
|
||||||
|
this.show = FUNCTION
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class HistoryInterface extends Interface {
|
export class HistoryInterface extends Interface {
|
||||||
undo = FUNCTION
|
constructor() {
|
||||||
redo = FUNCTION
|
super()
|
||||||
clear = FUNCTION
|
this.undo = FUNCTION
|
||||||
addToHistory = FUNCTION
|
this.redo = FUNCTION
|
||||||
unserialize = FUNCTION
|
this.clear = FUNCTION
|
||||||
serialize = FUNCTION
|
this.addToHistory = FUNCTION
|
||||||
|
this.unserialize = FUNCTION
|
||||||
|
this.serialize = FUNCTION
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { Module } from "./common.mjs"
|
||||||
import Objects from "./objects.mjs"
|
import Objects from "./objects.mjs"
|
||||||
import History from "./history.mjs"
|
import History from "./history.mjs"
|
||||||
import Canvas from "./canvas.mjs"
|
import Canvas from "./canvas.mjs"
|
||||||
import { DialogInterface, RootInterface, SettingsInterface } from "./interface.mjs"
|
import { DialogInterface, FUNCTION, Interface, RootInterface, SettingsInterface } from "./interface.mjs"
|
||||||
|
|
||||||
|
|
||||||
class IOAPI extends Module {
|
class IOAPI extends Module {
|
||||||
|
@ -97,7 +97,7 @@ class IOAPI extends Module {
|
||||||
* Loads the diagram from a certain \c filename.
|
* Loads the diagram from a certain \c filename.
|
||||||
* @param {string} filename
|
* @param {string} filename
|
||||||
*/
|
*/
|
||||||
async loadDiagram(filename) {
|
loadDiagram(filename) {
|
||||||
if(!this.initialized) throw new Error("Attempting loadDiagram before initialize!")
|
if(!this.initialized) throw new Error("Attempting loadDiagram before initialize!")
|
||||||
if(!History.initialized) throw new Error("Attempting loadDiagram before history is initialized!")
|
if(!History.initialized) throw new Error("Attempting loadDiagram before history is initialized!")
|
||||||
let basename = filename.split("/").pop()
|
let basename = filename.split("/").pop()
|
||||||
|
|
|
@ -20,8 +20,7 @@ import { Module } from "./common.mjs"
|
||||||
import * as Instruction from "../lib/expr-eval/instruction.mjs"
|
import * as Instruction from "../lib/expr-eval/instruction.mjs"
|
||||||
import { escapeValue } from "../lib/expr-eval/expression.mjs"
|
import { escapeValue } from "../lib/expr-eval/expression.mjs"
|
||||||
|
|
||||||
const unicodechars = [
|
const unicodechars = ["α", "β", "γ", "δ", "ε", "ζ", "η",
|
||||||
"α", "β", "γ", "δ", "ε", "ζ", "η",
|
|
||||||
"π", "θ", "κ", "λ", "μ", "ξ", "ρ",
|
"π", "θ", "κ", "λ", "μ", "ξ", "ρ",
|
||||||
"ς", "σ", "τ", "φ", "χ", "ψ", "ω",
|
"ς", "σ", "τ", "φ", "χ", "ψ", "ω",
|
||||||
"Γ", "Δ", "Θ", "Λ", "Ξ", "Π", "Σ",
|
"Γ", "Δ", "Θ", "Λ", "Ξ", "Π", "Σ",
|
||||||
|
@ -31,8 +30,7 @@ const unicodechars = [
|
||||||
"⁷", "⁸", "⁹", "⁰", "₁", "₂", "₃",
|
"⁷", "⁸", "⁹", "⁰", "₁", "₂", "₃",
|
||||||
"₄", "₅", "₆", "₇", "₈", "₉", "₀",
|
"₄", "₅", "₆", "₇", "₈", "₉", "₀",
|
||||||
"pi", "∞"]
|
"pi", "∞"]
|
||||||
const equivalchars = [
|
const equivalchars = ["\\alpha", "\\beta", "\\gamma", "\\delta", "\\epsilon", "\\zeta", "\\eta",
|
||||||
"\\alpha", "\\beta", "\\gamma", "\\delta", "\\epsilon", "\\zeta", "\\eta",
|
|
||||||
"\\pi", "\\theta", "\\kappa", "\\lambda", "\\mu", "\\xi", "\\rho",
|
"\\pi", "\\theta", "\\kappa", "\\lambda", "\\mu", "\\xi", "\\rho",
|
||||||
"\\sigma", "\\sigma", "\\tau", "\\phi", "\\chi", "\\psi", "\\omega",
|
"\\sigma", "\\sigma", "\\tau", "\\phi", "\\chi", "\\psi", "\\omega",
|
||||||
"\\Gamma", "\\Delta", "\\Theta", "\\Lambda", "\\Xi", "\\Pi", "\\Sigma",
|
"\\Gamma", "\\Delta", "\\Theta", "\\Lambda", "\\Xi", "\\Pi", "\\Sigma",
|
||||||
|
@ -67,13 +65,26 @@ class LatexAPI extends Module {
|
||||||
this.enabled = Helper.getSettingBool("enable_latex")
|
this.enabled = Helper.getSettingBool("enable_latex")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares and renders a latex string into a png file.
|
||||||
|
*
|
||||||
|
* @param {string} markup - LaTeX markup to render.
|
||||||
|
* @param {number} fontSize - Font size (in pt) to render.
|
||||||
|
* @param {color} color - Color of the text to render.
|
||||||
|
* @returns {LatexRenderResult}
|
||||||
|
*/
|
||||||
|
renderSync(markup, fontSize, color) {
|
||||||
|
let args = Latex.render(markup, fontSize, color).split(",")
|
||||||
|
return new LatexRenderResult(...args)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the given markup (with given font size and color) has already been
|
* Checks if the given markup (with given font size and color) has already been
|
||||||
* rendered, and if so, returns its data. Otherwise, returns null.
|
* rendered, and if so, returns its data. Otherwise, returns null.
|
||||||
*
|
*
|
||||||
* @param {string} markup - LaTeX markup to render.
|
* @param {string} markup - LaTeX markup to render.
|
||||||
* @param {number} fontSize - Font size (in pt) to render.
|
* @param {number} fontSize - Font size (in pt) to render.
|
||||||
* @param {string} color - Color of the text to render.
|
* @param {color} color - Color of the text to render.
|
||||||
* @returns {LatexRenderResult|null}
|
* @returns {LatexRenderResult|null}
|
||||||
*/
|
*/
|
||||||
findPrerendered(markup, fontSize, color) {
|
findPrerendered(markup, fontSize, color) {
|
||||||
|
@ -89,12 +100,13 @@ class LatexAPI extends Module {
|
||||||
*
|
*
|
||||||
* @param {string} markup - LaTeX markup to render.
|
* @param {string} markup - LaTeX markup to render.
|
||||||
* @param {number} fontSize - Font size (in pt) to render.
|
* @param {number} fontSize - Font size (in pt) to render.
|
||||||
* @param {string} color - Color of the text to render.
|
* @param {color} color - Color of the text to render.
|
||||||
* @returns {Promise<LatexRenderResult>}
|
* @returns {Promise<LatexRenderResult>}
|
||||||
*/
|
*/
|
||||||
async requestAsyncRender(markup, fontSize, color) {
|
requestAsyncRender(markup, fontSize, color) {
|
||||||
let args = Latex.render(markup, fontSize, color).split(",")
|
return new Promise(resolve => {
|
||||||
return new LatexRenderResult(...args)
|
resolve(this.renderSync(markup, fontSize, color))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -121,7 +133,7 @@ class LatexAPI extends Module {
|
||||||
if(elem[0] !== "(" && elem[elem.length - 1] !== ")" && contents.some(x => elem.indexOf(x) > 0))
|
if(elem[0] !== "(" && elem[elem.length - 1] !== ")" && contents.some(x => elem.indexOf(x) > 0))
|
||||||
return this.par(elem)
|
return this.par(elem)
|
||||||
if(elem[0] === "(" && elem[elem.length - 1] === ")")
|
if(elem[0] === "(" && elem[elem.length - 1] === ")")
|
||||||
return elem.removeEnclosure()
|
return elem.substr(1, elem.length - 2)
|
||||||
return elem
|
return elem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,24 +148,24 @@ class LatexAPI extends Module {
|
||||||
switch(f) {
|
switch(f) {
|
||||||
case "derivative":
|
case "derivative":
|
||||||
if(args.length === 3)
|
if(args.length === 3)
|
||||||
return `\\frac{d${args[0].removeEnclosure().replaceAll(args[1].removeEnclosure(), "x")}}{dx}`
|
return "\\frac{d" + args[0].substr(1, args[0].length - 2).replace(new RegExp(args[1].substr(1, args[1].length - 2), "g"), "x") + "}{dx}"
|
||||||
else
|
else
|
||||||
return `\\frac{d${args[0]}}{dx}(x)`
|
return "\\frac{d" + args[0] + "}{dx}(x)"
|
||||||
case "integral":
|
case "integral":
|
||||||
if(args.length === 4)
|
if(args.length === 4)
|
||||||
return `\\int\\limits_{${args[0]}}^{${args[1]}}${args[2].removeEnclosure()} d${args[3].removeEnclosure()}`
|
return "\\int\\limits_{" + args[0] + "}^{" + args[1] + "}" + args[2].substr(1, args[2].length - 2) + " d" + args[3].substr(1, args[3].length - 2)
|
||||||
else
|
else
|
||||||
return `\\int\\limits_{${args[0]}}^{${args[1]}}${args[2]}(t) dt`
|
return "\\int\\limits_{" + args[0] + "}^{" + args[1] + "}" + args[2] + "(t) dt"
|
||||||
case "sqrt":
|
case "sqrt":
|
||||||
return `\\sqrt\\left(${args.join(", ")}\\right)`
|
return "\\sqrt\\left(" + args.join(", ") + "\\right)"
|
||||||
case "abs":
|
case "abs":
|
||||||
return `\\left|${args.join(", ")}\\right|`
|
return "\\left|" + args.join(", ") + "\\right|"
|
||||||
case "floor":
|
case "floor":
|
||||||
return `\\left\\lfloor${args.join(", ")}\\right\\rfloor`
|
return "\\left\\lfloor" + args.join(", ") + "\\right\\rfloor"
|
||||||
case "ceil":
|
case "ceil":
|
||||||
return `\\left\\lceil${args.join(", ")}\\right\\rceil`
|
return "\\left\\lceil" + args.join(", ") + "\\right\\rceil"
|
||||||
default:
|
default:
|
||||||
return `\\mathrm{${f}}\\left(${args.join(", ")}\\right)`
|
return "\\mathrm{" + f + "}\\left(" + args.join(", ") + "\\right)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,12 +180,12 @@ class LatexAPI extends Module {
|
||||||
if(wrapIn$)
|
if(wrapIn$)
|
||||||
for(let i = 0; i < unicodechars.length; i++) {
|
for(let i = 0; i < unicodechars.length; i++) {
|
||||||
if(vari.includes(unicodechars[i]))
|
if(vari.includes(unicodechars[i]))
|
||||||
vari = vari.replaceAll(unicodechars[i], "$" + equivalchars[i] + "$")
|
vari = vari.replace(new RegExp(unicodechars[i], "g"), "$" + equivalchars[i] + "$")
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
for(let i = 0; i < unicodechars.length; i++) {
|
for(let i = 0; i < unicodechars.length; i++) {
|
||||||
if(vari.includes(unicodechars[i]))
|
if(vari.includes(unicodechars[i]))
|
||||||
vari = vari.replaceAll(unicodechars[i], equivalchars[i])
|
vari = vari.replace(new RegExp(unicodechars[i], "g"), equivalchars[i])
|
||||||
}
|
}
|
||||||
return vari
|
return vari
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,13 +16,13 @@
|
||||||
* 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 { Module } from "./common.mjs"
|
import { Module } from './common.mjs'
|
||||||
import { textsub } from "../utils.mjs"
|
import { textsub } from '../utils.mjs'
|
||||||
|
|
||||||
class ObjectsAPI extends Module {
|
class ObjectsAPI extends Module {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super("Objects")
|
super('Objects')
|
||||||
|
|
||||||
this.types = {}
|
this.types = {}
|
||||||
/**
|
/**
|
||||||
|
@ -45,7 +45,7 @@ class ObjectsAPI extends Module {
|
||||||
* @param {string} prefix - Prefix to the name.
|
* @param {string} prefix - Prefix to the name.
|
||||||
* @return {string} New unused name for a new object.
|
* @return {string} New unused name for a new object.
|
||||||
*/
|
*/
|
||||||
getNewName(allowedLetters, prefix = "") {
|
getNewName(allowedLetters, prefix='') {
|
||||||
// Allows to get a new name, based on the allowed letters,
|
// Allows to get a new name, based on the allowed letters,
|
||||||
// as well as adding a sub number when needs be.
|
// as well as adding a sub number when needs be.
|
||||||
let newid = 0
|
let newid = 0
|
||||||
|
@ -53,7 +53,7 @@ class ObjectsAPI extends Module {
|
||||||
do {
|
do {
|
||||||
let letter = allowedLetters[newid % allowedLetters.length]
|
let letter = allowedLetters[newid % allowedLetters.length]
|
||||||
let num = Math.floor((newid - (newid % allowedLetters.length)) / allowedLetters.length)
|
let num = Math.floor((newid - (newid % allowedLetters.length)) / allowedLetters.length)
|
||||||
ret = prefix + letter + (num > 0 ? textsub(num - 1) : "")
|
ret = prefix + letter + (num > 0 ? textsub(num-1) : '')
|
||||||
newid += 1
|
newid += 1
|
||||||
} while(ret in this.currentObjectsByName)
|
} while(ret in this.currentObjectsByName)
|
||||||
return ret
|
return ret
|
||||||
|
@ -78,7 +78,7 @@ class ObjectsAPI extends Module {
|
||||||
deleteObject(objName) {
|
deleteObject(objName) {
|
||||||
let obj = this.currentObjectsByName[objName]
|
let obj = this.currentObjectsByName[objName]
|
||||||
if(obj !== undefined) {
|
if(obj !== undefined) {
|
||||||
this.currentObjects[obj.type].splice(this.currentObjects[obj.type].indexOf(obj), 1)
|
this.currentObjects[obj.type].splice(this.currentObjects[obj.type].indexOf(obj),1)
|
||||||
obj.delete()
|
obj.delete()
|
||||||
delete this.currentObjectsByName[objName]
|
delete this.currentObjectsByName[objName]
|
||||||
}
|
}
|
||||||
|
@ -90,10 +90,15 @@ class ObjectsAPI extends Module {
|
||||||
* @returns {string[]} List of names of the objects.
|
* @returns {string[]} List of names of the objects.
|
||||||
*/
|
*/
|
||||||
getObjectsName(objType) {
|
getObjectsName(objType) {
|
||||||
if(objType === "ExecutableObject")
|
if(objType === "ExecutableObject") {
|
||||||
return this.getExecutableTypes().flatMap(
|
// NOTE: QMLJS does not support flatMap.
|
||||||
elemType => this.currentObjects[elemType].map(obj => obj.name)
|
// return getExecutableTypes().flatMap(elemType => currentObjects[elemType].map(obj => obj.name))
|
||||||
)
|
let types = this.getExecutableTypes()
|
||||||
|
let elementNames = ['']
|
||||||
|
for(let elemType of types)
|
||||||
|
elementNames = elementNames.concat(this.currentObjects[elemType].map(obj => obj.name))
|
||||||
|
return elementNames
|
||||||
|
}
|
||||||
if(this.currentObjects[objType] === undefined) return []
|
if(this.currentObjects[objType] === undefined) return []
|
||||||
return this.currentObjects[objType].map(obj => obj.name)
|
return this.currentObjects[objType].map(obj => obj.name)
|
||||||
}
|
}
|
||||||
|
@ -112,9 +117,9 @@ class ObjectsAPI extends Module {
|
||||||
* @param {string[]} args - List of arguments for the objects (can be empty).
|
* @param {string[]} args - List of arguments for the objects (can be empty).
|
||||||
* @return {DrawableObject<objType>} Newly created object.
|
* @return {DrawableObject<objType>} Newly created object.
|
||||||
*/
|
*/
|
||||||
createNewRegisteredObject(objType, args = []) {
|
createNewRegisteredObject(objType, args= []) {
|
||||||
if(Object.keys(this.types).indexOf(objType) === -1) return null // Object type does not exist.
|
if(Object.keys(this.types).indexOf(objType) === -1) return null // Object type does not exist.
|
||||||
const newobj = new this.types[objType](...args)
|
let newobj = new this.types[objType](...args)
|
||||||
if(Object.keys(this.currentObjects).indexOf(objType) === -1) {
|
if(Object.keys(this.currentObjects).indexOf(objType) === -1) {
|
||||||
this.currentObjects[objType] = []
|
this.currentObjects[objType] = []
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,19 +15,19 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* 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 { Module } from "./common.mjs"
|
import {Module} from "./common.mjs"
|
||||||
import General from "../preferences/general.mjs"
|
import General from "../preferences/general.mjs"
|
||||||
import Editor from "../preferences/expression.mjs"
|
import Editor from "../preferences/expression.mjs"
|
||||||
import DefaultGraph from "../preferences/default.mjs"
|
import DefaultGraph from "../preferences/default.mjs"
|
||||||
|
|
||||||
class PreferencesAPI extends Module {
|
class PreferencesAPI extends Module {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("Preferences")
|
super('Preferences')
|
||||||
|
|
||||||
this.categories = {
|
this.categories = {
|
||||||
[QT_TRANSLATE_NOOP("settingCategory", "general")]: General,
|
[QT_TRANSLATE_NOOP('settingCategory', 'general')]: General,
|
||||||
[QT_TRANSLATE_NOOP("settingCategory", "editor")]: Editor,
|
[QT_TRANSLATE_NOOP('settingCategory', 'editor')]: Editor,
|
||||||
[QT_TRANSLATE_NOOP("settingCategory", "default")]: DefaultGraph
|
[QT_TRANSLATE_NOOP('settingCategory', 'default')]: DefaultGraph,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,50 +27,39 @@ import { ExecutableObject } from "./common.mjs"
|
||||||
import Function from "./function.mjs"
|
import Function from "./function.mjs"
|
||||||
|
|
||||||
export default class BodeMagnitude extends ExecutableObject {
|
export default class BodeMagnitude extends ExecutableObject {
|
||||||
static type() {
|
static type(){return 'Gain Bode'}
|
||||||
return "Gain Bode"
|
static displayType(){return qsTranslate("bodemagnitude", 'Bode Magnitude')}
|
||||||
}
|
static displayTypeMultiple(){return qsTranslate("bodemagnitude", 'Bode Magnitudes')}
|
||||||
|
static properties() {return {
|
||||||
|
[QT_TRANSLATE_NOOP('prop','om_0')]: new P.ObjectType('Point'),
|
||||||
|
[QT_TRANSLATE_NOOP('prop','pass')]: P.Enum.BodePass,
|
||||||
|
[QT_TRANSLATE_NOOP('prop','gain')]: new P.Expression(),
|
||||||
|
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||||
|
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
||||||
|
[QT_TRANSLATE_NOOP('prop','omGraduation')]: 'boolean'
|
||||||
|
}}
|
||||||
|
|
||||||
static displayType() {
|
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
||||||
return qsTranslate("bodemagnitude", "Bode Magnitude")
|
om_0 = '', pass = 'high', gain = '20', labelPosition = 'above', labelX = 1, omGraduation = false) {
|
||||||
}
|
if(name == null) name = Objects.getNewName('G')
|
||||||
|
if(name === 'G') name = 'G₀' // G is reserved for sum of BODE magnitudes (Somme gains Bode).
|
||||||
static displayTypeMultiple() {
|
|
||||||
return qsTranslate("bodemagnitude", "Bode Magnitudes")
|
|
||||||
}
|
|
||||||
|
|
||||||
static properties() {
|
|
||||||
return {
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "om_0")]: new P.ObjectType("Point"),
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "pass")]: P.Enum.BodePass,
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "gain")]: new P.Expression(),
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position,
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "labelX")]: "number",
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "omGraduation")]: "boolean"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(name = null, visible = true, color = null, labelContent = "name + value",
|
|
||||||
om_0 = "", pass = "high", gain = "20", labelPosition = "above", labelX = 1, omGraduation = false) {
|
|
||||||
if(name == null) name = Objects.getNewName("G")
|
|
||||||
if(name === "G") name = "G₀" // G is reserved for sum of BODE magnitudes (Somme gains Bode).
|
|
||||||
super(name, visible, color, labelContent)
|
super(name, visible, color, labelContent)
|
||||||
if(typeof om_0 == "string") {
|
if(typeof om_0 == "string") {
|
||||||
// Point name or create one
|
// Point name or create one
|
||||||
om_0 = Objects.currentObjectsByName[om_0]
|
om_0 = Objects.currentObjectsByName[om_0]
|
||||||
if(om_0 == null) {
|
if(om_0 == null) {
|
||||||
// Create new point
|
// Create new point
|
||||||
om_0 = Objects.createNewRegisteredObject("Point", [Objects.getNewName("ω"), true, this.color, "name"])
|
om_0 = Objects.createNewRegisteredObject('Point', [Objects.getNewName('ω'), true, this.color, 'name'])
|
||||||
History.addToHistory(new CreateNewObject(om_0.name, "Point", om_0.export()))
|
History.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} */
|
/** @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())
|
||||||
this.gain = gain
|
this.gain = gain
|
||||||
this.labelPosition = labelPosition
|
this.labelPosition = labelPosition
|
||||||
this.labelX = labelX
|
this.labelX = labelX
|
||||||
|
@ -78,21 +67,21 @@ export default class BodeMagnitude extends ExecutableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
getReadableString() {
|
getReadableString() {
|
||||||
let pass = this.pass === "low" ? qsTranslate("bodemagnitude", "low-pass") : qsTranslate("bodemagnitude", "high-pass")
|
let pass = this.pass === "low" ? qsTranslate("bodemagnitude", "low-pass") : qsTranslate("bodemagnitude", "high-pass");
|
||||||
return `${this.name}: ${pass}; ${this.om_0.name} = ${this.om_0.x}\n ${" ".repeat(this.name.length)}${this.gain.toString(true)} dB/dec`
|
return `${this.name}: ${pass}; ${this.om_0.name} = ${this.om_0.x}\n ${' '.repeat(this.name.length)}${this.gain.toString(true)} dB/dec`
|
||||||
}
|
}
|
||||||
|
|
||||||
getLatexString() {
|
getLatexString() {
|
||||||
let pass = this.pass === "low" ? qsTranslate("bodemagnitude", "low-pass") : qsTranslate("bodemagnitude", "high-pass")
|
let pass = this.pass === "low" ? qsTranslate("bodemagnitude", "low-pass") : qsTranslate("bodemagnitude", "high-pass");
|
||||||
return `\\mathrm{${Latex.variable(this.name)}:}\\begin{array}{l}
|
return `\\mathrm{${Latex.variable(this.name)}:}\\begin{array}{l}
|
||||||
\\textsf{${pass}};${Latex.variable(this.om_0.name)} = ${this.om_0.x.latexMarkup} \\\\
|
\\textsf{${pass}};${Latex.variable(this.om_0.name)} = ${this.om_0.x.latexMarkup} \\\\
|
||||||
${this.gain.latexMarkup}\\textsf{ dB/dec}
|
${this.gain.latexMarkup}\\textsf{ dB/dec}
|
||||||
\\end{array}`
|
\\end{array}`
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(x = 1) {
|
execute(x=1) {
|
||||||
if(typeof x == "string") x = executeExpression(x)
|
if(typeof x == 'string') x = executeExpression(x)
|
||||||
if((this.pass === "high" && x < this.om_0.x) || (this.pass === "low" && x > this.om_0.x)) {
|
if((this.pass === 'high' && x < this.om_0.x) || (this.pass === 'low' && x > this.om_0.x)) {
|
||||||
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.execute(x)
|
return dbfn.execute(x)
|
||||||
} else {
|
} else {
|
||||||
|
@ -102,8 +91,8 @@ export default class BodeMagnitude 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.execute()) || (this.pass === "low" && xval > this.om_0.x.execute())) {
|
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 {
|
||||||
|
@ -118,9 +107,9 @@ export default class BodeMagnitude extends ExecutableObject {
|
||||||
draw(canvas) {
|
draw(canvas) {
|
||||||
let base = [canvas.x2px(this.om_0.x.execute()), canvas.y2px(this.om_0.y.execute())]
|
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
|
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(base[0], base[1], canvas.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}[`)
|
||||||
|
@ -133,8 +122,8 @@ export default class BodeMagnitude extends ExecutableObject {
|
||||||
// Dashed line representing break in function
|
// Dashed line representing break in function
|
||||||
let xpos = canvas.x2px(this.om_0.x.execute())
|
let xpos = canvas.x2px(this.om_0.x.execute())
|
||||||
let dashPxSize = 10
|
let dashPxSize = 10
|
||||||
for(let i = 0; i < canvas.height && this.omGraduation; i += dashPxSize * 2)
|
for(let i = 0; i < canvas.height && this.omGraduation; i += dashPxSize*2)
|
||||||
canvas.drawLine(xpos, i, xpos, i + dashPxSize)
|
canvas.drawLine(xpos, i, xpos, i+dashPxSize)
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
this.drawLabel(canvas, 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)))
|
||||||
|
@ -142,19 +131,17 @@ export default class BodeMagnitude extends ExecutableObject {
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
super.update()
|
super.update()
|
||||||
/** @type {BodeMagnitudeSum[]} */
|
let sumObjs = Objects.currentObjects['Somme gains Bode']
|
||||||
let sumObjs = Objects.currentObjects["Somme gains Bode"]
|
|
||||||
if(sumObjs !== undefined && sumObjs.length > 0) {
|
if(sumObjs !== undefined && sumObjs.length > 0) {
|
||||||
sumObjs[0].recalculateCache()
|
sumObjs[0].recalculateCache()
|
||||||
} else {
|
} else {
|
||||||
Objects.createNewRegisteredObject("Somme gains Bode")
|
Objects.createNewRegisteredObject('Somme gains Bode')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete() {
|
delete() {
|
||||||
super.delete()
|
super.delete()
|
||||||
/** @type {BodeMagnitudeSum[]} */
|
let sumObjs = Objects.currentObjects['Somme gains Bode']
|
||||||
let sumObjs = Objects.currentObjects["Somme gains Bode"]
|
|
||||||
if(sumObjs !== undefined && sumObjs.length > 0) {
|
if(sumObjs !== undefined && sumObjs.length > 0) {
|
||||||
sumObjs[0].recalculateCache()
|
sumObjs[0].recalculateCache()
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,32 +26,18 @@ import Function from "./function.mjs"
|
||||||
|
|
||||||
|
|
||||||
export default class BodeMagnitudeSum extends ExecutableObject {
|
export default class BodeMagnitudeSum extends ExecutableObject {
|
||||||
static type() {
|
static type(){return 'Somme gains Bode'}
|
||||||
return "Somme gains Bode"
|
static displayType(){return qsTranslate("bodemagnitudesum", 'Bode Magnitudes Sum')}
|
||||||
}
|
static displayTypeMultiple(){return qsTranslate("bodemagnitudesum", 'Bode Magnitudes Sum')}
|
||||||
|
static createable() {return false}
|
||||||
|
static properties() {return {
|
||||||
|
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||||
|
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
||||||
|
}}
|
||||||
|
|
||||||
static displayType() {
|
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
||||||
return qsTranslate("bodemagnitudesum", "Bode Magnitudes Sum")
|
labelPosition = 'above', labelX = 1) {
|
||||||
}
|
if(name == null) name = 'G'
|
||||||
|
|
||||||
static displayTypeMultiple() {
|
|
||||||
return qsTranslate("bodemagnitudesum", "Bode Magnitudes Sum")
|
|
||||||
}
|
|
||||||
|
|
||||||
static createable() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
static properties() {
|
|
||||||
return {
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position,
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "labelX")]: "number"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(name = null, visible = true, color = null, labelContent = "name + value",
|
|
||||||
labelPosition = "above", labelX = 1) {
|
|
||||||
if(name == null) name = "G"
|
|
||||||
super(name, visible, color, labelContent)
|
super(name, visible, color, labelContent)
|
||||||
this.labelPosition = labelPosition
|
this.labelPosition = labelPosition
|
||||||
this.labelX = labelX
|
this.labelX = labelX
|
||||||
|
@ -59,11 +45,11 @@ export default class BodeMagnitudeSum extends ExecutableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
getReadableString() {
|
getReadableString() {
|
||||||
return `${this.name} = ${Objects.getObjectsName("Gain Bode").join(" + ")}`
|
return `${this.name} = ${Objects.getObjectsName('Gain Bode').join(' + ')}`
|
||||||
}
|
}
|
||||||
|
|
||||||
getLatexString() {
|
getLatexString() {
|
||||||
return `${Latex.variable(this.name)} = ${Objects.getObjectsName("Gain Bode").map(name => Latex.variable(name)).join(" + ")}`
|
return `${Latex.variable(this.name)} = ${Objects.getObjectsName('Gain Bode').map(name => Latex.variable(name)).join(' + ')}`
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(x = 0) {
|
execute(x = 0) {
|
||||||
|
@ -85,17 +71,17 @@ export default class BodeMagnitudeSum extends ExecutableObject {
|
||||||
return limitedDrawFunction.simplify(x)
|
return limitedDrawFunction.simplify(x)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ""
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
recalculateCache() {
|
recalculateCache() {
|
||||||
this.cachedParts = []
|
this.cachedParts = []
|
||||||
// Calculating this is fairly resource expansive so it's cached.
|
// Calculating this is fairly resource expansive so it's cached.
|
||||||
let magnitudeObjects = Objects.currentObjects["Gain Bode"]
|
let magnitudeObjects = Objects.currentObjects['Gain Bode']
|
||||||
if(magnitudeObjects === undefined || magnitudeObjects.length < 1) {
|
if(magnitudeObjects === undefined || magnitudeObjects.length < 1) {
|
||||||
Objects.deleteObject(this.name)
|
Objects.deleteObject(this.name)
|
||||||
} else {
|
} else {
|
||||||
console.log("Recalculating cache gain")
|
console.log('Recalculating cache gain')
|
||||||
// Minimum to draw (can be expended if needed, just not infinite or it'll cause issues.
|
// Minimum to draw (can be expended if needed, just not infinite or it'll cause issues.
|
||||||
const MIN_DRAW = 1e-20
|
const MIN_DRAW = 1e-20
|
||||||
// Format: [[x value of where the filter transitions, magnitude, high-pass (bool)]]
|
// Format: [[x value of where the filter transitions, magnitude, high-pass (bool)]]
|
||||||
|
@ -106,18 +92,18 @@ export default class BodeMagnitudeSum extends ExecutableObject {
|
||||||
magnitudes.push([Number.MAX_VALUE, 0, true]) // Draw the ending section
|
magnitudes.push([Number.MAX_VALUE, 0, true]) // Draw the ending section
|
||||||
// Collect data from current magnitude (or gain in French) objects.
|
// Collect data from current magnitude (or gain in French) objects.
|
||||||
let baseY = 0
|
let baseY = 0
|
||||||
for(/** @type {BodeMagnitude} */ let magnitudeObj of magnitudeObjects) { // Sorting by their om_0 position.
|
for(/** @type {Bodemagnitude} */ let magnitudeObj of magnitudeObjects) { // Sorting by their om_0 position.
|
||||||
const om0x = magnitudeObj.om_0.x.execute()
|
const om0x = magnitudeObj.om_0.x.execute()
|
||||||
magnitudes.push([om0x, magnitudeObj.gain.execute(), magnitudeObj.pass === "high"])
|
magnitudes.push([om0x, magnitudeObj.gain.execute(), magnitudeObj.pass === 'high'])
|
||||||
baseY += magnitudeObj.execute(MIN_DRAW)
|
baseY += magnitudeObj.execute(MIN_DRAW)
|
||||||
}
|
}
|
||||||
// Sorting the data by their x transitions value
|
// Sorting the data by their x transitions value
|
||||||
magnitudes.sort((a, b) => a[XVALUE] - b[XVALUE])
|
magnitudes.sort((a,b) => a[XVALUE] - b[XVALUE])
|
||||||
// Adding the total gains.
|
// Adding the total gains.
|
||||||
let magnitudesBeforeTransition = []
|
let magnitudesBeforeTransition = []
|
||||||
let magnitudesAfterTransition = []
|
let magnitudesAfterTransition = []
|
||||||
let totalMagnitudeAtStart = 0 // Magnitude at the lowest x value (sum of all high-pass magnitudes)
|
let totalMagnitudeAtStart = 0 // Magnitude at the lowest x value (sum of all high-pass magnitudes)
|
||||||
for(let [om0x, magnitude, highpass] of magnitudes) {
|
for(let [om0x, magnitude, highpass] of magnitudes){
|
||||||
if(highpass) {
|
if(highpass) {
|
||||||
magnitudesBeforeTransition.push(magnitude)
|
magnitudesBeforeTransition.push(magnitude)
|
||||||
magnitudesAfterTransition.push(0)
|
magnitudesAfterTransition.push(0)
|
||||||
|
|
|
@ -27,44 +27,33 @@ import { ExecutableObject } from "./common.mjs"
|
||||||
|
|
||||||
|
|
||||||
export default class BodePhase extends ExecutableObject {
|
export default class BodePhase extends ExecutableObject {
|
||||||
static type() {
|
static type(){return 'Phase Bode'}
|
||||||
return "Phase Bode"
|
static displayType(){return qsTranslate("bodephase", 'Bode Phase')}
|
||||||
}
|
static displayTypeMultiple(){return qsTranslate("bodephase", 'Bode Phases')}
|
||||||
|
static properties() {return {
|
||||||
|
[QT_TRANSLATE_NOOP('prop','om_0')]: new P.ObjectType('Point'),
|
||||||
|
[QT_TRANSLATE_NOOP('prop','phase')]: new P.Expression(),
|
||||||
|
[QT_TRANSLATE_NOOP('prop','unit')]: new P.Enum('°', 'deg', 'rad'),
|
||||||
|
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||||
|
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number'
|
||||||
|
}}
|
||||||
|
|
||||||
static displayType() {
|
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
||||||
return qsTranslate("bodephase", "Bode Phase")
|
om_0 = '', phase = 90, unit = '°', labelPosition = 'above', labelX = 1) {
|
||||||
}
|
if(name == null) name = Objects.getNewName('φ')
|
||||||
|
if(name === 'φ') name = 'φ₀' // φ is reserved for sum of Bode phases.
|
||||||
static displayTypeMultiple() {
|
|
||||||
return qsTranslate("bodephase", "Bode Phases")
|
|
||||||
}
|
|
||||||
|
|
||||||
static properties() {
|
|
||||||
return {
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "om_0")]: new P.ObjectType("Point"),
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "phase")]: new P.Expression(),
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "unit")]: new P.Enum("°", "deg", "rad"),
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position,
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "labelX")]: "number"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(name = null, visible = true, color = null, labelContent = "name + value",
|
|
||||||
om_0 = "", phase = 90, unit = "°", labelPosition = "above", labelX = 1) {
|
|
||||||
if(name == null) name = Objects.getNewName("φ")
|
|
||||||
if(name === "φ") name = "φ₀" // φ is reserved for sum of Bode phases.
|
|
||||||
super(name, visible, color, labelContent)
|
super(name, visible, color, labelContent)
|
||||||
if(typeof phase == "number" || typeof phase == "string") phase = new Expression(phase.toString())
|
if(typeof phase == 'number' || typeof phase == 'string') phase = new Expression(phase.toString())
|
||||||
this.phase = phase
|
this.phase = phase
|
||||||
if(typeof om_0 == "string") {
|
if(typeof om_0 == "string") {
|
||||||
// Point name or create one
|
// Point name or create one
|
||||||
om_0 = Objects.currentObjectsByName[om_0]
|
om_0 = Objects.currentObjectsByName[om_0]
|
||||||
if(om_0 == null) {
|
if(om_0 == null) {
|
||||||
// Create new point
|
// Create new point
|
||||||
om_0 = Objects.createNewRegisteredObject("Point", [Objects.getNewName("ω"), this.color, "name"])
|
om_0 = Objects.createNewRegisteredObject('Point', [Objects.getNewName('ω'), this.color, 'name'])
|
||||||
om_0.labelPosition = this.phase.execute() >= 0 ? "above" : "below"
|
om_0.labelPosition = this.phase.execute() >= 0 ? 'above' : 'below'
|
||||||
History.history.addToHistory(new CreateNewObject(om_0.name, "Point", om_0.export()))
|
History.history.addToHistory(new CreateNewObject(om_0.name, 'Point', om_0.export()))
|
||||||
labelPosition = "below"
|
labelPosition = 'below'
|
||||||
}
|
}
|
||||||
om_0.requiredBy.push(this)
|
om_0.requiredBy.push(this)
|
||||||
}
|
}
|
||||||
|
@ -83,8 +72,8 @@ export default class BodePhase extends ExecutableObject {
|
||||||
return `${Latex.variable(this.name)}: ${this.phase.latexMarkup}\\textsf{${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) {
|
execute(x=1) {
|
||||||
if(typeof x == "string") x = executeExpression(x)
|
if(typeof x == 'string') x = executeExpression(x)
|
||||||
if(x < this.om_0.x) {
|
if(x < this.om_0.x) {
|
||||||
return this.om_0.y.execute()
|
return this.om_0.y.execute()
|
||||||
} else {
|
} else {
|
||||||
|
@ -94,11 +83,11 @@ export default class BodePhase 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(xval < this.om_0.x) {
|
if(xval < this.om_0.x) {
|
||||||
return this.om_0.y.toString()
|
return this.om_0.y.toString()
|
||||||
} else {
|
} else {
|
||||||
let newExp = this.om_0.y.toEditableString() + " + " + this.phase.toEditableString()
|
let newExp = this.om_0.y.toEditableString() + ' + ' + this.phase.toEditableString()
|
||||||
return new Expression(newExp)
|
return new Expression(newExp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,7 +101,7 @@ export default class BodePhase extends ExecutableObject {
|
||||||
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(0, baseY, Math.min(baseX, canvas.height), baseY)
|
canvas.drawLine(0, baseY, Math.min(baseX, canvas.height), baseY)
|
||||||
// Transition line.
|
// Transition line.
|
||||||
|
@ -126,19 +115,17 @@ export default class BodePhase extends ExecutableObject {
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
super.update()
|
super.update()
|
||||||
/** @type {BodePhaseSum[]} */
|
let sumObjs = Objects.currentObjects['Somme phases Bode']
|
||||||
let sumObjs = Objects.currentObjects["Somme phases Bode"]
|
|
||||||
if(sumObjs !== undefined && sumObjs.length > 0) {
|
if(sumObjs !== undefined && sumObjs.length > 0) {
|
||||||
sumObjs[0].recalculateCache()
|
sumObjs[0].recalculateCache()
|
||||||
} else {
|
} else {
|
||||||
Objects.createNewRegisteredObject("Somme phases Bode")
|
Objects.createNewRegisteredObject('Somme phases Bode')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete() {
|
delete() {
|
||||||
super.update()
|
super.update()
|
||||||
/** @type {BodePhaseSum[]} */
|
let sumObjs = Objects.currentObjects['Somme phases Bode']
|
||||||
let sumObjs = Objects.currentObjects["Somme phases Bode"]
|
|
||||||
if(sumObjs !== undefined && sumObjs.length > 0) {
|
if(sumObjs !== undefined && sumObjs.length > 0) {
|
||||||
sumObjs[0].recalculateCache()
|
sumObjs[0].recalculateCache()
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,32 +24,18 @@ import Latex from "../module/latex.mjs"
|
||||||
import { ExecutableObject } from "./common.mjs"
|
import { ExecutableObject } from "./common.mjs"
|
||||||
|
|
||||||
export default class BodePhaseSum extends ExecutableObject {
|
export default class BodePhaseSum extends ExecutableObject {
|
||||||
static type() {
|
static type(){return 'Somme phases Bode'}
|
||||||
return "Somme phases Bode"
|
static displayType(){return qsTranslate("bodephasesum", 'Bode Phases Sum')}
|
||||||
}
|
static displayTypeMultiple(){return qsTranslate("bodephasesum", 'Bode Phases Sum')}
|
||||||
|
static createable() {return false}
|
||||||
|
static properties() {return {
|
||||||
|
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||||
|
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
||||||
|
}}
|
||||||
|
|
||||||
static displayType() {
|
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
||||||
return qsTranslate("bodephasesum", "Bode Phases Sum")
|
labelPosition = 'above', labelX = 1) {
|
||||||
}
|
if(name == null) name = 'φ'
|
||||||
|
|
||||||
static displayTypeMultiple() {
|
|
||||||
return qsTranslate("bodephasesum", "Bode Phases Sum")
|
|
||||||
}
|
|
||||||
|
|
||||||
static createable() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
static properties() {
|
|
||||||
return {
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position,
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "labelX")]: "number"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(name = null, visible = true, color = null, labelContent = "name + value",
|
|
||||||
labelPosition = "above", labelX = 1) {
|
|
||||||
if(name == null) name = "φ"
|
|
||||||
super(name, visible, color, labelContent)
|
super(name, visible, color, labelContent)
|
||||||
this.labelPosition = labelPosition
|
this.labelPosition = labelPosition
|
||||||
this.labelX = labelX
|
this.labelX = labelX
|
||||||
|
@ -57,29 +43,29 @@ export default class BodePhaseSum extends ExecutableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
getReadableString() {
|
getReadableString() {
|
||||||
return `${this.name} = ${Objects.getObjectsName("Phase Bode").join(" + ")}`
|
return `${this.name} = ${Objects.getObjectsName('Phase Bode').join(' + ')}`
|
||||||
}
|
}
|
||||||
|
|
||||||
getLatexString() {
|
getLatexString() {
|
||||||
return `${Latex.variable(this.name)} = ${Objects.getObjectsName("Phase Bode").map(name => Latex.variable(name)).join(" + ")}`
|
return `${Latex.variable(this.name)} = ${Objects.getObjectsName('Phase Bode').map(name => Latex.variable(name)).join(' + ')}`
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(x = 1) {
|
execute(x=1) {
|
||||||
if(typeof x == "string") x = executeExpression(x)
|
if(typeof x == 'string') x = executeExpression(x)
|
||||||
for(let i = 0; i < this.om0xList.length - 1; i++) {
|
for(let i = 0; i < this.om0xList.length-1; i++) {
|
||||||
if(x >= this.om0xList[i] && x < this.om0xList[i + 1]) return this.phasesList[i]
|
if(x >= this.om0xList[i] && x < this.om0xList[i+1]) return this.phasesList[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
for(let i = 0; i < this.om0xList.length - 1; i++) {
|
for(let i = 0; i < this.om0xList.length-1; i++) {
|
||||||
if(xval >= this.om0xList[i] && xval < this.om0xList[i + 1]) {
|
if(xval >= this.om0xList[i] && xval < this.om0xList[i+1]) {
|
||||||
return (new Expression(this.phasesExprList[i])).simplify()
|
return (new Expression(this.phasesExprList[i])).simplify()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "0"
|
return '0'
|
||||||
}
|
}
|
||||||
|
|
||||||
canExecute(x = 1) {
|
canExecute(x = 1) {
|
||||||
|
@ -92,42 +78,43 @@ export default class BodePhaseSum extends ExecutableObject {
|
||||||
let drawMax = 1e20
|
let drawMax = 1e20
|
||||||
this.om0xList = [drawMin, drawMax]
|
this.om0xList = [drawMin, drawMax]
|
||||||
this.phasesList = [0]
|
this.phasesList = [0]
|
||||||
this.phasesExprList = ["0"]
|
this.phasesExprList = ['0']
|
||||||
let phasesDict = new Map()
|
let phasesDict = new Map()
|
||||||
let phasesExprDict = new Map()
|
let phasesExprDict = new Map()
|
||||||
phasesDict.set(drawMax, 0)
|
phasesDict.set(drawMax, 0)
|
||||||
|
|
||||||
let phaseObjects = Objects.currentObjects["Phase Bode"]
|
let phaseObjects = Objects.currentObjects['Phase Bode']
|
||||||
if(phaseObjects === undefined || phaseObjects.length < 1) {
|
if(phaseObjects === undefined || phaseObjects.length < 1) {
|
||||||
Objects.deleteObject(this.name)
|
Objects.deleteObject(this.name)
|
||||||
} else {
|
} else {
|
||||||
console.log("Recalculating cache phase")
|
console.log('Recalculating cache phase')
|
||||||
for(/** @type {BodePhase} */ let obj of phaseObjects) {
|
for(/** @type {Bodephase} */ let obj of phaseObjects) {
|
||||||
this.om0xList.push(obj.om_0.x.execute())
|
this.om0xList.push(obj.om_0.x.execute())
|
||||||
if(!phasesDict.has(obj.om_0.x.execute())) {
|
if(!phasesDict.has(obj.om_0.x.execute())) {
|
||||||
phasesDict.set(obj.om_0.x.execute(), obj.phase.execute())
|
phasesDict.set(obj.om_0.x.execute(), obj.phase.execute())
|
||||||
phasesExprDict.set(obj.om_0.x.execute(), obj.phase.toEditableString())
|
phasesExprDict.set(obj.om_0.x.execute(), obj.phase.toEditableString())
|
||||||
} else {
|
} else {
|
||||||
phasesDict.set(obj.om_0.x.execute(), obj.phase.execute() + phasesDict.get(obj.om_0.x.execute()))
|
phasesDict.set(obj.om_0.x.execute(), obj.phase.execute() + phasesDict.get(obj.om_0.x.execute()))
|
||||||
phasesExprDict.set(obj.om_0.x.execute(), obj.phase.toEditableString() + "+" + phasesExprDict.get(obj.om_0.x.execute()))
|
phasesExprDict.set(obj.om_0.x.execute(), obj.phase.toEditableString() + '+' + phasesExprDict.get(obj.om_0.x.execute()))
|
||||||
}
|
}
|
||||||
this.phasesList[0] += obj.om_0.y.execute()
|
this.phasesList[0] += obj.om_0.y.execute()
|
||||||
this.phasesExprList[0] += "+" + obj.om_0.y.toEditableString()
|
this.phasesExprList[0] += '+' + obj.om_0.y.toEditableString()
|
||||||
}
|
}
|
||||||
this.om0xList.sort((a, b) => a - b)
|
this.om0xList.sort((a,b) => a - b)
|
||||||
|
let totalAdded = this.phasesList[0]
|
||||||
for(let i = 1; i < this.om0xList.length; i++) {
|
for(let i = 1; i < this.om0xList.length; i++) {
|
||||||
this.phasesList[i] = this.phasesList[i - 1] + phasesDict.get(this.om0xList[i])
|
this.phasesList[i] = this.phasesList[i-1] + phasesDict.get(this.om0xList[i])
|
||||||
this.phasesExprList[i] = this.phasesExprList[i - 1] + "+" + phasesDict.get(this.om0xList[i])
|
this.phasesExprList[i] = this.phasesExprList[i-1] + '+' + phasesDict.get(this.om0xList[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(canvas) {
|
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(om0xBegin, baseY, om0xEnd, baseY)
|
canvas.drawLine(om0xBegin, baseY, om0xEnd, baseY)
|
||||||
canvas.drawLine(om0xEnd, baseY, om0xEnd, nextY)
|
canvas.drawLine(om0xEnd, baseY, om0xEnd, nextY)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,10 +16,10 @@
|
||||||
* 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 { getRandomColor } from "../utils.mjs"
|
import { getRandomColor, textsub } from "../utils.mjs"
|
||||||
import Objects from "../module/objects.mjs"
|
import Objects from "../module/objects.mjs"
|
||||||
import Latex from "../module/latex.mjs"
|
import Latex from "../module/latex.mjs"
|
||||||
import { ensureTypeSafety, serializesByPropertyType } from "../parameters.mjs"
|
import {ensureTypeSafety, serializesByPropertyType} from "../parameters.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
|
||||||
|
|
||||||
|
@ -32,34 +32,26 @@ export class DrawableObject {
|
||||||
* Base name of the object. Needs to be constant over time.
|
* Base name of the object. Needs to be constant over time.
|
||||||
* @return {string} Type of the object.
|
* @return {string} Type of the object.
|
||||||
*/
|
*/
|
||||||
static type() {
|
static type(){return 'Unknown'}
|
||||||
return "Unknown"
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translated name of the object to be shown to the user.
|
* Translated name of the object to be shown to the user.
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
static displayType() {
|
static displayType(){return 'Unknown'}
|
||||||
return "Unknown"
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translated name of the object in plural form to be shown to the user.
|
* Translated name of the object in plural form to be shown to the user.
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
static displayTypeMultiple() {
|
static displayTypeMultiple(){return 'Unknowns'}
|
||||||
return "Unknowns"
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if this object can be created by the user, false if it can only
|
* True if this object can be created by the user, false if it can only
|
||||||
* be instantiated by other objects
|
* be instantiated by other objects
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
static createable() {
|
static createable() {return true}
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of properties used in the Object Editor.
|
* List of properties used in the Object Editor.
|
||||||
|
@ -78,18 +70,14 @@ export class DrawableObject {
|
||||||
*
|
*
|
||||||
* @return {Object.<string,string|Enum|List|ObjectType|Dictionary>}
|
* @return {Object.<string,string|Enum|List|ObjectType|Dictionary>}
|
||||||
*/
|
*/
|
||||||
static properties() {
|
static properties() {return {}}
|
||||||
return {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if this object can be executed, so that an y value might be computed
|
* True if this object can be executed, so that an y value might be computed
|
||||||
* for an x value. Only ExecutableObjects have that property set to true.
|
* for an x value. Only ExecutableObjects have that property set to true.
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
static executable() {
|
static executable() {return false}
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports the object from its serialized form.
|
* Imports the object from its serialized form.
|
||||||
|
@ -97,10 +85,10 @@ export class DrawableObject {
|
||||||
*/
|
*/
|
||||||
static import(name, visible, color, labelContent, ...args) {
|
static import(name, visible, color, labelContent, ...args) {
|
||||||
let importedArgs = [name.toString(), visible === true, color.toString(), labelContent]
|
let importedArgs = [name.toString(), visible === true, color.toString(), labelContent]
|
||||||
console.log("Importing", this.type(), name, args)
|
console.log('Importing', this.type(), name, args)
|
||||||
for(let [name, propType] of Object.entries(this.properties()))
|
for(let [name, propType] of Object.entries(this.properties()))
|
||||||
if(!name.startsWith("comment")) {
|
if(!name.startsWith('comment')) {
|
||||||
importedArgs.push(ensureTypeSafety(propType, args[importedArgs.length - 4]))
|
importedArgs.push(ensureTypeSafety(propType, args[importedArgs.length-4]))
|
||||||
}
|
}
|
||||||
return new this(...importedArgs)
|
return new this(...importedArgs)
|
||||||
}
|
}
|
||||||
|
@ -110,10 +98,10 @@ export class DrawableObject {
|
||||||
* @param {string} name - Name of the object
|
* @param {string} name - Name of the object
|
||||||
* @param {boolean} visible - true if the object is visible, false otherwise.
|
* @param {boolean} visible - true if the object is visible, false otherwise.
|
||||||
* @param {color|string} color - Color of the object (can be string or QColor)
|
* @param {color|string} color - Color of the object (can be string or QColor)
|
||||||
* @param {"null"|"name"|"name + value"} labelContent - One of 'null', 'name' or 'name + value' describing the content of the label.
|
* @param {'null'|'name'|'name + value'} labelContent - One of 'null', 'name' or 'name + value' describing the content of the label.
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
constructor(name, visible = true, color = null, labelContent = "name + value") {
|
constructor(name, visible = true, color = null, labelContent = 'name + value') {
|
||||||
if(color == null) color = getRandomColor()
|
if(color == null) color = getRandomColor()
|
||||||
this.type = this.constructor.type()
|
this.type = this.constructor.type()
|
||||||
this.name = name
|
this.name = name
|
||||||
|
@ -132,7 +120,7 @@ export class DrawableObject {
|
||||||
export() {
|
export() {
|
||||||
let exportList = [this.name, this.visible, this.color.toString(), this.labelContent]
|
let exportList = [this.name, this.visible, this.color.toString(), this.labelContent]
|
||||||
for(let [name, propType] of Object.entries(this.constructor.properties()))
|
for(let [name, propType] of Object.entries(this.constructor.properties()))
|
||||||
if(!name.startsWith("comment"))
|
if(!name.startsWith('comment'))
|
||||||
exportList.push(serializesByPropertyType(propType, this[name]))
|
exportList.push(serializesByPropertyType(propType, this[name]))
|
||||||
return exportList
|
return exportList
|
||||||
}
|
}
|
||||||
|
@ -163,12 +151,12 @@ export class DrawableObject {
|
||||||
*/
|
*/
|
||||||
getLabel() {
|
getLabel() {
|
||||||
switch(this.labelContent) {
|
switch(this.labelContent) {
|
||||||
case "name":
|
case 'name':
|
||||||
return this.name
|
return this.name
|
||||||
case "name + value":
|
case 'name + value':
|
||||||
return this.getReadableString()
|
return this.getReadableString()
|
||||||
case "null":
|
case 'null':
|
||||||
return ""
|
return ''
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -182,12 +170,12 @@ export class DrawableObject {
|
||||||
*/
|
*/
|
||||||
getLatexLabel() {
|
getLatexLabel() {
|
||||||
switch(this.labelContent) {
|
switch(this.labelContent) {
|
||||||
case "name":
|
case 'name':
|
||||||
return Latex.variable(this.name)
|
return Latex.variable(this.name)
|
||||||
case "name + value":
|
case 'name + value':
|
||||||
return this.getLatexString()
|
return this.getLatexString()
|
||||||
case "null":
|
case 'null':
|
||||||
return ""
|
return ''
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,8 +203,8 @@ export class DrawableObject {
|
||||||
let currentObjectsByName = Objects.currentObjectsByName
|
let currentObjectsByName = Objects.currentObjectsByName
|
||||||
let properties = this.constructor.properties()
|
let properties = this.constructor.properties()
|
||||||
for(let property in properties)
|
for(let property in properties)
|
||||||
if(typeof properties[property] == "object" && "type" in properties[property])
|
if(typeof properties[property] == 'object' && 'type' in properties[property])
|
||||||
if(properties[property].type === "Expression" && this[property] != null) {
|
if(properties[property].type === 'Expression' && this[property] != null) {
|
||||||
// Expressions with dependencies
|
// Expressions with dependencies
|
||||||
for(let objName of this[property].requiredObjects()) {
|
for(let objName of this[property].requiredObjects()) {
|
||||||
if(objName in currentObjectsByName && !this.requires.includes(currentObjectsByName[objName])) {
|
if(objName in currentObjectsByName && !this.requires.includes(currentObjectsByName[objName])) {
|
||||||
|
@ -228,7 +216,7 @@ export class DrawableObject {
|
||||||
// Recalculate
|
// Recalculate
|
||||||
this[property].recache()
|
this[property].recache()
|
||||||
|
|
||||||
} else if(properties[property].type === "ObjectType" && this[property] != null) {
|
} else if(properties[property].type === 'ObjectType' && this[property] != null) {
|
||||||
// Object dependency
|
// Object dependency
|
||||||
this.requires.push(this[property])
|
this.requires.push(this[property])
|
||||||
this[property].requiredBy.push(this)
|
this[property].requiredBy.push(this)
|
||||||
|
@ -254,8 +242,7 @@ export class DrawableObject {
|
||||||
* Abstract method. Draw the object onto the canvas with the.
|
* Abstract method. Draw the object onto the canvas with the.
|
||||||
* @param {CanvasAPI} canvas
|
* @param {CanvasAPI} canvas
|
||||||
*/
|
*/
|
||||||
draw(canvas) {
|
draw(canvas) {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applicates a drawFunction with two position arguments depending on
|
* Applicates a drawFunction with two position arguments depending on
|
||||||
|
@ -265,46 +252,46 @@ export class DrawableObject {
|
||||||
*
|
*
|
||||||
* @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 {{width: number, height: number}} size - Size of the label item, containing two properties, "width" and "height"
|
* @param {Object.<string, int>} size - Size of the label item, containing two properties, "width" and "height"
|
||||||
* @param {number} posX - Component of the marked position on the x-axis
|
* @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 {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
|
||||||
*/
|
*/
|
||||||
drawPositionDivergence(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)
|
||||||
break
|
break;
|
||||||
case "top":
|
case 'top':
|
||||||
case "above":
|
case 'above':
|
||||||
drawFunction(posX - size.width / 2, posY - size.height - offset)
|
drawFunction(posX-size.width/2, posY-size.height-offset)
|
||||||
break
|
break;
|
||||||
case "bottom":
|
case 'bottom':
|
||||||
case "below":
|
case 'below':
|
||||||
drawFunction(posX - size.width / 2, posY + offset)
|
drawFunction(posX-size.width/2, posY+offset)
|
||||||
break
|
break;
|
||||||
case "left":
|
case 'left':
|
||||||
drawFunction(posX - size.width - offset, posY - size.height / 2)
|
drawFunction(posX-size.width-offset, posY-size.height/2)
|
||||||
break
|
break;
|
||||||
case "right":
|
case 'right':
|
||||||
drawFunction(posX + offset, posY - size.height / 2)
|
drawFunction(posX+offset, posY-size.height/2)
|
||||||
break
|
break;
|
||||||
case "top-left":
|
case 'top-left':
|
||||||
case "above-left":
|
case 'above-left':
|
||||||
drawFunction(posX - size.width, posY - size.height - offset)
|
drawFunction(posX-size.width, posY-size.height-offset)
|
||||||
break
|
break;
|
||||||
case "top-right":
|
case 'top-right':
|
||||||
case "above-right":
|
case 'above-right':
|
||||||
drawFunction(posX + offset, posY - size.height - offset)
|
drawFunction(posX+offset, posY-size.height-offset)
|
||||||
break
|
break;
|
||||||
case "bottom-left":
|
case 'bottom-left':
|
||||||
case "below-left":
|
case 'below-left':
|
||||||
drawFunction(posX - size.width - offset, posY + offset)
|
drawFunction(posX-size.width-offset, posY+offset)
|
||||||
break
|
break;
|
||||||
case "bottom-right":
|
case 'bottom-right':
|
||||||
case "below-right":
|
case 'below-right':
|
||||||
drawFunction(posX + offset, posY + offset)
|
drawFunction(posX+offset, posY+offset)
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,37 +315,38 @@ 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, 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)
|
||||||
getLatexFunction = this.getLatexLabel.bind(this)
|
getLatexFunction = this.getLatexLabel.bind(this)
|
||||||
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(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(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
|
||||||
if(!forceText && Latex.enabled) {
|
if(!forceText && Latex.enabled) {
|
||||||
// With latex
|
// With latex
|
||||||
let drawLblCb = ((ltxImg) => {
|
let drawLblCb = ((ltxImg) => {
|
||||||
this.drawPositionDivergence(labelPosition, 8 + canvas.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)
|
canvas.renderLatexImage(ltxLabel, this.color, drawLblCb.bind(this))
|
||||||
} else {
|
} else {
|
||||||
// Without latex
|
// Without latex
|
||||||
let text = getTextFunction()
|
let text = getTextFunction()
|
||||||
canvas.font = `${canvas.textsize}px sans-serif`
|
canvas.font = `${canvas.textsize}px sans-serif`
|
||||||
let textSize = canvas.measureText(text)
|
let textSize = canvas.measureText(text)
|
||||||
this.drawPositionDivergence(labelPosition, 8 + canvas.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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return this.name
|
return this.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,38 +367,28 @@ export class ExecutableObject extends DrawableObject {
|
||||||
* @param {number} x
|
* @param {number} x
|
||||||
* @returns {number|null}
|
* @returns {number|null}
|
||||||
*/
|
*/
|
||||||
execute(x = 1) {
|
execute(x = 1) {return 0}
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns false if the object isn't defined on the given x, true otherwise.
|
* Returns false if the object isn't defined on the given x, true otherwise.
|
||||||
*
|
*
|
||||||
* @param {number} x
|
* @param {number} x
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
canExecute(x = 1) {
|
canExecute(x = 1) {return true}
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the simplified expression string for a given x.
|
* Returns the simplified expression string for a given x.
|
||||||
*
|
*
|
||||||
* @param {number} x
|
* @param {number} x
|
||||||
* @returns {string|Expression}
|
* @returns {string|Expression}
|
||||||
*/
|
*/
|
||||||
simplify(x = 1) {
|
simplify(x = 1) {return '0'}
|
||||||
return "0"
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if this object can be executed, so that an y value might be computed
|
* True if this object can be executed, so that an y value might be computed
|
||||||
* for an x value. Only ExecutableObjects have that property set to true.
|
* for an x value. Only ExecutableObjects have that property set to true.
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
static executable() {
|
static executable() {return true}
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,30 +24,18 @@ import { ExecutableObject } from "./common.mjs"
|
||||||
|
|
||||||
|
|
||||||
export default class DistributionFunction extends ExecutableObject {
|
export default class DistributionFunction extends ExecutableObject {
|
||||||
static type() {
|
static type(){return 'Repartition'}
|
||||||
return "Repartition"
|
static displayType(){return qsTranslate("distribution", 'Repartition')}
|
||||||
}
|
static displayTypeMultiple(){return qsTranslate("distribution", 'Repartition functions')}
|
||||||
|
static properties() {return {
|
||||||
static displayType() {
|
'comment1': QT_TRANSLATE_NOOP(
|
||||||
return qsTranslate("distribution", "Repartition")
|
'comment',
|
||||||
}
|
'Note: Specify the probability for each value.'
|
||||||
|
),
|
||||||
static displayTypeMultiple() {
|
[QT_TRANSLATE_NOOP('prop','probabilities')]: new P.Dictionary('string', 'double', /^-?[\d.,]+$/, /^-?[\d.,]+$/, 'P({name_} = ', ') = '),
|
||||||
return qsTranslate("distribution", "Repartition functions")
|
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||||
}
|
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
||||||
|
}}
|
||||||
static properties() {
|
|
||||||
return {
|
|
||||||
"comment1": QT_TRANSLATE_NOOP(
|
|
||||||
"comment",
|
|
||||||
"Note: Specify the probability for each value."
|
|
||||||
),
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "probabilities")]: new P.Dictionary("string", "double", /^-?[\d.,]+$/, /^-?[\d.,]+$/, "P({name_} = ", ") = "),
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position,
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "labelX")]: "number"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static import(name, visible, color, labelContent, ...args) {
|
static import(name, visible, color, labelContent, ...args) {
|
||||||
console.log(args, args.length)
|
console.log(args, args.length)
|
||||||
if(args.length === 5) {
|
if(args.length === 5) {
|
||||||
|
@ -58,9 +46,9 @@ export default class DistributionFunction extends ExecutableObject {
|
||||||
return super.import(name, visible, color, labelContent, ...args)
|
return super.import(name, visible, color, labelContent, ...args)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(name = null, visible = true, color = null, labelContent = "name + value",
|
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
||||||
probabilities = { "0": "0" }, labelPosition = "above", labelX = 1) {
|
probabilities = {'0': '0'}, labelPosition = 'above', labelX = 1) {
|
||||||
if(name == null) name = Objects.getNewName("XYZUVW", "F_")
|
if(name == null) name = Objects.getNewName('XYZUVW', "F_")
|
||||||
super(name, visible, color, labelContent)
|
super(name, visible, color, labelContent)
|
||||||
this.probabilities = probabilities
|
this.probabilities = probabilities
|
||||||
this.labelPosition = labelPosition
|
this.labelPosition = labelPosition
|
||||||
|
@ -69,29 +57,27 @@ export default class DistributionFunction extends ExecutableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
getReadableString() {
|
getReadableString() {
|
||||||
let keys = Object.keys(this.probabilities).sort((a, b) => a - b)
|
let keys = Object.keys(this.probabilities).sort((a,b) => a-b);
|
||||||
let varname = this.name.substring(this.name.indexOf("_") + 1)
|
let varname = this.name.substring(this.name.indexOf("_")+1)
|
||||||
return `${this.name}(x) = P(${varname} ≤ x)\n` + keys.map(idx => `P(${varname}=${idx})=${this.probabilities[idx]}`).join("; ")
|
return `${this.name}(x) = P(${varname} ≤ x)\n` + keys.map(idx => `P(${varname}=${idx})=${this.probabilities[idx]}`).join("; ")
|
||||||
}
|
}
|
||||||
|
|
||||||
getLatexString() {
|
getLatexString() {
|
||||||
let keys = Object.keys(this.probabilities).sort((a, b) => a - b)
|
let keys = Object.keys(this.probabilities).sort((a,b) => a-b);
|
||||||
let funcName = Latex.variable(this.name)
|
let funcName = Latex.variable(this.name)
|
||||||
let varName = Latex.variable(this.name.substring(this.name.indexOf("_") + 1))
|
let varName = Latex.variable(this.name.substring(this.name.indexOf("_")+1))
|
||||||
return `\\begin{array}{l}{${funcName}}(x) = P(${varName} \\le x)\\\\` + keys.map(idx => `P(${varName}=${idx})=${this.probabilities[idx]}`).join("; ") + "\\end{array}"
|
return `\\begin{array}{l}{${funcName}}(x) = P(${varName} \\le x)\\\\` + keys.map(idx => `P(${varName}=${idx})=${this.probabilities[idx]}`).join("; ") + '\\end{array}'
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(x = 1) {
|
execute(x = 1) {
|
||||||
let ret = 0
|
let ret = 0;
|
||||||
Object.keys(this.probabilities).sort((a, b) => a - b).forEach(idx => {
|
Object.keys(this.probabilities).sort((a,b) => a-b).forEach(idx => {
|
||||||
if(x >= idx) ret += parseFloat(this.probabilities[idx].replace(/,/g, "."))
|
if(x >= idx) ret += parseFloat(this.probabilities[idx].replace(/,/g, '.'))
|
||||||
})
|
})
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
canExecute(x = 1) {
|
canExecute(x = 1) {return true}
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simplify returns the simplified string of the expression.
|
// Simplify returns the simplified string of the expression.
|
||||||
simplify(x = 1) {
|
simplify(x = 1) {
|
||||||
|
@ -100,53 +86,53 @@ export default class DistributionFunction extends ExecutableObject {
|
||||||
|
|
||||||
getLabel() {
|
getLabel() {
|
||||||
switch(this.labelContent) {
|
switch(this.labelContent) {
|
||||||
case "name":
|
case 'name':
|
||||||
return `${this.name}(x)`
|
return `${this.name}(x)`
|
||||||
case "name + value":
|
case 'name + value':
|
||||||
return this.getReadableString()
|
return this.getReadableString()
|
||||||
case "null":
|
case 'null':
|
||||||
return ""
|
return ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(canvas) {
|
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(0, canvas.y2px(0), canvas.x2px(keys[0]), canvas.y2px(0))
|
canvas.drawLine(0, canvas.y2px(0), canvas.x2px(keys[0]), canvas.y2px(0))
|
||||||
if(canvas.isVisible(keys[0], 0)) {
|
if(canvas.isVisible(keys[0],0)) {
|
||||||
canvas.arc(canvas.x2px(keys[0]) + 4, canvas.y2px(0), 4, Math.PI / 2, 3 * Math.PI / 2)
|
canvas.arc(canvas.x2px(keys[0])+4,canvas.y2px(0), 4, Math.PI / 2, 3 * Math.PI / 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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(
|
canvas.drawLine(
|
||||||
Math.max(0, canvas.x2px(idx)),
|
Math.max(0,canvas.x2px(idx)),
|
||||||
canvas.y2px(currentY),
|
canvas.y2px(currentY),
|
||||||
Math.min(canvas.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)) {
|
||||||
canvas.disc(canvas.x2px(idx), canvas.y2px(currentY), 4)
|
canvas.disc(canvas.x2px(idx), canvas.y2px(currentY), 4)
|
||||||
}
|
}
|
||||||
if(canvas.isVisible(keys[i + 1], currentY)) {
|
if(canvas.isVisible(keys[i+1],currentY)) {
|
||||||
canvas.arc(canvas.x2px(keys[i + 1]) + 4, canvas.y2px(currentY), 4, Math.PI / 2, 3 * Math.PI / 2)
|
canvas.arc(canvas.x2px(keys[i+1])+4,canvas.y2px(currentY), 4, Math.PI / 2, 3 * Math.PI / 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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(
|
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.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, '.')))
|
||||||
)
|
)
|
||||||
canvas.disc(
|
canvas.disc(
|
||||||
canvas.x2px(keys[keys.length - 1]),
|
canvas.x2px(keys[keys.length-1]),
|
||||||
canvas.y2px(
|
canvas.y2px(
|
||||||
currentY + parseFloat(this.probabilities[keys[keys.length - 1]].replace(/,/g, "."))
|
currentY+parseFloat(this.probabilities[keys[keys.length-1]].replace(/,/g, '.'))
|
||||||
),
|
),
|
||||||
4
|
4
|
||||||
)
|
)
|
||||||
|
|
|
@ -25,50 +25,39 @@ import Latex from "../module/latex.mjs"
|
||||||
|
|
||||||
|
|
||||||
export default class Function extends ExecutableObject {
|
export default class Function extends ExecutableObject {
|
||||||
static type() {
|
static type(){return 'Function'}
|
||||||
return "Function"
|
static displayType(){return qsTranslate("function", 'Function')}
|
||||||
}
|
static displayTypeMultiple(){return qsTranslate("function", 'Functions')}
|
||||||
|
static properties() {return {
|
||||||
|
[QT_TRANSLATE_NOOP('prop','expression')]: new P.Expression('x'),
|
||||||
|
[QT_TRANSLATE_NOOP('prop','definitionDomain')]: 'Domain',
|
||||||
|
[QT_TRANSLATE_NOOP('prop','destinationDomain')]: 'Domain',
|
||||||
|
'comment1': QT_TRANSLATE_NOOP(
|
||||||
|
'comment',
|
||||||
|
'Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5}'
|
||||||
|
),
|
||||||
|
[QT_TRANSLATE_NOOP('prop','displayMode')]: P.Enum.FunctionDisplayType,
|
||||||
|
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||||
|
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
||||||
|
'comment2': QT_TRANSLATE_NOOP(
|
||||||
|
'comment',
|
||||||
|
'The following parameters are used when the definition domain is a non-continuous set. (Ex: ℕ, ℤ, sets like {0;3}...)'
|
||||||
|
),
|
||||||
|
[QT_TRANSLATE_NOOP('prop','drawPoints')]: 'boolean',
|
||||||
|
[QT_TRANSLATE_NOOP('prop','drawDashedLines')]: 'boolean'
|
||||||
|
}}
|
||||||
|
|
||||||
static displayType() {
|
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
||||||
return qsTranslate("function", "Function")
|
expression = 'x', definitionDomain = 'RPE', destinationDomain = 'R',
|
||||||
}
|
displayMode = 'application', labelPosition = 'above', labelX = 1,
|
||||||
|
drawPoints = true, drawDashedLines = true) {
|
||||||
static displayTypeMultiple() {
|
if(name == null) name = Objects.getNewName('fghjqlmnopqrstuvwabcde')
|
||||||
return qsTranslate("function", "Functions")
|
|
||||||
}
|
|
||||||
|
|
||||||
static properties() {
|
|
||||||
return {
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "expression")]: new P.Expression("x"),
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "definitionDomain")]: "Domain",
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "destinationDomain")]: "Domain",
|
|
||||||
"comment1": QT_TRANSLATE_NOOP(
|
|
||||||
"comment",
|
|
||||||
"Ex: R+* (ℝ⁺*), N (ℕ), Z-* (ℤ⁻*), ]0;1[, {3;4;5}"
|
|
||||||
),
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "displayMode")]: P.Enum.FunctionDisplayType,
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position,
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "labelX")]: "number",
|
|
||||||
"comment2": QT_TRANSLATE_NOOP(
|
|
||||||
"comment",
|
|
||||||
"The following parameters are used when the definition domain is a non-continuous set. (Ex: ℕ, ℤ, sets like {0;3}...)"
|
|
||||||
),
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "drawPoints")]: "boolean",
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "drawDashedLines")]: "boolean"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(name = null, visible = true, color = null, labelContent = "name + value",
|
|
||||||
expression = "x", definitionDomain = "RPE", destinationDomain = "R",
|
|
||||||
displayMode = "application", labelPosition = "above", labelX = 1,
|
|
||||||
drawPoints = true, drawDashedLines = true) {
|
|
||||||
if(name == null) name = Objects.getNewName("fghjqlmnopqrstuvwabcde")
|
|
||||||
super(name, visible, color, labelContent)
|
super(name, visible, color, labelContent)
|
||||||
if(typeof expression == "number" || typeof expression == "string") expression = new Expression(expression.toString())
|
if(typeof expression == 'number' || typeof expression == 'string') expression = new Expression(expression.toString())
|
||||||
this.expression = expression
|
this.expression = expression
|
||||||
if(typeof definitionDomain == "string") definitionDomain = parseDomain(definitionDomain)
|
if(typeof definitionDomain == 'string') definitionDomain = parseDomain(definitionDomain)
|
||||||
this.definitionDomain = definitionDomain
|
this.definitionDomain = definitionDomain
|
||||||
if(typeof destinationDomain == "string") destinationDomain = parseDomain(destinationDomain)
|
if(typeof destinationDomain == 'string') destinationDomain = parseDomain(destinationDomain)
|
||||||
this.destinationDomain = destinationDomain
|
this.destinationDomain = destinationDomain
|
||||||
this.displayMode = displayMode
|
this.displayMode = displayMode
|
||||||
this.labelPosition = labelPosition
|
this.labelPosition = labelPosition
|
||||||
|
@ -78,15 +67,15 @@ export default class Function extends ExecutableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
getReadableString() {
|
getReadableString() {
|
||||||
if(this.displayMode === "application") {
|
if(this.displayMode === 'application') {
|
||||||
return `${this.name}: ${this.definitionDomain} ⟶ ${this.destinationDomain}\n ${" ".repeat(this.name.length)}x ⟼ ${this.expression.toString()}`
|
return `${this.name}: ${this.definitionDomain} ⟶ ${this.destinationDomain}\n ${' '.repeat(this.name.length)}x ⟼ ${this.expression.toString()}`
|
||||||
} else {
|
} else {
|
||||||
return `${this.name}(x) = ${this.expression.toString()}\nD${textsub(this.name)} = ${this.definitionDomain}`
|
return `${this.name}(x) = ${this.expression.toString()}\nD${textsub(this.name)} = ${this.definitionDomain}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getLatexString() {
|
getLatexString() {
|
||||||
if(this.displayMode === "application") {
|
if(this.displayMode === 'application') {
|
||||||
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 {
|
||||||
|
@ -107,7 +96,7 @@ export default class Function extends ExecutableObject {
|
||||||
simplify(x = 1) {
|
simplify(x = 1) {
|
||||||
if(this.definitionDomain.includes(x))
|
if(this.definitionDomain.includes(x))
|
||||||
return this.expression.simplify(x)
|
return this.expression.simplify(x)
|
||||||
return ""
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(canvas) {
|
draw(canvas) {
|
||||||
|
@ -123,7 +112,7 @@ export default class Function extends ExecutableObject {
|
||||||
static drawFunction(canvas, 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;
|
||||||
if(definitionDomain instanceof SpecialDomain && definitionDomain.moveSupported) {
|
if(definitionDomain instanceof SpecialDomain && definitionDomain.moveSupported) {
|
||||||
// Point based functions.
|
// Point based functions.
|
||||||
previousX = definitionDomain.next(previousX)
|
previousX = definitionDomain.next(previousX)
|
||||||
|
@ -132,16 +121,16 @@ export default class Function extends ExecutableObject {
|
||||||
if(!drawPoints && !drawDash) return
|
if(!drawPoints && !drawDash) return
|
||||||
while(previousX !== null && canvas.x2px(previousX) < canvas.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)
|
||||||
if(currentX === null) break
|
if(currentX === null) break;
|
||||||
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(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) {
|
||||||
canvas.fillRect(canvas.x2px(previousX) - 5, canvas.y2px(previousY) - 1, 10, 2)
|
canvas.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2)
|
||||||
canvas.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
|
||||||
|
@ -149,8 +138,8 @@ export default class Function extends ExecutableObject {
|
||||||
}
|
}
|
||||||
if(drawPoints) {
|
if(drawPoints) {
|
||||||
// Drawing the last cross
|
// Drawing the last cross
|
||||||
canvas.fillRect(canvas.x2px(previousX) - 5, canvas.y2px(previousY) - 1, 10, 2)
|
canvas.fillRect(canvas.x2px(previousX)-5, canvas.y2px(previousY)-1, 10, 2)
|
||||||
canvas.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.
|
||||||
|
@ -165,9 +154,9 @@ export default class Function extends ExecutableObject {
|
||||||
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.
|
||||||
// Need to find the starting point.
|
// Need to find the starting point.
|
||||||
let tmpPx = px - pxprecision
|
let tmpPx = px-pxprecision
|
||||||
do {
|
do {
|
||||||
tmpPx++
|
tmpPx++;
|
||||||
previousX = canvas.px2x(tmpPx)
|
previousX = canvas.px2x(tmpPx)
|
||||||
} while(!definitionDomain.includes(previousX))
|
} while(!definitionDomain.includes(previousX))
|
||||||
// Recaclulate previousY
|
// Recaclulate previousY
|
||||||
|
@ -177,16 +166,16 @@ export default class Function extends ExecutableObject {
|
||||||
// Augmenting the pixel precision until this condition is fulfilled.
|
// Augmenting the pixel precision until this condition is fulfilled.
|
||||||
let tmpPx = px
|
let tmpPx = px
|
||||||
do {
|
do {
|
||||||
tmpPx--
|
tmpPx--;
|
||||||
currentX = canvas.px2x(tmpPx)
|
currentX = canvas.px2x(tmpPx)
|
||||||
} 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.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(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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,35 +24,25 @@ import Latex from "../module/latex.mjs"
|
||||||
import { DrawableObject } from "./common.mjs"
|
import { DrawableObject } from "./common.mjs"
|
||||||
|
|
||||||
|
|
||||||
export default class Point extends DrawableObject {
|
export default class Point extends DrawableObject {
|
||||||
static type() {
|
static type(){return 'Point'}
|
||||||
return "Point"
|
static displayType(){return qsTranslate("point", 'Point')}
|
||||||
}
|
static displayTypeMultiple(){return qsTranslate("point", 'Points')}
|
||||||
|
|
||||||
static displayType() {
|
static properties() {return {
|
||||||
return qsTranslate("point", "Point")
|
[QT_TRANSLATE_NOOP('prop','x')]: new P.Expression(),
|
||||||
}
|
[QT_TRANSLATE_NOOP('prop','y')]: new P.Expression(),
|
||||||
|
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||||
|
[QT_TRANSLATE_NOOP('prop','pointStyle')]: new P.Enum('●', '✕', '+')
|
||||||
|
}}
|
||||||
|
|
||||||
static displayTypeMultiple() {
|
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
||||||
return qsTranslate("point", "Points")
|
x = 1, y = 0, labelPosition = 'above', pointStyle = '●') {
|
||||||
}
|
if(name == null) name = Objects.getNewName('ABCDEFJKLMNOPQRSTUVW')
|
||||||
|
|
||||||
static properties() {
|
|
||||||
return {
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "x")]: new P.Expression(),
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "y")]: new P.Expression(),
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position,
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "pointStyle")]: new P.Enum("●", "✕", "+")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(name = null, visible = true, color = null, labelContent = "name + value",
|
|
||||||
x = 1, y = 0, labelPosition = "above", pointStyle = "●") {
|
|
||||||
if(name == null) name = Objects.getNewName("ABCDEFJKLMNOPQRSTUVW")
|
|
||||||
super(name, visible, color, labelContent)
|
super(name, visible, color, labelContent)
|
||||||
if(typeof x == "number" || typeof x == "string") x = new Expression(x.toString())
|
if(typeof x == 'number' || typeof x == 'string') x = new Expression(x.toString())
|
||||||
this.x = x
|
this.x = x
|
||||||
if(typeof y == "number" || typeof y == "string") y = new Expression(y.toString())
|
if(typeof y == 'number' || typeof y == 'string') y = new Expression(y.toString())
|
||||||
this.y = y
|
this.y = y
|
||||||
this.labelPosition = labelPosition
|
this.labelPosition = labelPosition
|
||||||
this.pointStyle = pointStyle
|
this.pointStyle = pointStyle
|
||||||
|
@ -68,19 +58,19 @@ export default class Point extends DrawableObject {
|
||||||
|
|
||||||
draw(canvas) {
|
draw(canvas) {
|
||||||
let [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())]
|
||||||
let pointSize = 8 + (canvas.linewidth * 2)
|
let pointSize = 8+(canvas.linewidth*2)
|
||||||
switch(this.pointStyle) {
|
switch(this.pointStyle) {
|
||||||
case "●":
|
case '●':
|
||||||
canvas.disc(canvasX, canvasY, pointSize / 2)
|
canvas.disc(canvasX, canvasY, pointSize/2)
|
||||||
break
|
break;
|
||||||
case "✕":
|
case '✕':
|
||||||
canvas.drawLine(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(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 '+':
|
||||||
canvas.fillRect(canvasX - pointSize / 2, canvasY - 1, pointSize, 2)
|
canvas.fillRect(canvasX-pointSize/2, canvasY-1, pointSize, 2)
|
||||||
canvas.fillRect(canvasX - 1, canvasY - pointSize / 2, 2, pointSize)
|
canvas.fillRect(canvasX-1, canvasY-pointSize/2, 2, pointSize)
|
||||||
break
|
break;
|
||||||
}
|
}
|
||||||
this.drawLabel(canvas, this.labelPosition, canvasX, canvasY)
|
this.drawLabel(canvas, this.labelPosition, canvasX, canvasY)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,37 +26,27 @@ import Function from "./function.mjs"
|
||||||
|
|
||||||
|
|
||||||
export default class Sequence extends ExecutableObject {
|
export default class Sequence extends ExecutableObject {
|
||||||
static type() {
|
static type(){return 'Sequence'}
|
||||||
return "Sequence"
|
static displayType(){return qsTranslate("sequence", 'Sequence')}
|
||||||
}
|
static displayTypeMultiple(){return qsTranslate("sequence", 'Sequences')}
|
||||||
|
|
||||||
static displayType() {
|
static properties() {return {
|
||||||
return qsTranslate("sequence", "Sequence")
|
[QT_TRANSLATE_NOOP('prop','drawPoints')]: 'boolean',
|
||||||
}
|
[QT_TRANSLATE_NOOP('prop','drawDashedLines')]: 'boolean',
|
||||||
|
[QT_TRANSLATE_NOOP('prop','defaultExpression')]: new P.Dictionary('string', 'int', /^.+$/, /^\d+$/, '{name}[n+', '] = ', true),
|
||||||
|
'comment1': QT_TRANSLATE_NOOP(
|
||||||
|
'comment',
|
||||||
|
'Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁...'
|
||||||
|
),
|
||||||
|
[QT_TRANSLATE_NOOP('prop','baseValues')]: new P.Dictionary('string', 'int', /^.+$/, /^\d+$/, '{name}[', '] = '),
|
||||||
|
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Position,
|
||||||
|
[QT_TRANSLATE_NOOP('prop','labelX')]: 'number',
|
||||||
|
}}
|
||||||
|
|
||||||
static displayTypeMultiple() {
|
constructor(name = null, visible = true, color = null, labelContent = 'name + value',
|
||||||
return qsTranslate("sequence", "Sequences")
|
drawPoints = true, drawDashedLines = true, defaultExp = {1: "n"},
|
||||||
}
|
baseValues = {0: 0}, labelPosition = 'above', labelX = 1) {
|
||||||
|
if(name == null) name = Objects.getNewName('uvwPSUVWabcde')
|
||||||
static properties() {
|
|
||||||
return {
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "drawPoints")]: "boolean",
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "drawDashedLines")]: "boolean",
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "defaultExpression")]: new P.Dictionary("string", "int", /^.+$/, /^\d+$/, "{name}[n+", "] = ", true),
|
|
||||||
"comment1": QT_TRANSLATE_NOOP(
|
|
||||||
"comment",
|
|
||||||
"Note: Use %1[n] to refer to %1ₙ, %1[n+1] for %1ₙ₊₁..."
|
|
||||||
),
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "baseValues")]: new P.Dictionary("string", "int", /^.+$/, /^\d+$/, "{name}[", "] = "),
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Position,
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "labelX")]: "number"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(name = null, visible = true, color = null, labelContent = "name + value",
|
|
||||||
drawPoints = true, drawDashedLines = true, defaultExp = { 1: "n" },
|
|
||||||
baseValues = { 0: 0 }, labelPosition = "above", labelX = 1) {
|
|
||||||
if(name == null) name = Objects.getNewName("uvwPSUVWabcde")
|
|
||||||
super(name, visible, color, labelContent)
|
super(name, visible, color, labelContent)
|
||||||
this.drawPoints = drawPoints
|
this.drawPoints = drawPoints
|
||||||
this.drawDashedLines = drawDashedLines
|
this.drawDashedLines = drawDashedLines
|
||||||
|
@ -68,17 +58,17 @@ export default class Sequence extends ExecutableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
console.log("Updating sequence", this.sequence)
|
console.log('Updating sequence', this.sequence)
|
||||||
super.update()
|
super.update()
|
||||||
if(
|
if(
|
||||||
this.sequence == null || this.baseValues !== this.sequence.baseValues ||
|
this.sequence == null || this.baseValues !== this.sequence.baseValues ||
|
||||||
this.sequence.name !== this.name ||
|
this.sequence.name !== this.name ||
|
||||||
this.sequence.expr !== Object.values(this.defaultExpression)[0] ||
|
this.sequence.expr !== Object.values(this.defaultExpression)[0] ||
|
||||||
this.sequence.valuePlus.toString() !== Object.keys(this.defaultExpression)[0]
|
this.sequence.valuePlus !== Object.keys(this.defaultExpression)[0]
|
||||||
)
|
)
|
||||||
this.sequence = new MathSequence(
|
this.sequence = new MathSequence(
|
||||||
this.name, this.baseValues,
|
this.name, this.baseValues,
|
||||||
parseFloat(Object.keys(this.defaultExpression)[0]),
|
Object.keys(this.defaultExpression)[0],
|
||||||
Object.values(this.defaultExpression)[0]
|
Object.values(this.defaultExpression)[0]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -96,10 +86,7 @@ export default class Sequence extends ExecutableObject {
|
||||||
return this.sequence.execute(x)
|
return this.sequence.execute(x)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
canExecute(x = 1) {return x%1 === 0}
|
||||||
canExecute(x = 1) {
|
|
||||||
return x % 1 === 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simplify returns the simplified string of the expression.
|
// Simplify returns the simplified string of the expression.
|
||||||
simplify(x = 1) {
|
simplify(x = 1) {
|
||||||
|
@ -110,23 +97,23 @@ export default class Sequence extends ExecutableObject {
|
||||||
|
|
||||||
getLabel() {
|
getLabel() {
|
||||||
switch(this.labelContent) {
|
switch(this.labelContent) {
|
||||||
case "name":
|
case 'name':
|
||||||
return `(${this.name}ₙ)`
|
return `(${this.name}ₙ)`
|
||||||
case "name + value":
|
case 'name + value':
|
||||||
return this.getReadableString()
|
return this.getReadableString()
|
||||||
case "null":
|
case 'null':
|
||||||
return ""
|
return ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getLatexLabel() {
|
getLatexLabel() {
|
||||||
switch(this.labelContent) {
|
switch(this.labelContent) {
|
||||||
case "name":
|
case 'name':
|
||||||
return `(${Latex.variable(this.name)}_n)`
|
return `(${Latex.variable(this.name)}_n)`
|
||||||
case "name + value":
|
case 'name + value':
|
||||||
return this.getLatexString()
|
return this.getLatexString()
|
||||||
case "null":
|
case 'null':
|
||||||
return ""
|
return ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,40 +24,29 @@ import Latex from "../module/latex.mjs"
|
||||||
import { DrawableObject } from "./common.mjs"
|
import { DrawableObject } from "./common.mjs"
|
||||||
|
|
||||||
|
|
||||||
export default class Text extends DrawableObject {
|
export default class Text extends DrawableObject {
|
||||||
static type() {
|
static type(){return 'Text'}
|
||||||
return "Text"
|
static displayType(){return qsTranslate("text", 'Text')}
|
||||||
}
|
static displayTypeMultiple(){return qsTranslate("text", 'Texts')}
|
||||||
|
static properties() {return {
|
||||||
|
[QT_TRANSLATE_NOOP('prop','x')]: new P.Expression(),
|
||||||
|
[QT_TRANSLATE_NOOP('prop','y')]: new P.Expression(),
|
||||||
|
[QT_TRANSLATE_NOOP('prop','labelPosition')]: P.Enum.Positioning,
|
||||||
|
[QT_TRANSLATE_NOOP('prop','text')]: 'string',
|
||||||
|
'comment1': QT_TRANSLATE_NOOP(
|
||||||
|
'comment',
|
||||||
|
'If you have latex enabled, you can use use latex markup in between $$ to create equations.'
|
||||||
|
),
|
||||||
|
[QT_TRANSLATE_NOOP('prop','disableLatex')]: 'boolean'
|
||||||
|
}}
|
||||||
|
|
||||||
static displayType() {
|
constructor(name = null, visible = true, color = null, labelContent = 'null',
|
||||||
return qsTranslate("text", "Text")
|
x = 1, y = 0, labelPosition = 'center', text = 'New text', disableLatex = false) {
|
||||||
}
|
if(name == null) name = Objects.getNewName('t')
|
||||||
|
|
||||||
static displayTypeMultiple() {
|
|
||||||
return qsTranslate("text", "Texts")
|
|
||||||
}
|
|
||||||
|
|
||||||
static properties() {
|
|
||||||
return {
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "x")]: new P.Expression(),
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "y")]: new P.Expression(),
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "labelPosition")]: P.Enum.Positioning,
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "text")]: "string",
|
|
||||||
"comment1": QT_TRANSLATE_NOOP(
|
|
||||||
"comment",
|
|
||||||
"If you have latex enabled, you can use use latex markup in between $$ to create equations."
|
|
||||||
),
|
|
||||||
[QT_TRANSLATE_NOOP("prop", "disableLatex")]: "boolean"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(name = null, visible = true, color = null, labelContent = "null",
|
|
||||||
x = 1, y = 0, labelPosition = "center", text = "New text", disableLatex = false) {
|
|
||||||
if(name == null) name = Objects.getNewName("t")
|
|
||||||
super(name, visible, color, labelContent)
|
super(name, visible, color, labelContent)
|
||||||
if(typeof x == "number" || typeof x == "string") x = new Expression(x.toString())
|
if(typeof x == 'number' || typeof x == 'string') x = new Expression(x.toString())
|
||||||
this.x = x
|
this.x = x
|
||||||
if(typeof y == "number" || typeof y == "string") y = new Expression(y.toString())
|
if(typeof y == 'number' || typeof y == 'string') y = new Expression(y.toString())
|
||||||
this.y = y
|
this.y = y
|
||||||
this.labelPosition = labelPosition
|
this.labelPosition = labelPosition
|
||||||
this.text = text
|
this.text = text
|
||||||
|
@ -71,17 +60,15 @@ export default class Text extends DrawableObject {
|
||||||
latexMarkupText() {
|
latexMarkupText() {
|
||||||
// Check whether the text contains latex escaped elements.
|
// Check whether the text contains latex escaped elements.
|
||||||
let txt = []
|
let txt = []
|
||||||
this.text.split("$$").forEach(function(t) {
|
this.text.split('$$').forEach(function(t) { txt = txt.concat(Latex.variable(t, true).replace(/\$\$/g, '').split('$')) })
|
||||||
txt = txt.concat(Latex.variable(t, true).replace(/\$\$/g, "").split("$"))
|
|
||||||
})
|
|
||||||
let newTxt = txt[0]
|
let newTxt = txt[0]
|
||||||
let i
|
let i
|
||||||
// Split between normal text and latex escaped.
|
// Split between normal text and latex escaped.
|
||||||
for(i = 0; i < txt.length - 1; i++)
|
for(i = 0; i < txt.length-1; i++)
|
||||||
if(i & 0x01) // Every odd number
|
if(i & 0x01) // Every odd number
|
||||||
newTxt += "\\textsf{" + Latex.variable(txt[i + 1])
|
newTxt += '\\textsf{'+Latex.variable(txt[i+1])
|
||||||
else
|
else
|
||||||
newTxt += "}" + txt[i + 1]
|
newTxt += '}'+txt[i+1]
|
||||||
// Finished by a }
|
// Finished by a }
|
||||||
if(i & 0x01)
|
if(i & 0x01)
|
||||||
newTxt += "{"
|
newTxt += "{"
|
||||||
|
@ -101,8 +88,8 @@ export default class Text extends DrawableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(canvas) {
|
draw(canvas) {
|
||||||
let yOffset = this.disableLatex ? canvas.textsize - 4 : 0
|
let yOffset = this.disableLatex ? canvas.textsize-4 : 0
|
||||||
this.drawLabel(canvas, 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,28 +73,29 @@ export default class XCursor extends DrawableObject {
|
||||||
\\end{array}`
|
\\end{array}`
|
||||||
}
|
}
|
||||||
|
|
||||||
getApprox() {
|
getTargetValueLabel() {
|
||||||
let approx = ''
|
var t = this.targetElement
|
||||||
|
var approx = ''
|
||||||
if(this.approximate) {
|
if(this.approximate) {
|
||||||
approx = (this.targetElement.execute(this.x.execute()))
|
approx = (t.execute(this.x.execute()))
|
||||||
let intLength = Math.round(approx).toString().length
|
let intLength = Math.round(approx).toString().length
|
||||||
let rounding = Math.min(this.rounding, approx.toString().length - intLength - 1)
|
let rounding = Math.min(this.rounding, approx.toString().length - intLength - 1)
|
||||||
approx = approx.toPrecision(rounding + intLength)
|
approx = approx.toPrecision(rounding + intLength)
|
||||||
}
|
}
|
||||||
return approx
|
|
||||||
}
|
|
||||||
|
|
||||||
getTargetValueLabel() {
|
|
||||||
const t = this.targetElement
|
|
||||||
const approx = this.getApprox()
|
|
||||||
return `${t.name}(${this.name}) = ${t.simplify(this.x.toEditableString())}` +
|
return `${t.name}(${this.name}) = ${t.simplify(this.x.toEditableString())}` +
|
||||||
(this.approximate ? ' ≈ ' + approx : '')
|
(this.approximate ? ' ≈ ' + approx : '')
|
||||||
}
|
}
|
||||||
|
|
||||||
getTargetValueLatexLabel() {
|
getTargetValueLatexLabel() {
|
||||||
const t = this.targetElement
|
let t = this.targetElement
|
||||||
const approx = this.getApprox()
|
let approx = ''
|
||||||
const simpl = t.simplify(this.x.toEditableString())
|
if(this.approximate) {
|
||||||
|
approx = (t.execute(this.x.execute()))
|
||||||
|
let intLength = Math.round(approx).toString().length
|
||||||
|
let rounding = Math.min(this.rounding, approx.toString().length - intLength - 1)
|
||||||
|
approx = approx.toPrecision(rounding + intLength)
|
||||||
|
}
|
||||||
|
let simpl = t.simplify(this.x.toEditableString())
|
||||||
return `${Latex.variable(t.name)}(${Latex.variable(this.name)}) = ${simpl.latexMarkup ? simpl.latexMarkup : Latex.variable(simpl)}` +
|
return `${Latex.variable(t.name)}(${Latex.variable(this.name)}) = ${simpl.latexMarkup ? simpl.latexMarkup : Latex.variable(simpl)}` +
|
||||||
(this.approximate ? ' \\simeq ' + approx : '')
|
(this.approximate ? ' \\simeq ' + approx : '')
|
||||||
}
|
}
|
||||||
|
@ -103,13 +104,16 @@ export default class XCursor extends DrawableObject {
|
||||||
switch(this.labelContent) {
|
switch(this.labelContent) {
|
||||||
case 'name':
|
case 'name':
|
||||||
return this.name
|
return this.name
|
||||||
|
break;
|
||||||
case 'name + value':
|
case 'name + value':
|
||||||
switch(this.targetValuePosition) {
|
switch(this.targetValuePosition) {
|
||||||
case 'Next to target':
|
case 'Next to target':
|
||||||
case 'Hidden':
|
case 'Hidden':
|
||||||
return `${this.name} = ${this.x.toString()}`
|
return `${this.name} = ${this.x.toString()}`
|
||||||
|
break;
|
||||||
case 'With label':
|
case 'With label':
|
||||||
return this.getReadableString()
|
return this.getReadableString()
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case 'null':
|
case 'null':
|
||||||
return ''
|
return ''
|
||||||
|
@ -120,13 +124,16 @@ export default class XCursor extends DrawableObject {
|
||||||
switch(this.labelContent) {
|
switch(this.labelContent) {
|
||||||
case 'name':
|
case 'name':
|
||||||
return Latex.variable(this.name)
|
return Latex.variable(this.name)
|
||||||
|
break;
|
||||||
case 'name + value':
|
case 'name + value':
|
||||||
switch(this.targetValuePosition) {
|
switch(this.targetValuePosition) {
|
||||||
case 'Next to target':
|
case 'Next to target':
|
||||||
case 'Hidden':
|
case 'Hidden':
|
||||||
return `${Latex.variable(this.name)} = ${this.x.latexMarkup}`
|
return `${Latex.variable(this.name)} = ${this.x.latexMarkup}`
|
||||||
|
break;
|
||||||
case 'With label':
|
case 'With label':
|
||||||
return this.getLatexString()
|
return this.getLatexString()
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case 'null':
|
case 'null':
|
||||||
return ''
|
return ''
|
||||||
|
|
|
@ -15,16 +15,15 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* 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 { parseDomain, Expression as Expr, Domain } from "./math/index.mjs"
|
import {parseDomain, Expression as Expr, Domain} from "./math/index.mjs"
|
||||||
import Objects from "./module/objects.mjs"
|
import Objects from "./module/objects.mjs"
|
||||||
|
|
||||||
const NONE = class Empty {
|
const NONE = class Empty {}
|
||||||
}
|
|
||||||
|
|
||||||
let stringValuesValidators = {
|
let stringValuesValidators = {
|
||||||
"int": [parseInt, (x) => !isNaN(x)],
|
'int': [parseInt, (x) => !isNaN(x)],
|
||||||
"double": [parseFloat, (x) => !isNaN(x)],
|
'double': [parseFloat, (x) => !isNaN(x)],
|
||||||
"string": [(x) => x, () => true]
|
'string': [(x) => x, () => true]
|
||||||
}
|
}
|
||||||
let stringValidatorTypes = Object.keys(stringValuesValidators)
|
let stringValidatorTypes = Object.keys(stringValuesValidators)
|
||||||
|
|
||||||
|
@ -53,17 +52,17 @@ class PropertyType {
|
||||||
export class Expression extends PropertyType {
|
export class Expression extends PropertyType {
|
||||||
constructor(...variables) {
|
constructor(...variables) {
|
||||||
super()
|
super()
|
||||||
this.type = "Expression"
|
this.type = 'Expression'
|
||||||
this.variables = variables
|
this.variables = variables
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return this.variables.length === 0 ? "Number" : `Expression(${this.variables.join(", ")})`
|
return this.variables.length === 0 ? 'Number' : `Expression(${this.variables.join(', ')})`
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(value) {
|
parse(value) {
|
||||||
let result = NONE
|
let result = NONE
|
||||||
if(typeof value == "string")
|
if(typeof value == 'string')
|
||||||
try {
|
try {
|
||||||
result = new Expr(value)
|
result = new Expr(value)
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
|
@ -87,14 +86,14 @@ export class Expression extends PropertyType {
|
||||||
export class Enum extends PropertyType {
|
export class Enum extends PropertyType {
|
||||||
constructor(...values) {
|
constructor(...values) {
|
||||||
super()
|
super()
|
||||||
this.type = "Enum"
|
this.type = 'Enum'
|
||||||
this.values = values
|
this.values = values
|
||||||
this.legacyValues = {}
|
this.legacyValues = {}
|
||||||
this.translatedValues = values.map(x => qsTranslate("parameters", x))
|
this.translatedValues = values.map(x => qsTranslate('parameters', x))
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return `${this.type}(${this.values.join(", ")})`
|
return `${this.type}(${this.values.join(', ')})`
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(value) {
|
parse(value) {
|
||||||
|
@ -112,15 +111,15 @@ export class Enum extends PropertyType {
|
||||||
else if(this.legacyValues[value])
|
else if(this.legacyValues[value])
|
||||||
return this.legacyValues[value]
|
return this.legacyValues[value]
|
||||||
else
|
else
|
||||||
throw new TypeError(`Exportation error: ${value} not one of ${this.values.join(", ")}.`)
|
throw new TypeError(`Exportation error: ${value} not one of ${this.values.join(', ')}.`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export class ObjectType extends PropertyType {
|
export class ObjectType extends PropertyType {
|
||||||
constructor(objType, allowNull = false) {
|
constructor(objType, allowNull=false) {
|
||||||
super()
|
super()
|
||||||
this.type = "ObjectType"
|
this.type = 'ObjectType'
|
||||||
this.objType = objType
|
this.objType = objType
|
||||||
this.allowNull = allowNull
|
this.allowNull = allowNull
|
||||||
}
|
}
|
||||||
|
@ -131,9 +130,9 @@ export class ObjectType extends PropertyType {
|
||||||
|
|
||||||
parse(name) {
|
parse(name) {
|
||||||
let result = NONE
|
let result = NONE
|
||||||
if(typeof name == "string" && name in Objects.currentObjectsByName) {
|
if(typeof name == 'string' && name in Objects.currentObjectsByName) {
|
||||||
let obj = Objects.currentObjectsByName[name]
|
let obj = Objects.currentObjectsByName[name]
|
||||||
if(obj.type === this.objType || (this.objType === "ExecutableObject" && obj.execute)) {
|
if (obj.type === this.objType || (this.objType === 'ExecutableObject' && obj.execute)) {
|
||||||
result = obj
|
result = obj
|
||||||
} else {
|
} else {
|
||||||
// Silently error and return null
|
// Silently error and return null
|
||||||
|
@ -148,7 +147,7 @@ export class ObjectType extends PropertyType {
|
||||||
export(value) {
|
export(value) {
|
||||||
if(value == null && this.allowNull)
|
if(value == null && this.allowNull)
|
||||||
return null
|
return null
|
||||||
else if(value.type === this.objType || (this.objType === "ExecutableObject" && value.execute))
|
else if(value.type === this.objType || (this.objType === 'ExecutableObject' && value.execute))
|
||||||
return value.name
|
return value.name
|
||||||
else
|
else
|
||||||
throw new TypeError(`Exportation error: ${value} not a ${this.objType}.`)
|
throw new TypeError(`Exportation error: ${value} not a ${this.objType}.`)
|
||||||
|
@ -157,13 +156,13 @@ export class ObjectType extends PropertyType {
|
||||||
|
|
||||||
|
|
||||||
export class List extends PropertyType {
|
export class List extends PropertyType {
|
||||||
constructor(type, format = /^.+$/, label = "", forbidAdding = false) {
|
constructor(type, format = /^.+$/, label = '', forbidAdding = false) {
|
||||||
super()
|
super()
|
||||||
// type can be string, int and double.
|
// type can be string, int and double.
|
||||||
this.type = "List"
|
this.type = 'List'
|
||||||
this.valueType = type
|
this.valueType = type
|
||||||
if(!stringValidatorTypes.includes(this.valueType))
|
if(!stringValidatorTypes.includes(this.valueType))
|
||||||
throw new TypeError(`${this.valueType} must be one of ${stringValidatorTypes.join(", ")}.`)
|
throw new TypeError(`${this.valueType} must be one of ${stringValidatorTypes.join(', ')}.`)
|
||||||
this.format = format
|
this.format = format
|
||||||
this.label = label
|
this.label = label
|
||||||
this.forbidAdding = forbidAdding
|
this.forbidAdding = forbidAdding
|
||||||
|
@ -175,10 +174,10 @@ export class List extends PropertyType {
|
||||||
|
|
||||||
parse(value) {
|
parse(value) {
|
||||||
let result = NONE
|
let result = NONE
|
||||||
if(typeof value == "object" && value.__proto__ === Array) {
|
if(typeof value == 'object' && value.__proto__ === Array) {
|
||||||
let valid = 0
|
let valid = 0
|
||||||
for(let v of value) {
|
for(let v of value) {
|
||||||
if(this.format.test(v)) {
|
if (this.format.test(v)) {
|
||||||
v = stringValuesValidators[this.valueType][0](v)
|
v = stringValuesValidators[this.valueType][0](v)
|
||||||
if(stringValuesValidators[this.valueType][1](v))
|
if(stringValuesValidators[this.valueType][1](v))
|
||||||
valid++
|
valid++
|
||||||
|
@ -191,7 +190,7 @@ export class List extends PropertyType {
|
||||||
}
|
}
|
||||||
|
|
||||||
export(value) {
|
export(value) {
|
||||||
if(typeof value == "object" && value.__proto__ === Array)
|
if(typeof value == 'object' && value.__proto__ === Array)
|
||||||
return value
|
return value
|
||||||
else
|
else
|
||||||
throw new TypeError(`Exportation error: ${value} not a list.`)
|
throw new TypeError(`Exportation error: ${value} not a list.`)
|
||||||
|
@ -201,10 +200,10 @@ export class List extends PropertyType {
|
||||||
|
|
||||||
|
|
||||||
export class Dictionary extends PropertyType {
|
export class Dictionary extends PropertyType {
|
||||||
constructor(type, keyType = "string", format = /^.+$/, keyFormat = /^.+$/, preKeyLabel = "", postKeyLabel = ": ", forbidAdding = false) {
|
constructor(type, keyType = 'string', format = /^.+$/, keyFormat = /^.+$/, preKeyLabel = '', postKeyLabel = ': ', forbidAdding = false) {
|
||||||
super()
|
super()
|
||||||
// type & keyType can be string, int and double.
|
// type & keyType can be string, int and double.
|
||||||
this.type = "Dict"
|
this.type = 'Dict'
|
||||||
this.valueType = type
|
this.valueType = type
|
||||||
this.keyType = keyType
|
this.keyType = keyType
|
||||||
this.format = format
|
this.format = format
|
||||||
|
@ -220,10 +219,10 @@ export class Dictionary extends PropertyType {
|
||||||
|
|
||||||
parse(value) {
|
parse(value) {
|
||||||
let result = NONE
|
let result = NONE
|
||||||
if(typeof value == "object" && value.__proto__ !== Array) {
|
if(typeof value == 'object' && value.__proto__ !== Array) {
|
||||||
let dict = []
|
let dict = []
|
||||||
for(let [k, v] of Object.entries(value)) {
|
for(let [k, v] of Object.entries(value)) {
|
||||||
if(this.format.test(v) && this.keyFormat.test(k)) {
|
if (this.format.test(v) && this.keyFormat.test(k)) {
|
||||||
k = stringValuesValidators[this.keyType][0](k)
|
k = stringValuesValidators[this.keyType][0](k)
|
||||||
v = stringValuesValidators[this.valueType][0](v)
|
v = stringValuesValidators[this.valueType][0](v)
|
||||||
if(stringValuesValidators[this.keyType][1](k))
|
if(stringValuesValidators[this.keyType][1](k))
|
||||||
|
@ -238,7 +237,7 @@ export class Dictionary extends PropertyType {
|
||||||
}
|
}
|
||||||
|
|
||||||
export(value) {
|
export(value) {
|
||||||
if(typeof value == "object" && value.__proto__ !== Array)
|
if(typeof value == 'object' && value.__proto__ !== Array)
|
||||||
return value
|
return value
|
||||||
else
|
else
|
||||||
throw new TypeError(`Exportation error: ${value} not a dictionary.`)
|
throw new TypeError(`Exportation error: ${value} not a dictionary.`)
|
||||||
|
@ -248,51 +247,51 @@ export class Dictionary extends PropertyType {
|
||||||
// Common parameters for Enums
|
// Common parameters for Enums
|
||||||
|
|
||||||
Enum.Position = new Enum(
|
Enum.Position = new Enum(
|
||||||
QT_TRANSLATE_NOOP("parameters", "above"),
|
QT_TRANSLATE_NOOP('parameters', 'above'),
|
||||||
QT_TRANSLATE_NOOP("parameters", "below"),
|
QT_TRANSLATE_NOOP('parameters', 'below'),
|
||||||
QT_TRANSLATE_NOOP("parameters", "left"),
|
QT_TRANSLATE_NOOP('parameters', 'left'),
|
||||||
QT_TRANSLATE_NOOP("parameters", "right"),
|
QT_TRANSLATE_NOOP('parameters', 'right'),
|
||||||
QT_TRANSLATE_NOOP("parameters", "above-left"),
|
QT_TRANSLATE_NOOP('parameters', 'above-left'),
|
||||||
QT_TRANSLATE_NOOP("parameters", "above-right"),
|
QT_TRANSLATE_NOOP('parameters', 'above-right'),
|
||||||
QT_TRANSLATE_NOOP("parameters", "below-left"),
|
QT_TRANSLATE_NOOP('parameters', 'below-left'),
|
||||||
QT_TRANSLATE_NOOP("parameters", "below-right")
|
QT_TRANSLATE_NOOP('parameters', 'below-right')
|
||||||
)
|
)
|
||||||
Enum.Position.legacyValues = {
|
Enum.Position.legacyValues = {
|
||||||
"top": "above",
|
'top': 'above',
|
||||||
"bottom": "below",
|
'bottom': 'below',
|
||||||
"top-left": "above-left",
|
'top-left': 'above-left',
|
||||||
"top-right": "above-right",
|
'top-right': 'above-right',
|
||||||
"bottom-left": "below-left",
|
'bottom-left': 'below-left',
|
||||||
"bottom-right": "below-right"
|
'bottom-right': 'below-right',
|
||||||
}
|
}
|
||||||
|
|
||||||
Enum.Positioning = new Enum(
|
Enum.Positioning = new Enum(
|
||||||
QT_TRANSLATE_NOOP("parameters", "center"),
|
QT_TRANSLATE_NOOP('parameters', 'center'),
|
||||||
QT_TRANSLATE_NOOP("parameters", "top"),
|
QT_TRANSLATE_NOOP('parameters', 'top'),
|
||||||
QT_TRANSLATE_NOOP("parameters", "bottom"),
|
QT_TRANSLATE_NOOP('parameters', 'bottom'),
|
||||||
QT_TRANSLATE_NOOP("parameters", "left"),
|
QT_TRANSLATE_NOOP('parameters', 'left'),
|
||||||
QT_TRANSLATE_NOOP("parameters", "right"),
|
QT_TRANSLATE_NOOP('parameters', 'right'),
|
||||||
QT_TRANSLATE_NOOP("parameters", "top-left"),
|
QT_TRANSLATE_NOOP('parameters', 'top-left'),
|
||||||
QT_TRANSLATE_NOOP("parameters", "top-right"),
|
QT_TRANSLATE_NOOP('parameters', 'top-right'),
|
||||||
QT_TRANSLATE_NOOP("parameters", "bottom-left"),
|
QT_TRANSLATE_NOOP('parameters', 'bottom-left'),
|
||||||
QT_TRANSLATE_NOOP("parameters", "bottom-right")
|
QT_TRANSLATE_NOOP('parameters', 'bottom-right')
|
||||||
)
|
)
|
||||||
|
|
||||||
Enum.FunctionDisplayType = new Enum(
|
Enum.FunctionDisplayType = new Enum(
|
||||||
QT_TRANSLATE_NOOP("parameters", "application"),
|
QT_TRANSLATE_NOOP('parameters', 'application'),
|
||||||
QT_TRANSLATE_NOOP("parameters", "function")
|
QT_TRANSLATE_NOOP('parameters', 'function')
|
||||||
)
|
)
|
||||||
|
|
||||||
Enum.BodePass = new Enum(
|
Enum.BodePass = new Enum(
|
||||||
QT_TRANSLATE_NOOP("parameters", "high"),
|
QT_TRANSLATE_NOOP('parameters', 'high'),
|
||||||
QT_TRANSLATE_NOOP("parameters", "low")
|
QT_TRANSLATE_NOOP('parameters', 'low')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
Enum.XCursorValuePosition = new Enum(
|
Enum.XCursorValuePosition = new Enum(
|
||||||
QT_TRANSLATE_NOOP("parameters", "Next to target"),
|
QT_TRANSLATE_NOOP('parameters', 'Next to target'),
|
||||||
QT_TRANSLATE_NOOP("parameters", "With label"),
|
QT_TRANSLATE_NOOP('parameters', 'With label'),
|
||||||
QT_TRANSLATE_NOOP("parameters", "Hidden")
|
QT_TRANSLATE_NOOP('parameters', 'Hidden')
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -304,23 +303,23 @@ Enum.XCursorValuePosition = new Enum(
|
||||||
export function ensureTypeSafety(propertyType, value) {
|
export function ensureTypeSafety(propertyType, value) {
|
||||||
let result
|
let result
|
||||||
let error = false
|
let error = false
|
||||||
if(typeof propertyType == "string")
|
if(typeof propertyType == 'string')
|
||||||
switch(propertyType) {
|
switch(propertyType) {
|
||||||
case "string":
|
case 'string':
|
||||||
result = value
|
result = value
|
||||||
error = typeof value !== "string"
|
error = typeof value !== 'string'
|
||||||
break
|
break
|
||||||
case "number":
|
case 'number':
|
||||||
result = parseFloat(value)
|
result = parseFloat(value)
|
||||||
error = isNaN(result)
|
error = isNaN(result)
|
||||||
break
|
break
|
||||||
case "boolean":
|
case 'boolean':
|
||||||
result = value
|
result = value
|
||||||
error = value !== true && value !== false
|
error = value !== true && value !== false
|
||||||
break
|
break
|
||||||
case "Domain":
|
case 'Domain':
|
||||||
try {
|
try {
|
||||||
error = typeof value !== "string"
|
error = typeof value !== 'string'
|
||||||
if(!error)
|
if(!error)
|
||||||
result = parseDomain(value)
|
result = parseDomain(value)
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
|
@ -354,18 +353,18 @@ export function ensureTypeSafety(propertyType, value) {
|
||||||
*/
|
*/
|
||||||
export function serializesByPropertyType(propertyType, value) {
|
export function serializesByPropertyType(propertyType, value) {
|
||||||
let result
|
let result
|
||||||
if(typeof propertyType == "string")
|
if(typeof propertyType == 'string')
|
||||||
switch(propertyType) {
|
switch(propertyType) {
|
||||||
case "string":
|
case 'string':
|
||||||
result = value.toString()
|
result = value.toString()
|
||||||
break
|
break
|
||||||
case "number":
|
case 'number':
|
||||||
result = parseFloat(value)
|
result = parseFloat(value)
|
||||||
break
|
break
|
||||||
case "boolean":
|
case 'boolean':
|
||||||
result = value === true
|
result = value === true
|
||||||
break
|
break
|
||||||
case "Domain":
|
case 'Domain':
|
||||||
if(value instanceof Domain)
|
if(value instanceof Domain)
|
||||||
result = value.toString()
|
result = value.toString()
|
||||||
else
|
else
|
||||||
|
|
|
@ -19,28 +19,28 @@
|
||||||
|
|
||||||
export default class InputExpression {
|
export default class InputExpression {
|
||||||
constructor(expression) {
|
constructor(expression) {
|
||||||
this.position = 0
|
this.position = 0;
|
||||||
this.input = expression
|
this.input = expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
next() {
|
next() {
|
||||||
return this.input[this.position++]
|
return this.input[this.position++];
|
||||||
}
|
}
|
||||||
|
|
||||||
peek() {
|
peek() {
|
||||||
return this.input[this.position]
|
return this.input[this.position];
|
||||||
}
|
}
|
||||||
|
|
||||||
skip(char) {
|
skip(char) {
|
||||||
if(!this.atEnd() && this.peek() === char) {
|
if(!this.atEnd() && this.peek() === char) {
|
||||||
this.position++
|
this.position++;
|
||||||
} else {
|
} else {
|
||||||
this.raise("Unexpected character " + this.peek() + ". Expected character " + char)
|
this.raise("Unexpected character " + this.peek() + ". Expected character " + char);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
atEnd() {
|
atEnd() {
|
||||||
return this.position >= this.input.length
|
return this.position >= this.input.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
raise(message) {
|
raise(message) {
|
||||||
|
|
|
@ -25,8 +25,8 @@ export const CONSTANTS = {
|
||||||
"infinity": Infinity,
|
"infinity": Infinity,
|
||||||
"∞": Infinity,
|
"∞": Infinity,
|
||||||
"e": Math.E
|
"e": Math.E
|
||||||
}
|
};
|
||||||
export const CONSTANTS_LIST = Object.keys(CONSTANTS)
|
export const CONSTANTS_LIST = Object.keys(CONSTANTS);
|
||||||
|
|
||||||
export const FUNCTIONS = {
|
export const FUNCTIONS = {
|
||||||
// The functions commented are the one either not implemented
|
// The functions commented are the one either not implemented
|
||||||
|
@ -36,66 +36,66 @@ export const FUNCTIONS = {
|
||||||
//'-': (x) => -x,
|
//'-': (x) => -x,
|
||||||
//'!'
|
//'!'
|
||||||
// Other operations
|
// Other operations
|
||||||
"length": (s) => Array.isArray(s) ? s.length : String(s).length,
|
'length': (s) => Array.isArray(s) ? s.length : String(s).length,
|
||||||
// Boolean functions
|
// Boolean functions
|
||||||
"not": (x) => !x,
|
'not': (x) => !x,
|
||||||
// Math functions
|
// Math functions
|
||||||
"abs": Math.abs,
|
'abs': Math.abs,
|
||||||
"acos": Math.acos,
|
'acos': Math.acos,
|
||||||
"acosh": Math.acosh,
|
'acosh': Math.acosh,
|
||||||
"asin": Math.asin,
|
'asin': Math.asin,
|
||||||
"asinh": Math.asinh,
|
'asinh': Math.asinh,
|
||||||
"atan": Math.atan,
|
'atan': Math.atan,
|
||||||
"atan2": Math.atan2,
|
'atan2': Math.atan2,
|
||||||
"atanh": Math.atanh,
|
'atanh': Math.atanh,
|
||||||
"cbrt": Math.cbrt,
|
'cbrt': Math.cbrt,
|
||||||
"ceil": Math.ceil,
|
'ceil': Math.ceil,
|
||||||
//'clz32': Math.clz32,
|
//'clz32': Math.clz32,
|
||||||
"cos": Math.cos,
|
'cos': Math.cos,
|
||||||
"cosh": Math.cosh,
|
'cosh': Math.cosh,
|
||||||
"exp": Math.exp,
|
'exp': Math.exp,
|
||||||
"expm1": Math.expm1,
|
'expm1': Math.expm1,
|
||||||
"floor": Math.floor,
|
'floor': Math.floor,
|
||||||
//'fround': Math.fround,
|
//'fround': Math.fround,
|
||||||
"hypot": Math.hypot,
|
'hypot': Math.hypot,
|
||||||
//'imul': Math.imul,
|
//'imul': Math.imul,
|
||||||
"lg": Math.log10,
|
'lg': Math.log10,
|
||||||
"ln": Math.log,
|
'ln': Math.log,
|
||||||
"log": Math.log,
|
'log': Math.log,
|
||||||
"log10": Math.log10,
|
'log10': Math.log10,
|
||||||
"log1p": Math.log1p,
|
'log1p': Math.log1p,
|
||||||
"log2": Math.log2,
|
'log2': Math.log2,
|
||||||
"max": Math.max,
|
'max': Math.max,
|
||||||
"min": Math.min,
|
'min': Math.min,
|
||||||
"pow": Math.log2,
|
'pow': Math.log2,
|
||||||
"random": Math.random,
|
'random': Math.random,
|
||||||
"round": Math.round,
|
'round': Math.round,
|
||||||
"sign": Math.sign,
|
'sign': Math.sign,
|
||||||
"sin": Math.sin,
|
'sin': Math.sin,
|
||||||
"sinh": Math.sinh,
|
'sinh': Math.sinh,
|
||||||
"sqrt": Math.sqrt,
|
'sqrt': Math.sqrt,
|
||||||
"tan": Math.tan,
|
'tan': Math.tan,
|
||||||
"tanh": Math.tanh,
|
'tanh': Math.tanh,
|
||||||
"trunc": Math.trunc,
|
'trunc': Math.trunc,
|
||||||
// Functions in expr-eval, ported here.
|
// Functions in expr-eval, ported here.
|
||||||
"fac": Polyfill.factorial,
|
'fac': Polyfill.factorial,
|
||||||
"gamma": Polyfill.gamma,
|
'gamma': Polyfill.gamma,
|
||||||
"Γ": Polyfill.gamma,
|
'Γ': Polyfill.gamma,
|
||||||
"roundTo": (x, exp) => Number(x).toFixed(exp),
|
'roundTo': (x, exp) => Number(x).toFixed(exp),
|
||||||
// 'map': Polyfill.arrayMap,
|
// 'map': Polyfill.arrayMap,
|
||||||
// 'fold': Polyfill.arrayFold,
|
// 'fold': Polyfill.arrayFold,
|
||||||
// 'filter': Polyfill.arrayFilter,
|
// 'filter': Polyfill.arrayFilter,
|
||||||
// 'indexOf': Polyfill.indexOf,
|
// 'indexOf': Polyfill.indexOf,
|
||||||
// 'join': Polyfill.arrayJoin,
|
// 'join': Polyfill.arrayJoin,
|
||||||
// Integral & derivative (only here for autocomplete).
|
// Integral & derivative (only here for autocomplete).
|
||||||
"integral": () => 0, // TODO: Implement
|
'integral': () => 0, // TODO: Implement
|
||||||
"derivative": () => 0
|
'derivative': () => 0,
|
||||||
}
|
}
|
||||||
export const FUNCTIONS_LIST = Object.keys(FUNCTIONS)
|
export const FUNCTIONS_LIST = Object.keys(FUNCTIONS);
|
||||||
|
|
||||||
export class P {
|
export class P {
|
||||||
// Parameter class.
|
// Parameter class.
|
||||||
constructor(type, name = "", optional = false, multipleAllowed = false) {
|
constructor(type, name = '', optional = false, multipleAllowed = false) {
|
||||||
this.name = name
|
this.name = name
|
||||||
this.type = type
|
this.type = type
|
||||||
this.optional = optional
|
this.optional = optional
|
||||||
|
@ -104,10 +104,10 @@ export class P {
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
let base_string = this.type
|
let base_string = this.type
|
||||||
if(this.name !== "")
|
if(this.name !== '')
|
||||||
base_string = `${this.name}: ${base_string}`
|
base_string = `${this.name}: ${base_string}`
|
||||||
if(this.multipleAllowed)
|
if(this.multipleAllowed)
|
||||||
base_string += "..."
|
base_string += '...'
|
||||||
if(!this.optional)
|
if(!this.optional)
|
||||||
base_string = `<${base_string}>`
|
base_string = `<${base_string}>`
|
||||||
else
|
else
|
||||||
|
@ -116,59 +116,59 @@ export class P {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export let string = new P("string")
|
export let string = new P('string')
|
||||||
export let bool = new P("bool")
|
export let bool = new P('bool')
|
||||||
export let number = new P("number")
|
export let number = new P('number')
|
||||||
export let array = new P("array")
|
export let array = new P('array')
|
||||||
|
|
||||||
export const FUNCTIONS_USAGE = {
|
export const FUNCTIONS_USAGE = {
|
||||||
"length": [string],
|
'length': [string],
|
||||||
"not": [bool],
|
'not': [bool],
|
||||||
// Math functions
|
// Math functions
|
||||||
"abs": [number],
|
'abs': [number],
|
||||||
"acos": [number],
|
'acos': [number],
|
||||||
"acosh": [number],
|
'acosh': [number],
|
||||||
"asin": [number],
|
'asin': [number],
|
||||||
"asinh": [number],
|
'asinh': [number],
|
||||||
"atan": [number],
|
'atan': [number],
|
||||||
"atan2": [number],
|
'atan2': [number],
|
||||||
"atanh": [number],
|
'atanh': [number],
|
||||||
"cbrt": [number],
|
'cbrt': [number],
|
||||||
"ceil": [number],
|
'ceil': [number],
|
||||||
//'clz32': [number],
|
//'clz32': [number],
|
||||||
"cos": [number],
|
'cos': [number],
|
||||||
"cosh": [number],
|
'cosh': [number],
|
||||||
"exp": [number],
|
'exp': [number],
|
||||||
"expm1": [number],
|
'expm1': [number],
|
||||||
"floor": [number],
|
'floor': [number],
|
||||||
//'fround': [number],
|
//'fround': [number],
|
||||||
"hypot": [number],
|
'hypot': [number],
|
||||||
//'imul': [number],
|
//'imul': [number],
|
||||||
"lg": [number],
|
'lg': [number],
|
||||||
"ln": [number],
|
'ln': [number],
|
||||||
"log": [number],
|
'log': [number],
|
||||||
"log10": [number],
|
'log10': [number],
|
||||||
"log1p": [number],
|
'log1p': [number],
|
||||||
"log2": [number],
|
'log2': [number],
|
||||||
"max": [number, number, new P("numbers", "", true, true)],
|
'max': [number, number, new P('numbers', '', true, true)],
|
||||||
"min": [number, number, new P("numbers", "", true, true)],
|
'min': [number, number, new P('numbers', '', true, true)],
|
||||||
"pow": [number, new P("number", "exp")],
|
'pow': [number, new P('number', 'exp')],
|
||||||
"random": [number, number],
|
'random': [number, number],
|
||||||
"round": [number],
|
'round': [number],
|
||||||
"sign": [number],
|
'sign': [number],
|
||||||
"sin": [number],
|
'sin': [number],
|
||||||
"sinh": [number],
|
'sinh': [number],
|
||||||
"sqrt": [number],
|
'sqrt': [number],
|
||||||
"tan": [number],
|
'tan': [number],
|
||||||
"tanh": [number],
|
'tanh': [number],
|
||||||
"trunc": [number],
|
'trunc': [number],
|
||||||
// Functions in expr-eval, ported here.
|
// Functions in expr-eval, ported here.
|
||||||
"fac": [number],
|
'fac': [number],
|
||||||
"gamma": [number],
|
'gamma': [number],
|
||||||
"Γ": [number],
|
'Γ': [number],
|
||||||
"roundTo": [number, new P("number")],
|
'roundTo': [number, new P('number')],
|
||||||
// Function manipulation
|
// Function manipulation
|
||||||
"derivative": [new P("f"), new P("string", "var", true), number],
|
'derivative': [new P('f'), new P('string', 'var', true), number],
|
||||||
"integral": [new P("from"), new P("to"), new P("f"), new P("string", "var", true)]
|
'integral': [new P('from'), new P('to'), new P('f'), new P('string', 'var', true)],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,9 @@
|
||||||
import * as Reference from "./reference.mjs"
|
import * as Reference from "./reference.mjs"
|
||||||
|
|
||||||
const WHITESPACES = " \t\n\r"
|
const WHITESPACES = " \t\n\r"
|
||||||
const STRING_LIMITERS = "\"'`"
|
const STRING_LIMITERS = '"\'`';
|
||||||
const OPERATORS = "+-*/^%?:=!><"
|
const OPERATORS = "+-*/^%?:=!><";
|
||||||
const PUNCTUATION = "()[]{},."
|
const PUNCTUATION = "()[]{},.";
|
||||||
const NUMBER_CHARS = "0123456789"
|
const NUMBER_CHARS = "0123456789"
|
||||||
const IDENTIFIER_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789_₀₁₂₃₄₅₆₇₈₉αβγδεζηθκλμξρςστφχψωₐₑₒₓₔₕₖₗₘₙₚₛₜ"
|
const IDENTIFIER_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789_₀₁₂₃₄₅₆₇₈₉αβγδεζηθκλμξρςστφχψωₐₑₒₓₔₕₖₗₘₙₚₛₜ"
|
||||||
|
|
||||||
|
@ -41,8 +41,8 @@ export const TokenType = {
|
||||||
|
|
||||||
export class Token {
|
export class Token {
|
||||||
constructor(type, value, startPosition) {
|
constructor(type, value, startPosition) {
|
||||||
this.type = type
|
this.type = type;
|
||||||
this.value = value
|
this.value = value;
|
||||||
this.startPosition = startPosition
|
this.startPosition = startPosition
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,38 +55,38 @@ export class ExpressionTokenizer {
|
||||||
* @param {boolean} errorOnUnknown
|
* @param {boolean} errorOnUnknown
|
||||||
*/
|
*/
|
||||||
constructor(input, tokenizeWhitespaces = false, errorOnUnknown = true) {
|
constructor(input, tokenizeWhitespaces = false, errorOnUnknown = true) {
|
||||||
this.input = input
|
this.input = input;
|
||||||
this.currentToken = null
|
this.currentToken = null;
|
||||||
this.tokenizeWhitespaces = tokenizeWhitespaces
|
this.tokenizeWhitespaces = tokenizeWhitespaces
|
||||||
this.errorOnUnknown = errorOnUnknown
|
this.errorOnUnknown = errorOnUnknown
|
||||||
}
|
}
|
||||||
|
|
||||||
skipWhitespaces() {
|
skipWhitespaces() {
|
||||||
while(!this.input.atEnd() && WHITESPACES.includes(this.input.peek()))
|
while(!this.input.atEnd() && WHITESPACES.includes(this.input.peek()))
|
||||||
this.input.next()
|
this.input.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
readWhitespaces() {
|
readWhitespaces() {
|
||||||
let included = ""
|
let included = "";
|
||||||
while(!this.input.atEnd() && WHITESPACES.includes(this.input.peek())) {
|
while(!this.input.atEnd() && WHITESPACES.includes(this.input.peek())) {
|
||||||
included += this.input.next()
|
included += this.input.next();
|
||||||
}
|
}
|
||||||
return new Token(TokenType.WHITESPACE, included, this.input.position - included.length)
|
return new Token(TokenType.WHITESPACE, included, this.input.position-included.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
readString() {
|
readString() {
|
||||||
let delimitation = this.input.peek()
|
let delimitation = this.input.peek();
|
||||||
if(STRING_LIMITERS.includes(delimitation)) {
|
if(STRING_LIMITERS.includes(delimitation)) {
|
||||||
this.input.skip(delimitation)
|
this.input.skip(delimitation)
|
||||||
let included = ""
|
let included = "";
|
||||||
let justEscaped = false
|
let justEscaped = false;
|
||||||
while(!this.input.atEnd() && (!STRING_LIMITERS.includes(this.input.peek()) || justEscaped)) {
|
while(!this.input.atEnd() && (!STRING_LIMITERS.includes(this.input.peek()) || justEscaped)) {
|
||||||
justEscaped = this.input.peek() === "\\"
|
justEscaped = this.input.peek() === "\\"
|
||||||
if(!justEscaped)
|
if(!justEscaped)
|
||||||
included += this.input.next()
|
included += this.input.next();
|
||||||
}
|
}
|
||||||
this.input.skip(delimitation)
|
this.input.skip(delimitation)
|
||||||
let token = new Token(TokenType.STRING, included, this.input.position - included.length)
|
let token = new Token(TokenType.STRING, included, this.input.position-included.length)
|
||||||
token.limitator = delimitation
|
token.limitator = delimitation
|
||||||
return token
|
return token
|
||||||
} else {
|
} else {
|
||||||
|
@ -95,80 +95,80 @@ export class ExpressionTokenizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
readNumber() {
|
readNumber() {
|
||||||
let included = ""
|
let included = "";
|
||||||
let hasDot = false
|
let hasDot = false;
|
||||||
while(!this.input.atEnd() && (NUMBER_CHARS.includes(this.input.peek()) || this.input.peek() === ".")) {
|
while(!this.input.atEnd() && (NUMBER_CHARS.includes(this.input.peek()) || this.input.peek() === '.')) {
|
||||||
if(this.input.peek() === ".") {
|
if(this.input.peek() === ".") {
|
||||||
if(hasDot) this.input.raise("Unexpected '.'. Expected digit")
|
if(hasDot) this.input.raise("Unexpected '.'. Expected digit")
|
||||||
hasDot = true
|
hasDot = true;
|
||||||
}
|
}
|
||||||
included += this.input.next()
|
included += this.input.next();
|
||||||
}
|
}
|
||||||
return new Token(TokenType.NUMBER, included, this.input.position - included.length)
|
return new Token(TokenType.NUMBER, included, this.input.position-included.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
readOperator() {
|
readOperator() {
|
||||||
let included = ""
|
let included = "";
|
||||||
while(!this.input.atEnd() && OPERATORS.includes(this.input.peek())) {
|
while(!this.input.atEnd() && OPERATORS.includes(this.input.peek())) {
|
||||||
included += this.input.next()
|
included += this.input.next();
|
||||||
}
|
}
|
||||||
return new Token(TokenType.OPERATOR, included, this.input.position - included.length)
|
return new Token(TokenType.OPERATOR, included, this.input.position-included.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
readIdentifier() {
|
readIdentifier() {
|
||||||
let identifier = ""
|
let identifier = "";
|
||||||
while(!this.input.atEnd() && IDENTIFIER_CHARS.includes(this.input.peek().toLowerCase())) {
|
while(!this.input.atEnd() && IDENTIFIER_CHARS.includes(this.input.peek().toLowerCase())) {
|
||||||
identifier += this.input.next()
|
identifier += this.input.next();
|
||||||
}
|
}
|
||||||
if(Reference.CONSTANTS_LIST.includes(identifier.toLowerCase())) {
|
if(Reference.CONSTANTS_LIST.includes(identifier.toLowerCase())) {
|
||||||
return new Token(TokenType.CONSTANT, identifier.toLowerCase(), this.input.position - identifier.length)
|
return new Token(TokenType.CONSTANT, identifier.toLowerCase(), this.input.position-identifier.length)
|
||||||
} else if(Reference.FUNCTIONS_LIST.includes(identifier.toLowerCase())) {
|
} else if(Reference.FUNCTIONS_LIST.includes(identifier.toLowerCase())) {
|
||||||
return new Token(TokenType.FUNCTION, identifier.toLowerCase(), this.input.position - identifier.length)
|
return new Token(TokenType.FUNCTION, identifier.toLowerCase(), this.input.position-identifier.length)
|
||||||
} else {
|
} else {
|
||||||
return new Token(TokenType.VARIABLE, identifier, this.input.position - identifier.length)
|
return new Token(TokenType.VARIABLE, identifier, this.input.position-identifier.length)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readNextToken() {
|
readNextToken() {
|
||||||
if(!this.tokenizeWhitespaces)
|
if(!this.tokenizeWhitespaces)
|
||||||
this.skipWhitespaces()
|
this.skipWhitespaces()
|
||||||
if(this.input.atEnd()) return null
|
if(this.input.atEnd()) return null;
|
||||||
let c = this.input.peek()
|
let c = this.input.peek();
|
||||||
if(this.tokenizeWhitespaces && WHITESPACES.includes(c)) return this.readWhitespaces()
|
if(this.tokenizeWhitespaces && WHITESPACES.includes(c)) return this.readWhitespaces();
|
||||||
if(STRING_LIMITERS.includes(c)) return this.readString()
|
if(STRING_LIMITERS.includes(c)) return this.readString();
|
||||||
if(NUMBER_CHARS.includes(c)) return this.readNumber()
|
if(NUMBER_CHARS.includes(c)) return this.readNumber();
|
||||||
if(IDENTIFIER_CHARS.includes(c.toLowerCase())) return this.readIdentifier()
|
if(IDENTIFIER_CHARS.includes(c.toLowerCase())) return this.readIdentifier();
|
||||||
if(OPERATORS.includes(c)) return this.readOperator()
|
if(OPERATORS.includes(c)) return this.readOperator();
|
||||||
if(Reference.CONSTANTS_LIST.includes(c)) return new Token(TokenType.CONSTANT, this.input.next(), this.input.position - 1)
|
if(Reference.CONSTANTS_LIST.includes(c)) return new Token(TokenType.CONSTANT, this.input.next(), this.input.position-1);
|
||||||
if(PUNCTUATION.includes(c)) return new Token(TokenType.PUNCT, this.input.next(), this.input.position - 1)
|
if(PUNCTUATION.includes(c)) return new Token(TokenType.PUNCT, this.input.next(), this.input.position-1);
|
||||||
if(this.errorOnUnknown)
|
if(this.errorOnUnknown)
|
||||||
this.input.raise("Unknown token character " + c)
|
this.input.raise("Unknown token character " + c)
|
||||||
else
|
else
|
||||||
return new Token(TokenType.UNKNOWN, this.input.next(), this.input.position - 1)
|
return new Token(TokenType.UNKNOWN, this.input.next(), this.input.position-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
peek() {
|
peek() {
|
||||||
if(this.currentToken == null) this.currentToken = this.readNextToken()
|
if(this.currentToken == null) this.currentToken = this.readNextToken();
|
||||||
return this.currentToken
|
return this.currentToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
next() {
|
next() {
|
||||||
let tmp
|
let tmp;
|
||||||
if(this.currentToken == null)
|
if(this.currentToken == null)
|
||||||
tmp = this.readNextToken()
|
tmp = this.readNextToken();
|
||||||
else
|
else
|
||||||
tmp = this.currentToken
|
tmp = this.currentToken;
|
||||||
this.currentToken = null
|
this.currentToken = null;
|
||||||
return tmp
|
return tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
atEnd() {
|
atEnd() {
|
||||||
return this.peek() == null
|
return this.peek() == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
skip(type) {
|
skip(type) {
|
||||||
let next = this.next()
|
let next = this.next();
|
||||||
if(next.type !== type)
|
if(next.type !== type)
|
||||||
this.input.raise("Unexpected token " + next.type.toLowerCase() + " \"" + next.value + "\". Expected " + type.toLowerCase())
|
this.input.raise("Unexpected token " + next.type.toLowerCase() + ' "' + next.value + '". Expected ' + type.toLowerCase());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 { Expression } from "../math/index.mjs"
|
import {Expression} from "../math/index.mjs"
|
||||||
|
|
||||||
class Setting {
|
class Setting {
|
||||||
constructor(type, name, nameInConfig, icon) {
|
constructor(type, name, nameInConfig, icon) {
|
||||||
|
@ -49,7 +49,7 @@ class Setting {
|
||||||
|
|
||||||
export class BoolSetting extends Setting {
|
export class BoolSetting extends Setting {
|
||||||
constructor(name, nameInConfig, icon) {
|
constructor(name, nameInConfig, icon) {
|
||||||
super("bool", name, nameInConfig, icon)
|
super('bool', name, nameInConfig, icon)
|
||||||
}
|
}
|
||||||
|
|
||||||
value() {
|
value() {
|
||||||
|
@ -63,9 +63,9 @@ export class BoolSetting extends Setting {
|
||||||
|
|
||||||
export class NumberSetting extends Setting {
|
export class NumberSetting extends Setting {
|
||||||
constructor(name, nameInConfig, icon, min = -Infinity, max = +Infinity) {
|
constructor(name, nameInConfig, icon, min = -Infinity, max = +Infinity) {
|
||||||
super("number", name, nameInConfig, icon)
|
super('number', name, nameInConfig, icon)
|
||||||
this.min = typeof min == "number" ? () => min : min
|
this.min = typeof min == 'number' ? () => min : min
|
||||||
this.max = typeof max == "number" ? () => max : max
|
this.max = typeof max == 'number' ? () => max : max
|
||||||
}
|
}
|
||||||
|
|
||||||
value() {
|
value() {
|
||||||
|
@ -79,7 +79,7 @@ export class NumberSetting extends Setting {
|
||||||
|
|
||||||
export class EnumIntSetting extends Setting {
|
export class EnumIntSetting extends Setting {
|
||||||
constructor(name, nameInConfig, icon, values = []) {
|
constructor(name, nameInConfig, icon, values = []) {
|
||||||
super("enum", name, nameInConfig, icon)
|
super('enum', name, nameInConfig, icon)
|
||||||
this.values = values
|
this.values = values
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ export class EnumIntSetting extends Setting {
|
||||||
|
|
||||||
export class ExpressionSetting extends Setting {
|
export class ExpressionSetting extends Setting {
|
||||||
constructor(name, nameInConfig, icon, variables = []) {
|
constructor(name, nameInConfig, icon, variables = []) {
|
||||||
super("expression", name, nameInConfig, icon)
|
super('expression', name, nameInConfig, icon)
|
||||||
this.variables = variables
|
this.variables = variables
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,17 +112,17 @@ export class ExpressionSetting extends Setting {
|
||||||
Helper.setSetting(this.nameInConfig, value)
|
Helper.setSetting(this.nameInConfig, value)
|
||||||
else {
|
else {
|
||||||
let undefinedVars = vars.filter(x => !this.variables.includes(x))
|
let undefinedVars = vars.filter(x => !this.variables.includes(x))
|
||||||
let allowed = ""
|
let allowed = ''
|
||||||
if(this.variables.length > 0)
|
if(this.variables.length > 0)
|
||||||
allowed = `Allowed variables: ${this.variables.join(", ")}.`
|
allowed = `Allowed variables: ${this.variables.join(', ')}.`
|
||||||
throw new TypeError(`Cannot use variable(s) ${undefinedVars.join(", or ")} to define ${this.displayName}. ${allowed}`)
|
throw new TypeError(`Cannot use variable(s) ${undefinedVars.join(', or ')} to define ${this.displayName}. ${allowed}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StringSetting extends Setting {
|
export class StringSetting extends Setting {
|
||||||
constructor(name, nameInConfig, icon, defaultValues = []) {
|
constructor(name, nameInConfig, icon, defaultValues = []) {
|
||||||
super("string", name, nameInConfig, icon)
|
super('string', name, nameInConfig, icon)
|
||||||
this.defaultValues = defaultValues
|
this.defaultValues = defaultValues
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 { BoolSetting, ExpressionSetting, NumberSetting, StringSetting } from "./common.mjs"
|
import {BoolSetting, ExpressionSetting, NumberSetting, StringSetting} from "./common.mjs"
|
||||||
|
|
||||||
|
|
||||||
const XZOOM = new NumberSetting(
|
const XZOOM = new NumberSetting(
|
||||||
|
@ -49,13 +49,13 @@ const YMAX = new NumberSetting(
|
||||||
const XAXISSTEP = new ExpressionSetting(
|
const XAXISSTEP = new ExpressionSetting(
|
||||||
qsTranslate("Settings", "X Axis Step"),
|
qsTranslate("Settings", "X Axis Step"),
|
||||||
"default_graph.xaxisstep",
|
"default_graph.xaxisstep",
|
||||||
"xaxisstep"
|
"xaxisstep",
|
||||||
)
|
)
|
||||||
|
|
||||||
const YAXISSTEP = new ExpressionSetting(
|
const YAXISSTEP = new ExpressionSetting(
|
||||||
qsTranslate("Settings", "Y Axis Step"),
|
qsTranslate("Settings", "Y Axis Step"),
|
||||||
"default_graph.yaxisstep",
|
"default_graph.yaxisstep",
|
||||||
"yaxisstep"
|
"yaxisstep",
|
||||||
)
|
)
|
||||||
|
|
||||||
const LINE_WIDTH = new NumberSetting(
|
const LINE_WIDTH = new NumberSetting(
|
||||||
|
@ -72,33 +72,33 @@ const TEXT_SIZE = new NumberSetting(
|
||||||
)
|
)
|
||||||
|
|
||||||
const X_LABEL = new StringSetting(
|
const X_LABEL = new StringSetting(
|
||||||
qsTranslate("Settings", "X Label"),
|
qsTranslate("Settings", 'X Label'),
|
||||||
"default_graph.xlabel",
|
"default_graph.xlabel",
|
||||||
"xlabel",
|
"xlabel",
|
||||||
["", "x", "ω (rad/s)"]
|
["", "x", "ω (rad/s)"]
|
||||||
)
|
)
|
||||||
|
|
||||||
const Y_LABEL = new StringSetting(
|
const Y_LABEL = new StringSetting(
|
||||||
qsTranslate("Settings", "Y Label"),
|
qsTranslate("Settings", 'Y Label'),
|
||||||
"default_graph.ylabel",
|
"default_graph.ylabel",
|
||||||
"xlabel",
|
"xlabel",
|
||||||
["", "y", "G (dB)", "φ (°)", "φ (deg)", "φ (rad)"]
|
["", "y", "G (dB)", "φ (°)", "φ (deg)", "φ (rad)"]
|
||||||
)
|
)
|
||||||
|
|
||||||
const LOG_SCALE_X = new BoolSetting(
|
const LOG_SCALE_X = new BoolSetting(
|
||||||
qsTranslate("Settings", "X Log scale"),
|
qsTranslate("Settings", 'X Log scale'),
|
||||||
"default_graph.logscalex",
|
"default_graph.logscalex",
|
||||||
"logscalex"
|
"logscalex"
|
||||||
)
|
)
|
||||||
|
|
||||||
const SHOW_X_GRAD = new BoolSetting(
|
const SHOW_X_GRAD = new BoolSetting(
|
||||||
qsTranslate("Settings", "Show X graduation"),
|
qsTranslate("Settings", 'Show X graduation'),
|
||||||
"default_graph.showxgrad",
|
"default_graph.showxgrad",
|
||||||
"showxgrad"
|
"showxgrad"
|
||||||
)
|
)
|
||||||
|
|
||||||
const SHOW_Y_GRAD = new BoolSetting(
|
const SHOW_Y_GRAD = new BoolSetting(
|
||||||
qsTranslate("Settings", "Show Y graduation"),
|
qsTranslate("Settings", 'Show Y graduation'),
|
||||||
"default_graph.showygrad",
|
"default_graph.showygrad",
|
||||||
"showygrad"
|
"showygrad"
|
||||||
)
|
)
|
||||||
|
|
|
@ -16,30 +16,30 @@
|
||||||
* 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 { BoolSetting, EnumIntSetting } from "./common.mjs"
|
import {BoolSetting, EnumIntSetting} from "./common.mjs"
|
||||||
|
|
||||||
const AUTOCLOSE_FORMULA = new BoolSetting(
|
const AUTOCLOSE_FORMULA = new BoolSetting(
|
||||||
qsTranslate("expression", "Automatically close parenthesises and brackets"),
|
qsTranslate("expression", "Automatically close parenthesises and brackets"),
|
||||||
"expression_editor.autoclose",
|
'expression_editor.autoclose',
|
||||||
"text"
|
'text'
|
||||||
)
|
)
|
||||||
|
|
||||||
const ENABLE_SYNTAX_HIGHLIGHTING = new BoolSetting(
|
const ENABLE_SYNTAX_HIGHLIGHTING = new BoolSetting(
|
||||||
qsTranslate("expression", "Enable syntax highlighting"),
|
qsTranslate("expression", "Enable syntax highlighting"),
|
||||||
"expression_editor.colorize",
|
'expression_editor.colorize',
|
||||||
"appearance"
|
'appearance'
|
||||||
)
|
)
|
||||||
|
|
||||||
const ENABLE_AUTOCOMPLETE = new BoolSetting(
|
const ENABLE_AUTOCOMPLETE = new BoolSetting(
|
||||||
qsTranslate("expression", "Enable autocompletion"),
|
qsTranslate("expression", "Enable autocompletion"),
|
||||||
"autocompletion.enabled",
|
'autocompletion.enabled',
|
||||||
"label"
|
'label'
|
||||||
)
|
)
|
||||||
|
|
||||||
const PICK_COLOR_SCHEME = new EnumIntSetting(
|
const PICK_COLOR_SCHEME = new EnumIntSetting(
|
||||||
qsTranslate("expression", "Color Scheme"),
|
qsTranslate("expression", "Color Scheme"),
|
||||||
"expression_editor.color_scheme",
|
'expression_editor.color_scheme',
|
||||||
"color",
|
'color',
|
||||||
["Breeze Light", "Breeze Dark", "Solarized", "Github Light", "Github Dark", "Nord", "Monokai"]
|
["Breeze Light", "Breeze Dark", "Solarized", "Github Light", "Github Dark", "Nord", "Monokai"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -16,25 +16,25 @@
|
||||||
* 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 { BoolSetting } from "./common.mjs"
|
import {BoolSetting} from "./common.mjs"
|
||||||
import Canvas from "../module/canvas.mjs"
|
import Canvas from "../module/canvas.mjs"
|
||||||
import LatexAPI from "../module/latex.mjs"
|
import LatexAPI from "../module/latex.mjs"
|
||||||
|
|
||||||
const CHECK_FOR_UPDATES = new BoolSetting(
|
const CHECK_FOR_UPDATES = new BoolSetting(
|
||||||
qsTranslate("general", "Check for updates on startup"),
|
qsTranslate("general", "Check for updates on startup"),
|
||||||
"check_for_updates",
|
'check_for_updates',
|
||||||
"update"
|
'update'
|
||||||
)
|
)
|
||||||
|
|
||||||
const RESET_REDO_STACK = new BoolSetting(
|
const RESET_REDO_STACK = new BoolSetting(
|
||||||
qsTranslate("general", "Reset redo stack automaticly"),
|
qsTranslate("general", "Reset redo stack automaticly"),
|
||||||
"reset_redo_stack",
|
'reset_redo_stack',
|
||||||
"timeline"
|
'timeline'
|
||||||
)
|
)
|
||||||
|
|
||||||
class EnableLatex extends BoolSetting {
|
class EnableLatex extends BoolSetting {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(qsTranslate("general", "Enable LaTeX rendering"), "enable_latex", "Expression")
|
super(qsTranslate("general","Enable LaTeX rendering"), 'enable_latex', 'Expression')
|
||||||
}
|
}
|
||||||
|
|
||||||
set(value) {
|
set(value) {
|
||||||
|
|
|
@ -16,25 +16,6 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Add string methods
|
|
||||||
/**
|
|
||||||
* Replaces latin characters with their uppercase versions.
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
String.prototype.toLatinUppercase = String.prototype.toLatinUppercase || function() {
|
|
||||||
return this.replace(/[a-z]/g, function(match) {
|
|
||||||
return match.toUpperCase()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the 'enclosers' of a string (e.g. quotes, parentheses, brackets...)
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
String.prototype.removeEnclosure = function() {
|
|
||||||
return this.substring(1, this.length - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
const powerpos = {
|
const powerpos = {
|
||||||
"-": "⁻",
|
"-": "⁻",
|
||||||
"+": "⁺",
|
"+": "⁺",
|
||||||
|
@ -369,6 +350,10 @@ export function parseName(str, removeUnallowed = true) {
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String.prototype.toLatinUppercase = function() {
|
||||||
|
return this.replace(/[a-z]/g, function(match){return match.toUpperCase()})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms camel case strings to a space separated one.
|
* Transforms camel case strings to a space separated one.
|
||||||
*
|
*
|
||||||
|
|
|
@ -29,7 +29,7 @@ class LOG_COLORS:
|
||||||
GRAY = "\033[90m"
|
GRAY = "\033[90m"
|
||||||
BLUE = "\033[94m"
|
BLUE = "\033[94m"
|
||||||
ORANGE = "\033[38;5;166m"
|
ORANGE = "\033[38;5;166m"
|
||||||
RED = "\033[38;5;204m"
|
RED = "\033[e[38;5;204m"
|
||||||
INVERT = "\033[7m"
|
INVERT = "\033[7m"
|
||||||
RESET_INVERT = "\033[27m"
|
RESET_INVERT = "\033[27m"
|
||||||
RESET = "\033[0m"
|
RESET = "\033[0m"
|
||||||
|
|
138
package-lock.json
generated
138
package-lock.json
generated
|
@ -13,6 +13,7 @@
|
||||||
"@rollup/plugin-babel": "^6.0.4",
|
"@rollup/plugin-babel": "^6.0.4",
|
||||||
"@rollup/plugin-commonjs": "^28.0.0",
|
"@rollup/plugin-commonjs": "^28.0.0",
|
||||||
"@rollup/plugin-node-resolve": "^15.3.0",
|
"@rollup/plugin-node-resolve": "^15.3.0",
|
||||||
|
"@rollup/plugin-terser": "^0.4.4",
|
||||||
"install": "^0.13.0",
|
"install": "^0.13.0",
|
||||||
"rollup": "^4.22.4",
|
"rollup": "^4.22.4",
|
||||||
"rollup-plugin-cleanup": "^3.2.1"
|
"rollup-plugin-cleanup": "^3.2.1"
|
||||||
|
@ -1723,6 +1724,16 @@
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@jridgewell/source-map": {
|
||||||
|
"version": "0.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
|
||||||
|
"integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/gen-mapping": "^0.3.5",
|
||||||
|
"@jridgewell/trace-mapping": "^0.3.25"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@jridgewell/sourcemap-codec": {
|
"node_modules/@jridgewell/sourcemap-codec": {
|
||||||
"version": "1.5.0",
|
"version": "1.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
|
||||||
|
@ -1827,6 +1838,28 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@rollup/plugin-terser": {
|
||||||
|
"version": "0.4.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz",
|
||||||
|
"integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"serialize-javascript": "^6.0.1",
|
||||||
|
"smob": "^1.0.0",
|
||||||
|
"terser": "^5.17.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"rollup": "^2.0.0||^3.0.0||^4.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"rollup": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rollup/pluginutils": {
|
"node_modules/@rollup/pluginutils": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.2.tgz",
|
||||||
|
@ -2081,6 +2114,18 @@
|
||||||
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
|
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/acorn": {
|
||||||
|
"version": "8.12.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
|
||||||
|
"integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"acorn": "bin/acorn"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ansi-styles": {
|
"node_modules/ansi-styles": {
|
||||||
"version": "3.2.1",
|
"version": "3.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||||
|
@ -2164,6 +2209,12 @@
|
||||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/buffer-from": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001663",
|
"version": "1.0.30001663",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001663.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001663.tgz",
|
||||||
|
@ -2213,6 +2264,12 @@
|
||||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/commander": {
|
||||||
|
"version": "2.20.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||||
|
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/commondir": {
|
"node_modules/commondir": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||||
|
@ -2545,6 +2602,15 @@
|
||||||
"url": "https://github.com/sponsors/jonschlinkert"
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/randombytes": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "^5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/regenerate": {
|
"node_modules/regenerate": {
|
||||||
"version": "1.4.2",
|
"version": "1.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
||||||
|
@ -2704,6 +2770,26 @@
|
||||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
|
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/safe-buffer": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
"version": "6.3.1",
|
"version": "6.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||||
|
@ -2713,6 +2799,15 @@
|
||||||
"semver": "bin/semver.js"
|
"semver": "bin/semver.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/serialize-javascript": {
|
||||||
|
"version": "6.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
|
||||||
|
"integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"randombytes": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/skip-regex": {
|
"node_modules/skip-regex": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/skip-regex/-/skip-regex-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/skip-regex/-/skip-regex-1.0.2.tgz",
|
||||||
|
@ -2722,6 +2817,31 @@
|
||||||
"node": ">=4.2"
|
"node": ">=4.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/smob": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/source-map-support": {
|
||||||
|
"version": "0.5.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
|
||||||
|
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"buffer-from": "^1.0.0",
|
||||||
|
"source-map": "^0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/source-map-support/node_modules/source-map": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||||
|
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/sourcemap-codec": {
|
"node_modules/sourcemap-codec": {
|
||||||
"version": "1.4.8",
|
"version": "1.4.8",
|
||||||
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
|
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
|
||||||
|
@ -2753,6 +2873,24 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/terser": {
|
||||||
|
"version": "5.33.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/terser/-/terser-5.33.0.tgz",
|
||||||
|
"integrity": "sha512-JuPVaB7s1gdFKPKTelwUyRq5Sid2A3Gko2S0PncwdBq7kN9Ti9HPWDQ06MPsEDGsZeVESjKEnyGy68quBk1w6g==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/source-map": "^0.3.3",
|
||||||
|
"acorn": "^8.8.2",
|
||||||
|
"commander": "^2.20.0",
|
||||||
|
"source-map-support": "~0.5.20"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"terser": "bin/terser"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/to-fast-properties": {
|
"node_modules/to-fast-properties": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
"@rollup/plugin-babel": "^6.0.4",
|
"@rollup/plugin-babel": "^6.0.4",
|
||||||
"@rollup/plugin-commonjs": "^28.0.0",
|
"@rollup/plugin-commonjs": "^28.0.0",
|
||||||
"@rollup/plugin-node-resolve": "^15.3.0",
|
"@rollup/plugin-node-resolve": "^15.3.0",
|
||||||
|
"@rollup/plugin-terser": "^0.4.4",
|
||||||
"install": "^0.13.0",
|
"install": "^0.13.0",
|
||||||
"rollup": "^4.22.4",
|
"rollup": "^4.22.4",
|
||||||
"rollup-plugin-cleanup": "^3.2.1"
|
"rollup-plugin-cleanup": "^3.2.1"
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { nodeResolve } from "@rollup/plugin-node-resolve"
|
||||||
import commonjs from "@rollup/plugin-commonjs"
|
import commonjs from "@rollup/plugin-commonjs"
|
||||||
import { babel } from "@rollup/plugin-babel"
|
import { babel } from "@rollup/plugin-babel"
|
||||||
import cleanup from "rollup-plugin-cleanup"
|
import cleanup from "rollup-plugin-cleanup"
|
||||||
|
import terser from "@rollup/plugin-terser"
|
||||||
|
|
||||||
const path = "LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js"
|
const path = "LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js"
|
||||||
|
|
||||||
|
@ -38,6 +39,9 @@ export default {
|
||||||
babel({
|
babel({
|
||||||
babelHelpers: "bundled"
|
babelHelpers: "bundled"
|
||||||
}),
|
}),
|
||||||
|
// terser({
|
||||||
|
// ecma: 2015
|
||||||
|
// })
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue