From 6d55ad2571ebf974bb39deb4986669a14d143be0 Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Mon, 2 Jun 2025 00:25:49 -0400 Subject: [PATCH 01/17] feat: Add Polygon primative to draw number of point polygons such as triangles, hexagon, and octagons including stars. Includes rounding corners and adjustable insetting. --- .../src/lib/components/Polygon.svelte | 216 ++++++++++++++++++ .../layerchart/src/lib/components/index.ts | 2 + packages/layerchart/src/lib/utils/path.ts | 33 +++ packages/layerchart/src/lib/utils/shape.ts | 57 +++++ .../layerchart/src/routes/_NavMenu.svelte | 1 + .../docs/components/Polygon/+page.svelte | 113 +++++++++ .../routes/docs/components/Polygon/+page.ts | 13 ++ .../routes/docs/components/Text/+page.svelte | 4 +- 8 files changed, 436 insertions(+), 3 deletions(-) create mode 100644 packages/layerchart/src/lib/components/Polygon.svelte create mode 100644 packages/layerchart/src/lib/utils/shape.ts create mode 100644 packages/layerchart/src/routes/docs/components/Polygon/+page.svelte create mode 100644 packages/layerchart/src/routes/docs/components/Polygon/+page.ts diff --git a/packages/layerchart/src/lib/components/Polygon.svelte b/packages/layerchart/src/lib/components/Polygon.svelte new file mode 100644 index 000000000..9e83533d4 --- /dev/null +++ b/packages/layerchart/src/lib/components/Polygon.svelte @@ -0,0 +1,216 @@ + + + + +{#if renderCtx === 'svg'} + +{/if} diff --git a/packages/layerchart/src/lib/components/index.ts b/packages/layerchart/src/lib/components/index.ts index a09017b2e..96a64e4ef 100644 --- a/packages/layerchart/src/lib/components/index.ts +++ b/packages/layerchart/src/lib/components/index.ts @@ -103,6 +103,8 @@ export { default as Point } from './Point.svelte'; export * from './Point.svelte'; export { default as Points } from './Points.svelte'; export * from './Points.svelte'; +export { default as Polygon } from './Polygon.svelte'; +export * from './Polygon.svelte'; export { default as RadialGradient } from './RadialGradient.svelte'; export * from './RadialGradient.svelte'; export { default as Rect } from './Rect.svelte'; diff --git a/packages/layerchart/src/lib/utils/path.ts b/packages/layerchart/src/lib/utils/path.ts index 98305a127..8e425838d 100644 --- a/packages/layerchart/src/lib/utils/path.ts +++ b/packages/layerchart/src/lib/utils/path.ts @@ -58,6 +58,39 @@ export function spikePath({ return pathData; } +/** Create rounded polygon path + * + * @param coords - Array of points (x, y) + * @param radius - Radius of the curve + * @param close - Whether to close the path + * @returns String of path data + */ +export function roundedPolygonPath( + coords: { x: number; y: number }[], + radius: number, + close: boolean +) { + // See: https://stackoverflow.com/questions/10177985/svg-rounded-corner + + let path = ''; + const length = coords.length + (close ? 1 : -1); + for (let i = 0; i < length; i++) { + const a = coords[i % coords.length]; + const b = coords[(i + 1) % coords.length]; + const t = Math.min(radius / Math.hypot(b.x - a.x, b.y - a.y), 0.5); + + if (i > 0) path += `Q${a.x},${a.y} ${a.x * (1 - t) + b.x * t},${a.y * (1 - t) + b.y * t}`; + + if (!close && i == 0) path += `M${a.x},${a.y}`; + else if (i == 0) path += `M${a.x * (1 - t) + b.x * t},${a.y * (1 - t) + b.y * t}`; + + if (!close && i == length - 1) path += `L${b.x},${b.y}`; + else if (i < length - 1) path += `L${a.x * t + b.x * (1 - t)},${a.y * t + b.y * (1 - t)}`; + } + if (close) path += 'Z'; + return path; +} + /** Flatten all `y` coordinates to `0` */ export function flattenPathData(pathData: string, yOverride = 0) { let result = pathData; diff --git a/packages/layerchart/src/lib/utils/shape.ts b/packages/layerchart/src/lib/utils/shape.ts new file mode 100644 index 000000000..98ae27472 --- /dev/null +++ b/packages/layerchart/src/lib/utils/shape.ts @@ -0,0 +1,57 @@ +import { range } from 'd3-array'; +import { degreesToRadians } from './math.js'; + +/** Get points to create a polygon with given number of points and radius + * + * @param count - Number of points + * @param radius - Radius of the polygon + * @returns Array of points (angle, radius) + */ +export function polygonPoints(count: number, radius: number) { + const angle = 360 / count; + const offsetDeg = 90 - (180 - angle) / 2; + const offset = degreesToRadians(offsetDeg); + + return range(count).map((index) => { + return { + angle: offset + degreesToRadians(angle * index), + radius, + }; + }); +} + +/** Create polygon + * + * @param cx - Center x coordinate + * @param cy - Center y coordinate + * @param count - Number of points + * @param radius - Radius of the polygon + * @returns Array of points (x, y) + */ +export function polygon(cx: number, cy: number, count: number, radius: number) { + return polygonPoints(count, radius).map(({ angle, radius }) => { + return { + x: cx + radius * Math.cos(angle), + y: cy + radius * Math.sin(angle), + }; + }); +} + +/** Create star using polygon points and adjusting odd points by `percent` (<1 inset, >1 outset) + * + * @param cx - Center x coordinate + * @param cy - Center y coordinate + * @param count - Number of points + * @param radius - Radius of the polygon + * @param percent - Percent to inset odd points + * @returns Array of points (x, y) + */ +export function star(cx: number, cy: number, count: number, radius: number, percent: number) { + return polygonPoints(count, radius).map(({ angle, radius }, i) => { + const scale = i % 2 == 0 ? 1 : percent; + return { + x: cx + radius * scale * Math.cos(angle), + y: cy + radius * scale * Math.sin(angle), + }; + }); +} diff --git a/packages/layerchart/src/routes/_NavMenu.svelte b/packages/layerchart/src/routes/_NavMenu.svelte index 0a855771d..e139a09b7 100644 --- a/packages/layerchart/src/routes/_NavMenu.svelte +++ b/packages/layerchart/src/routes/_NavMenu.svelte @@ -74,6 +74,7 @@ 'Line', 'Marker', 'Point', + 'Polygon', 'Rect', 'Text', ], diff --git a/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte new file mode 100644 index 000000000..447406f90 --- /dev/null +++ b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte @@ -0,0 +1,113 @@ + + +

Playground

+ +
+ + + +
+ +
+ + {#snippet children({ context })} + + + + {/snippet} + +
+ +

Examples

+ +
+

Simple

+ +
+ +
+ {#each simpleExamples as example} +
+

{example.label}

+ +
+ + {#snippet children({ context })} + + + + {/snippet} + +
+
+
+ {/each} +
+ +

Stars

+ +
+ + +
+ +
+ {#each starExamples as example} +
+

{example.label}

+ +
+ + + + + +
+
+
+ {/each} +
diff --git a/packages/layerchart/src/routes/docs/components/Polygon/+page.ts b/packages/layerchart/src/routes/docs/components/Polygon/+page.ts new file mode 100644 index 000000000..7efff572c --- /dev/null +++ b/packages/layerchart/src/routes/docs/components/Polygon/+page.ts @@ -0,0 +1,13 @@ +import api from '$lib/components/Polygon.svelte?raw&sveld'; +import source from '$lib/components/Polygon.svelte?raw'; +import pageSource from './+page.svelte?raw'; + +export async function load() { + return { + meta: { + api, + source, + pageSource, + }, + }; +} diff --git a/packages/layerchart/src/routes/docs/components/Text/+page.svelte b/packages/layerchart/src/routes/docs/components/Text/+page.svelte index 89666e161..3300d5e69 100644 --- a/packages/layerchart/src/routes/docs/components/Text/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/Text/+page.svelte @@ -29,9 +29,7 @@ }); -

Examples

- -

Playground

+

Playground

From c7083a74cc67713a898583fa05a651272a5543dd Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Mon, 2 Jun 2025 07:29:28 -0400 Subject: [PATCH 02/17] Simplify roundedPolygonPath --- packages/layerchart/src/lib/components/Polygon.svelte | 2 +- packages/layerchart/src/lib/utils/path.ts | 11 +++-------- .../src/routes/docs/components/Polygon/+page.svelte | 4 ++-- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/packages/layerchart/src/lib/components/Polygon.svelte b/packages/layerchart/src/lib/components/Polygon.svelte index 9e83533d4..b286ddfc8 100644 --- a/packages/layerchart/src/lib/components/Polygon.svelte +++ b/packages/layerchart/src/lib/components/Polygon.svelte @@ -139,7 +139,7 @@ star(motionCx.current, motionCy.current, points, motionR.current, inset) ); - let d = $derived(roundedPolygonPath(starPoints, curveRadius, true)); + let d = $derived(roundedPolygonPath(starPoints, curveRadius)); const extractedTween = extractTweenConfig(motion); diff --git a/packages/layerchart/src/lib/utils/path.ts b/packages/layerchart/src/lib/utils/path.ts index 8e425838d..76a94ef24 100644 --- a/packages/layerchart/src/lib/utils/path.ts +++ b/packages/layerchart/src/lib/utils/path.ts @@ -62,18 +62,13 @@ export function spikePath({ * * @param coords - Array of points (x, y) * @param radius - Radius of the curve - * @param close - Whether to close the path * @returns String of path data */ -export function roundedPolygonPath( - coords: { x: number; y: number }[], - radius: number, - close: boolean -) { +export function roundedPolygonPath(coords: { x: number; y: number }[], radius: number) { // See: https://stackoverflow.com/questions/10177985/svg-rounded-corner let path = ''; - const length = coords.length + (close ? 1 : -1); + const length = coords.length + 1; for (let i = 0; i < length; i++) { const a = coords[i % coords.length]; const b = coords[(i + 1) % coords.length]; @@ -87,7 +82,7 @@ export function roundedPolygonPath( if (!close && i == length - 1) path += `L${b.x},${b.y}`; else if (i < length - 1) path += `L${a.x * t + b.x * (1 - t)},${a.y * t + b.y * (1 - t)}`; } - if (close) path += 'Z'; + path += 'Z'; return path; } diff --git a/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte index 447406f90..b53a3ef8e 100644 --- a/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte @@ -3,9 +3,9 @@ import { RangeField } from 'svelte-ux'; import Preview from 'layerchart/docs/Preview.svelte'; - let points = $state(3); + let points = $state(10); let curveRadius = $state(0); - let inset = $state(0.5); + let inset = $state(0.3); const simpleExamples = [ { label: 'Triangle', points: 3 }, From d32d8ec16c49abef5f10535f1e147279cefd4ccc Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Mon, 2 Jun 2025 07:55:19 -0400 Subject: [PATCH 03/17] Cleanup --- packages/layerchart/src/lib/components/Polygon.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/layerchart/src/lib/components/Polygon.svelte b/packages/layerchart/src/lib/components/Polygon.svelte index b286ddfc8..be841ec36 100644 --- a/packages/layerchart/src/lib/components/Polygon.svelte +++ b/packages/layerchart/src/lib/components/Polygon.svelte @@ -96,7 +96,7 @@ import { renderPathData, type ComputedStylesOptions } from '$lib/utils/canvas.js'; import { createKey } from '$lib/utils/key.svelte.js'; import { layerClass } from '$lib/utils/attributes.js'; - import { polygon, star } from '$lib/utils/shape.js'; + import { star } from '$lib/utils/shape.js'; import { roundedPolygonPath } from '$lib/utils/path.js'; let { From 36eae0cec6fa1d7453ef38e15bbbcf9d99251b75 Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Mon, 2 Jun 2025 10:49:31 -0400 Subject: [PATCH 04/17] Support rotation and simplify --- .../src/lib/components/Polygon.svelte | 27 +++++++++---- packages/layerchart/src/lib/utils/shape.ts | 40 +++++++------------ .../docs/components/Polygon/+page.svelte | 15 +++---- 3 files changed, 43 insertions(+), 39 deletions(-) diff --git a/packages/layerchart/src/lib/components/Polygon.svelte b/packages/layerchart/src/lib/components/Polygon.svelte index be841ec36..e0d8237b9 100644 --- a/packages/layerchart/src/lib/components/Polygon.svelte +++ b/packages/layerchart/src/lib/components/Polygon.svelte @@ -58,6 +58,13 @@ */ curveRadius?: number; + /** + * The rotation of the polygon. + * + * @default 0 + */ + rotate?: number; + /** * The percent to inset the odd points of the star (<1 inset, >1 outset) * @@ -96,7 +103,7 @@ import { renderPathData, type ComputedStylesOptions } from '$lib/utils/canvas.js'; import { createKey } from '$lib/utils/key.svelte.js'; import { layerClass } from '$lib/utils/attributes.js'; - import { star } from '$lib/utils/shape.js'; + import { polygon } from '$lib/utils/shape.js'; import { roundedPolygonPath } from '$lib/utils/path.js'; let { @@ -108,6 +115,7 @@ initialR: initialRProp, points = 4, curveRadius = 0, + rotate = 0, inset = 1, motion, fill, @@ -134,12 +142,17 @@ const motionCy = createMotion(initialCy, () => cy, motion); const motionR = createMotion(initialR, () => r, motion); - // let polygonPoints = $derived(polygon(cx, cy, points, r)); - let starPoints = $derived( - star(motionCx.current, motionCy.current, points, motionR.current, inset) + let polygonPoints = $derived( + polygon({ + cx: motionCx.current, + cy: motionCy.current, + count: points, + radius: motionR.current, + rotate, + inset, + }) ); - - let d = $derived(roundedPolygonPath(starPoints, curveRadius)); + let d = $derived(roundedPolygonPath(polygonPoints, curveRadius)); const extractedTween = extractTweenConfig(motion); @@ -150,7 +163,7 @@ } : undefined; - const tweenedState = createMotion(null /*defaultPathData()*/, () => d, tweenedOptions); + const tweenedState = createMotion(null, () => d, tweenedOptions); const renderCtx = getRenderContext(); diff --git a/packages/layerchart/src/lib/utils/shape.ts b/packages/layerchart/src/lib/utils/shape.ts index 98ae27472..d728850fd 100644 --- a/packages/layerchart/src/lib/utils/shape.ts +++ b/packages/layerchart/src/lib/utils/shape.ts @@ -7,14 +7,12 @@ import { degreesToRadians } from './math.js'; * @param radius - Radius of the polygon * @returns Array of points (angle, radius) */ -export function polygonPoints(count: number, radius: number) { +export function polygonPoints(count: number, radius: number, rotate: number = 0) { const angle = 360 / count; - const offsetDeg = 90 - (180 - angle) / 2; - const offset = degreesToRadians(offsetDeg); return range(count).map((index) => { return { - angle: offset + degreesToRadians(angle * index), + angle: degreesToRadians(angle * index) + degreesToRadians(rotate), radius, }; }); @@ -26,29 +24,21 @@ export function polygonPoints(count: number, radius: number) { * @param cy - Center y coordinate * @param count - Number of points * @param radius - Radius of the polygon + * @param rotate - Rotation of the polygon (degrees) + * @param inset - Percent to inset odd points (<1 inset, >1 outset) * @returns Array of points (x, y) */ -export function polygon(cx: number, cy: number, count: number, radius: number) { - return polygonPoints(count, radius).map(({ angle, radius }) => { - return { - x: cx + radius * Math.cos(angle), - y: cy + radius * Math.sin(angle), - }; - }); -} - -/** Create star using polygon points and adjusting odd points by `percent` (<1 inset, >1 outset) - * - * @param cx - Center x coordinate - * @param cy - Center y coordinate - * @param count - Number of points - * @param radius - Radius of the polygon - * @param percent - Percent to inset odd points - * @returns Array of points (x, y) - */ -export function star(cx: number, cy: number, count: number, radius: number, percent: number) { - return polygonPoints(count, radius).map(({ angle, radius }, i) => { - const scale = i % 2 == 0 ? 1 : percent; +export function polygon(options: { + cx: number; + cy: number; + count: number; + radius: number; + rotate?: number; + inset?: number; +}) { + const { cx, cy, count, radius, rotate = 0, inset = 1 } = options; + return polygonPoints(count, radius, rotate).map(({ angle, radius }, i) => { + const scale = i % 2 == 0 ? 1 : inset; return { x: cx + radius * scale * Math.cos(angle), y: cy + radius * scale * Math.sin(angle), diff --git a/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte index b53a3ef8e..27189bb09 100644 --- a/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte @@ -6,10 +6,11 @@ let points = $state(10); let curveRadius = $state(0); let inset = $state(0.3); + let rotate = $state(0); const simpleExamples = [ { label: 'Triangle', points: 3 }, - { label: 'Square', points: 4 }, + { label: 'Square / Diamond', points: 4 }, { label: 'Pentagon', points: 5 }, { label: 'Hexagon', points: 6 }, { label: 'Octagon', points: 8 }, @@ -29,10 +30,11 @@

Playground

-
+
- + +
@@ -46,10 +48,7 @@ {points} {curveRadius} {inset} - motion={{ - type: 'tween', - duration: 200, - }} + {rotate} /> {/snippet} @@ -60,6 +59,7 @@

Simple

+
@@ -78,6 +78,7 @@ r={60} points={example.points} {curveRadius} + {rotate} /> {/snippet} From 599047ef07766ffd89c0c9fa9603adf0e82ffc14 Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Mon, 2 Jun 2025 10:55:36 -0400 Subject: [PATCH 05/17] Simplify roundedPolygonPath if no radius --- packages/layerchart/src/lib/utils/path.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/layerchart/src/lib/utils/path.ts b/packages/layerchart/src/lib/utils/path.ts index 76a94ef24..f8a4344c4 100644 --- a/packages/layerchart/src/lib/utils/path.ts +++ b/packages/layerchart/src/lib/utils/path.ts @@ -65,7 +65,13 @@ export function spikePath({ * @returns String of path data */ export function roundedPolygonPath(coords: { x: number; y: number }[], radius: number) { - // See: https://stackoverflow.com/questions/10177985/svg-rounded-corner + if (radius === 0) { + // Simple polygon with straight lines + return `M${coords[0].x},${coords[0].y}${coords + .slice(1) + .map((p) => `L${p.x},${p.y}`) + .join('')}Z`; + } let path = ''; const length = coords.length + 1; @@ -74,13 +80,9 @@ export function roundedPolygonPath(coords: { x: number; y: number }[], radius: n const b = coords[(i + 1) % coords.length]; const t = Math.min(radius / Math.hypot(b.x - a.x, b.y - a.y), 0.5); + if (i == 0) path += `M${a.x * (1 - t) + b.x * t},${a.y * (1 - t) + b.y * t}`; if (i > 0) path += `Q${a.x},${a.y} ${a.x * (1 - t) + b.x * t},${a.y * (1 - t) + b.y * t}`; - - if (!close && i == 0) path += `M${a.x},${a.y}`; - else if (i == 0) path += `M${a.x * (1 - t) + b.x * t},${a.y * (1 - t) + b.y * t}`; - - if (!close && i == length - 1) path += `L${b.x},${b.y}`; - else if (i < length - 1) path += `L${a.x * t + b.x * (1 - t)},${a.y * t + b.y * (1 - t)}`; + if (i < length - 1) path += `L${a.x * t + b.x * (1 - t)},${a.y * t + b.y * (1 - t)}`; } path += 'Z'; return path; From a57a425c34844d6274a5b4c7203cf962ce11a72e Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Mon, 2 Jun 2025 10:57:03 -0400 Subject: [PATCH 06/17] Update docs --- .../routes/docs/components/Polygon/+page.svelte | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte index 27189bb09..9f701d279 100644 --- a/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte @@ -46,9 +46,9 @@ cy={context.height / 2} r={100} {points} - {curveRadius} {inset} {rotate} + {curveRadius} /> {/snippet} @@ -77,8 +77,8 @@ cy={context.height / 2} r={60} points={example.points} - {curveRadius} {rotate} + {curveRadius} /> {/snippet} @@ -92,8 +92,9 @@

Stars

- + +
@@ -104,7 +105,15 @@
- +
From 894b4aaa6879971edcb78db5b1bc88750ec5c506 Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Tue, 3 Jun 2025 00:39:24 -0400 Subject: [PATCH 07/17] feat: Add Ellipse primitive --- .changeset/nine-pens-design.md | 5 + .changeset/wide-berries-invite.md | 5 + .../src/lib/components/Ellipse.svelte | 187 ++++++++++++++++++ .../layerchart/src/lib/components/index.ts | 2 + packages/layerchart/src/lib/utils/canvas.ts | 22 +++ .../layerchart/src/routes/_NavMenu.svelte | 1 + .../docs/components/Ellipse/+page.svelte | 26 +++ .../routes/docs/components/Ellipse/+page.ts | 14 ++ 8 files changed, 262 insertions(+) create mode 100644 .changeset/nine-pens-design.md create mode 100644 .changeset/wide-berries-invite.md create mode 100644 packages/layerchart/src/lib/components/Ellipse.svelte create mode 100644 packages/layerchart/src/routes/docs/components/Ellipse/+page.svelte create mode 100644 packages/layerchart/src/routes/docs/components/Ellipse/+page.ts diff --git a/.changeset/nine-pens-design.md b/.changeset/nine-pens-design.md new file mode 100644 index 000000000..b37e68316 --- /dev/null +++ b/.changeset/nine-pens-design.md @@ -0,0 +1,5 @@ +--- +'layerchart': patch +--- + +feat: Add Polygon primitive diff --git a/.changeset/wide-berries-invite.md b/.changeset/wide-berries-invite.md new file mode 100644 index 000000000..396e96a1a --- /dev/null +++ b/.changeset/wide-berries-invite.md @@ -0,0 +1,5 @@ +--- +'layerchart': patch +--- + +feat: Add Ellipse primitive diff --git a/packages/layerchart/src/lib/components/Ellipse.svelte b/packages/layerchart/src/lib/components/Ellipse.svelte new file mode 100644 index 000000000..3bde17ec2 --- /dev/null +++ b/packages/layerchart/src/lib/components/Ellipse.svelte @@ -0,0 +1,187 @@ + + + + +{#if renderCtx === 'svg'} + +{/if} diff --git a/packages/layerchart/src/lib/components/index.ts b/packages/layerchart/src/lib/components/index.ts index 96a64e4ef..1e4cfa35a 100644 --- a/packages/layerchart/src/lib/components/index.ts +++ b/packages/layerchart/src/lib/components/index.ts @@ -43,6 +43,8 @@ export { default as Connector } from './Connector.svelte'; export * from './Connector.svelte'; export { default as Dagre } from './Dagre.svelte'; export * from './Dagre.svelte'; +export { default as Ellipse } from './Ellipse.svelte'; +export * from './Ellipse.svelte'; export { default as Frame } from './Frame.svelte'; export * from './Frame.svelte'; export { default as ForceSimulation } from './ForceSimulation.svelte'; diff --git a/packages/layerchart/src/lib/utils/canvas.ts b/packages/layerchart/src/lib/utils/canvas.ts index 7ee44231f..329dbac31 100644 --- a/packages/layerchart/src/lib/utils/canvas.ts +++ b/packages/layerchart/src/lib/utils/canvas.ts @@ -232,6 +232,28 @@ export function renderCircle( ctx.closePath(); } +export function renderEllipse( + ctx: CanvasRenderingContext2D, + coords: { cx: number; cy: number; rx: number; ry: number }, + styleOptions: ComputedStylesOptions = {} +) { + ctx.beginPath(); + ctx.ellipse(coords.cx, coords.cy, coords.rx, coords.ry, 0, 0, 2 * Math.PI); + render( + ctx, + { + fill: (ctx) => { + ctx.fill(); + }, + stroke: (ctx) => { + ctx.stroke(); + }, + }, + styleOptions + ); + ctx.closePath(); +} + /** Clear canvas accounting for Canvas `context.translate(...)` */ export function clearCanvasContext( ctx: CanvasRenderingContext2D, diff --git a/packages/layerchart/src/routes/_NavMenu.svelte b/packages/layerchart/src/routes/_NavMenu.svelte index e139a09b7..7fc79bf8b 100644 --- a/packages/layerchart/src/routes/_NavMenu.svelte +++ b/packages/layerchart/src/routes/_NavMenu.svelte @@ -70,6 +70,7 @@ 'Bar', 'Circle', 'Connector', + 'Ellipse', 'Group', 'Line', 'Marker', diff --git a/packages/layerchart/src/routes/docs/components/Ellipse/+page.svelte b/packages/layerchart/src/routes/docs/components/Ellipse/+page.svelte new file mode 100644 index 000000000..633b162d4 --- /dev/null +++ b/packages/layerchart/src/routes/docs/components/Ellipse/+page.svelte @@ -0,0 +1,26 @@ + + +

Examples

+ + +
+ + + + + + + + + +
+
diff --git a/packages/layerchart/src/routes/docs/components/Ellipse/+page.ts b/packages/layerchart/src/routes/docs/components/Ellipse/+page.ts new file mode 100644 index 000000000..544a32480 --- /dev/null +++ b/packages/layerchart/src/routes/docs/components/Ellipse/+page.ts @@ -0,0 +1,14 @@ +import api from '$lib/components/Ellipse.svelte?raw&sveld'; +import source from '$lib/components/Ellipse.svelte?raw'; +import pageSource from './+page.svelte?raw'; + +export async function load() { + return { + meta: { + api, + source, + pageSource, + description: '`` element with tweened properties using `motionStore`', + }, + }; +} From 29a07998ba9c660fd5935b885f11254efb0274e5 Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Tue, 3 Jun 2025 00:49:49 -0400 Subject: [PATCH 08/17] feat(Polygon): Suppoprt scaleX / scaleY --- .../src/lib/components/Polygon.svelte | 18 ++++++++++++++++++ packages/layerchart/src/lib/utils/shape.ts | 10 +++++++--- .../docs/components/Polygon/+page.svelte | 6 ++++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/packages/layerchart/src/lib/components/Polygon.svelte b/packages/layerchart/src/lib/components/Polygon.svelte index e0d8237b9..3b5bdc3c4 100644 --- a/packages/layerchart/src/lib/components/Polygon.svelte +++ b/packages/layerchart/src/lib/components/Polygon.svelte @@ -72,6 +72,20 @@ */ inset?: number; + /** + * The horizontal stretch factor of the polygon. + * + * @default 1 + */ + scaleX?: number; + + /** + * The vertical stretch factor of the polygon. + * + * @default 1 + */ + scaleY?: number; + /** * A bindable reference to the `` element * @@ -117,6 +131,8 @@ curveRadius = 0, rotate = 0, inset = 1, + scaleX = 1, + scaleY = 1, motion, fill, fillOpacity, @@ -150,6 +166,8 @@ radius: motionR.current, rotate, inset, + scaleX, + scaleY, }) ); let d = $derived(roundedPolygonPath(polygonPoints, curveRadius)); diff --git a/packages/layerchart/src/lib/utils/shape.ts b/packages/layerchart/src/lib/utils/shape.ts index d728850fd..e89cdf050 100644 --- a/packages/layerchart/src/lib/utils/shape.ts +++ b/packages/layerchart/src/lib/utils/shape.ts @@ -26,6 +26,8 @@ export function polygonPoints(count: number, radius: number, rotate: number = 0) * @param radius - Radius of the polygon * @param rotate - Rotation of the polygon (degrees) * @param inset - Percent to inset odd points (<1 inset, >1 outset) + * @param scaleX - Horizontal stretch factor + * @param scaleY - Vertical stretch factor * @returns Array of points (x, y) */ export function polygon(options: { @@ -35,13 +37,15 @@ export function polygon(options: { radius: number; rotate?: number; inset?: number; + scaleX?: number; + scaleY?: number; }) { - const { cx, cy, count, radius, rotate = 0, inset = 1 } = options; + const { cx, cy, count, radius, rotate = 0, inset = 1, scaleX = 1, scaleY = 1 } = options; return polygonPoints(count, radius, rotate).map(({ angle, radius }, i) => { const scale = i % 2 == 0 ? 1 : inset; return { - x: cx + radius * scale * Math.cos(angle), - y: cy + radius * scale * Math.sin(angle), + x: cx + radius * scale * Math.cos(angle) * scaleX, + y: cy + radius * scale * Math.sin(angle) * scaleY, }; }); } diff --git a/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte index 9f701d279..471a4aa87 100644 --- a/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte @@ -7,6 +7,8 @@ let curveRadius = $state(0); let inset = $state(0.3); let rotate = $state(0); + let scaleX = $state(1); + let scaleY = $state(1); const simpleExamples = [ { label: 'Triangle', points: 3 }, @@ -35,6 +37,8 @@ + +
@@ -49,6 +53,8 @@ {inset} {rotate} {curveRadius} + {scaleX} + {scaleY} /> {/snippet} From a2e8e2b1018d9415060a22b3ca676d22d9c02c0a Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Tue, 3 Jun 2025 11:57:41 -0400 Subject: [PATCH 09/17] feat(Polygon): Support `skewX` / `skewY` to create rhombus / parallelogram --- .../src/lib/components/Polygon.svelte | 18 ++ packages/layerchart/src/lib/utils/shape.ts | 27 +- .../docs/components/Polygon/+page.svelte | 230 ++++++++++++++++-- 3 files changed, 249 insertions(+), 26 deletions(-) diff --git a/packages/layerchart/src/lib/components/Polygon.svelte b/packages/layerchart/src/lib/components/Polygon.svelte index 3b5bdc3c4..d3c7becdb 100644 --- a/packages/layerchart/src/lib/components/Polygon.svelte +++ b/packages/layerchart/src/lib/components/Polygon.svelte @@ -86,6 +86,20 @@ */ scaleY?: number; + /** + * The skew angle in degrees along the X axis. + * + * @default 0 + */ + skewX?: number; + + /** + * The skew angle in degrees along the Y axis. + * + * @default 0 + */ + skewY?: number; + /** * A bindable reference to the `` element * @@ -133,6 +147,8 @@ inset = 1, scaleX = 1, scaleY = 1, + skewX = 0, + skewY = 0, motion, fill, fillOpacity, @@ -168,6 +184,8 @@ inset, scaleX, scaleY, + skewX, + skewY, }) ); let d = $derived(roundedPolygonPath(polygonPoints, curveRadius)); diff --git a/packages/layerchart/src/lib/utils/shape.ts b/packages/layerchart/src/lib/utils/shape.ts index e89cdf050..5317b9735 100644 --- a/packages/layerchart/src/lib/utils/shape.ts +++ b/packages/layerchart/src/lib/utils/shape.ts @@ -28,6 +28,8 @@ export function polygonPoints(count: number, radius: number, rotate: number = 0) * @param inset - Percent to inset odd points (<1 inset, >1 outset) * @param scaleX - Horizontal stretch factor * @param scaleY - Vertical stretch factor + * @param skewX - Skew angle in degrees along the X axis + * @param skewY - Skew angle in degrees along the Y axis * @returns Array of points (x, y) */ export function polygon(options: { @@ -39,13 +41,32 @@ export function polygon(options: { inset?: number; scaleX?: number; scaleY?: number; + skewX?: number; + skewY?: number; }) { - const { cx, cy, count, radius, rotate = 0, inset = 1, scaleX = 1, scaleY = 1 } = options; + const { + cx, + cy, + count, + radius, + rotate = 0, + inset = 1, + scaleX = 1, + scaleY = 1, + skewX = 0, + skewY = 0, + } = options; + const skewXRad = degreesToRadians(skewX); + const skewYRad = degreesToRadians(skewY); return polygonPoints(count, radius, rotate).map(({ angle, radius }, i) => { const scale = i % 2 == 0 ? 1 : inset; + let x = radius * scale * Math.cos(angle) * scaleX; + let y = radius * scale * Math.sin(angle) * scaleY; + const xSkewed = x + Math.tan(skewXRad) * y; + const ySkewed = y + Math.tan(skewYRad) * x; return { - x: cx + radius * scale * Math.cos(angle) * scaleX, - y: cy + radius * scale * Math.sin(angle) * scaleY, + x: cx + xSkewed, + y: cy + ySkewed, }; }); } diff --git a/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte index 471a4aa87..04182c119 100644 --- a/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte @@ -9,6 +9,8 @@ let rotate = $state(0); let scaleX = $state(1); let scaleY = $state(1); + let skewX = $state(0); + let skewY = $state(0); const simpleExamples = [ { label: 'Triangle', points: 3 }, @@ -39,6 +41,8 @@ + +
@@ -55,6 +59,8 @@ {curveRadius} {scaleX} {scaleY} + {skewX} + {skewY} /> {/snippet} @@ -70,29 +76,207 @@
- {#each simpleExamples as example} -
-

{example.label}

- -
- - {#snippet children({ context })} - - - - {/snippet} - -
-
-
- {/each} +
+

Triangle

+ +
+ + {#snippet children({ context })} + + + + {/snippet} + +
+
+
+ +
+

Square

+ +
+ + {#snippet children({ context })} + + + + {/snippet} + +
+
+
+ +
+

Diamond

+ +
+ + {#snippet children({ context })} + + + + {/snippet} + +
+
+
+ +
+

Rhombus

+ +
+ + {#snippet children({ context })} + + + + {/snippet} + +
+
+
+ +
+

Parallelogram

+ +
+ + {#snippet children({ context })} + + + + {/snippet} + +
+
+
+ +
+

Pentagon

+ +
+ + {#snippet children({ context })} + + + + {/snippet} + +
+
+
+ +
+

Hexagon

+ +
+ + {#snippet children({ context })} + + + + {/snippet} + +
+
+
+ +
+

Octagon

+ +
+ + {#snippet children({ context })} + + + + {/snippet} + +
+
+
+ +
+

Octagon

+ +
+ + {#snippet children({ context })} + + + + {/snippet} + +
+
+

Stars

From e229d5b83d6fd679eea392813779cb72aa536ada Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Tue, 3 Jun 2025 12:35:59 -0400 Subject: [PATCH 10/17] feat(Polygon): Support `tiltX` / `tiltY` to create trapezoid --- .../src/lib/components/Polygon.svelte | 18 ++++++++++ packages/layerchart/src/lib/utils/shape.ts | 15 +++++++++ .../docs/components/Polygon/+page.svelte | 33 +++++++++++++++++-- 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/packages/layerchart/src/lib/components/Polygon.svelte b/packages/layerchart/src/lib/components/Polygon.svelte index d3c7becdb..f221e39c8 100644 --- a/packages/layerchart/src/lib/components/Polygon.svelte +++ b/packages/layerchart/src/lib/components/Polygon.svelte @@ -100,6 +100,20 @@ */ skewY?: number; + /** + * The tilt factor for x-coordinates. + * + * @default 1 + */ + tiltX?: number; + + /** + * The tilt factor for y-coordinates. + * + * @default 1 + */ + tiltY?: number; + /** * A bindable reference to the `` element * @@ -149,6 +163,8 @@ scaleY = 1, skewX = 0, skewY = 0, + tiltX = 1, + tiltY = 1, motion, fill, fillOpacity, @@ -186,6 +202,8 @@ scaleY, skewX, skewY, + tiltX, + tiltY, }) ); let d = $derived(roundedPolygonPath(polygonPoints, curveRadius)); diff --git a/packages/layerchart/src/lib/utils/shape.ts b/packages/layerchart/src/lib/utils/shape.ts index 5317b9735..9b1521ec8 100644 --- a/packages/layerchart/src/lib/utils/shape.ts +++ b/packages/layerchart/src/lib/utils/shape.ts @@ -30,6 +30,8 @@ export function polygonPoints(count: number, radius: number, rotate: number = 0) * @param scaleY - Vertical stretch factor * @param skewX - Skew angle in degrees along the X axis * @param skewY - Skew angle in degrees along the Y axis + * @param tiltX - Tilt factor for x-coordinates (1 = no tilt, <1 = tilt left, >1 = tilt right) + * @param tiltY - Tilt factor for y-coordinates (1 = no tilt, <1 = tilt up, >1 = tilt down) * @returns Array of points (x, y) */ export function polygon(options: { @@ -43,6 +45,8 @@ export function polygon(options: { scaleY?: number; skewX?: number; skewY?: number; + tiltX?: number; + tiltY?: number; }) { const { cx, @@ -55,6 +59,8 @@ export function polygon(options: { scaleY = 1, skewX = 0, skewY = 0, + tiltX = 1, + tiltY = 1, } = options; const skewXRad = degreesToRadians(skewX); const skewYRad = degreesToRadians(skewY); @@ -62,6 +68,15 @@ export function polygon(options: { const scale = i % 2 == 0 ? 1 : inset; let x = radius * scale * Math.cos(angle) * scaleX; let y = radius * scale * Math.sin(angle) * scaleY; + + // Apply tilt effects + const normalizedY = (y + radius) / (2 * radius); // 0 to 1 from bottom to top + const normalizedX = (x + radius) / (2 * radius); // 0 to 1 from left to right + const tiltScaleX = 1 + (tiltX - 1) * normalizedY; + const tiltScaleY = 1 + (tiltY - 1) * normalizedX; + x *= tiltScaleX; + y *= tiltScaleY; + const xSkewed = x + Math.tan(skewXRad) * y; const ySkewed = y + Math.tan(skewYRad) * x; return { diff --git a/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte index 04182c119..2e5ab50fa 100644 --- a/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte @@ -11,6 +11,8 @@ let scaleY = $state(1); let skewX = $state(0); let skewY = $state(0); + let tiltX = $state(1); + let tiltY = $state(1); const simpleExamples = [ { label: 'Triangle', points: 3 }, @@ -39,10 +41,12 @@ - - + + + +
@@ -61,6 +65,8 @@ {scaleY} {skewX} {skewY} + {tiltX} + {tiltY} /> {/snippet} @@ -190,6 +196,29 @@
+
+

Trapezoid

+ +
+ + {#snippet children({ context })} + + + + {/snippet} + +
+
+
+

Pentagon

From 4d689d9f4117f8a14adfef649aab7b4dae3cfb8a Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Tue, 3 Jun 2025 13:30:27 -0400 Subject: [PATCH 11/17] docs(Polygon): Cleanup example --- .../docs/components/Polygon/+page.svelte | 57 ++++++------------- 1 file changed, 18 insertions(+), 39 deletions(-) diff --git a/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte index 2e5ab50fa..6d048a89c 100644 --- a/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte @@ -3,7 +3,7 @@ import { RangeField } from 'svelte-ux'; import Preview from 'layerchart/docs/Preview.svelte'; - let points = $state(10); + let points = $state(8); let curveRadius = $state(0); let inset = $state(0.3); let rotate = $state(0); @@ -13,40 +13,21 @@ let skewY = $state(0); let tiltX = $state(1); let tiltY = $state(1); - - const simpleExamples = [ - { label: 'Triangle', points: 3 }, - { label: 'Square / Diamond', points: 4 }, - { label: 'Pentagon', points: 5 }, - { label: 'Hexagon', points: 6 }, - { label: 'Octagon', points: 8 }, - ]; - - const starExamples = [ - { label: '6 point', points: 6 }, - { label: '8 point', points: 8 }, - { label: '10 point', points: 10 }, - { label: '12 point', points: 12 }, - { label: '14 point', points: 14 }, - { label: '16 point', points: 16 }, - { label: '18 point', points: 18 }, - { label: '20 point', points: 20 }, - ];

Playground

- + - - + + - - + +
@@ -308,31 +289,29 @@
-

Stars

-
- +

Stars

+
- {#each starExamples as example} + {#each [6, 8, 10, 12, 14, 16, 18, 20] as points}
-

{example.label}

+

{points} point

- +
From 90db1d20d9a48077d0b299c98cfe6cdcae4fd6cb Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Fri, 6 Jun 2025 11:55:09 -0400 Subject: [PATCH 12/17] refactor(Polygon): Rename `curveRadius` to `cornerRadius` (alogn with `Arc`) --- .../src/lib/components/Polygon.svelte | 6 ++-- .../docs/components/Polygon/+page.svelte | 32 +++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/layerchart/src/lib/components/Polygon.svelte b/packages/layerchart/src/lib/components/Polygon.svelte index f221e39c8..3aa1431c0 100644 --- a/packages/layerchart/src/lib/components/Polygon.svelte +++ b/packages/layerchart/src/lib/components/Polygon.svelte @@ -56,7 +56,7 @@ * * @default 0 */ - curveRadius?: number; + cornerRadius?: number; /** * The rotation of the polygon. @@ -156,7 +156,7 @@ r = 1, initialR: initialRProp, points = 4, - curveRadius = 0, + cornerRadius = 0, rotate = 0, inset = 1, scaleX = 1, @@ -206,7 +206,7 @@ tiltY, }) ); - let d = $derived(roundedPolygonPath(polygonPoints, curveRadius)); + let d = $derived(roundedPolygonPath(polygonPoints, cornerRadius)); const extractedTween = extractTweenConfig(motion); diff --git a/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte index 6d048a89c..7ec250199 100644 --- a/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte @@ -4,7 +4,7 @@ import Preview from 'layerchart/docs/Preview.svelte'; let points = $state(8); - let curveRadius = $state(0); + let cornerRadius = $state(0); let inset = $state(0.3); let rotate = $state(0); let scaleX = $state(1); @@ -21,7 +21,7 @@ - + @@ -41,7 +41,7 @@ {points} {inset} {rotate} - {curveRadius} + {cornerRadius} {scaleX} {scaleY} {skewX} @@ -59,7 +59,7 @@

Simple

- +
@@ -76,7 +76,7 @@ r={60} points={3} {rotate} - {curveRadius} + {cornerRadius} /> {/snippet} @@ -98,7 +98,7 @@ r={60} points={4} rotate={rotate + 45} - {curveRadius} + {cornerRadius} /> {/snippet} @@ -121,7 +121,7 @@ points={4} scaleX={0.5} {rotate} - {curveRadius} + {cornerRadius} /> {/snippet} @@ -144,7 +144,7 @@ points={4} skewX={-20} rotate={rotate + 45} - {curveRadius} + {cornerRadius} /> {/snippet} @@ -168,7 +168,7 @@ skewX={-20} scaleX={1.5} rotate={rotate + 45} - {curveRadius} + {cornerRadius} /> {/snippet} @@ -191,7 +191,7 @@ points={4} tiltX={2} rotate={rotate + 45} - {curveRadius} + {cornerRadius} /> {/snippet} @@ -213,7 +213,7 @@ r={60} points={5} {rotate} - {curveRadius} + {cornerRadius} /> {/snippet} @@ -235,7 +235,7 @@ r={60} points={6} {rotate} - {curveRadius} + {cornerRadius} /> {/snippet} @@ -257,7 +257,7 @@ r={60} points={8} {rotate} - {curveRadius} + {cornerRadius} /> {/snippet} @@ -279,7 +279,7 @@ r={60} points={8} {rotate} - {curveRadius} + {cornerRadius} /> {/snippet} @@ -300,7 +300,7 @@ format="decimal" /> - +
@@ -311,7 +311,7 @@
- +
From a9d7600037b8cec05ae90f7f358e0ff55d87c361 Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Fri, 6 Jun 2025 12:02:09 -0400 Subject: [PATCH 13/17] docs(BarChart): Use Polygon for indicator example --- .../docs/components/BarChart/+page.svelte | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/layerchart/src/routes/docs/components/BarChart/+page.svelte b/packages/layerchart/src/routes/docs/components/BarChart/+page.svelte index 1f5ab1717..d5cc638ad 100644 --- a/packages/layerchart/src/routes/docs/components/BarChart/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/BarChart/+page.svelte @@ -13,6 +13,7 @@ Tooltip, Circle, Group, + Polygon, } from 'layerchart'; import { extent, group, mean, sum } from 'd3-array'; import { scaleLinear, scaleLog, scaleThreshold, scaleTime } from 'd3-scale'; @@ -1254,14 +1255,14 @@ {/snippet} {#snippet aboveMarks({ context })} - - {@const width = 12} - {@const height = 12} - - + {/snippet} {#snippet tooltip({ context })} From a133a7f87baf1bb02e544494c8585c08171b3bcc Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Fri, 6 Jun 2025 12:19:48 -0400 Subject: [PATCH 14/17] docs(BarChart): Fix indicator example when using canvas (add padding to fix clipping) --- .../src/routes/docs/components/BarChart/+page.svelte | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/layerchart/src/routes/docs/components/BarChart/+page.svelte b/packages/layerchart/src/routes/docs/components/BarChart/+page.svelte index d5cc638ad..b40e3ba6d 100644 --- a/packages/layerchart/src/routes/docs/components/BarChart/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/BarChart/+page.svelte @@ -1189,7 +1189,7 @@

Single stack with indicator

-
+
{/snippet} From 10720d6a138de2ab61ded6a8abac363b91097202 Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Fri, 6 Jun 2025 12:23:52 -0400 Subject: [PATCH 15/17] fix(Polygon): Apply same default classes for canvas as svg --- .../src/lib/components/Polygon.svelte | 2 +- .../docs/components/Polygon/+page.svelte | 26 ++++++++++--------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/packages/layerchart/src/lib/components/Polygon.svelte b/packages/layerchart/src/lib/components/Polygon.svelte index 3aa1431c0..3bc1cc1a3 100644 --- a/packages/layerchart/src/lib/components/Polygon.svelte +++ b/packages/layerchart/src/lib/components/Polygon.svelte @@ -232,7 +232,7 @@ ? merge({ styles: { strokeWidth } }, styleOverrides) : { styles: { fill, fillOpacity, stroke, strokeWidth, opacity }, - classes: className, + classes: cls(layerClass('polygon'), fill == null && 'fill-surface-content', className), } ); } diff --git a/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte index 7ec250199..3367971f5 100644 --- a/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte @@ -13,6 +13,8 @@ let skewY = $state(0); let tiltX = $state(1); let tiltY = $state(1); + + let renderContext = $state<'svg' | 'canvas'>('svg');

Playground

@@ -33,7 +35,7 @@
{#snippet children({ context })} - + {#snippet children({ context })} - + {#snippet children({ context })} - + {#snippet children({ context })} - + {#snippet children({ context })} - + {#snippet children({ context })} - + {#snippet children({ context })} - + {#snippet children({ context })} - + {#snippet children({ context })} - + {#snippet children({ context })} - + {#snippet children({ context })} - +
- + From 80deb617454a4cb464d85b7347f72f4bab9a1a30 Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Fri, 6 Jun 2025 12:48:05 -0400 Subject: [PATCH 16/17] refactor(Polygon): Change default of `inset` to `0` (no inset instead of `1`) and positve values insets inward, negative outward --- .../src/lib/components/Polygon.svelte | 4 ++-- packages/layerchart/src/lib/utils/shape.ts | 2 +- .../docs/components/Polygon/+page.svelte | 18 +++++++++++------- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/packages/layerchart/src/lib/components/Polygon.svelte b/packages/layerchart/src/lib/components/Polygon.svelte index 3bc1cc1a3..7fd1db548 100644 --- a/packages/layerchart/src/lib/components/Polygon.svelte +++ b/packages/layerchart/src/lib/components/Polygon.svelte @@ -68,7 +68,7 @@ /** * The percent to inset the odd points of the star (<1 inset, >1 outset) * - * @default 1 + * @default 0 */ inset?: number; @@ -158,7 +158,7 @@ points = 4, cornerRadius = 0, rotate = 0, - inset = 1, + inset = 0, scaleX = 1, scaleY = 1, skewX = 0, diff --git a/packages/layerchart/src/lib/utils/shape.ts b/packages/layerchart/src/lib/utils/shape.ts index 9b1521ec8..aeac055e0 100644 --- a/packages/layerchart/src/lib/utils/shape.ts +++ b/packages/layerchart/src/lib/utils/shape.ts @@ -65,7 +65,7 @@ export function polygon(options: { const skewXRad = degreesToRadians(skewX); const skewYRad = degreesToRadians(skewY); return polygonPoints(count, radius, rotate).map(({ angle, radius }, i) => { - const scale = i % 2 == 0 ? 1 : inset; + const scale = i % 2 == 0 ? 1 : 1 - inset; let x = radius * scale * Math.cos(angle) * scaleX; let y = radius * scale * Math.sin(angle) * scaleY; diff --git a/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte index 3367971f5..117657e2f 100644 --- a/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte @@ -5,7 +5,7 @@ let points = $state(8); let cornerRadius = $state(0); - let inset = $state(0.3); + let inset = $state(0); let rotate = $state(0); let scaleX = $state(1); let scaleY = $state(1); @@ -14,6 +14,9 @@ let tiltX = $state(1); let tiltY = $state(1); + // Separate inset to allow playground to default to 0 + let starInset = $state(0.7); + let renderContext = $state<'svg' | 'canvas'>('svg'); @@ -21,15 +24,15 @@
- + - - + +
@@ -296,8 +299,9 @@ @@ -313,7 +317,7 @@
- +
From b10bd4b414c2405f7956346f7e914f9cf6675089 Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Fri, 6 Jun 2025 13:09:41 -0400 Subject: [PATCH 17/17] refactor(Polygon): Change default of `tiltX` and `tiltY` to `0` and adjust logic to support adjusting each side (top/bottom, left/rigth) based on positive/negative) --- .../src/lib/components/Polygon.svelte | 4 +-- packages/layerchart/src/lib/utils/shape.ts | 28 +++++++++++-------- .../docs/components/Polygon/+page.svelte | 10 +++---- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/packages/layerchart/src/lib/components/Polygon.svelte b/packages/layerchart/src/lib/components/Polygon.svelte index 7fd1db548..fad2ba4dd 100644 --- a/packages/layerchart/src/lib/components/Polygon.svelte +++ b/packages/layerchart/src/lib/components/Polygon.svelte @@ -163,8 +163,8 @@ scaleY = 1, skewX = 0, skewY = 0, - tiltX = 1, - tiltY = 1, + tiltX = 0, + tiltY = 0, motion, fill, fillOpacity, diff --git a/packages/layerchart/src/lib/utils/shape.ts b/packages/layerchart/src/lib/utils/shape.ts index aeac055e0..c22eb3611 100644 --- a/packages/layerchart/src/lib/utils/shape.ts +++ b/packages/layerchart/src/lib/utils/shape.ts @@ -30,8 +30,8 @@ export function polygonPoints(count: number, radius: number, rotate: number = 0) * @param scaleY - Vertical stretch factor * @param skewX - Skew angle in degrees along the X axis * @param skewY - Skew angle in degrees along the Y axis - * @param tiltX - Tilt factor for x-coordinates (1 = no tilt, <1 = tilt left, >1 = tilt right) - * @param tiltY - Tilt factor for y-coordinates (1 = no tilt, <1 = tilt up, >1 = tilt down) + * @param tiltX - Tilt factor for x-coordinates (0 = no tilt, positive moves points top => down, negative moves points bottom => up) + * @param tiltY - Tilt factor for y-coordinates (0 = no tilt, positive moves points left => right, negative moves points right => left) * @returns Array of points (x, y) */ export function polygon(options: { @@ -59,24 +59,28 @@ export function polygon(options: { scaleY = 1, skewX = 0, skewY = 0, - tiltX = 1, - tiltY = 1, + tiltX = 0, + tiltY = 0, } = options; const skewXRad = degreesToRadians(skewX); const skewYRad = degreesToRadians(skewY); return polygonPoints(count, radius, rotate).map(({ angle, radius }, i) => { - const scale = i % 2 == 0 ? 1 : 1 - inset; - let x = radius * scale * Math.cos(angle) * scaleX; - let y = radius * scale * Math.sin(angle) * scaleY; + // inset + const insetScale = i % 2 == 0 ? 1 : 1 - inset; - // Apply tilt effects - const normalizedY = (y + radius) / (2 * radius); // 0 to 1 from bottom to top - const normalizedX = (x + radius) / (2 * radius); // 0 to 1 from left to right - const tiltScaleX = 1 + (tiltX - 1) * normalizedY; - const tiltScaleY = 1 + (tiltY - 1) * normalizedX; + // scale + let x = radius * insetScale * Math.cos(angle) * scaleX; + let y = radius * insetScale * Math.sin(angle) * scaleY; + + // tilt + const normalizedY = (y + radius) / (2 * radius); + const normalizedX = (x + radius) / (2 * radius); + const tiltScaleX = tiltX > 0 ? 1 + tiltX * (1 - normalizedY) : 1 - tiltX * normalizedY; + const tiltScaleY = tiltY > 0 ? 1 + tiltY * (1 - normalizedX) : 1 - tiltY * normalizedX; x *= tiltScaleX; y *= tiltScaleY; + // skew const xSkewed = x + Math.tan(skewXRad) * y; const ySkewed = y + Math.tan(skewYRad) * x; return { diff --git a/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte index 117657e2f..c750ba83d 100644 --- a/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte +++ b/packages/layerchart/src/routes/docs/components/Polygon/+page.svelte @@ -11,8 +11,8 @@ let scaleY = $state(1); let skewX = $state(0); let skewY = $state(0); - let tiltX = $state(1); - let tiltY = $state(1); + let tiltX = $state(0); + let tiltY = $state(0); // Separate inset to allow playground to default to 0 let starInset = $state(0.7); @@ -31,8 +31,8 @@ - - + +
@@ -194,7 +194,7 @@ cy={context.height / 2} r={60} points={4} - tiltX={2} + tiltX={-1} rotate={rotate + 45} {cornerRadius} />