From fb7739eb59a4ee6d0edfab1744c449f06ff23420 Mon Sep 17 00:00:00 2001 From: facobackup Date: Sun, 27 Aug 2023 13:06:31 -0300 Subject: [PATCH 1/5] Starting functions architecture --- src/core/CanvasRenderEngine.ts | 4 +- src/core/components/AttributeEditor.svelte | 6 +-- src/core/instances/AbstractDraggable.ts | 8 +--- .../{Comment.ts => CommentDraggable.ts} | 8 ++-- src/core/instances/FunctionDraggable.ts | 39 +++++++++++++++++++ src/core/libs/ActionHistory.ts | 6 +-- src/core/libs/Serializer.ts | 4 +- src/core/pscript.g.ts | 2 +- src/core/util/IDraggableUtil.ts | 6 +-- .../javascript/javascript-nodes.ts | 16 +++++++- ...e-node.test.ts => serialize-state.test.ts} | 0 11 files changed, 72 insertions(+), 27 deletions(-) rename src/core/instances/{Comment.ts => CommentDraggable.ts} (76%) create mode 100644 src/core/instances/FunctionDraggable.ts rename test/{serialize-node.test.ts => serialize-state.test.ts} (100%) diff --git a/src/core/CanvasRenderEngine.ts b/src/core/CanvasRenderEngine.ts index 95a8d2e..f63dd6a 100644 --- a/src/core/CanvasRenderEngine.ts +++ b/src/core/CanvasRenderEngine.ts @@ -1,7 +1,7 @@ import RendererUtil from "./util/RendererUtil" import AbstractNode from "./instances/AbstractNode" import type AbstractLink from "./instances/AbstractLink" -import Comment from "./instances/Comment" +import CommentDraggable from "./instances/CommentDraggable" import ActionHistory from "./libs/ActionHistory" import PScriptUtil from "./util/PScriptUtil"; import PScriptRendererState from "./libs/PScriptRendererState"; @@ -137,7 +137,7 @@ export default class CanvasRenderEngine implements IRenderEngine { // this.history.save([node]) // } const state = this.getState() - if (node instanceof Comment) { + if (node instanceof CommentDraggable) { state.comments.push(node) } else { state.nodes.push(node) diff --git a/src/core/components/AttributeEditor.svelte b/src/core/components/AttributeEditor.svelte index 80e805b..df3bab2 100644 --- a/src/core/components/AttributeEditor.svelte +++ b/src/core/components/AttributeEditor.svelte @@ -2,11 +2,11 @@ import Attribute from "./Attribute.svelte"; import type AbstractNode from "../instances/AbstractNode"; - import Comment from "../instances/Comment"; + import CommentDraggable from "../instances/CommentDraggable"; import LocalizationEN from "../resources/LocalizationEN"; import ColorPicker from "../../components/color-picker/ColorPicker.svelte"; - export let node: AbstractNode | Comment + export let node: AbstractNode | CommentDraggable export let updateCanvas: Function @@ -34,7 +34,7 @@ placeholder={LocalizationEN.NAME} /> - {#if node instanceof Comment} + {#if node instanceof CommentDraggable}
{LocalizationEN.COLOR} implements IDraggable { +export default abstract class AbstractDraggable extends AbstractStateful implements IDraggable { static HEADER_HEIGHT = 25 static SCALE_BUTTON_SIZE = 10 diff --git a/src/core/instances/Comment.ts b/src/core/instances/CommentDraggable.ts similarity index 76% rename from src/core/instances/Comment.ts rename to src/core/instances/CommentDraggable.ts index 8e37dc3..adf1e06 100644 --- a/src/core/instances/Comment.ts +++ b/src/core/instances/CommentDraggable.ts @@ -3,14 +3,14 @@ import RendererUtil from "../util/RendererUtil" import AbstractDraggable from "./AbstractDraggable" -export default class Comment extends AbstractDraggable implements ICommentDraggable { - static of(props: CommentProps) { - const instance = new Comment() +export default class CommentDraggable extends AbstractDraggable implements ICommentDraggable { + static of(props: AbstractDraggableProps) { + const instance = new CommentDraggable() instance.from(props) return instance } - from(props: CommentProps) { + from(props: AbstractDraggableProps) { super.from(props) this.height = 200 } diff --git a/src/core/instances/FunctionDraggable.ts b/src/core/instances/FunctionDraggable.ts new file mode 100644 index 0000000..67993d8 --- /dev/null +++ b/src/core/instances/FunctionDraggable.ts @@ -0,0 +1,39 @@ +import AbstractDraggable from "./AbstractDraggable"; +import CanvasRenderEngine from "../CanvasRenderEngine"; +import RendererUtil from "../util/RendererUtil"; + +export default class FunctionDraggable extends AbstractDraggable implements IFunctionDraggable { + collapsed = false + + static of(props: AbstractDraggableProps) { + const instance = new FunctionDraggable() + instance.from(props) + return instance + } + + from(props: AbstractDraggableProps) { + super.from(props) + this.height = 200 + } + + drawToCanvas() { + const ctx = this.__canvas.__ctx + if(!this.collapsed) { + RendererUtil.drawRoundedRect(ctx, this, 3, this.__canvas.__selectionMap.get(this.id) !== undefined, this.__canvas.lastSelection === this, `rgba(${[this.colorRGBA[0], this.colorRGBA[1], this.colorRGBA[2], .5]})`) + } + RendererUtil.drawDraggableHeader(ctx, this) + this.#drawCollapseButton() + this.drawScale() + } + + #drawCollapseButton(){ + const ctx = this.__canvas.__ctx + const state = this.__canvas.getState() + + + } + + getInitialProperties(): MutableObject | undefined { + return undefined; + } +} diff --git a/src/core/libs/ActionHistory.ts b/src/core/libs/ActionHistory.ts index 9e3167c..78f1ae9 100644 --- a/src/core/libs/ActionHistory.ts +++ b/src/core/libs/ActionHistory.ts @@ -1,6 +1,6 @@ import type CanvasRenderEngine from "../CanvasRenderEngine" import AbstractNode from "../instances/AbstractNode" -import Comment from "../instances/Comment" +import CommentDraggable from "../instances/CommentDraggable" import ToastNotificationSystem from "../../components/alert/ToastNotificationSystem"; import LocalizationEN from "../resources/LocalizationEN"; import UndoRedo from "./UndoRedo"; @@ -15,7 +15,7 @@ export default class ActionHistory { this.#cache.history = [null] } - save(value: (AbstractNode | Comment)[], isRemoval?: boolean) { + save(value: (AbstractNode | CommentDraggable)[], isRemoval?: boolean) { // if (value.length === 0) // return // const data = value.map(v => { @@ -53,7 +53,7 @@ export default class ActionHistory { for (let i = 0; i < toAdd.length; i++) { const current = toAdd[i] if (current.DATA_TYPE === "comment") { - // const parsed = new Comment(current.x, current.y) + // const parsed = new CommentDraggable(current.x, current.y) // parsed.color = current.color // parsed.name = current.name // parsed.width = current.width diff --git a/src/core/libs/Serializer.ts b/src/core/libs/Serializer.ts index ed09828..a07bc18 100644 --- a/src/core/libs/Serializer.ts +++ b/src/core/libs/Serializer.ts @@ -1,5 +1,5 @@ import AbstractStateful from "../instances/AbstractStateful"; -import Comment from "../instances/Comment" +import CommentDraggable from "../instances/CommentDraggable" import PropertyType from "../instances/PropertyType"; import NodeType from "../instances/NodeType"; import Output from "../instances/Output"; @@ -15,7 +15,7 @@ import AbstractDraggable from "../instances/AbstractDraggable"; export default class Serializer { static #types: typeof AbstractSerializable[] = [ - Comment, + CommentDraggable, ExecutionInput, ExecutionOutput, ExecutionLink, diff --git a/src/core/pscript.g.ts b/src/core/pscript.g.ts index 3e1bf1f..fa0c93d 100644 --- a/src/core/pscript.g.ts +++ b/src/core/pscript.g.ts @@ -12,7 +12,7 @@ type NodeProps = { inputs?: IInput[]; } -type CommentProps = { +type AbstractDraggableProps = { canvas: IRenderEngine; x: number; y: number; diff --git a/src/core/util/IDraggableUtil.ts b/src/core/util/IDraggableUtil.ts index 07885bb..3ec62cf 100644 --- a/src/core/util/IDraggableUtil.ts +++ b/src/core/util/IDraggableUtil.ts @@ -1,7 +1,7 @@ import AbstractDraggable from "../instances/AbstractDraggable" import AbstractNode from "../instances/AbstractNode" import RendererUtil from "./RendererUtil"; -import Comment from "../instances/Comment"; +import CommentDraggable from "../instances/CommentDraggable"; import CanvasRenderEngine from "../CanvasRenderEngine"; import PScriptUtil from "./PScriptUtil"; @@ -79,7 +79,7 @@ export default class IDraggableUtil { }, nodesOnDrag, canvasAPI: CanvasRenderEngine, parentBBox, parentElement: HTMLElement, event: MouseEvent) { const state = canvasAPI.getState() const nodes = state.nodes - const comments = state.comments + const comments = state.comments const links = state.links const X = (event.clientX - BBox.x) / state.scale @@ -182,7 +182,7 @@ export default class IDraggableUtil { RendererUtil.drawTempLink(event, parentElement, parentBBox, canvasAPI) } - private static processCommentClick(onHeader: boolean, nodesOnDrag, event: MouseEvent, comment: Comment, parentBBox, X: number, Y: number, canvasAPI: CanvasRenderEngine) { + private static processCommentClick(onHeader: boolean, nodesOnDrag, event: MouseEvent, comment: CommentDraggable, parentBBox, X: number, Y: number, canvasAPI: CanvasRenderEngine) { if (onHeader) { nodesOnDrag.push(IDraggableUtil.drag(event, comment, parentBBox, true)) comment.isOnDrag = true diff --git a/src/environment/javascript/javascript-nodes.ts b/src/environment/javascript/javascript-nodes.ts index 3af3ca2..1ad8cc1 100644 --- a/src/environment/javascript/javascript-nodes.ts +++ b/src/environment/javascript/javascript-nodes.ts @@ -1,9 +1,10 @@ -import Comment from "../../core/instances/Comment"; +import CommentDraggable from "../../core/instances/CommentDraggable"; import Add from "./basic-functions/Add"; import BooleanVal from "./basic-types/BooleanVal"; import NumberVal from "./basic-types/NumberVal"; import {Colors} from "./javascript.enum"; import Do from "./basic-functions/Do"; +import FunctionDraggable from "../../core/instances/FunctionDraggable"; export default [ { @@ -20,7 +21,7 @@ export default [ { label: "Comment", class: "CommentDraggable", - getInstance: (x, y, canvas) => Comment.of({ + getInstance: (x, y, canvas) => CommentDraggable.of({ canvas, x, y, @@ -28,6 +29,17 @@ export default [ colorRGBA: Colors.COMMENT }) }, + { + label: "Function", + class: "FunctionDraggable", + getInstance: (x, y, canvas) => FunctionDraggable.of({ + canvas, + x, + y, + label: "New function", + colorRGBA: Colors.FUNCTION + }) + }, { label: "Boolean", diff --git a/test/serialize-node.test.ts b/test/serialize-state.test.ts similarity index 100% rename from test/serialize-node.test.ts rename to test/serialize-state.test.ts From 90df38c0a061284587fb56ba6945a5d70fa2e3a8 Mon Sep 17 00:00:00 2001 From: facobackup Date: Sun, 27 Aug 2023 16:35:01 -0300 Subject: [PATCH 2/5] - Organizing bound checks - Tweaking collapse button - Adding new store for selection --- src/core/CanvasRenderEngine.ts | 32 ++++++++-------- src/core/PScript.svelte | 4 +- src/core/components/ActionBar.svelte | 4 +- src/core/components/SideBar.svelte | 7 +++- src/core/instances/AbstractDraggable.ts | 13 ++----- src/core/instances/AbstractNode.ts | 26 +++---------- src/core/instances/CommentDraggable.ts | 3 +- src/core/instances/FunctionDraggable.ts | 37 ++++++++++++++++--- ...RendererState.ts => CanvasStateManager.ts} | 2 +- src/core/libs/SelectionStore.ts | 25 +++++++++++++ src/core/pscript.g.ts | 2 - src/core/util/IDraggableUtil.ts | 36 ++++++++++++------ src/core/util/MathUtil.ts | 27 ++++++++++++++ src/core/util/PScriptUtil.ts | 3 +- src/core/util/RendererUtil.ts | 13 +++++-- .../javascript/JavascriptScriptingView.svelte | 4 +- test/serialize-state.test.ts | 4 +- 17 files changed, 159 insertions(+), 83 deletions(-) rename src/core/libs/{PScriptRendererState.ts => CanvasStateManager.ts} (97%) create mode 100644 src/core/libs/SelectionStore.ts create mode 100644 src/core/util/MathUtil.ts diff --git a/src/core/CanvasRenderEngine.ts b/src/core/CanvasRenderEngine.ts index f63dd6a..d0bc2a0 100644 --- a/src/core/CanvasRenderEngine.ts +++ b/src/core/CanvasRenderEngine.ts @@ -4,21 +4,18 @@ import type AbstractLink from "./instances/AbstractLink" import CommentDraggable from "./instances/CommentDraggable" import ActionHistory from "./libs/ActionHistory" import PScriptUtil from "./util/PScriptUtil"; -import PScriptRendererState from "./libs/PScriptRendererState"; +import CanvasStateManager from "./libs/CanvasStateManager"; import AbstractDraggable from "./instances/AbstractDraggable"; import IDraggableUtil from "./util/IDraggableUtil"; +import SelectionStore from "./libs/SelectionStore"; export default class CanvasRenderEngine implements IRenderEngine { #id: string #initialized = false - //history = new ActionHistory() __ctx: CanvasRenderingContext2D __canvas: HTMLCanvasElement - __lastSelectionListener?: Function - __selectionMap = new Map() - #lastSelection: IDraggable #frame: number; - __observer + __observer: ResizeObserver #state: RendererState constructor(id: string) { @@ -27,11 +24,16 @@ export default class CanvasRenderEngine implements IRenderEngine { } getState(): RendererState { - return this.#state ?? (this.#state = PScriptRendererState.getState(this.getId())) + return this.#state ?? (this.#state = CanvasStateManager.getState(this.getId())) } clearState() { this.#state = undefined + SelectionStore.updateStore({ + selected: new Map(), + lastSelection: undefined, + focusedFunction: undefined + }) } initialize(canvas: HTMLCanvasElement) { @@ -104,7 +106,7 @@ export default class CanvasRenderEngine implements IRenderEngine { } #getState() { - return PScriptRendererState.getState(this.getId()) + return CanvasStateManager.getState(this.getId()) } addLink(link: AbstractLink, noUpdate?: boolean) { @@ -122,15 +124,6 @@ export default class CanvasRenderEngine implements IRenderEngine { this.clear() } - get lastSelection() { - return this.#lastSelection - } - - set lastSelection(data: IDraggable) { - this.#lastSelection = data - this.__lastSelectionListener?.() - } - addDraggable(node: IDraggable) { // if (!noSerialization) { // this.history.save([node], true) @@ -151,8 +144,13 @@ export default class CanvasRenderEngine implements IRenderEngine { // this.history.save(mapped) // this.history.save(mapped, true) // } + const focusedFunction = SelectionStore.getData().focusedFunction for (let i = 0; i < toRemove.length; i++) { const draggable = toRemove[i]; + SelectionStore.getSelectionMap().delete(draggable.id) + if(focusedFunction === draggable.id){ + SelectionStore.updateStore({focusedFunction: undefined}) + } if (draggable instanceof AbstractNode) { const index = this.#state.nodes.indexOf(draggable) if (index > -1) { diff --git a/src/core/PScript.svelte b/src/core/PScript.svelte index 966338d..6fdaf23 100644 --- a/src/core/PScript.svelte +++ b/src/core/PScript.svelte @@ -1,7 +1,7 @@ diff --git a/src/core/instances/AbstractDraggable.ts b/src/core/instances/AbstractDraggable.ts index a537c86..481e183 100644 --- a/src/core/instances/AbstractDraggable.ts +++ b/src/core/instances/AbstractDraggable.ts @@ -1,5 +1,6 @@ import AbstractStateful from "./AbstractStateful"; import uuid from "uuidv4" +import MathUtil from "../util/MathUtil"; export default abstract class AbstractDraggable extends AbstractStateful implements IDraggable { static HEADER_HEIGHT = 25 @@ -34,11 +35,7 @@ export default abstract class AbstractDraggable extends AbstractStateful= XI && x < XF && y >= YI && y < YF + return MathUtil.isPointInsideRect(x, y, XI, YI, AbstractDraggable.SCALE_BUTTON_SIZE, AbstractDraggable.SCALE_BUTTON_SIZE) } getMinHeight() { @@ -51,14 +48,12 @@ export default abstract class AbstractDraggable extends AbstractStateful= coord.x && x < coord.x + this.width && y >= coord.y && y < coord.y + AbstractDraggable.HEADER_HEIGHT + return MathUtil.isPointInsideRect(x, y, coord.x, coord.y, this.width, AbstractDraggable.HEADER_HEIGHT) } checkBodyClick(x: number, y: number): boolean { const coord = this.getTransformedCoordinates() - const XI = coord.x - 4, XF = coord.x + 4 + this.width - const YI = coord.y + AbstractDraggable.HEADER_HEIGHT, YF = YI + this.height - AbstractDraggable.HEADER_HEIGHT - return x >= XI && x < XF && y >= YI && y < YF + return MathUtil.isPointInsideRect(x, y, coord.x, coord.y + AbstractDraggable.HEADER_HEIGHT, this.width, this.height - AbstractDraggable.HEADER_HEIGHT) } drawScale() { diff --git a/src/core/instances/AbstractNode.ts b/src/core/instances/AbstractNode.ts index 03590ff..a062edc 100644 --- a/src/core/instances/AbstractNode.ts +++ b/src/core/instances/AbstractNode.ts @@ -4,6 +4,8 @@ import IDraggableUtil from "../util/IDraggableUtil" import NodeType from "./NodeType"; import ExecutionOutput from "./ExecutionOutput"; import ExecutionInput from "./ExecutionInput"; +import MathUtil from "../util/MathUtil"; +import SelectionStore from "../libs/SelectionStore"; export default abstract class AbstractNode extends AbstractDraggable implements INodeDraggable { static IO_RADIUS = 4 @@ -59,34 +61,18 @@ export default abstract class AbstractNode extends AbstractDraggable implements if (io instanceof ExecutionInput) { const startX = linePosition.x + AbstractNode.IO_RADIUS * 3 const positions = AbstractNode.#getTrianglePoints(startX, linePosition.y) - isValid = AbstractNode.#isPointInsideTriangle(x, y, positions) + isValid = MathUtil.isPointInsideTriangle(x, y, positions) } else if (io instanceof ExecutionOutput) { const positions = AbstractNode.#getTrianglePoints(linePosition.x - AbstractNode.IO_RADIUS, linePosition.y) - isValid = AbstractNode.#isPointInsideTriangle(x, y, positions) + isValid = MathUtil.isPointInsideTriangle(x, y, positions) } else { - isValid = ((x - linePosition.x) ** 2 + (y - linePosition.y) ** 2 < R2) + isValid = MathUtil.isPointInsideCircle(x, y, linePosition.x, linePosition.y, R2) } if (isValid) return io } } - static #areaOfTriangle(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number) { - return Math.abs((x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) / 2.0); - } - - static #isPointInsideTriangle(x: number, y: number, positions: [number, number, number, number, number, number]): boolean { - const x1 = positions[0], y1 = positions[1], - x2 = positions[2], y2 = positions[3], - x3 = positions[4], y3 = positions[5] - - const A = AbstractNode.#areaOfTriangle(x1, y1, x2, y2, x3, y3); - const A1 = AbstractNode.#areaOfTriangle(x, y, x2, y2, x3, y3); - const A2 = AbstractNode.#areaOfTriangle(x1, y1, x, y, x3, y3); - const A3 = AbstractNode.#areaOfTriangle(x1, y1, x2, y2, x, y); - return (A === A1 + A2 + A3); - } - static #getTrianglePoints(startX: number, startY: number): [number, number, number, number, number, number] { const X = startX - RendererUtil.EXECUTION_IO_SIZE return [startX, startY, X, startY + RendererUtil.EXECUTION_IO_SIZE, X, startY - RendererUtil.EXECUTION_IO_SIZE] @@ -94,7 +80,7 @@ export default abstract class AbstractNode extends AbstractDraggable implements drawToCanvas() { const ctx = this.__canvas.__ctx - RendererUtil.drawRoundedRect(ctx, this, 3, this.__canvas.__selectionMap.get(this.id) !== undefined, this.__canvas.lastSelection === this, this.__canvas.getState().rectColor) + RendererUtil.drawDraggableBody(ctx, this, 3, SelectionStore.getSelectionMap().get(this.id) !== undefined, SelectionStore.getLastSelection() === this, this.__canvas.getState().rectColor) RendererUtil.drawDraggableHeader(ctx, this) for (let j = 0; j < this.outputs.length; j++) { diff --git a/src/core/instances/CommentDraggable.ts b/src/core/instances/CommentDraggable.ts index adf1e06..a4cdbd0 100644 --- a/src/core/instances/CommentDraggable.ts +++ b/src/core/instances/CommentDraggable.ts @@ -1,6 +1,7 @@ import type CanvasRenderEngine from "../CanvasRenderEngine" import RendererUtil from "../util/RendererUtil" import AbstractDraggable from "./AbstractDraggable" +import SelectionStore from "../libs/SelectionStore"; export default class CommentDraggable extends AbstractDraggable implements ICommentDraggable { @@ -17,7 +18,7 @@ export default class CommentDraggable extends AbstractDraggable implements IComm drawToCanvas() { const ctx = this.__canvas.__ctx - RendererUtil.drawRoundedRect(ctx, this, 3, this.__canvas.__selectionMap.get(this.id) !== undefined, this.__canvas.lastSelection === this, `rgba(${[this.colorRGBA[0], this.colorRGBA[1], this.colorRGBA[2], .5]})`) + RendererUtil.drawDraggableBody(ctx, this, 3, SelectionStore.getSelectionMap().get(this.id) !== undefined, SelectionStore.getLastSelection() === this, `rgba(${[this.colorRGBA[0], this.colorRGBA[1], this.colorRGBA[2], .5]})`) RendererUtil.drawDraggableHeader(ctx, this) this.drawScale() } diff --git a/src/core/instances/FunctionDraggable.ts b/src/core/instances/FunctionDraggable.ts index 67993d8..bebe93d 100644 --- a/src/core/instances/FunctionDraggable.ts +++ b/src/core/instances/FunctionDraggable.ts @@ -1,9 +1,12 @@ import AbstractDraggable from "./AbstractDraggable"; import CanvasRenderEngine from "../CanvasRenderEngine"; import RendererUtil from "../util/RendererUtil"; +import MathUtil from "../util/MathUtil"; +import SelectionStore from "../libs/SelectionStore"; export default class FunctionDraggable extends AbstractDraggable implements IFunctionDraggable { collapsed = false + #darkerColor: string; static of(props: AbstractDraggableProps) { const instance = new FunctionDraggable() @@ -14,23 +17,45 @@ export default class FunctionDraggable extends AbstractDraggable implements IFun from(props: AbstractDraggableProps) { super.from(props) this.height = 200 + this.#darkerColor = `rgba(${this.colorRGBA[0] * .85},${this.colorRGBA[1] * .85},${this.colorRGBA[2] * .85},1)` } drawToCanvas() { const ctx = this.__canvas.__ctx - if(!this.collapsed) { - RendererUtil.drawRoundedRect(ctx, this, 3, this.__canvas.__selectionMap.get(this.id) !== undefined, this.__canvas.lastSelection === this, `rgba(${[this.colorRGBA[0], this.colorRGBA[1], this.colorRGBA[2], .5]})`) + if (!this.collapsed) { + RendererUtil.drawDraggableBody( + ctx, + this, + 3, + SelectionStore.getFocusedFunction() === this.id || SelectionStore.getSelectionMap().get(this.id) !== undefined, + SelectionStore.getLastSelection() === this, `rgba(${[this.colorRGBA[0], this.colorRGBA[1], this.colorRGBA[2], .5]})`) + this.drawScale() } - RendererUtil.drawDraggableHeader(ctx, this) + RendererUtil.drawDraggableHeader(ctx, this, this.collapsed ? 3 : undefined) this.#drawCollapseButton() - this.drawScale() } - #drawCollapseButton(){ + #drawCollapseButton() { const ctx = this.__canvas.__ctx - const state = this.__canvas.getState() + const coord = this.getTransformedCoordinates() + const offset = AbstractDraggable.HEADER_HEIGHT * .1 + const size = AbstractDraggable.HEADER_HEIGHT * .75 + const XI = coord.x + this.width - size - offset + const YI = coord.y + offset + + ctx.fillStyle = this.#darkerColor + RendererUtil.drawRoundedRect(ctx, XI, YI, size, size, 3) + } + + checkAgainstCollapse(x: number, y: number): boolean { + const coord = this.getTransformedCoordinates() + const offset = AbstractDraggable.HEADER_HEIGHT * .1 + const size = AbstractDraggable.HEADER_HEIGHT * .75 + const XI = coord.x + this.width - size - offset + const YI = coord.y + offset + return MathUtil.isPointInsideRect(x, y, XI, YI, size, size) } getInitialProperties(): MutableObject | undefined { diff --git a/src/core/libs/PScriptRendererState.ts b/src/core/libs/CanvasStateManager.ts similarity index 97% rename from src/core/libs/PScriptRendererState.ts rename to src/core/libs/CanvasStateManager.ts index 292d912..b4c272b 100644 --- a/src/core/libs/PScriptRendererState.ts +++ b/src/core/libs/CanvasStateManager.ts @@ -1,7 +1,7 @@ import DynamicMap from "./DynamicMap"; import CanvasRenderEngine from "../CanvasRenderEngine"; -export default class PScriptRendererState { +export default class CanvasStateManager { static #state = new DynamicMap>() static getState(id: string) { diff --git a/src/core/libs/SelectionStore.ts b/src/core/libs/SelectionStore.ts new file mode 100644 index 0000000..0d071e0 --- /dev/null +++ b/src/core/libs/SelectionStore.ts @@ -0,0 +1,25 @@ +import AbstractStore from "./AbstractStore" +import FunctionDraggable from "../instances/FunctionDraggable"; + +export default class SelectionStore extends AbstractStore{ + constructor() { + super({ + selected: new Map(), + lastSelection: undefined, + focusedFunction: undefined + }) + } + + static getSelectionMap(): Map{ + return this.getData().selected + } + + static getLastSelection(): IDraggable{ + return this.getData().lastSelection + } + + static getFocusedFunction(): string { + return this.getData().focusedFunction + } +} + diff --git a/src/core/pscript.g.ts b/src/core/pscript.g.ts index fa0c93d..29152ba 100644 --- a/src/core/pscript.g.ts +++ b/src/core/pscript.g.ts @@ -21,8 +21,6 @@ type AbstractDraggableProps = { } interface IRenderEngine { - lastSelection: IDraggable; - __selectionMap: Map; __observer: ResizeObserver; stop: VoidFunction; __ctx: CanvasRenderingContext2D; diff --git a/src/core/util/IDraggableUtil.ts b/src/core/util/IDraggableUtil.ts index 3ec62cf..7310df8 100644 --- a/src/core/util/IDraggableUtil.ts +++ b/src/core/util/IDraggableUtil.ts @@ -4,6 +4,8 @@ import RendererUtil from "./RendererUtil"; import CommentDraggable from "../instances/CommentDraggable"; import CanvasRenderEngine from "../CanvasRenderEngine"; import PScriptUtil from "./PScriptUtil"; +import FunctionDraggable from "../instances/FunctionDraggable"; +import SelectionStore from "../libs/SelectionStore"; export default class IDraggableUtil { @@ -86,12 +88,14 @@ export default class IDraggableUtil { const Y = (event.clientY - BBox.y) / state.scale if (!event.ctrlKey) { - canvasAPI.lastSelection = undefined - canvasAPI.__selectionMap.clear() - } else - canvasAPI.__selectionMap.forEach(node => { + SelectionStore.updateStore({lastSelection: undefined}) + SelectionStore.getSelectionMap().clear() + } else { + SelectionStore.getSelectionMap().forEach(node => { nodesOnDrag.push(IDraggableUtil.drag(event, node, parentBBox, true)) }) + } + let executionBroken = false for (let i = nodes.length - 1; i >= 0; i--) { @@ -100,11 +104,19 @@ export default class IDraggableUtil { const onHeader = node.checkHeaderClick(X, Y) if (!onHeader && !onBody) continue - canvasAPI.__selectionMap.set(node.id, node) - canvasAPI.lastSelection = node + const isFunctionNode = node instanceof FunctionDraggable + if(isFunctionNode){ + SelectionStore.updateStore({focusedFunction: node.id}) + } + SelectionStore.getSelectionMap().set(node.id, node) + SelectionStore.updateStore({lastSelection: node}) if (onHeader) { - nodesOnDrag.push(IDraggableUtil.drag(event, node, parentBBox, true)) - node.isOnDrag = true + if (isFunctionNode && node.checkAgainstCollapse(X, Y)) { + node.collapsed = !node.collapsed + } else { + nodesOnDrag.push(IDraggableUtil.drag(event, node, parentBBox, true)) + node.isOnDrag = true + } } else if (!event.ctrlKey) { const isOnScale = node.checkAgainstScale(X, Y) if (isOnScale) { @@ -132,7 +144,6 @@ export default class IDraggableUtil { executionBroken = true break } - if (!executionBroken) { for (let i = comments.length - 1; i >= 0; i--) { const comment = comments[i] @@ -144,7 +155,7 @@ export default class IDraggableUtil { } } } - + SelectionStore.updateStore({selected: SelectionStore.getSelectionMap()}) if (nodesOnDrag.length > 0 || IO.node !== undefined) canvasAPI.__ctx.canvas.style.cursor = "grabbing" canvasAPI.clear() @@ -193,8 +204,9 @@ export default class IDraggableUtil { comment.isOnDrag = true } } - canvasAPI.__selectionMap.set(comment.id, comment) - canvasAPI.lastSelection = comment + + SelectionStore.getSelectionMap().set(comment.id, comment) + SelectionStore.updateStore({selected: SelectionStore.getSelectionMap(), lastSelection: comment}) } static #checkOffset(ev1: MouseEvent, ev2: { x: number, y: number }): boolean { diff --git a/src/core/util/MathUtil.ts b/src/core/util/MathUtil.ts new file mode 100644 index 0000000..7387384 --- /dev/null +++ b/src/core/util/MathUtil.ts @@ -0,0 +1,27 @@ +export default class MathUtil { + static isPointInsideCircle(x: number, y: number, centerX: number, centerY: number, radiusSquared: number): boolean { + return ((x - centerX) ** 2 + (y - centerY) ** 2 < radiusSquared) + } + + static isPointInsideRect(x: number, y: number, centerX: number, centerY: number, width: number, height: number): boolean { + const XI = centerX + width + const YI = centerY + height + return x >= centerX && x <= XI && y >= centerY && y < YI + } + + static #areaOfTriangle(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number) { + return Math.abs((x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) / 2.0); + } + + static isPointInsideTriangle(x: number, y: number, positions: [number, number, number, number, number, number]): boolean { + const x1 = positions[0], y1 = positions[1], + x2 = positions[2], y2 = positions[3], + x3 = positions[4], y3 = positions[5] + + const A = this.#areaOfTriangle(x1, y1, x2, y2, x3, y3); + const A1 = this.#areaOfTriangle(x, y, x2, y2, x3, y3); + const A2 = this.#areaOfTriangle(x1, y1, x, y, x3, y3); + const A3 = this.#areaOfTriangle(x1, y1, x2, y2, x, y); + return (A === A1 + A2 + A3); + } +} diff --git a/src/core/util/PScriptUtil.ts b/src/core/util/PScriptUtil.ts index fff192d..c529f33 100644 --- a/src/core/util/PScriptUtil.ts +++ b/src/core/util/PScriptUtil.ts @@ -4,6 +4,7 @@ import {MaterialDataTypes} from "../pscript.enum"; import LocalizationEN from "../resources/LocalizationEN"; import ToastNotificationSystem from "../../components/alert/ToastNotificationSystem"; import Link from "../instances/Link"; +import SelectionStore from "../libs/SelectionStore"; export default class PScriptUtil { /** @@ -16,7 +17,7 @@ export default class PScriptUtil { biggestX: number | undefined, biggestY: number | undefined - canvasAPI.__selectionMap + SelectionStore.getSelectionMap() .forEach(n => { if (!smallestX || n.x < smallestX) smallestX = n.x diff --git a/src/core/util/RendererUtil.ts b/src/core/util/RendererUtil.ts index dd6ea2d..0706a25 100644 --- a/src/core/util/RendererUtil.ts +++ b/src/core/util/RendererUtil.ts @@ -131,7 +131,7 @@ export default class RendererUtil { state.needsUpdate = true } - static drawDraggableHeader(ctx: CanvasRenderingContext2D, node: IDraggable) { + static drawDraggableHeader(ctx: CanvasRenderingContext2D, node: IDraggable, borderRadius?: number) { const state = node.__canvas.getState() const name = node.label const color = node.colorRGBA @@ -141,7 +141,7 @@ export default class RendererUtil { ctx.fillStyle = `rgb(${color})` ctx.strokeStyle = node.__canvas.getState().borderColor ctx.lineWidth = .5 - ctx.roundRect(coord.x, coord.y, node.width, RendererUtil.#HEADER_LABEL_HEIGHT, RendererUtil.#BORDER_RADIUS) + ctx.roundRect(coord.x, coord.y, node.width, RendererUtil.#HEADER_LABEL_HEIGHT, borderRadius ?? RendererUtil.#BORDER_RADIUS) ctx.stroke() ctx.fill() ctx.font = state.defaultFont @@ -164,7 +164,7 @@ export default class RendererUtil { } - static drawRoundedRect(ctx: CanvasRenderingContext2D, node: AbstractDraggable, r: number, isSelected: boolean, isFirstSelected: boolean, color: string) { + static drawDraggableBody(ctx: CanvasRenderingContext2D, node: AbstractDraggable, r: number, isSelected: boolean, isFirstSelected: boolean, color: string) { const coord = node.getTransformedCoordinates() const state = node.__canvas.getState() const w = node.width, h = node.height, x = coord.x, y = coord.y @@ -178,9 +178,14 @@ export default class RendererUtil { ctx.lineWidth = isSelected ? 2 : 1 ctx.strokeStyle = outlineColor + RendererUtil.drawRoundedRect(ctx, x, y, w, h, r) + } + + static drawRoundedRect(ctx: CanvasRenderingContext2D, x: number, y: number, w: number, h: number, r: number) { ctx.beginPath() ctx.roundRect(x, y, w, h, r) - ctx.stroke() ctx.fill() + ctx.closePath() + ctx.stroke() } } diff --git a/src/environment/javascript/JavascriptScriptingView.svelte b/src/environment/javascript/JavascriptScriptingView.svelte index 9c4d0c6..26c09fa 100644 --- a/src/environment/javascript/JavascriptScriptingView.svelte +++ b/src/environment/javascript/JavascriptScriptingView.svelte @@ -1,7 +1,7 @@ diff --git a/test/serialize-state.test.ts b/test/serialize-state.test.ts index 66ae40f..1c09741 100644 --- a/test/serialize-state.test.ts +++ b/test/serialize-state.test.ts @@ -2,11 +2,11 @@ import {expect, test} from '@jest/globals'; import BooleanVal from "../src/environment/javascript/basic-types/BooleanVal"; import {Colors} from "../src/environment/javascript/javascript.enum"; import Serializer from "../src/core/libs/Serializer"; -import PScriptRendererState from "../src/core/libs/PScriptRendererState"; +import CanvasStateManager from "../src/core/libs/CanvasStateManager"; test('Serialized node is valid', () => { Serializer.addTypes(BooleanVal) - const canvas = PScriptRendererState.createState("TEST") + const canvas = CanvasStateManager.createState("TEST") const newNode = BooleanVal.of({canvas, x: 0, y: 0, label: "Boolean", colorRGBA: Colors.BOOLEAN}) canvas.addDraggable(newNode) From 0326c4af174315e29eb973726a741786d9048112 Mon Sep 17 00:00:00 2001 From: facobackup Date: Mon, 28 Aug 2023 20:47:26 -0300 Subject: [PATCH 3/5] - Improving UI --- src/components/tabs/Tabs.svelte | 69 ++++++++++++++ src/core/PScript.svelte | 88 +++++++++++++++-- src/core/components/ActionBar.svelte | 15 ++- src/core/components/AttributeEditor.svelte | 15 ++- src/core/components/Functions.svelte | 9 ++ src/core/components/ManagementSideBar.svelte | 17 ++++ src/core/components/Nodes.svelte | 93 +++++++++++++----- src/core/components/ResourcesSideBar.svelte | 39 ++++++++ src/core/components/SideBar.svelte | 95 ------------------- src/core/components/Variables.svelte | 9 ++ src/core/instances/FunctionDraggable.ts | 1 - .../javascript/JavascriptScriptingView.svelte | 7 +- 12 files changed, 316 insertions(+), 141 deletions(-) create mode 100644 src/components/tabs/Tabs.svelte create mode 100644 src/core/components/Functions.svelte create mode 100644 src/core/components/ManagementSideBar.svelte create mode 100644 src/core/components/ResourcesSideBar.svelte delete mode 100644 src/core/components/SideBar.svelte create mode 100644 src/core/components/Variables.svelte diff --git a/src/components/tabs/Tabs.svelte b/src/components/tabs/Tabs.svelte new file mode 100644 index 0000000..2ad3544 --- /dev/null +++ b/src/components/tabs/Tabs.svelte @@ -0,0 +1,69 @@ + + +
+ {#each tabs as tab, index} +
+
setSelected(index)}> + {#if tab.icon} + {tab.icon} + {/if} + {tab.label} +
+ {#if tab.removable} + + {/if} +
+ {/each} +
+ + diff --git a/src/core/PScript.svelte b/src/core/PScript.svelte index 6fdaf23..56e48f0 100644 --- a/src/core/PScript.svelte +++ b/src/core/PScript.svelte @@ -3,11 +3,29 @@ import CanvasRenderEngine from "./CanvasRenderEngine" import CanvasStateManager from "./libs/CanvasStateManager"; import AbstractDraggable from "./instances/AbstractDraggable"; + import ActionBar from "./components/ActionBar.svelte"; + import SideBar from "./components/ResourcesSideBar.svelte"; + import Tabs from "../components/tabs/Tabs.svelte"; + import ResizableBar from "../components/resizable/ResizableBar.svelte"; + import PropertiesSideBar from "./components/ManagementSideBar.svelte"; export let scriptCanvas: CanvasRenderEngine - export let allNodes: { label: string, getInstance: (x: number, y: number, canvas: CanvasRenderEngine) => typeof AbstractDraggable, class: string }[] + export let allNodes: { + label: string, + getInstance: (x: number, y: number, canvas: CanvasRenderEngine) => typeof AbstractDraggable, + class: string + }[] + let tabs = [ + { + label: "Editor", + removable: false, + key: "EDITOR" + } + ] + let selectedTab = 0 let canvasElement + onMount(() => scriptCanvas.initialize(canvasElement)) onDestroy(() => { CanvasStateManager.destroyState(scriptCanvas.getId()) @@ -21,20 +39,42 @@ const x = e.clientX - rect.left - state.offsetX; const y = e.clientY - rect.top - state.offsetY; const instance = allNodes.find(n => n.class === nodeId)?.getInstance?.(x, y, scriptCanvas) - if(instance != null) { + if (instance != null) { scriptCanvas.addDraggable(instance) } } -
- e.preventDefault()} - class="canvas" - bind:this={canvasElement} - > + + selectedTab = v} + selected={selectedTab} + removeTab={index => { + tabs.slice(index, 1) + tabs = tabs + }} +/> +
+ + +
+ e.preventDefault()} + class="canvas" + bind:this={canvasElement} + > +
+ +
+ diff --git a/src/core/components/ActionBar.svelte b/src/core/components/ActionBar.svelte index fce8e3a..ff681f8 100644 --- a/src/core/components/ActionBar.svelte +++ b/src/core/components/ActionBar.svelte @@ -36,8 +36,7 @@ } -
- +
+ + diff --git a/src/core/components/AttributeEditor.svelte b/src/core/components/AttributeEditor.svelte index df3bab2..5cf86d0 100644 --- a/src/core/components/AttributeEditor.svelte +++ b/src/core/components/AttributeEditor.svelte @@ -5,6 +5,8 @@ import CommentDraggable from "../instances/CommentDraggable"; import LocalizationEN from "../resources/LocalizationEN"; import ColorPicker from "../../components/color-picker/ColorPicker.svelte"; + import Input from "../../components/input/Input.svelte"; + import Icon from "../../components/icon/Icon.svelte"; export let node: AbstractNode | CommentDraggable export let updateCanvas: Function @@ -12,8 +14,8 @@ function handleNodeChange(value: any, attr: MutableObject) { node[attr.key] = value - const input = (node as AbstractNode).inputs.find(i => i.key === attr.key) - input.onChange?.(value) + // const input = (node as AbstractNode).inputs.find(i => i.key === attr.key) + // input.onChange?.(value) updateCanvas() } @@ -64,11 +66,16 @@ {/each} {/if} -
- + {:else} +
+ category + No node selected +
{/if} + + diff --git a/src/core/components/ManagementSideBar.svelte b/src/core/components/ManagementSideBar.svelte new file mode 100644 index 0000000..f855572 --- /dev/null +++ b/src/core/components/ManagementSideBar.svelte @@ -0,0 +1,17 @@ + + +
+
+
+ +
+
+
+ diff --git a/src/core/components/Nodes.svelte b/src/core/components/Nodes.svelte index 6030087..c97b81c 100644 --- a/src/core/components/Nodes.svelte +++ b/src/core/components/Nodes.svelte @@ -3,46 +3,89 @@ import Input from "../../components/input/Input.svelte"; import Icon from "../../components/icon/Icon.svelte"; - export let allNodes:{ label: string, class: string }[] = [] + export let allNodes: { label: string, class: string }[] = [] let inputValue = "" let nodes + let interval + $: nodes = !inputValue ? allNodes : allNodes.filter(i => i.label.toLowerCase().includes(inputValue.toLowerCase())) + + function onChange(e: InputEvent) { + clearInterval(interval) + const value = (e.target as HTMLInputElement).value + interval = setInterval(() => inputValue = value) + } -