Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

86 legendbox refinement #87

Merged
merged 10 commits into from
Aug 5, 2024
78 changes: 53 additions & 25 deletions src/Components/LegendBox.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
import renderToString from "katex";
import LegendText from "./LegendText";
import Plot from "./Plot";
import Point from "./Point";
import State from "./State";
import { Component, GuiComponent } from "./interfaces";

class LegendBox implements GuiComponent {
elements: (Component | string | State<number>)[];
elements: (Component | LegendText | State<number>)[];
states: { [key: string]: State<number> };
htmlElement: HTMLElement;

constructor(elements?: (Component | string | State<number>)[]) {
this.elements = elements || [];
constructor(elements?: (Component | LegendText | State<number>)[]) {
this.elements = [];
if (elements) {
for (const element of elements || []) {
if (!this.elements.includes(element)) {
this.elements.push(element);
}
}
}
this.states = {};
this.htmlElement = this.createLegendBoxWrapper();
const button = this.createSizeAdjustButton();
Expand Down Expand Up @@ -54,8 +62,13 @@ class LegendBox implements GuiComponent {
this.elements.forEach((element) => {
if (element instanceof State) {
this.states[element.getStateName()] = element;
} else if (typeof element === "string") {
this.addStringAsObserver(element);
for (const legendText of this.elements) {
if (legendText instanceof LegendText && legendText.getUseStates()) {
this.addStringAsObserver(legendText.getExpression());
}
}
} else if (element instanceof LegendText && element.getUseStates()) {
this.addStringAsObserver(element.getExpression());
}
});
}
Expand All @@ -82,9 +95,14 @@ class LegendBox implements GuiComponent {
this.elements.length === 0 ? "none" : "block";
}

private processElement(element: Component | string | State<number>) {
private processElement(element: Component | LegendText | State<number>) {
const functionContainer = document.createElement("div");
functionContainer.className = "function-container";

if (element instanceof State && !element.inLegend) {
return;
}

const icon = this.createIcon(element);
functionContainer.appendChild(icon);

Expand All @@ -103,7 +121,7 @@ class LegendBox implements GuiComponent {
this.htmlElement.appendChild(functionContainer);
}

private createIcon(element: Component | string | State<number>) {
private createIcon(element: Component | LegendText | State<number>) {
const icon = document.createElement("span");
icon.className = this.getIconClass(element);
if (icon.className === "triangle-icon") {
Expand All @@ -114,27 +132,30 @@ class LegendBox implements GuiComponent {
return icon;
}

private getTextToDisplay(element: Component | string | State<number>) {
private getTextToDisplay(element: Component | LegendText | State<number>) {
let textToDisplay = "";

if (typeof element === "string") {
textToDisplay =
element + ": " + this.replaceStateNamesWithValues(element);
} else if (element instanceof State) {
if (element instanceof State) {
textToDisplay =
element.getStateName() + ": " + element.getState().toFixed(1);
} else if (element instanceof LegendText) {
if (element.getUseStates()) {
textToDisplay = this.replaceStateNamesWithValues(
element.getExpression()
);
} else {
textToDisplay = element.getExpression();
}
} else if (element instanceof Component) {
textToDisplay = element.getDisplayText
? element.getName() + ": " + element.getDisplayText()
: element.getName();
}

return textToDisplay;
}

private createHtmlElementText(
renderedEquation: string,
element: Component | string | State<number>
element: Component | LegendText | string | State<number>
) {
const htmlElementText = document.createElement("div");
htmlElementText.innerHTML = renderedEquation;
Expand Down Expand Up @@ -164,27 +185,29 @@ class LegendBox implements GuiComponent {
});
}

private getIconClass(element: Component | string | State<number>) {
if (typeof element === "string" || element instanceof State) {
return "point-icon";
private getIconClass(element: Component | LegendText | State<number>) {
if (element instanceof State || element instanceof Point) {
return "circle-icon";
} else if (element instanceof Plot) {
return "plot-icon";
} else if (element instanceof Point) {
return "point-icon";
return "rectangle-icon";
} else if (element instanceof LegendText) {
return element.getIcon();
} else {
return "triangle-icon";
}
}

private getIconColor(element: Component | string | State<number>) {
private getIconColor(element: Component | LegendText | State<number>) {
if (element instanceof Component) {
return "#" + element.getColorAsString();
} else if (element instanceof LegendText) {
return element.getColor();
} else {
return "#faa307";
}
}

public addElement(element: Component | string | State<number>) {
public addElement(element: Component | LegendText | State<number>) {
if (this.elements.includes(element)) {
return;
}
Expand All @@ -196,11 +219,16 @@ class LegendBox implements GuiComponent {
return;
}
this.states[element.getStateName()] = element;
for (const expressions of this.elements) {
if (expressions instanceof LegendText && expressions.getUseStates()) {
this.addStringAsObserver(expressions.getExpression());
}
}
}

// If the element is a string, add it as an observer to the state variables it contains
if (typeof element === "string") {
this.addStringAsObserver(element);
if (element instanceof LegendText && element.getUseStates()) {
this.addStringAsObserver(element.getExpression());
}

this.updateComponents();
Expand Down
59 changes: 59 additions & 0 deletions src/Components/LegendText.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
type LegendTextOptions = {
color?: string;
shape?: string;
useStates?: boolean;
};

const defaultLegendTextOptions = {
color: "#faa307",
shape: "circle",
useStates: false,
};

class LegendText {
private expression: string;
private color: string;
private shape: string;
private useStates: boolean;

constructor(expression: string, options?: LegendTextOptions) {
const { color, shape, useStates } = {
...defaultLegendTextOptions,
...options,
};
this.expression = expression;
this.color = color;
this.shape = shape;
this.useStates = useStates;
}

getExpression(): string {
return this.expression;
}

getColor(): string {
return this.color;
}

getShape(): string {
return this.shape;
}

getUseStates(): boolean {
return this.useStates;
}

getIcon(): string {
switch (this.getShape()) {
case "circle":
return "circle-icon";
case "rectangle":
return "rectangle-icon";
case "triangle":
return "triangle-icon";
default:
return "circle-icon";
}
}
}
export default LegendText;
76 changes: 56 additions & 20 deletions src/Components/Point.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ type PointOptions = {
color?: string;
draggable?: Draggable;
dragListeners?: ((point: Point) => void)[];
customName?: string;
showName?: boolean;
legendCoordinates?: string;
};

const defaultPointOptions = {
Expand All @@ -26,23 +29,38 @@ const defaultPointOptions = {
decimals: 1,
label: false,
dragListeners: [],
showName: true,
legendCoordinates: "",
};

class Point extends Component implements Collider, DragListener<Point> {
private pointName: string | undefined;
private showName: boolean;
private legendCoordinates: string;
private static pointCounter = 0;
static emitter = new EventEmitter();
private color: string;
dragListeners: ((point: Point) => void)[];
constructor(x = 0, y = 0, options?: PointOptions) {
super();
const { color, draggable, decimals, label, dragListeners } = {
const {
color,
draggable,
decimals,
label,
dragListeners,
customName,
showName,
legendCoordinates,
} = {
...defaultPointOptions,
...options,
};
//set point name and color
this.setPointName();
this.setPointName(customName);
this.color = color;
this.showName = showName;
this.legendCoordinates = legendCoordinates;

// set position of the point instance
this.draggable = draggable;
Expand Down Expand Up @@ -79,16 +97,18 @@ class Point extends Component implements Collider, DragListener<Point> {
}

//add name of point
const nameText = new Text(this.pointName, {
color: "black",
fontSize: 18,
anchorY: "middle",
anchorX: "left",
position: [15, 0],
responsiveScale: false,
});
nameText.name = "name";
this.add(nameText);
if (this.showName) {
const nameText = new Text(this.pointName, {
color: "black",
fontSize: 18,
anchorY: "middle",
anchorX: "left",
position: [15, 0],
responsiveScale: false,
});
nameText.name = "name";
this.add(nameText);
}
}

addDragListener(listener: (point: Point) => void) {
Expand Down Expand Up @@ -141,19 +161,35 @@ class Point extends Component implements Collider, DragListener<Point> {
public setPosition(x: number, y: number) {
this.position.set(x, y, this.position.z);
}
private setPointName() {
this.pointName = String.fromCharCode(
"A".charCodeAt(0) + Point.pointCounter
);
Point.pointCounter++;
private setPointName(customName?: string) {
if (customName) {
this.pointName = customName;
} else {
this.pointName = String.fromCharCode(
"A".charCodeAt(0) + Point.pointCounter
);
Point.pointCounter++;
}
}

public getName(): string {
return this.pointName as string;
}

public getDisplayText(): string {
return (
"(" + this.position.x.toFixed(1) + ", " + this.position.y.toFixed(1) + ")"
);
if (this.legendCoordinates === "x") {
return this.position.x.toFixed(1);
} else if (this.legendCoordinates === "y") {
return this.position.y.toFixed(1);
} else {
return (
"(" +
this.position.x.toFixed(1) +
", " +
this.position.y.toFixed(1) +
")"
);
}
}

public hover() {
Expand Down
16 changes: 15 additions & 1 deletion src/Components/State.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,28 @@ import EventEmitter from "eventemitter3";

const STATE_CHANGE_EVENT = "stateChange";

type StateOptions = {
inLegend?: boolean;
};

const defaultStateOptions = {
inLegend: true,
};

class State<T> {
private stateName: string;
private state: T;
private emitter: EventEmitter;
public inLegend: boolean;

constructor(stateName: string, initialState: T) {
constructor(stateName: string, initialState: T, options?: StateOptions) {
const { inLegend } = {
...defaultStateOptions,
...options,
};
this.stateName = stateName;
this.state = initialState;
this.inLegend = inLegend;
this.emitter = new EventEmitter();
}

Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export { default as InfiniteLine } from "./Components/InfiniteLine";
export { default as InputField } from "./Components/InputField";
export { default as Label } from "./Components/Label";
export { default as Latex } from "./Components/Latex";
export { default as LegendText } from "./Components/LegendText";
export { default as LegendBox } from "./Components/LegendBox";
export { default as Line } from "./Components/Line";
export { default as Plot } from "./Components/Plot";
Expand Down
Loading
Loading