-
-
Save yagudaev/0c2b89674c6aee8b38cd379752ef58d0 to your computer and use it in GitHub Desktop.
import { | |
convertGradientHandlesToTransform, | |
convertTransformToGradientHandles | |
} from "./setPropertyFill" | |
describe("convertGradientHandlesToTransform", () => { | |
it("identity matrix", () => { | |
expect( | |
convertGradientHandlesToTransform([ | |
{ | |
x: 0, | |
y: 0.5 | |
}, | |
{ | |
x: 1, | |
y: 0.5 | |
}, | |
{ | |
x: 0, | |
y: 1 | |
} | |
]) | |
).toEqual([ | |
[1, 0, 0], | |
[0, 1, 0] | |
]) | |
}) | |
it('scale "up" matrix', () => { | |
expect( | |
convertGradientHandlesToTransform([ | |
{ | |
x: 0, | |
y: 0.25 | |
}, | |
{ | |
x: 0.5, | |
y: 0.25 | |
}, | |
{ | |
x: 0, | |
y: 0.5 | |
} | |
]) | |
).toEqual([ | |
[2, 0, 0], | |
[0, 2, 0] | |
]) | |
}) | |
it("complex transform", () => { | |
expect( | |
convertGradientHandlesToTransform([ | |
{ | |
x: 0.06041662833364192, | |
y: 0.9474249294453632 | |
}, | |
{ | |
x: 0.9397033965856045, | |
y: 0.05248769196422948 | |
}, | |
{ | |
x: 0.5078852470742088, | |
y: 1.4138814828771724 | |
} | |
]) | |
).toEqual( | |
[ | |
[0.5754421949386597, -0.5520178079605103, 0.4882291555404663], | |
[0.5520178079605103, 0.542364239692688, -0.04720045626163483] | |
].map((row) => row.map((v) => expect.closeTo(v, 15))) | |
) | |
}) | |
}) | |
describe("convertTransformToGradientHandles", () => { | |
it("identity matrix", () => { | |
expect( | |
convertTransformToGradientHandles([ | |
[1, 0, 0], | |
[0, 1, 0], | |
[0, 0, 1] | |
]) | |
).toEqual([ | |
{ | |
x: 0, | |
y: 0.5 | |
}, | |
{ | |
x: 1, | |
y: 0.5 | |
}, | |
{ | |
x: 0, | |
y: 1 | |
} | |
]) | |
}) | |
// TODO: test adds row if missing | |
it('matrix with scale "up"', () => { | |
expect( | |
convertTransformToGradientHandles([ | |
[2, 0, 0], | |
[0, 2, 0], | |
[0, 0, 1] | |
]) | |
).toEqual([ | |
{ | |
x: 0, | |
y: 0.25 | |
}, | |
{ | |
x: 0.5, | |
y: 0.25 | |
}, | |
{ | |
x: 0, | |
y: 0.5 | |
} | |
]) | |
}) | |
it("complex matrix", () => { | |
expect( | |
convertTransformToGradientHandles([ | |
[0.5754421949386597, -0.5520178079605103, 0.4882291555404663], | |
[0.5520178079605103, 0.542364239692688, -0.04720045626163483], | |
[0, 0, 1] | |
]) | |
).toEqual( | |
[ | |
{ | |
x: 0.06041662833364192, | |
y: 0.9474249294453632 | |
}, | |
{ | |
x: 0.9397033965856045, | |
y: 0.05248769196422948 | |
}, | |
{ | |
x: 0.5078852470742088, | |
y: 1.4138814828771724 | |
} | |
].map(({ x, y }) => ({ x: expect.closeTo(x, 15), y: expect.closeTo(y, 15) })) | |
) | |
}) | |
}) |
import * as math from "mathjs" | |
const identityMatrixHandlePositions = [ | |
[0, 1, 0], | |
[0.5, 0.5, 1], | |
[1, 1, 1] | |
] | |
export function convertGradientHandlesToTransform( | |
gradientHandlePositions: [ | |
{ x: number; y: number }, | |
{ x: number; y: number }, | |
{ x: number; y: number } | |
] | |
) { | |
const gh = gradientHandlePositions | |
const d = [ | |
[gh[0].x, gh[1].x, gh[2].x], | |
[gh[0].y, gh[1].y, gh[2].y], | |
[1, 1, 1] | |
] | |
const o = identityMatrixHandlePositions | |
const m = math.multiply(o, math.inv(d)) | |
return [m[0], m[1]] | |
} | |
export function convertTransformToGradientHandles(transform: number[][]) { | |
const inverseTransform = math.inv(transform) | |
// point matrix | |
const mp = math.multiply(inverseTransform, identityMatrixHandlePositions) | |
return [ | |
{ x: mp[0][0], y: mp[1][0] }, | |
{ x: mp[0][1], y: mp[1][1] }, | |
{ x: mp[0][2], y: mp[1][2] } | |
] | |
} |
This was extremely useful, thanks!
Thanks for this!!
Also, how can I convert this matrix for the radial_gradiant() of CSS?
@prakhart111 I haven't yet done the math of Radial Gradient, but looking at the W3C spec will really help here: https://www.w3.org/TR/css-images-3/#radial-gradients -- they specify how points are placed in color stops.
You can use that an observing Figma's default behaviour and how it differs from CSS to figure out how to do that
Thanks, @yagudaev
And I figured out the maths but forgot to update it here.
Also found a repository with some useful helper functions for such applications.
Here's the radial gradient function from that repository.
https://github.com/figma-plugin-helper-functions/figma-plugin-helpers/blob/5f3a767/src/helpers/extractRadialOrDiamondGradientParams.ts#L11
@prakhart111 Amazing, thank you for the link, didn't know they had that function there.
If you have time, love to add support to my Open Source Plugin: https://github.com/yagudaev/css-gradient-to-figma. Happy to review PR or point you in the right direction 😁
how to import mathjs in figma plugin app?
once I add the import statement, plugin is not run on Figma.
i need to this converter to my plugin
@Dinothan-IdeaBits take a look at: https://github.com/yagudaev/css-gradient-to-figma/blob/main/src/shared/math.ts
You can try the repo too, should give you a good idea
Thanks for this!!This has been of great assistance to me.
By the way, can I ask you some questions? It has been bothering me for a long time.
Q1: How do you find the identity matrix? [[0, 0.5], [1, 0.5]]
Q2: Why do we need to use the inverse of the transformation matrix instead of transformation matrix itself? Isn't the transformation matrix representing the transformation from identity matrix to new matrix ?
Q3: When we want to obtain the coordinates of the gradient line, why do we need to multiply GradientHandles by the width and height of the shape?
I look forward to your response. Thank you very much.
Glad it is helpful @KyrieChen 😊.
Q1) The identity matrix is always [[1, 0], [0, 1]] (x and y in normal positions). The initial position of a gradient when created through figma is just that. I found it through observation. I created it through the UI and through the plugin api and looked at the values.
Q2) The inverse is used to solve the equation. We have 3 points from the REST API, they are the points that Figma displays when you look at the gradient in the UI (the 3 dots). Our second thing that is known is the initial values of the 3 points when we create a brand new gradient without any changes. We use that to solve for the transformation matrix needed to change the points.
Note I tried to solve this initially by breaking it down to SRT (scale, rotate, transform) values independently. The math there is much more complex and I got a large margin of error +/-10%.
Using linear algebra instead of trigonometry was far easier here.
Q3) As far as I remember Figma uses a normalized vector [0, 1], so to fill a shape you need to scale it. This causes some surprises compared to typical Computer Graphics and probably when I ended up doing that.
Hope this help, sorry it's been almost 2 years so forgot some of this 😊.
Btw give the css gradient repo a try, lots of goodies there. Also got the help of html.to.design CTO there.
Hi guys, Im not very good in handling matrix and vectors, maybe you guys can help me. I need to convert figma gradient into css (Opposite to your css->figma plugin), but I have problem calculating gradient angle from 'gradientTransform' matrix from figma's plugin api. I would really appreciate it if someone could help me out with this. :)
@urosrakovicdev take a look at: https://github.com/bernaferrari/FigmaToCode/blob/7066b2745c87c620e298b779ecae3e6d94896f1c/packages/backend/src/html/builderImpl/htmlColor.ts#L61
Also take a look at: https://github.com/BuilderIO/figma-html
Super cool open-source stuff available now, exciting times 😊
@DanielPopOut glad you found it useful 😁. Ended up using some of these ideas in https://www.figma.com/community/plugin/1157089605295322526/CSS-Gradient-to-Figma