From 316b49d17dad9f8c6ed7e5688a7f29e1e42353b7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 13:29:19 +0100 Subject: [PATCH 1/2] Version Packages (#3221) Co-authored-by: github-actions[bot] --- .changeset/seven-keys-tease.md | 5 ----- packages/tokens-studio-for-figma/CHANGELOG.md | 6 ++++++ packages/tokens-studio-for-figma/package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 .changeset/seven-keys-tease.md diff --git a/.changeset/seven-keys-tease.md b/.changeset/seven-keys-tease.md deleted file mode 100644 index 1072ae80c..000000000 --- a/.changeset/seven-keys-tease.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@tokens-studio/figma-plugin": patch ---- - -Fixes an issue when updating a token's value from a reference to a hard value, the check we introduced in the last release caused those to not be updated (and only worked when changing to another reference). diff --git a/packages/tokens-studio-for-figma/CHANGELOG.md b/packages/tokens-studio-for-figma/CHANGELOG.md index 9ee4b3390..a9433a9b8 100644 --- a/packages/tokens-studio-for-figma/CHANGELOG.md +++ b/packages/tokens-studio-for-figma/CHANGELOG.md @@ -1,5 +1,11 @@ # @tokens-studio/figma-plugin +## 2.2.3 + +### Patch Changes + +- 01ccff2a: Fixes an issue when updating a token's value from a reference to a hard value, the check we introduced in the last release caused those to not be updated (and only worked when changing to another reference). + ## 2.2.2 ### Patch Changes diff --git a/packages/tokens-studio-for-figma/package.json b/packages/tokens-studio-for-figma/package.json index 48f648910..10e79ddc1 100644 --- a/packages/tokens-studio-for-figma/package.json +++ b/packages/tokens-studio-for-figma/package.json @@ -1,6 +1,6 @@ { "name": "@tokens-studio/figma-plugin", - "version": "2.2.2", + "version": "2.2.3", "description": "Tokens Studio plugin for Figma", "license": "MIT", "private": true, From 41f7e3402b17b8e06e664538e42e93c691347e6e Mon Sep 17 00:00:00 2001 From: macintoshhelper Date: Tue, 10 Dec 2024 07:00:20 +0100 Subject: [PATCH 2/2] Fix custom linear gradient angles (#3109) * fix: align linear gradient start/stop transform to rectangle edges for custom angles * fix: remove linear gradient node type check for isPaintEqual * fix: lint errors * fix: further lint errors * fix: add missing test back to gradients.test * Create clever-turtles-fry.md --------- Co-authored-by: macintoshhelper <6757532+macintoshhelper@users.noreply.github.com> Co-authored-by: Akshay Gupta --- .changeset/clever-turtles-fry.md | 5 ++ .../plugin/figmaTransforms/gradients.test.ts | 87 +++++++++++++------ .../src/plugin/figmaTransforms/gradients.ts | 68 ++++++++------- 3 files changed, 102 insertions(+), 58 deletions(-) create mode 100644 .changeset/clever-turtles-fry.md diff --git a/.changeset/clever-turtles-fry.md b/.changeset/clever-turtles-fry.md new file mode 100644 index 000000000..4144c11e7 --- /dev/null +++ b/.changeset/clever-turtles-fry.md @@ -0,0 +1,5 @@ +--- +"@tokens-studio/figma-plugin": patch +--- + +Fix for linear gradient start/end points being outside the node bounding box when using angles that aren't divisible by 45. diff --git a/packages/tokens-studio-for-figma/src/plugin/figmaTransforms/gradients.test.ts b/packages/tokens-studio-for-figma/src/plugin/figmaTransforms/gradients.test.ts index a72b59ae0..dddaf1a42 100644 --- a/packages/tokens-studio-for-figma/src/plugin/figmaTransforms/gradients.test.ts +++ b/packages/tokens-studio-for-figma/src/plugin/figmaTransforms/gradients.test.ts @@ -25,8 +25,8 @@ describe('convertStringtoFigmaGradient', () => { }, ], gradientTransform: [ - [0.5000000000000001, -0.5, 0.49999999999999994], - [0.5, 0.5000000000000001, -5.551115123125784e-17], + [0.5, -0.5, 0.5], + [0.5, 0.5, 0], ], }, }; @@ -64,8 +64,8 @@ describe('convertStringtoFigmaGradient', () => { }, ], gradientTransform: [ - [0.5000000000000001, -0.5, 0.49999999999999994], - [0.5, 0.5000000000000001, -5.551115123125784e-17], + [0.5, -0.5, 0.5], + [0.5, 0.5, 0], ], }, }; @@ -103,8 +103,8 @@ describe('convertStringtoFigmaGradient', () => { }, ], gradientTransform: [ - [0.5000000000000001, -0.5, 0.49999999999999994], - [0.5, 0.5000000000000001, -5.551115123125784e-17], + [0.5, -0.5, 0.5], + [0.5, 0.5, 0], ], }, }; @@ -118,25 +118,25 @@ describe('convertStringtoFigmaGradient', () => { r: 0, g: 0, b: 0, - a: 1, + a: 1 }, - position: 0, + position: 0 }, { color: { r: 1, g: 1, b: 1, - a: 1, + a: 1 }, - position: 1, - }, + position: 1 + } ], gradientTransform: [ - [6.123233995736766e-17, 1, -6.123233995736766e-17], - [-1, 6.123233995736766e-17, 1], + [0, 1, 0], + [-1, 0, 1], ], - }, + } }; const test5 = { @@ -148,22 +148,22 @@ describe('convertStringtoFigmaGradient', () => { r: 0.9019607843137255, g: 0.39215686274509803, b: 0.396078431372549, - a: 1, + a: 1 }, - position: 0, + position: 0 }, { color: { r: 0.5686274509803921, g: 0.596078431372549, b: 0.8980392156862745, - a: 1, + a: 1 }, - position: 1, - }, + position: 1 + } ], - gradientTransform: [[1, 0, 0], [0, 1, 0]], - }, + gradientTransform: [[1, 0, 0], [0, 1, 0]] + } }; const test6 = { @@ -175,33 +175,64 @@ describe('convertStringtoFigmaGradient', () => { r: 0.9019607843137255, g: 0.39215686274509803, b: 0.396078431372549, - a: 1, + a: 1 }, - position: 0, + position: 0 }, { color: { r: 0.5686274509803921, g: 0.596078431372549, b: 0.8980392156862745, - a: 1, + a: 1 }, - position: 1, - }, + position: 1 + } ], gradientTransform: [ - [6.123233995736766e-17, 1, -6.123233995736766e-17], - [-1, 6.123233995736766e-17, 1], + [0, 1, 0], + [-1, 0, 1], ], }, }; + const test7 = { + input: 'linear-gradient(106.84deg, #FF0000 5.61%, #cc00ff00 89.41%)', + output: { + gradientStops: [ + { + color: { + r: 1, + g: 0, + b: 0, + a: 1, + }, + position: 0.056100000000000004, + }, + { + color: { + r: 0.8, + g: 0, + b: 1, + a: 0, + }, + position: 0.8941, + }, + ], + gradientTransform: [ + [0.9160738743, 0.2772769934, -0.0966754339], + [-0.2772769934, 0.9160738743, 0.1806015595], + ], + } + } + expect(convertStringToFigmaGradient(test1.input)).toEqual(test1.output); expect(convertStringToFigmaGradient(test2.input)).toEqual(test2.output); expect(convertStringToFigmaGradient(test3.input)).toEqual(test3.output); expect(convertStringToFigmaGradient(test4.input)).toEqual(test4.output); expect(convertStringToFigmaGradient(test5.input)).toEqual(test5.output); expect(convertStringToFigmaGradient(test6.input)).toEqual(test6.output); + expect(convertStringToFigmaGradient(test7.input)).toEqual(test7.output); }); describe('convertFigmaGradientToString', () => { diff --git a/packages/tokens-studio-for-figma/src/plugin/figmaTransforms/gradients.ts b/packages/tokens-studio-for-figma/src/plugin/figmaTransforms/gradients.ts index 6f3daca65..cfbfaccf1 100644 --- a/packages/tokens-studio-for-figma/src/plugin/figmaTransforms/gradients.ts +++ b/packages/tokens-studio-for-figma/src/plugin/figmaTransforms/gradients.ts @@ -1,5 +1,5 @@ import { figmaRGBToHex, extractLinearGradientParamsFromTransform } from '@figma-plugin/helpers'; -import { Matrix, inverse } from 'ml-matrix'; +import { Matrix } from 'ml-matrix'; import { convertToFigmaColor } from './colors'; export function convertDegreeToNumber(degreeString: string): number { @@ -17,6 +17,12 @@ export function convertFigmaGradientToString(paint: GradientPaint) { return `linear-gradient(${angleInDeg + 90}deg, ${gradientStopsString})`; } +const roundToPrecision = (value, precision = 10) => { + const roundToPrecisionVal = 10 ** precision; + return Math.round((value + Number.EPSILON) * roundToPrecisionVal) / roundToPrecisionVal; +}; + +// if node type check is needed due to bugs caused by obscure node types, use (value: string/*, node?: BaseNode | PaintStyle) and convertStringToFigmaGradient(value, target) export function convertStringToFigmaGradient(value: string) { const parts = value.substring(value.indexOf('(') + 1, value.lastIndexOf(')')).split(', ').map((s) => s.trim()); @@ -63,39 +69,41 @@ export function convertStringToFigmaGradient(value: string) { const degrees = -(angle - 90); const rad = degrees * (Math.PI / 180); - const scale = angle % 90 === 0 ? 1 : Math.sqrt(1 + Math.tan(angle * (Math.PI / 180)) ** 2); - // start by transforming to the gradient center + let normalizedAngleRad = Math.abs(rad) % (Math.PI / 2); + if (normalizedAngleRad > Math.PI / 4) { + // adjust angle after 45 degrees to scale down correctly towards 90 degrees + normalizedAngleRad = Math.PI / 2 - normalizedAngleRad; + } + + const sin = Math.sin(rad); + const cos = Math.cos(rad); + + let scale = 1; + + const normalisedCos = Math.cos(normalizedAngleRad); + scale = normalisedCos; + // Implement fallback if bugs are caused by obscure node types. This appears to be unnecessary + // if (!['RECTANGLE', 'FRAME', 'VECTOR'].includes(node?.type || '')) { + // // Old scale computation: + // scale = angle % 90 === 0 ? 1 : Math.sqrt(1 + Math.tan(angle * (Math.PI / 180)) ** 2); + // } + + const scaledCos = cos * scale; + const scaledSin = sin * scale; + + // start by transforming to the gradient center, to keep the gradient centered after scaling // which for figma is .5 .5 as it is a relative transform + const tx = 0.5 - 0.5 * scaledCos + 0.5 * scaledSin; + const ty = 0.5 - 0.5 * scaledSin - 0.5 * scaledCos; + const transformationMatrix = new Matrix([ - [1, 0, 0.5], - [0, 1, 0.5], + [roundToPrecision(scaledCos), roundToPrecision(-scaledSin), roundToPrecision(tx)], + [roundToPrecision(scaledSin), roundToPrecision(scaledCos), roundToPrecision(ty)], [0, 0, 1], - ]).mmul( - // we can then multiply this with the rotation matrix - new Matrix([ - [Math.cos(rad), Math.sin(rad), 0], - [-Math.sin(rad), Math.cos(rad), 0], - [0, 0, 1], - ]), - ).mmul( - // next we need to multiply it with a scale matrix to fill the entire shape - new Matrix([ - [scale, 0, 0], - [0, scale, 0], - [0, 0, 1], - ]), - ).mmul( - // lastly we need to translate it back to the 0,0 origin - // by negating the center transform - new Matrix([ - [1, 0, -0.5], - [0, 1, -0.5], - [0, 0, 1], - ]), - ); - - const gradientTransformMatrix = inverse(transformationMatrix).to2DArray(); + ]); + + const gradientTransformMatrix = transformationMatrix.to2DArray(); const gradientStops = parts.map((stop, i, arr) => { const seperatedStop = stop.split(' ');