From 73b0209ce59ffa8960b8d10a5bc33b541ba9e9d3 Mon Sep 17 00:00:00 2001 From: Craig Harshbarger Date: Wed, 25 Oct 2023 12:41:29 -0500 Subject: [PATCH 1/3] Start adding borders --- src/consts.ts | 5 ++- src/factories/arrow.ts | 40 ++++++-------------- src/factories/border.ts | 83 +++++++++++++++++++++++++++++++++++++++++ src/textures/cap.ts | 5 +-- src/textures/corner.ts | 31 +++++++++++++++ 5 files changed, 130 insertions(+), 34 deletions(-) create mode 100644 src/factories/border.ts create mode 100644 src/textures/corner.ts diff --git a/src/consts.ts b/src/consts.ts index 5c675a72..da6b22b1 100644 --- a/src/consts.ts +++ b/src/consts.ts @@ -6,5 +6,6 @@ export const DEFAULT_TIME_COLUMN_SPAN_SECONDS = 1 export const DEFAULT_TIME_COLUMN_SIZE_PIXELS = 20 export const DEFAULT_LINEAR_COLUMN_SIZE_PIXELS = 200 export const DEFAULT_LABEL_CULLING_THRESHOLD = 0.2 -export const DEFAULT_EDGE_MINIMUM_BEZIER = 64 -export const DEFAULT_EDGE_POINTS = 20 \ No newline at end of file +export const DEFAULT_TEXTURE_RESOLUTION = 10 +export const DEFAULT_EDGE_POINTS = 20 +export const DEFAULT_EDGE_MINIMUM_BEZIER = 64 \ No newline at end of file diff --git a/src/factories/arrow.ts b/src/factories/arrow.ts index eaa4c358..31a9b6d7 100644 --- a/src/factories/arrow.ts +++ b/src/factories/arrow.ts @@ -1,13 +1,5 @@ -import { useSubscription } from '@prefecthq/vue-compositions' -import { Graphics, Rectangle, Sprite, Texture } from 'pixi.js' -import { waitForApplication } from '@/objects' - -export type ArrowStyle = { - size: number, - radius?: number, - stroke?: number, - rotate?: number, -} +import { Sprite } from 'pixi.js' +import { CornerStyle, getCornerTexture } from '@/textures/corner' export enum ArrowDirection { Up = 0, @@ -16,22 +8,8 @@ export enum ArrowDirection { Right = 90 } -async function getArrowTexture({ size, stroke = 1, radius = 0 }: ArrowStyle): Promise { - const application = await waitForApplication() - - const graphic = new Graphics() - graphic.lineStyle(stroke, '#fff', 1, 0) - graphic.drawRoundedRect(0, 0, size * 2, size * 2, radius) - - const arrow = application.renderer.generateTexture(graphic, { - // drew a rounded rectangle and then just using one corner as the "arrow" - region: new Rectangle(0, 0, size, size), - - // manually bumping up the resolution to keep the border radius from being blurry - resolution: 10, - }) - - return arrow +export type ArrowStyle = CornerStyle & { + rotate?: number, } // eslint-disable-next-line @typescript-eslint/explicit-function-return-type @@ -39,10 +17,14 @@ export function arrowFactory() { const arrow = new Sprite() async function render(style: ArrowStyle): Promise { - const texture = (await useSubscription(getArrowTexture, [style]).promise()).response - arrow.texture = texture - const { rotate = 0 } = style + const cornerStyles: CornerStyle = { + size: style.size, + radius: style.radius, + stroke: style.stroke, + } + const texture = await getCornerTexture(cornerStyles) + arrow.texture = texture arrow.anchor.set(0.5, 0.5) // texture is the corner of a rectangle so 45 deg defaults the arrow to pointing up diff --git a/src/factories/border.ts b/src/factories/border.ts new file mode 100644 index 00000000..bc516904 --- /dev/null +++ b/src/factories/border.ts @@ -0,0 +1,83 @@ +import { ColorSource, Container, Sprite, Texture } from 'pixi.js' +import { CornerStyle, getCornerTexture } from '@/textures/corner' +import { getPixelTexture } from '@/textures/pixel' + +export type BorderStyle = { + width: number, + height: number, + stroke: number, + radius?: number, + color?: ColorSource, +} + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +export function borderFactory() { + const container = new Container() + const topLeft = new Sprite() + const topRight = new Sprite() + const bottomLeft = new Sprite() + const bottomRight = new Sprite() + const left = new Sprite() + const right = new Sprite() + const top = new Sprite() + const bottom = new Sprite() + + container.addChild(topLeft) + container.addChild(topRight) + container.addChild(bottomLeft) + container.addChild(bottomRight) + container.addChild(left) + container.addChild(right) + container.addChild(top) + container.addChild(bottom) + + async function render(style: BorderStyle): Promise { + const { radius = 0, color = '#fff', stroke } = style + const size = radius * 2 + + const cornerStyle: CornerStyle = { + size, + radius, + stroke, + } + + const corner = await getCornerTexture(cornerStyle) + const pixel = await getPixelTexture() + + setCornerTexture(corner) + setBorderTexture(pixel) + setTint(color) + + return container + } + + function setCornerTexture(texture: Texture): void { + topLeft.texture = texture + topRight.texture = texture + bottomLeft.texture = texture + bottomRight.texture = texture + } + + function setBorderTexture(texture: Texture): void { + top.texture = texture + left.texture = texture + right.texture = texture + bottom.texture = texture + } + + function setTint(color: ColorSource): void { + topLeft.tint = color + topRight.tint = color + bottomLeft.tint = color + bottomRight.tint = color + top.tint = color + left.tint = color + right.tint = color + bottom.tint = color + } + + return { + container, + render, + } +} \ No newline at end of file diff --git a/src/textures/cap.ts b/src/textures/cap.ts index b2bcc24b..a64e152c 100644 --- a/src/textures/cap.ts +++ b/src/textures/cap.ts @@ -1,5 +1,6 @@ import { useSubscription } from '@prefecthq/vue-compositions' import { Graphics, Rectangle, Texture } from 'pixi.js' +import { DEFAULT_TEXTURE_RESOLUTION } from '@/consts' import { waitForApplication } from '@/objects/application' export type CapStyle = { @@ -18,9 +19,7 @@ async function cap({ height, radius }: CapStyle): Promise { const cap = application.renderer.generateTexture(graphic, { // drew a rounded rectangle and then just using half of the graphic to get just the left "cap" region: new Rectangle(0, 0, radius, height), - - // manually bumping up the resolution to keep the border radius from being blurry - resolution: 10, + resolution: DEFAULT_TEXTURE_RESOLUTION, }) return cap diff --git a/src/textures/corner.ts b/src/textures/corner.ts new file mode 100644 index 00000000..8f34ee98 --- /dev/null +++ b/src/textures/corner.ts @@ -0,0 +1,31 @@ +import { useSubscription } from '@prefecthq/vue-compositions' +import { Graphics, Rectangle, Texture } from 'pixi.js' +import { waitForApplication } from '@/objects/application' + +export type CornerStyle = { + size: number, + radius?: number, + stroke?: number, +} + +async function texture({ size, stroke = 1, radius = 0 }: CornerStyle): Promise { + const application = await waitForApplication() + + const graphic = new Graphics() + graphic.lineStyle(stroke, '#fff', 1, 0) + graphic.drawRoundedRect(0, 0, size * 2, size * 2, radius) + + const arrow = application.renderer.generateTexture(graphic, { + // drew a rounded rectangle and then just using one corner as the "arrow" + region: new Rectangle(0, 0, size, size), + + // manually bumping up the resolution to keep the border radius from being blurry + resolution: 10, + }) + + return arrow +} + +export async function getCornerTexture(style: CornerStyle): Promise { + return (await useSubscription(texture, [style]).promise()).response +} \ No newline at end of file From 11b3f22e1ade7294e6d28e5b9a2f6de3acdfac12 Mon Sep 17 00:00:00 2001 From: Craig Harshbarger Date: Thu, 26 Oct 2023 08:22:15 -0500 Subject: [PATCH 2/3] Get some borders working --- src/factories/border.ts | 82 +++++++++++++++++++++++++++++--- src/factories/nodeArrowButton.ts | 11 +++++ 2 files changed, 87 insertions(+), 6 deletions(-) diff --git a/src/factories/border.ts b/src/factories/border.ts index bc516904..0a0db804 100644 --- a/src/factories/border.ts +++ b/src/factories/border.ts @@ -22,6 +22,24 @@ export function borderFactory() { const top = new Sprite() const bottom = new Sprite() + topLeft.name = 'border-corner-top-left' + topRight.name = 'border-corner-top-right' + bottomLeft.name = 'border-corner-bottom-left' + bottomRight.name = 'border-corner-bottom-right' + left.name = 'border-corner-left' + right.name = 'border-corner-right' + top.name = 'border-corner-top' + bottom.name = 'border-corner-bottom' + + topRight.anchor.x = 1 + topRight.scale.x = -1 + bottomLeft.anchor.y = 1 + bottomLeft.scale.y = -1 + bottomRight.anchor.x = 1 + bottomRight.scale.x = -1 + bottomRight.anchor.y = 1 + bottomRight.scale.y = -1 + container.addChild(topLeft) container.addChild(topRight) container.addChild(bottomLeft) @@ -32,7 +50,7 @@ export function borderFactory() { container.addChild(bottom) async function render(style: BorderStyle): Promise { - const { radius = 0, color = '#fff', stroke } = style + const { radius = 0, color = '#fff', stroke, width, height } = style const size = radius * 2 const cornerStyle: CornerStyle = { @@ -44,25 +62,77 @@ export function borderFactory() { const corner = await getCornerTexture(cornerStyle) const pixel = await getPixelTexture() - setCornerTexture(corner) - setBorderTexture(pixel) + updateCorners({ + texture: corner, + width, + height, + size, + }) + + updateBorders({ + texture: pixel, + width, + height, + size, + stroke, + }) + setTint(color) return container } - function setCornerTexture(texture: Texture): void { + type UpdateCorners = { + texture: Texture, + width: number, + height: number, + size: number, + } + + function updateCorners({ texture, width, height, size }: UpdateCorners): void { topLeft.texture = texture topRight.texture = texture bottomLeft.texture = texture bottomRight.texture = texture + + topLeft.position.set(0, 0) + topRight.position.set(width - size, 0) + bottomLeft.position.set(0, height - size) + bottomRight.position.set(width - size, height - size) + } + + type UpdateBorders = { + texture: Texture, + width: number, + height: number, + size: number, + stroke: number, } - function setBorderTexture(texture: Texture): void { + function updateBorders({ texture, size, width, height, stroke }: UpdateBorders): void { + const sidesHeight = height - size * 2 + const topAndBottomWidth = width - size * 2 + top.texture = texture left.texture = texture right.texture = texture bottom.texture = texture + + left.position.set(0, size) + left.height = sidesHeight + left.width = stroke + + right.position.set(width - stroke, size) + right.height = sidesHeight + right.width = stroke + + top.position.set(size, 0) + top.width = topAndBottomWidth + top.height = stroke + + bottom.position.set(size, height - stroke) + bottom.width = topAndBottomWidth + bottom.height = stroke } function setTint(color: ColorSource): void { @@ -77,7 +147,7 @@ export function borderFactory() { } return { - container, + border: container, render, } } \ No newline at end of file diff --git a/src/factories/nodeArrowButton.ts b/src/factories/nodeArrowButton.ts index 7ea0dcb3..2f1c7ade 100644 --- a/src/factories/nodeArrowButton.ts +++ b/src/factories/nodeArrowButton.ts @@ -1,6 +1,7 @@ import { Container, ColorMatrixFilter } from 'pixi.js' import { ArrowDirection, ArrowStyle, arrowFactory } from '@/factories/arrow' import { BarStyle, barFactory } from '@/factories/bar' +import { borderFactory } from '@/factories/border' type NodeArrowBarStyles = { arrow: Omit, @@ -13,12 +14,14 @@ export async function nodeArrowButtonFactory() { const container = new Container() const { arrow, render: renderArrow } = await arrowFactory() const { bar, render: renderBar } = await barFactory() + const { border, render: renderBorder } = await borderFactory() const filter = new ColorMatrixFilter() container.eventMode = 'static' container.cursor = 'pointer' container.addChild(bar) container.addChild(arrow) + container.addChild(border) container.on('mouseover', onMouseover) container.on('mouseout', onMouseout) @@ -30,6 +33,14 @@ export async function nodeArrowButtonFactory() { const arrow = await renderArrow({ ...arrowStyles, rotate }) const bar = await renderBar(buttonStyles) + await renderBorder({ + width: buttonStyles.width, + height: buttonStyles.height, + radius: buttonStyles.radius, + stroke: 1, + color: 'green', + }) + const middle = { y: bar.height / 2, x: bar.width / 2, From 76279e1a8a696476501aba61a5f8404fedc31e8a Mon Sep 17 00:00:00 2001 From: Craig Harshbarger Date: Thu, 26 Oct 2023 11:27:28 -0500 Subject: [PATCH 3/3] Get the node toggle styled up with a border --- package-lock.json | 22 ----------------- package.json | 1 - src/factories/nodeArrowButton.ts | 41 ++++++++++++++++++++++++-------- src/factories/nodeFlowRun.ts | 33 ++++--------------------- src/models/RunGraph.ts | 8 +++++-- src/objects/config.ts | 3 +++ 6 files changed, 45 insertions(+), 63 deletions(-) diff --git a/package-lock.json b/package-lock.json index f70cfb3c..682b89e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31,7 +31,6 @@ "eslint": "8.52.0", "postcss": "8.4.31", "tailwindcss": "3.3.3", - "ts-essentials": "^9.4.1", "tsc-alias": "1.8.8", "typescript": "5.2.2", "vite": "4.5.0", @@ -6599,20 +6598,6 @@ "typescript": ">=4.2.0" } }, - "node_modules/ts-essentials": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-9.4.1.tgz", - "integrity": "sha512-oke0rI2EN9pzHsesdmrOrnqv1eQODmJpd/noJjwj2ZPC3Z4N2wbjrOEqnsEgmvlO2+4fBb0a794DCna2elEVIQ==", - "dev": true, - "peerDependencies": { - "typescript": ">=4.1.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -11806,13 +11791,6 @@ "dev": true, "requires": {} }, - "ts-essentials": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-9.4.1.tgz", - "integrity": "sha512-oke0rI2EN9pzHsesdmrOrnqv1eQODmJpd/noJjwj2ZPC3Z4N2wbjrOEqnsEgmvlO2+4fBb0a794DCna2elEVIQ==", - "dev": true, - "requires": {} - }, "ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", diff --git a/package.json b/package.json index 02b83641..f4319905 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,6 @@ "eslint": "8.52.0", "postcss": "8.4.31", "tailwindcss": "3.3.3", - "ts-essentials": "^9.4.1", "tsc-alias": "1.8.8", "typescript": "5.2.2", "vite": "4.5.0", diff --git a/src/factories/nodeArrowButton.ts b/src/factories/nodeArrowButton.ts index 2f1c7ade..f25b148b 100644 --- a/src/factories/nodeArrowButton.ts +++ b/src/factories/nodeArrowButton.ts @@ -1,16 +1,18 @@ -import { Container, ColorMatrixFilter } from 'pixi.js' -import { ArrowDirection, ArrowStyle, arrowFactory } from '@/factories/arrow' -import { BarStyle, barFactory } from '@/factories/bar' +import { Container, ColorMatrixFilter, ColorSource } from 'pixi.js' +import { ArrowDirection, arrowFactory } from '@/factories/arrow' +import { barFactory } from '@/factories/bar' import { borderFactory } from '@/factories/border' +import { waitForConfig } from '@/objects/config' type NodeArrowBarStyles = { - arrow: Omit, - button: BarStyle, + inside: boolean, + background: ColorSource, isOpen: boolean, } // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export async function nodeArrowButtonFactory() { + const config = await waitForConfig() const container = new Container() const { arrow, render: renderArrow } = await arrowFactory() const { bar, render: renderBar } = await barFactory() @@ -28,19 +30,36 @@ export async function nodeArrowButtonFactory() { bar.filters = [filter] - async function render({ arrow: arrowStyles, button: buttonStyles, isOpen }: NodeArrowBarStyles): Promise { - const rotate = isOpen ? ArrowDirection.Up : ArrowDirection.Down - const arrow = await renderArrow({ ...arrowStyles, rotate }) + border.visible = false + + async function render({ inside, isOpen, background }: NodeArrowBarStyles): Promise { + const arrowStyles = { + size: 10, + stroke: 2, + rotate: isOpen ? ArrowDirection.Up : ArrowDirection.Down, + } + + const arrow = await renderArrow(arrowStyles) + + const buttonStyles = { + width: config.styles.nodeToggleSize, + height: config.styles.nodeToggleSize, + background: inside ? background : '#333', + radius: config.styles.nodeToggleBorderRadius, + } + const bar = await renderBar(buttonStyles) - await renderBorder({ + const border = await renderBorder({ width: buttonStyles.width, height: buttonStyles.height, radius: buttonStyles.radius, stroke: 1, - color: 'green', + color: config.styles.nodeToggleBorderColor, }) + border.alpha = inside ? 0 : 1 + const middle = { y: bar.height / 2, x: bar.width / 2, @@ -56,10 +75,12 @@ export async function nodeArrowButtonFactory() { function onMouseover(): void { filter.brightness(0.5, false) + border.visible = true } function onMouseout(): void { filter.brightness(1, false) + border.visible = false } return { diff --git a/src/factories/nodeFlowRun.ts b/src/factories/nodeFlowRun.ts index deffaaef..9c3b831c 100644 --- a/src/factories/nodeFlowRun.ts +++ b/src/factories/nodeFlowRun.ts @@ -78,22 +78,14 @@ export async function flowRunContainerFactory(node: RunGraphNode) { } async function renderArrowButton(): Promise { - const offset = 4 - const buttonSize = config.styles.nodeHeight - offset + const buttonSize = config.styles.nodeToggleSize + const offset = config.styles.nodeHeight - buttonSize const inside = bar.width > buttonSize - const background = getArrowButtonBackground({ inside }) + const { background = '#fff' } = config.styles.node(node) const container = await renderArrowButtonContainer({ - arrow: { - size: 10, - stroke: 2, - }, - button: { - width: buttonSize, - height: buttonSize, - background: background, - radius: config.styles.nodeBorderRadius - offset / 2, - }, + background, + inside, isOpen, }) @@ -103,21 +95,6 @@ export async function flowRunContainerFactory(node: RunGraphNode) { return container } - type ArrowButtonBackgroundParameters = { - inside: boolean, - } - - function getArrowButtonBackground({ inside }: ArrowButtonBackgroundParameters): ColorSource { - if (inside) { - const { background } = config.styles.node(node) - - return background ?? '#fff' - } - - return '#333' - } - - async function renderLabel(): Promise { const label = await renderLabelText(node) diff --git a/src/models/RunGraph.ts b/src/models/RunGraph.ts index 31795695..c36db992 100644 --- a/src/models/RunGraph.ts +++ b/src/models/RunGraph.ts @@ -1,5 +1,4 @@ import { ColorSource } from 'pixi.js' -import { DeepRequired } from 'ts-essentials' import { StateType } from '@/models/states' import { ViewportDateRange } from '@/models/viewport' @@ -51,6 +50,9 @@ export type RunGraphStyles = { nodeHeight?: number, nodeMargin?: number, nodeBorderRadius?: number, + nodeToggleSize?: number, + nodeToggleBorderRadius?: number, + nodeToggleBorderColor?: ColorSource, edgeColor?: string, node?: (node: RunGraphNode) => RunGraphNodeStyles, } @@ -62,4 +64,6 @@ export type RunGraphConfig = { styles?: RunGraphStyles, } -export type RequiredGraphConfig = DeepRequired \ No newline at end of file +export type RequiredGraphConfig = Omit & { + styles: Required, +} \ No newline at end of file diff --git a/src/objects/config.ts b/src/objects/config.ts index 354ac695..af1caa17 100644 --- a/src/objects/config.ts +++ b/src/objects/config.ts @@ -12,6 +12,9 @@ const defaults: Omit = { nodeHeight: 30, nodeMargin: 4, nodeBorderRadius: 8, + nodeToggleSize: 26, + nodeToggleBorderRadius: 6, + nodeToggleBorderColor: '#51525C', edgeColor: '#51525C', node: () => ({ background: '#ffffff',