Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement rotation for remaining figures #163

Merged
merged 4 commits into from
Jun 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/rare-pianos-complain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"penpot-exporter": minor
---

Implement rotation for the missing figures
7 changes: 3 additions & 4 deletions plugin-src/transformers/partials/transformChildren.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@ const nodeActsAsMask = (node: SceneNode): boolean => {

export const transformChildren = async (
node: ChildrenMixin,
baseX: number = 0,
baseY: number = 0
baseRotation: number = 0
): Promise<Children> => {
const maskIndex = node.children.findIndex(nodeActsAsMask);
const containsMask = maskIndex !== -1;

return {
children: containsMask
? await translateMaskChildren(node.children, maskIndex, baseX, baseY)
: await translateChildren(node.children, baseX, baseY)
? await translateMaskChildren(node.children, maskIndex, baseRotation)
: await translateChildren(node.children, baseRotation)
};
};
36 changes: 3 additions & 33 deletions plugin-src/transformers/partials/transformDimensionAndPosition.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { getBoundingBox } from '@plugin/utils';

import { ShapeGeomAttributes } from '@ui/lib/types/shapes/shape';

export const transformDimension = (
Expand All @@ -11,41 +9,13 @@ export const transformDimension = (
};
};

export const transformPosition = (
node: DimensionAndPositionMixin,
baseX: number,
baseY: number
): Pick<ShapeGeomAttributes, 'x' | 'y'> => {
return {
x: node.x + baseX,
y: node.y + baseY
};
};

export const transformDimensionAndPosition = (
node: DimensionAndPositionMixin,
baseX: number,
baseY: number
node: DimensionAndPositionMixin
): ShapeGeomAttributes => {
return {
x: node.x + baseX,
y: node.y + baseY,
x: node.absoluteTransform[0][2],
y: node.absoluteTransform[1][2],
width: node.width,
height: node.height
};
};

export const transformDimensionAndPositionFromVectorPath = (
vectorPath: VectorPath,
baseX: number,
baseY: number
): ShapeGeomAttributes => {
const boundingBox = getBoundingBox(vectorPath);

return {
x: boundingBox.x1 + baseX,
y: boundingBox.y1 + baseY,
width: boundingBox.x2 - boundingBox.x1,
height: boundingBox.y2 - boundingBox.y1
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ import { ShapeBaseAttributes, ShapeGeomAttributes } from '@ui/lib/types/shapes/s

export const transformRotationAndPosition = (
node: LayoutMixin,
baseX: number,
baseY: number
baseRotation: number
): Pick<ShapeBaseAttributes, 'transform' | 'transformInverse' | 'rotation'> &
Pick<ShapeGeomAttributes, 'x' | 'y'> => {
const rotation = node.rotation;
const x = node.x + baseX;
const y = node.y + baseY;
const rotation = node.rotation + baseRotation;
const x = node.absoluteTransform[0][2];
const y = node.absoluteTransform[1][2];

if (!hasRotation(rotation) || !node.absoluteBoundingBox) {
return {
Expand Down
22 changes: 9 additions & 13 deletions plugin-src/transformers/partials/transformVectorPaths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { parseSVG } from 'svg-path-parser';

import {
transformBlend,
transformDimensionAndPositionFromVectorPath,
transformEffects,
transformLayoutAttributes,
transformProportion,
Expand All @@ -14,11 +13,7 @@ import { translateCommandsToSegments, translateWindingRule } from '@plugin/trans

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

export const transformVectorPaths = (
node: VectorNode,
baseX: number,
baseY: number
): PathShape[] => {
export const transformVectorPaths = (node: VectorNode): PathShape[] => {
const pathShapes = node.vectorPaths
.filter((vectorPath, index) => {
return (
Expand All @@ -27,7 +22,7 @@ export const transformVectorPaths = (
);
})
.map((vectorPath, index) =>
transformVectorPath(node, vectorPath, (node.vectorNetwork.regions ?? [])[index], baseX, baseY)
transformVectorPath(node, vectorPath, (node.vectorNetwork.regions ?? [])[index])
);

const geometryShapes = node.fillGeometry
Expand All @@ -37,7 +32,7 @@ export const transformVectorPaths = (
vectorPath => normalizePath(vectorPath.data) === normalizePath(geometry.data)
)
)
.map(geometry => transformVectorPath(node, geometry, undefined, baseX, baseY));
.map(geometry => transformVectorPath(node, geometry, undefined));

return [...geometryShapes, ...pathShapes];
};
Expand All @@ -63,16 +58,18 @@ const nodeHasFills = (
const transformVectorPath = (
node: VectorNode,
vectorPath: VectorPath,
vectorRegion: VectorRegion | undefined,
baseX: number,
baseY: number
vectorRegion: VectorRegion | undefined
): PathShape => {
const normalizedPaths = parseSVG(vectorPath.data);

return {
type: 'path',
name: 'svg-path',
content: translateCommandsToSegments(normalizedPaths, baseX + node.x, baseY + node.y),
content: translateCommandsToSegments(
normalizedPaths,
node.absoluteTransform[0][2],
node.absoluteTransform[1][2]
),
fills:
vectorPath.windingRule === 'NONE' ? [] : translateFills(vectorRegion?.fills ?? node.fills),
svgAttrs: {
Expand All @@ -82,7 +79,6 @@ const transformVectorPath = (
constraintsV: 'scale',
...transformStrokesFromVector(node, normalizedPaths, vectorRegion),
...transformEffects(node),
...transformDimensionAndPositionFromVectorPath(vectorPath, baseX, baseY),
...transformSceneNode(node),
...transformBlend(node),
...transformProportion(node),
Expand Down
11 changes: 6 additions & 5 deletions plugin-src/transformers/transformBooleanNode.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import {
transformBlend,
transformChildren,
transformDimensionAndPosition,
transformDimension,
transformEffects,
transformFigmaIds,
transformFills,
transformLayoutAttributes,
transformProportion,
transformRotationAndPosition,
transformSceneNode,
transformStrokes
} from '@plugin/transformers/partials';
Expand All @@ -16,19 +17,19 @@ import { BoolShape } from '@ui/lib/types/shapes/boolShape';

export const transformBooleanNode = async (
node: BooleanOperationNode,
baseX: number,
baseY: number
baseRotation: number
): Promise<BoolShape> => {
return {
type: 'bool',
name: node.name,
boolType: translateBoolType(node.booleanOperation),
...transformFigmaIds(node),
...(await transformChildren(node, baseX, baseY)),
...(await transformChildren(node, baseRotation)),
...transformFills(node),
...transformEffects(node),
...transformStrokes(node),
...transformDimensionAndPosition(node, baseX, baseY),
...transformDimension(node),
...transformRotationAndPosition(node, baseRotation),
...transformSceneNode(node),
...transformBlend(node),
...transformProportion(node),
Expand Down
11 changes: 6 additions & 5 deletions plugin-src/transformers/transformComponentNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import {
transformChildren,
transformConstraints,
transformCornerRadius,
transformDimensionAndPosition,
transformDimension,
transformEffects,
transformFigmaIds,
transformFills,
transformLayoutAttributes,
transformProportion,
transformRotationAndPosition,
transformSceneNode,
transformStrokes
} from '@plugin/transformers/partials';
Expand All @@ -19,8 +20,7 @@ import { ComponentRoot } from '@ui/types';

export const transformComponentNode = async (
node: ComponentNode,
baseX: number,
baseY: number
baseRotation: number
): Promise<ComponentRoot> => {
componentsLibrary.register(node.id, {
type: 'component',
Expand All @@ -36,8 +36,9 @@ export const transformComponentNode = async (
...transformProportion(node),
...transformLayoutAttributes(node),
...transformCornerRadius(node),
...(await transformChildren(node, baseX + node.x, baseY + node.y)),
...transformDimensionAndPosition(node, baseX, baseY),
...(await transformChildren(node, node.rotation + baseRotation)),
...transformDimension(node),
...transformRotationAndPosition(node, baseRotation),
...transformConstraints(node),
...transformAutoLayout(node)
});
Expand Down
8 changes: 2 additions & 6 deletions plugin-src/transformers/transformEllipseNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,7 @@ import {

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

export const transformEllipseNode = (
node: EllipseNode,
baseX: number,
baseY: number
): CircleShape => {
export const transformEllipseNode = (node: EllipseNode, baseRotation: number): CircleShape => {
return {
type: 'circle',
name: node.name,
Expand All @@ -27,7 +23,7 @@ export const transformEllipseNode = (
...transformEffects(node),
...transformStrokes(node),
...transformDimension(node),
...transformRotationAndPosition(node, baseX, baseY),
...transformRotationAndPosition(node, baseRotation),
...transformSceneNode(node),
...transformBlend(node),
...transformProportion(node),
Expand Down
22 changes: 16 additions & 6 deletions plugin-src/transformers/transformFrameNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,38 @@ import {
transformChildren,
transformConstraints,
transformCornerRadius,
transformDimensionAndPosition,
transformDimension,
transformEffects,
transformFigmaIds,
transformFills,
transformLayoutAttributes,
transformProportion,
transformRotationAndPosition,
transformSceneNode,
transformStrokes
} from '@plugin/transformers/partials';

import { FrameShape } from '@ui/lib/types/shapes/frameShape';
import { Point } from '@ui/lib/types/utils/point';

const isSectionNode = (node: FrameNode | SectionNode | ComponentSetNode): node is SectionNode => {
return node.type === 'SECTION';
};

export const transformFrameNode = async (
node: FrameNode | SectionNode | ComponentSetNode,
baseX: number,
baseY: number
baseRotation: number
): Promise<FrameShape> => {
let frameSpecificAttributes: Partial<FrameShape> = {};
let referencePoint: Point = { x: node.absoluteTransform[0][2], y: node.absoluteTransform[1][2] };
let rotation = baseRotation;

if (!isSectionNode(node)) {
const { x, y, ...transformAndRotation } = transformRotationAndPosition(node, baseRotation);

referencePoint = { x, y };
rotation += node.rotation;

// Figma API does not expose strokes, blend modes, corner radius, or constraint proportions for sections,
// they plan to add it in the future. Refactor this when available.
frameSpecificAttributes = {
Expand All @@ -40,7 +48,8 @@ export const transformFrameNode = async (
...transformCornerRadius(node),
...transformEffects(node),
...transformConstraints(node),
...transformAutoLayout(node)
...transformAutoLayout(node),
...transformAndRotation
};
}

Expand All @@ -50,9 +59,10 @@ export const transformFrameNode = async (
showContent: isSectionNode(node) ? true : !node.clipsContent,
...transformFigmaIds(node),
...transformFills(node),
...referencePoint,
...frameSpecificAttributes,
...(await transformChildren(node, baseX + node.x, baseY + node.y)),
...transformDimensionAndPosition(node, baseX, baseY),
...transformDimension(node),
...(await transformChildren(node, rotation)),
...transformSceneNode(node)
};
};
18 changes: 9 additions & 9 deletions plugin-src/transformers/transformGroupNode.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import {
transformBlend,
transformDimensionAndPosition,
transformDimension,
transformEffects,
transformFigmaIds,
transformRotationAndPosition,
transformSceneNode
} from '@plugin/transformers/partials';
import { transformChildren } from '@plugin/transformers/partials';
Expand All @@ -11,27 +12,26 @@ import { GroupShape } from '@ui/lib/types/shapes/groupShape';

export const transformGroupNode = async (
node: GroupNode,
baseX: number,
baseY: number
baseRotation: number
): Promise<GroupShape> => {
return {
...transformFigmaIds(node),
...transformGroupNodeLike(node, baseX, baseY),
...transformGroupNodeLike(node, baseRotation),
...transformEffects(node),
...transformBlend(node),
...(await transformChildren(node, baseX, baseY))
...(await transformChildren(node, baseRotation))
};
};

export const transformGroupNodeLike = (
node: BaseNodeMixin & DimensionAndPositionMixin & SceneNodeMixin,
baseX: number,
baseY: number
node: BaseNodeMixin & LayoutMixin & SceneNodeMixin,
baseRotation: number
): GroupShape => {
return {
type: 'group',
name: node.name,
...transformDimensionAndPosition(node, baseX, baseY),
...transformDimension(node),
...transformRotationAndPosition(node, baseRotation),
...transformSceneNode(node)
};
};
Loading