Skip to content

Commit

Permalink
Vectors - Line & Arrow (#37)
Browse files Browse the repository at this point in the history
* lines & arrows draft

* simplify vectors

* try to refactor

* remove comments

* add more clarity to code

* fix translate strokes

* minor style fix

* fix for vectors without geometry

* reduce code

---------

Co-authored-by: Alex Sánchez <[email protected]>
Co-authored-by: Jordi Sala Morales <[email protected]>
  • Loading branch information
3 people authored Apr 17, 2024
1 parent 73ccf83 commit c9f8a0d
Show file tree
Hide file tree
Showing 11 changed files with 126 additions and 53 deletions.
3 changes: 1 addition & 2 deletions plugin-src/transformers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ export * from './transformFrameNode';
export * from './transformGroupNode';
export * from './transformImageNode';
export * from './transformPageNode';
export * from './transformPolygonNode';
export * from './transformPathNode';
export * from './transformRectangleNode';
export * from './transformSceneNode';
export * from './transformTextNode';
export * from './transformVectorNode';
2 changes: 2 additions & 0 deletions plugin-src/transformers/partials/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ export * from './transformBlend';
export * from './transformChildren';
export * from './transformDimensionAndPosition';
export * from './transformSceneNode';
export * from './transformStrokes';
export * from './transformVectorPaths';
24 changes: 24 additions & 0 deletions plugin-src/transformers/partials/transformStrokes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { translateStrokes } from '@plugin/translators';

import { ShapeAttributes } from '@ui/lib/types/shape/shapeAttributes';

const isVectorLike = (node: GeometryMixin | VectorLikeMixin): node is VectorLikeMixin => {
return 'vectorNetwork' in node;
};

const hasFillGeometry = (node: GeometryMixin | (GeometryMixin & VectorLikeMixin)): boolean => {
return node.fillGeometry.length > 0;
};

export const transformStrokes = (
node: GeometryMixin | (GeometryMixin & VectorLikeMixin)
): Partial<ShapeAttributes> => {
return {
strokes: translateStrokes(
node.strokes,
node.strokeWeight,
hasFillGeometry(node),
isVectorLike(node) ? node.vectorNetwork : undefined
)
};
};
28 changes: 28 additions & 0 deletions plugin-src/transformers/partials/transformVectorPaths.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { translateVectorPaths } from '@plugin/translators';

import { PathAttributes } from '@ui/lib/types/path/pathAttributes';

const getVectorPaths = (node: VectorNode | StarNode | LineNode | PolygonNode): VectorPaths => {
switch (node.type) {
case 'STAR':
case 'POLYGON':
return node.fillGeometry;
case 'VECTOR':
return node.vectorPaths;
case 'LINE':
return node.strokeGeometry;
}
};

export const transformVectorPaths = (
node: VectorNode | StarNode | LineNode | PolygonNode,
baseX: number,
baseY: number
): PathAttributes => {
const vectorPaths = getVectorPaths(node);

return {
type: 'path',
content: translateVectorPaths(vectorPaths, baseX + node.x, baseY + node.y)
};
};
7 changes: 4 additions & 3 deletions plugin-src/transformers/transformEllipseNode.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {
transformBlend,
transformDimensionAndPosition,
transformSceneNode
transformSceneNode,
transformStrokes
} from '@plugin/transformers/partials';
import { translateFills, translateStrokes } from '@plugin/translators';
import { translateFills } from '@plugin/translators';

import { CircleShape } from '@ui/lib/types/circle/circleShape';

Expand All @@ -16,7 +17,7 @@ export const transformEllipseNode = (
type: 'circle',
name: node.name,
fills: translateFills(node.fills, node.width, node.height),
strokes: translateStrokes(node),
...transformStrokes(node),
...transformDimensionAndPosition(node, baseX, baseY),
...transformSceneNode(node),
...transformBlend(node)
Expand Down
10 changes: 7 additions & 3 deletions plugin-src/transformers/transformFrameNode.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { transformDimensionAndPosition, transformSceneNode } from '@plugin/transformers/partials';
import {
transformDimensionAndPosition,
transformSceneNode,
transformStrokes
} from '@plugin/transformers/partials';
import { transformChildren } from '@plugin/transformers/partials';
import { translateFills, translateStrokes } from '@plugin/translators';
import { translateFills } from '@plugin/translators';

import { FrameShape } from '@ui/lib/types/frame/frameShape';

Expand All @@ -13,7 +17,7 @@ export const transformFrameNode = async (
type: 'frame',
name: node.name,
fills: translateFills(node.fills, node.width, node.height),
strokes: translateStrokes(node),
...transformStrokes(node),
...(await transformChildren(node, baseX, baseY)),
...transformDimensionAndPosition(node, baseX, baseY),
...transformSceneNode(node)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import {
transformBlend,
transformDimensionAndPosition,
transformSceneNode
transformSceneNode,
transformStrokes,
transformVectorPaths
} from '@plugin/transformers/partials';
import { translateFills, translateStrokes, translateVectorPaths } from '@plugin/translators';
import { translateFills } from '@plugin/translators';

import { PathShape } from '@ui/lib/types/path/pathShape';

export const transformVectorNode = (node: VectorNode, baseX: number, baseY: number): PathShape => {
export const transformPathNode = (
node: VectorNode | StarNode | LineNode | PolygonNode,
baseX: number,
baseY: number
): PathShape => {
return {
type: 'path',
name: node.name,
fills: node.fillGeometry.length ? translateFills(node.fills, node.width, node.height) : [],
content: translateVectorPaths(node.vectorPaths, baseX + node.x, baseY + node.y),
strokes: translateStrokes(node),
...transformStrokes(node),
...transformVectorPaths(node, baseX, baseY),
...transformDimensionAndPosition(node, baseX, baseY),
...transformSceneNode(node),
...transformBlend(node)
Expand Down
25 changes: 0 additions & 25 deletions plugin-src/transformers/transformPolygonNode.ts

This file was deleted.

7 changes: 4 additions & 3 deletions plugin-src/transformers/transformRectangleNode.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import {
transformBlend,
transformDimensionAndPosition,
transformSceneNode
transformSceneNode,
transformStrokes
} from '@plugin/transformers/partials';
import { translateFills, translateStrokes } from '@plugin/translators';
import { translateFills } from '@plugin/translators';

import { RectShape } from '@ui/lib/types/rect/rectShape';

Expand All @@ -16,7 +17,7 @@ export const transformRectangleNode = (
type: 'rect',
name: node.name,
fills: translateFills(node.fills, node.width, node.height),
strokes: translateStrokes(node),
...transformStrokes(node),
...transformDimensionAndPosition(node, baseX, baseY),
...transformSceneNode(node),
...transformBlend(node)
Expand Down
9 changes: 4 additions & 5 deletions plugin-src/transformers/transformSceneNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ import {
transformFrameNode,
transformGroupNode,
transformImageNode,
transformPolygonNode,
transformPathNode,
transformRectangleNode,
transformTextNode,
transformVectorNode
transformTextNode
} from '.';

export const transformSceneNode = async (
Expand Down Expand Up @@ -44,9 +43,9 @@ export const transformSceneNode = async (
return transformTextNode(node, baseX, baseY);
case 'STAR':
case 'POLYGON':
return transformPolygonNode(node, baseX, baseY);
case 'VECTOR':
return transformVectorNode(node, baseX, baseY);
case 'LINE':
return transformPathNode(node, baseX, baseY);
}

throw new Error(`Unsupported node type: ${node.type}`);
Expand Down
47 changes: 41 additions & 6 deletions plugin-src/translators/translateStrokes.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,49 @@
import { translateFill } from '@plugin/translators/translateFills';

import { Stroke } from '@ui/lib/types/utils/stroke';
import { Stroke, StrokeCaps } from '@ui/lib/types/utils/stroke';

export const translateStrokes = (node: MinimalStrokesMixin): Stroke[] => {
return node.strokes.map(stroke => {
const fill = translateFill(stroke, 0, 0);
return {
export const translateStrokes = (
paints: readonly Paint[],
strokeWeight: number | typeof figma.mixed,
hasFillGeometry?: boolean,
vectorNetwork?: VectorNetwork
): Stroke[] => {
return paints.map((paint, index) => {
const fill = translateFill(paint, 0, 0);
const stroke: Stroke = {
strokeColor: fill?.fillColor,
strokeOpacity: fill?.fillOpacity,
strokeWidth: node.strokeWeight === figma.mixed ? 1 : node.strokeWeight
strokeWidth: strokeWeight === figma.mixed ? 1 : strokeWeight
};

if (!hasFillGeometry && index === 0 && vectorNetwork && vectorNetwork.vertices.length) {
stroke.strokeCapStart = translateStrokeCap(vectorNetwork.vertices[0]);
stroke.strokeCapEnd = translateStrokeCap(
vectorNetwork.vertices[vectorNetwork.vertices.length - 1]
);
}

return stroke;
});
};

const translateStrokeCap = (vertex: VectorVertex): StrokeCaps | undefined => {
switch (vertex.strokeCap as StrokeCap | ConnectorStrokeCap) {
case 'NONE':
return;
case 'ROUND':
return 'round';
case 'ARROW_EQUILATERAL':
case 'TRIANGLE_FILLED':
return 'triangle-arrow';
case 'SQUARE':
return 'square';
case 'CIRCLE_FILLED':
return 'circle-marker';
case 'DIAMOND_FILLED':
return 'diamond-marker';
case 'ARROW_LINES':
default:
return 'line-arrow';
}
};

0 comments on commit c9f8a0d

Please sign in to comment.