mirror of
https://github.com/penpot/penpot-exporter-figma-plugin.git
synced 2024-12-22 13:43:03 -05:00
Vectors - Line & Arrow (#37)
* 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 <alejandro@runroom.com> Co-authored-by: Jordi Sala Morales <jordism91@gmail.com>
This commit is contained in:
parent
73ccf83657
commit
c9f8a0dcd2
11 changed files with 126 additions and 53 deletions
|
@ -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,3 +2,5 @@ export * from './transformBlend';
|
|||
export * from './transformChildren';
|
||||
export * from './transformDimensionAndPosition';
|
||||
export * from './transformSceneNode';
|
||||
export * from './transformStrokes';
|
||||
export * from './transformVectorPaths';
|
||||
|
|
24
plugin-src/transformers/partials/transformStrokes.ts
Normal file
24
plugin-src/transformers/partials/transformStrokes.ts
Normal file
|
@ -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
plugin-src/transformers/partials/transformVectorPaths.ts
Normal file
28
plugin-src/transformers/partials/transformVectorPaths.ts
Normal file
|
@ -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)
|
||||
};
|
||||
};
|
|
@ -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';
|
||||
|
||||
|
@ -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)
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -1,25 +0,0 @@
|
|||
import {
|
||||
transformBlend,
|
||||
transformDimensionAndPosition,
|
||||
transformSceneNode
|
||||
} from '@plugin/transformers/partials';
|
||||
import { translateFills, translateStrokes, translateVectorPaths } from '@plugin/translators';
|
||||
|
||||
import { PathShape } from '@ui/lib/types/path/pathShape';
|
||||
|
||||
export const transformPolygonNode = (
|
||||
node: DefaultShapeMixin,
|
||||
baseX: number,
|
||||
baseY: number
|
||||
): PathShape => {
|
||||
return {
|
||||
type: 'path',
|
||||
name: node.name,
|
||||
content: translateVectorPaths(node.fillGeometry, baseX + node.x, baseY + node.y),
|
||||
strokes: translateStrokes(node),
|
||||
fills: translateFills(node.fills, node.width, node.height),
|
||||
...transformDimensionAndPosition(node, baseX, baseY),
|
||||
...transformSceneNode(node),
|
||||
...transformBlend(node)
|
||||
};
|
||||
};
|
|
@ -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';
|
||||
|
||||
|
@ -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)
|
||||
|
|
|
@ -7,10 +7,9 @@ import {
|
|||
transformFrameNode,
|
||||
transformGroupNode,
|
||||
transformImageNode,
|
||||
transformPolygonNode,
|
||||
transformPathNode,
|
||||
transformRectangleNode,
|
||||
transformTextNode,
|
||||
transformVectorNode
|
||||
transformTextNode
|
||||
} from '.';
|
||||
|
||||
export const transformSceneNode = async (
|
||||
|
@ -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}`);
|
||||
|
|
|
@ -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';
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue