mirror of
https://github.com/jcf-402/datacharts.git
synced 2026-07-22 06:08:15 +00:00
Added support for independant variable nested
equations
This commit is contained in:
parent
ab696338f4
commit
48ad7dfb9d
4 changed files with 303 additions and 204 deletions
|
|
@ -371,7 +371,7 @@ export function evaluateExpressions(parsedText: parsedText, xRange: [number,numb
|
|||
compiledNested[obj.signature] = parsed // expr is an array
|
||||
}
|
||||
else {
|
||||
compiledNested[obj.signature] = math.compile(expr); // expr is supposed to be a valid equation. We compile it and the compiled object.
|
||||
compiledNested[obj.signature] = (expr); // expr is supposed to be a valid equation.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -398,20 +398,20 @@ export function evaluateExpressions(parsedText: parsedText, xRange: [number,numb
|
|||
if (!isConstant) {
|
||||
scope[variable] = val;
|
||||
}
|
||||
for (let name in compiledNested) {
|
||||
const v = compiledNested[name]; // grabs the expression with "name" signature
|
||||
for (let signature in compiledNested) {
|
||||
const nestedExpr = compiledNested[signature]; // grabs the expression with "name" signature
|
||||
|
||||
if (v === undefined) continue;
|
||||
if (nestedExpr === undefined) continue;
|
||||
|
||||
if (typeof v === "number") {
|
||||
scope[name] = v; // if the expression is a scalar (constant). Simply assign it to the scope with the signature as its variable.
|
||||
if (typeof nestedExpr === "number") {
|
||||
scope[signature] = nestedExpr; // if the expression is a scalar (constant). Simply assign it to the scope with the signature as its variable.
|
||||
}
|
||||
else if (Array.isArray(v)) { // if the expression is a list of values the current equation needs to be evaluated with the current val
|
||||
else if (Array.isArray(nestedExpr)) { // if the expression is a list of values the current equation needs to be evaluated with the current val
|
||||
// at each value in the array.
|
||||
// So if f(x) = x^2 * D -> D: [0.3,0.4,0.5]. We get three plots, one for each D value.
|
||||
// ONLY the equation that contains the nested function needs to be plotted. Other functions work as normal.
|
||||
|
||||
if ( vars.includes(name) ) { // Checks if the signature that matches the list is a variable of the current equation.
|
||||
if ( vars.includes(signature) ) { // Checks if the signature that matches the list is a variable of the current equation.
|
||||
|
||||
// Essentially evaluateExpressions needs to be called for each one. But evaluateExpressions has too much logic not need at this point.
|
||||
// I also need to stop evaluating this equation entirely.
|
||||
|
|
@ -419,12 +419,32 @@ export function evaluateExpressions(parsedText: parsedText, xRange: [number,numb
|
|||
// Then continue to the next equation
|
||||
if (variable === undefined) continue equationLoop;
|
||||
|
||||
results.push(...handleNestedArray(equation,variable,localRange,v,name));
|
||||
results.push(...handleNestedArray(equation.expr,variable,localRange,nestedExpr,signature,scope));
|
||||
continue equationLoop;
|
||||
}
|
||||
}
|
||||
else {
|
||||
scope[name] = v.evaluate(scope); // if the expression is neither a scalar nor an array then its an expression. evaluate it with normal the current val
|
||||
const xranges = parsedText.lineProperties.map(prop => `${prop.signature}.${prop.property}=${prop.value}`).filter(s => s.includes("xrange"));
|
||||
const indexRange = xranges.findIndex(s => s.startsWith(`${signature}.xrange=`));
|
||||
if (indexRange !== -1){
|
||||
// As of version 1.0.3 I want to support independant variables inside the nested equation.
|
||||
// Re = DV/v
|
||||
// D: 0.3
|
||||
// v: 2T + 10
|
||||
// v.xrange = [100,300,50] Which creates a reynolds plot for each viscosity. Where Velocity is the main variable
|
||||
if (!xranges[indexRange]) continue equationLoop;
|
||||
const nestedLocalRangeString = xranges[indexRange].split("=")[1];
|
||||
if (!nestedLocalRangeString) continue equationLoop;
|
||||
let nestedLocalRange: [number,number,number] = JSON.parse(nestedLocalRangeString);
|
||||
if (variable === undefined) continue equationLoop;
|
||||
results.push(...handleNestedIndependant(equation,variable,localRange,nestedLocalRange,nestedExpr,signature, nestInfo,scope));
|
||||
continue equationLoop;
|
||||
|
||||
|
||||
} else {
|
||||
const compiledNestedExpr = math.compile(nestedExpr);
|
||||
scope[signature] = compiledNestedExpr.evaluate(scope); // if the expression is neither a scalar nor an array then its an expression. evaluate it with normal the current val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -467,18 +487,20 @@ export function getVariable(expr: Equation | NestedEquations | RawExpr) {
|
|||
|
||||
}
|
||||
|
||||
|
||||
function isNumberString(val: string): boolean {
|
||||
return val.trim() !== "" && !isNaN(Number(val));
|
||||
}
|
||||
|
||||
function handleNestedArray(eq: Equation,variable: string, localRange: [number, number, number], v: number[], name: string) {
|
||||
const expr = math.compile(eq.expr);
|
||||
function handleNestedArray(mainExpr: string ,variable: string, localRange: [number, number, number], v: number[], name: string, baseScope: Record<string, string | number>, legendName?: string): PlotData[] {
|
||||
const expr = math.compile(mainExpr);
|
||||
const results: PlotData[] = [];
|
||||
|
||||
for (let i of v) {
|
||||
const mDataPoints: Data[] = []
|
||||
for (let val = localRange[0]; val <= localRange[1]; val += localRange[2]) {
|
||||
const scope = {
|
||||
...baseScope,
|
||||
[variable]: val,
|
||||
[name]: i
|
||||
}
|
||||
|
|
@ -491,11 +513,46 @@ function handleNestedArray(eq: Equation,variable: string, localRange: [number, n
|
|||
}
|
||||
mDataPoints.push({x:val,y});
|
||||
}
|
||||
results.push({signature: `${name}=${i}`, data: mDataPoints});
|
||||
if (!legendName) results.push({signature: `${name}=${i}`, data: mDataPoints});
|
||||
else results.push({signature: `${legendName}=${i}`, data: mDataPoints});
|
||||
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function handleNestedIndependant(eq: Equation, variable: string, localRange: [number,number,number],
|
||||
nestedLocalRange: [number,number,number], nestedExpr: any, signature: string, nestInfo: string[][], baseScope: Record<string, string | number>): PlotData[]{
|
||||
|
||||
const nestedResults: number[] = [];
|
||||
// Get Main Equation Variable --------------------------------------
|
||||
const nestedEquationObject: Equation = {
|
||||
expr: nestedExpr,
|
||||
signature:signature
|
||||
};
|
||||
const vars = getVariable(nestedEquationObject).filter((v) => !builtInConstants.includes(v));
|
||||
// vars can include builtInConstants that need to be filtered out so they arent recognized as the variable.
|
||||
const newVars = vars.flatMap(v => {
|
||||
if (nestInfo[1]?.includes(v)) {
|
||||
return [];
|
||||
}
|
||||
return [v];
|
||||
})
|
||||
// vars also filters out any variabales that are actually nested function signatures. Think if f(x) = G + x -> filters out G as long as G is declared as G: val
|
||||
const nestedVariable = newVars[0];
|
||||
if (!nestedVariable) return [];
|
||||
const nestedEquation = math.compile(nestedExpr);
|
||||
for (let i = nestedLocalRange[0]; i <= nestedLocalRange[1]; i += nestedLocalRange[2]){
|
||||
const nestedScope: Record<string, number | string> = {
|
||||
...baseScope,
|
||||
};
|
||||
nestedScope[nestedVariable] = i;
|
||||
let nestedY = nestedEquation.evaluate(nestedScope);
|
||||
nestedResults.push(nestedY);
|
||||
};
|
||||
return handleNestedArray(eq.expr, variable, localRange, nestedResults, signature, baseScope, nestedVariable);
|
||||
|
||||
}
|
||||
|
||||
function handleDiscontinuities(mDataPoints: Data[], localRange: [number, number, number], y: number) {
|
||||
// ---------------- Discontinuities ---------------------------------------
|
||||
const prev = mDataPoints[mDataPoints.length - 1];
|
||||
|
|
|
|||
194
helpers/svgFormatting.ts
Normal file
194
helpers/svgFormatting.ts
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
import { ChartType} from "chart.js/auto";
|
||||
import {Data} from "./parser";
|
||||
|
||||
export function generateSVG(chartInstance: any, chartType: ChartType) {
|
||||
const width = chartInstance.width;
|
||||
const height = chartInstance.height;
|
||||
|
||||
//const backgroundColor = this.settings.backgroundColor;
|
||||
const xScale = chartInstance.scales.x;
|
||||
const yScale = chartInstance.scales.y;
|
||||
const area = chartInstance.chartArea;
|
||||
const style = getComputedStyle(document.body);
|
||||
const bg = style.getPropertyValue("--background-secondary").trim() || "#ffffff";
|
||||
const text = style.getPropertyValue("--text-normal").trim() || "#000000";
|
||||
const muted = style.getPropertyValue("--text-muted").trim() || "#666666";
|
||||
const border = style.getPropertyValue("--background-modifier-border").trim() || "#d0d0d0";
|
||||
let svg = "";
|
||||
switch(chartType) {
|
||||
case "line": {
|
||||
svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"><rect width="100%" height="100%" fill="${bg}" />`;
|
||||
svg += `
|
||||
<line
|
||||
x1="${area.left}"
|
||||
y1="${area.bottom}"
|
||||
x2="${area.right}"
|
||||
y2="${area.bottom}"
|
||||
stroke="${muted}"
|
||||
stroke-width="1"
|
||||
/>
|
||||
|
||||
<line
|
||||
x1="${area.left}"
|
||||
y1="${area.top}"
|
||||
x2="${area.left}"
|
||||
y2="${area.bottom}"
|
||||
stroke="${muted}"
|
||||
stroke-width="1"
|
||||
/>
|
||||
`;
|
||||
const title = chartInstance.options?.plugins?.title;
|
||||
|
||||
if (title?.display && title.text) {
|
||||
svg += `
|
||||
<text
|
||||
x="${width / 2}"
|
||||
y="20"
|
||||
font-size="16"
|
||||
font-weight="bold"
|
||||
text-anchor="middle"
|
||||
fill="${text}">${title.text}</text>
|
||||
`;
|
||||
}
|
||||
xScale.ticks.forEach((tick: any, i: number) => {
|
||||
const x = xScale.getPixelForTick(i);
|
||||
|
||||
svg += `
|
||||
<line
|
||||
x1="${x}"
|
||||
y1="${area.top}"
|
||||
x2="${x}"
|
||||
y2="${area.bottom}"
|
||||
stroke="${border}"
|
||||
stroke-width="1"
|
||||
/>
|
||||
`;
|
||||
svg += `
|
||||
<line
|
||||
x1="${x}"
|
||||
y1="${area.bottom}"
|
||||
x2="${x}"
|
||||
y2="${area.bottom + 6}"
|
||||
stroke="${border}"
|
||||
stroke-width="1"
|
||||
/>
|
||||
<text
|
||||
x="${x}"
|
||||
y="${area.bottom + 18}"
|
||||
font-size="12"
|
||||
text-anchor="middle"
|
||||
dominant-baseline="middle"
|
||||
fill="${text}">
|
||||
${tick.label}
|
||||
</text>
|
||||
`;
|
||||
|
||||
|
||||
});
|
||||
|
||||
yScale.ticks.forEach((tick: any, i: number) => {
|
||||
const y = yScale.getPixelForTick(i);
|
||||
svg += `
|
||||
<line
|
||||
x1="${area.left}"
|
||||
y1="${y}"
|
||||
x2="${area.right}"
|
||||
y2="${y}"
|
||||
stroke="${border}"
|
||||
stroke-width="1"
|
||||
/>
|
||||
`;
|
||||
svg += `
|
||||
<line
|
||||
x1="${area.left - 6}"
|
||||
y1="${y}"
|
||||
x2="${area.left}"
|
||||
y2="${y}"
|
||||
stroke="${border}"
|
||||
stroke-width="1"
|
||||
|
||||
/>
|
||||
<text
|
||||
x="${area.left - 8}"
|
||||
y="${y}"
|
||||
font-size="12"
|
||||
text-anchor="end"
|
||||
dominant-baseline="middle"
|
||||
fill="${text}">${tick.label}</text>
|
||||
`;
|
||||
});
|
||||
let lx = width - 74;
|
||||
let ly = 25;
|
||||
|
||||
chartInstance.data.datasets.forEach((dataset: any, i: number) => {
|
||||
const y = ly + i*20;
|
||||
const meta = chartInstance.getDatasetMeta(i);
|
||||
const points = meta.data.map((pt: Data) => `${pt.x},${pt.y}`).join(" ");
|
||||
const color = dataset.borderColor || "black";
|
||||
const radius = (dataset.pointRadius && dataset.pointRadius === 0) ? 0 : dataset.pointRadius;
|
||||
//const radius = dataset.pointRadius && dataset.pointRadius > 0 ? dataset.pointRadius : 3;
|
||||
svg += `
|
||||
<rect x="${lx}" y="${y-10}" width="12" height="12" fill="${color}" />
|
||||
<text
|
||||
x="${lx + 18}"
|
||||
y="${y}"
|
||||
font-size="12"
|
||||
dominant-baseline="middle"
|
||||
fill="${text}">${dataset.label || `Series ${i+1}`}</text>
|
||||
|
||||
<polyline
|
||||
fill="none"
|
||||
stroke="${color}"
|
||||
stroke-width="${dataset.borderWidth || 2}"
|
||||
points="${points}"
|
||||
|
||||
/>`;
|
||||
meta.data.forEach((pt: Data) => {
|
||||
svg += `
|
||||
<circle
|
||||
cx="${pt.x}"
|
||||
cy="${pt.y}"
|
||||
r="${radius}"
|
||||
fill="${color}"
|
||||
/>
|
||||
`;
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
const xTitle = chartInstance.options?.scales?.x?.title;
|
||||
|
||||
if (xTitle?.display && xTitle.text) {
|
||||
svg += `
|
||||
<text
|
||||
x="${(area.left + area.right) / 2}"
|
||||
y="${height - 8}"
|
||||
font-size="13"
|
||||
font-weight="bold"
|
||||
text-anchor="middle"
|
||||
fill="${text}">${xTitle.text}</text>
|
||||
`;
|
||||
}
|
||||
|
||||
const yTitle = chartInstance.options?.scales?.y?.title;
|
||||
|
||||
if (yTitle?.display && yTitle.text) {
|
||||
svg += `
|
||||
<text
|
||||
x="16"
|
||||
y="${(area.top + area.bottom) / 2}"
|
||||
font-size="13"
|
||||
font-weight="bold"
|
||||
text-anchor="middle"
|
||||
fill="${text}"
|
||||
transform="rotate(-90 16 ${(area.top + area.bottom) / 2})">
|
||||
${yTitle.text}
|
||||
</text>
|
||||
`;
|
||||
}
|
||||
svg += `</svg>`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return svg
|
||||
}
|
||||
221
src/main.ts
221
src/main.ts
|
|
@ -1,18 +1,19 @@
|
|||
|
||||
import {handleMarkdown, handleGlobalOptions, evaluateExpressions, PlotData, parsedText, handleTableData, Equation, GlobalProperties,Data} from "../helpers/parser"
|
||||
import {handleMarkdown, evaluateExpressions, PlotData, parsedText, handleTableData, Equation, GlobalProperties,Data} from "../helpers/parser"
|
||||
import { createPlot, buildDatasets} from "../helpers/graphs";
|
||||
import {Menu, Notice, Plugin, MarkdownView} from "obsidian";
|
||||
import { apply } from "mathjs";
|
||||
import { setApp } from "../helpers/appContext";
|
||||
import { PlotPluginSettings, DEFAULT_SETTINGS, PlotSettingTab } from "settings";
|
||||
import { ChartOptions, ChartType, Ticks } from "chart.js/auto";
|
||||
import { ChartOptions, ChartType} from "chart.js/auto";
|
||||
import {autocompletion} from "@codemirror/autocomplete";
|
||||
|
||||
import {generateSVG} from "../helpers/svgFormatting";
|
||||
import { PlotCompletionSource } from "../helpers/plotProperties";
|
||||
import { zoom } from "chartjs-plugin-zoom";
|
||||
import { divideScalarDependencies } from "mathjs";
|
||||
import { codePointSize } from "@codemirror/state";
|
||||
import { e } from "mathjs";
|
||||
import { simplify } from "mathjs";
|
||||
|
||||
export default class PlotPlugin extends Plugin {
|
||||
settings!: PlotPluginSettings;
|
||||
|
|
@ -40,6 +41,7 @@ export default class PlotPlugin extends Plugin {
|
|||
const defaultProperties: ChartOptions<ChartType> = getDefaultPlotProperties(this.settings,chartType);
|
||||
const sourcePaths = getSourcePaths(newMarkdown); // Gets any source:: item from path PATHS. Stores them for comparisons later.
|
||||
if (defaultProperties === undefined) return;
|
||||
|
||||
let cachedParsedText = await handleMarkdown(newMarkdown,defaultProperties,chartType); // Evaluates all the markdown in the codeblock and creates a ParsedText type object.
|
||||
let chartInstance: any = undefined;
|
||||
|
||||
|
|
@ -228,7 +230,7 @@ export default class PlotPlugin extends Plugin {
|
|||
.setTitle("Save SVG to Vault")
|
||||
.setIcon("image-file")
|
||||
.onClick( async () => {
|
||||
await this.exportChartSVG(chartInstance)
|
||||
await this.exportChartSVG(chartInstance,chartType)
|
||||
}));
|
||||
|
||||
menu.showAtPosition({x: e.pageX, y: e.pageY});
|
||||
|
|
@ -251,193 +253,10 @@ export default class PlotPlugin extends Plugin {
|
|||
}
|
||||
await this.app.vault.createBinary(path,bytes.buffer);
|
||||
}
|
||||
async exportChartSVG(chartInstance: any, path = `${this.settings.saveImagesPath}/Chart_${Date.now()}.svg`) {
|
||||
const width = chartInstance.width;
|
||||
const height = chartInstance.height;
|
||||
|
||||
//const backgroundColor = this.settings.backgroundColor;
|
||||
const xScale = chartInstance.scales.x;
|
||||
const yScale = chartInstance.scales.y;
|
||||
const area = chartInstance.chartArea;
|
||||
const style = getComputedStyle(document.body);
|
||||
const bg = style.getPropertyValue("--background-secondary").trim() || "#ffffff";
|
||||
const text = style.getPropertyValue("--text-normal").trim() || "#000000";
|
||||
const muted = style.getPropertyValue("--text-muted").trim() || "#666666";
|
||||
const border = style.getPropertyValue("--background-modifier-border").trim() || "#d0d0d0";
|
||||
|
||||
let svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"><rect width="100%" height="100%" fill="${bg}" />`;
|
||||
svg += `
|
||||
<line
|
||||
x1="${area.left}"
|
||||
y1="${area.bottom}"
|
||||
x2="${area.right}"
|
||||
y2="${area.bottom}"
|
||||
stroke="${muted}"
|
||||
stroke-width="1"
|
||||
/>
|
||||
|
||||
<line
|
||||
x1="${area.left}"
|
||||
y1="${area.top}"
|
||||
x2="${area.left}"
|
||||
y2="${area.bottom}"
|
||||
stroke="${muted}"
|
||||
stroke-width="1"
|
||||
/>
|
||||
`;
|
||||
const title = chartInstance.options?.plugins?.title;
|
||||
|
||||
if (title?.display && title.text) {
|
||||
svg += `
|
||||
<text
|
||||
x="${width / 2}"
|
||||
y="20"
|
||||
font-size="16"
|
||||
font-weight="bold"
|
||||
text-anchor="middle"
|
||||
fill="${text}">${title.text}</text>
|
||||
`;
|
||||
}
|
||||
xScale.ticks.forEach((tick: any, i: number) => {
|
||||
const x = xScale.getPixelForTick(i);
|
||||
|
||||
svg += `
|
||||
<line
|
||||
x1="${x}"
|
||||
y1="${area.top}"
|
||||
x2="${x}"
|
||||
y2="${area.bottom}"
|
||||
stroke="${border}"
|
||||
stroke-width="1"
|
||||
/>
|
||||
`;
|
||||
svg += `
|
||||
<line
|
||||
x1="${x}"
|
||||
y1="${area.bottom}"
|
||||
x2="${x}"
|
||||
y2="${area.bottom + 6}"
|
||||
stroke="${border}"
|
||||
stroke-width="1"
|
||||
/>
|
||||
<text
|
||||
x="${x}"
|
||||
y="${area.bottom + 18}"
|
||||
font-size="12"
|
||||
text-anchor="middle"
|
||||
dominant-baseline="middle"
|
||||
fill="${text}">
|
||||
${tick.label}
|
||||
</text>
|
||||
`;
|
||||
|
||||
|
||||
});
|
||||
|
||||
yScale.ticks.forEach((tick: any, i: number) => {
|
||||
const y = yScale.getPixelForTick(i);
|
||||
svg += `
|
||||
<line
|
||||
x1="${area.left}"
|
||||
y1="${y}"
|
||||
x2="${area.right}"
|
||||
y2="${y}"
|
||||
stroke="${border}"
|
||||
stroke-width="1"
|
||||
/>
|
||||
`;
|
||||
svg += `
|
||||
<line
|
||||
x1="${area.left - 6}"
|
||||
y1="${y}"
|
||||
x2="${area.left}"
|
||||
y2="${y}"
|
||||
stroke="${border}"
|
||||
stroke-width="1"
|
||||
|
||||
/>
|
||||
<text
|
||||
x="${area.left - 8}"
|
||||
y="${y}"
|
||||
font-size="12"
|
||||
text-anchor="end"
|
||||
dominant-baseline="middle"
|
||||
fill="${text}">${tick.label}</text>
|
||||
`;
|
||||
});
|
||||
let lx = width - 74;
|
||||
let ly = 25;
|
||||
|
||||
chartInstance.data.datasets.forEach((dataset: any, i: number) => {
|
||||
const y = ly + i*20;
|
||||
const meta = chartInstance.getDatasetMeta(i);
|
||||
const points = meta.data.map((pt: Data) => `${pt.x},${pt.y}`).join(" ");
|
||||
const color = dataset.borderColor || "black";
|
||||
const radius = (dataset.pointRadius && dataset.pointRadius === 0) ? 0 : dataset.pointRadius;
|
||||
//const radius = dataset.pointRadius && dataset.pointRadius > 0 ? dataset.pointRadius : 3;
|
||||
svg += `
|
||||
<rect x="${lx}" y="${y-10}" width="12" height="12" fill="${color}" />
|
||||
<text
|
||||
x="${lx + 18}"
|
||||
y="${y}"
|
||||
font-size="12"
|
||||
dominant-baseline="middle"
|
||||
fill="${text}">${dataset.label || `Series ${i+1}`}</text>
|
||||
|
||||
<polyline
|
||||
fill="none"
|
||||
stroke="${color}"
|
||||
stroke-width="${dataset.borderWidth || 2}"
|
||||
points="${points}"
|
||||
|
||||
/>`;
|
||||
meta.data.forEach((pt: Data) => {
|
||||
svg += `
|
||||
<circle
|
||||
cx="${pt.x}"
|
||||
cy="${pt.y}"
|
||||
r="${radius}"
|
||||
fill="${color}"
|
||||
/>
|
||||
`;
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
const xTitle = chartInstance.options?.scales?.x?.title;
|
||||
|
||||
if (xTitle?.display && xTitle.text) {
|
||||
svg += `
|
||||
<text
|
||||
x="${(area.left + area.right) / 2}"
|
||||
y="${height - 8}"
|
||||
font-size="13"
|
||||
font-weight="bold"
|
||||
text-anchor="middle"
|
||||
fill="${text}">${xTitle.text}</text>
|
||||
`;
|
||||
}
|
||||
|
||||
const yTitle = chartInstance.options?.scales?.y?.title;
|
||||
|
||||
if (yTitle?.display && yTitle.text) {
|
||||
svg += `
|
||||
<text
|
||||
x="16"
|
||||
y="${(area.top + area.bottom) / 2}"
|
||||
font-size="13"
|
||||
font-weight="bold"
|
||||
text-anchor="middle"
|
||||
fill="${text}"
|
||||
transform="rotate(-90 16 ${(area.top + area.bottom) / 2})">
|
||||
${yTitle.text}
|
||||
</text>
|
||||
`;
|
||||
}
|
||||
svg += `</svg>`;
|
||||
async exportChartSVG(chartInstance: any, chartType: ChartType, path = `${this.settings.saveImagesPath}/Chart_${Date.now()}.svg`) {
|
||||
const svg = generateSVG(chartInstance,chartType);
|
||||
await this.app.vault.create(path, svg);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -494,7 +313,7 @@ export function customNotice(msg: string, cls = "", timeout = 4000) {
|
|||
const notice = new Notice(msg, timeout);
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const el = notice.noticeEl;
|
||||
const el = notice.containerEl;
|
||||
if (el && cls) el.classList.add(cls);
|
||||
});
|
||||
}
|
||||
|
|
@ -506,7 +325,27 @@ export function getDefaultPlotProperties(settings: PlotPluginSettings, chartType
|
|||
};
|
||||
}
|
||||
|
||||
function showError(container: HTMLElement, title: string, err: unknown) {
|
||||
const msg = err instanceof Error ? err.message: String(err);
|
||||
container.empty();
|
||||
const box = container.createDiv("datachart-error");
|
||||
box.createEl("div",{text: title, cls: "datachart-error-title"});
|
||||
box.createEl("div",{text: simplifyError(msg)});
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
function simplifyError(err: unknown): string {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
|
||||
if (msg.includes("Undefined symbol")) {
|
||||
const m = msg.match(/Undefined symbol (\w+)/);
|
||||
if (m) return `Unknown variable: ${m[1]}. Variables written as var(x) are not currently supported.`;
|
||||
}
|
||||
if (msg.includes("Unexpected token")) return "Syntax error in expression";
|
||||
if (msg.includes("Cannot read")) return "Missing property or invalid object path.";
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
export function getTypeDefaults(chartType: ChartType, settings: PlotPluginSettings): ChartOptions<ChartType> {
|
||||
const style = getComputedStyle(document.body);
|
||||
|
|
|
|||
|
|
@ -56,3 +56,12 @@
|
|||
box-shadow:none ;
|
||||
}
|
||||
|
||||
.datachart-error{
|
||||
padding:12px;
|
||||
border-radius:8px;
|
||||
background: var(--background-secondary);
|
||||
border:1px solid var(--color-red);
|
||||
color: var(--text-normal);
|
||||
font-family: var(--font-monospace);
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
Loading…
Reference in a new issue