feat: add crop and canvas resize workflows
This commit is contained in:
@@ -58,6 +58,11 @@ describe("document read indexes", () => {
|
||||
expect(resolveIndexedLayerBounds(index, raster("missing", "Missing", "missing-asset"))).toBeUndefined();
|
||||
});
|
||||
|
||||
test("resolves a cropped layer from its retained source-pixel position", () => {
|
||||
const cropped = { ...document, artboards: [{ ...document.artboards[0]!, layers: [{ ...raster("cropped", "Cropped", "asset-target", { x: 10, y: 20 }, { x: 2, y: 3 }), sourceRect: { x: 5, y: 4, w: 20, h: 10 } }] }] };
|
||||
expect(resolveIndexedLayerBounds(createDocumentReadIndex(cropped), "cropped")).toEqual({ x: 20, y: 32, w: 40, h: 30 });
|
||||
});
|
||||
|
||||
test("visits layers back to front without mutating source order", () => {
|
||||
const layers = document.artboards[0]!.layers;
|
||||
const visited: string[] = [];
|
||||
|
||||
@@ -79,12 +79,13 @@ export function resolveIndexedLayerBounds(index: DocumentReadIndex, layerOrId: L
|
||||
case "raster": {
|
||||
const asset = index.assetById.get(layer.assetId);
|
||||
if (!asset) return undefined;
|
||||
const source = layer.sourceRect ?? { x: 0, y: 0, ...asset.intrinsicSize };
|
||||
|
||||
return {
|
||||
x: layer.transform.position.x,
|
||||
y: layer.transform.position.y,
|
||||
w: asset.intrinsicSize.w * layer.transform.scale.x,
|
||||
h: asset.intrinsicSize.h * layer.transform.scale.y,
|
||||
x: layer.transform.position.x + source.x * layer.transform.scale.x,
|
||||
y: layer.transform.position.y + source.y * layer.transform.scale.y,
|
||||
w: source.w * layer.transform.scale.x,
|
||||
h: source.h * layer.transform.scale.y,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,13 @@ describe("transform targets", () => {
|
||||
expect(layer?.transform).toEqual({ position: { x: 30, y: 40 }, scale: { x: 2, y: 0.5 }, rotation: 0 });
|
||||
});
|
||||
|
||||
test("applies visible bounds to a cropped layer while retaining its source crop", () => {
|
||||
const cropped = { ...document, artboards: [{ ...document.artboards[0]!, layers: [{ ...document.artboards[0]!.layers[0]!, sourceRect: { x: 10, y: 5, w: 40, h: 20 } }] }] };
|
||||
const next = applyTransformTargetBounds(cropped, { type: "layer", id: "l1" }, { x: 100, y: 80, w: 200, h: 60 });
|
||||
expect(next.artboards[0]?.layers[0]).toMatchObject({ sourceRect: { x: 10, y: 5, w: 40, h: 20 }, transform: { position: { x: 50, y: 65 }, scale: { x: 5, y: 3 } } });
|
||||
expect(resolveTransformTargetBounds(next, { type: "layer", id: "l1" })).toEqual({ x: 100, y: 80, w: 200, h: 60 });
|
||||
});
|
||||
|
||||
test("keeps attached mask bounds in sync with transformed layers", () => {
|
||||
const maskedDocument: ImageDocument = {
|
||||
...document,
|
||||
|
||||
@@ -53,12 +53,13 @@ function resolveLayerBounds(document: ImageDocument, layer: Layer): Rect | undef
|
||||
case "raster": {
|
||||
const asset = document.assets.find((candidate) => candidate.id === layer.assetId);
|
||||
if (!asset) return undefined;
|
||||
const source = layer.sourceRect ?? { x: 0, y: 0, ...asset.intrinsicSize };
|
||||
|
||||
return {
|
||||
x: layer.transform.position.x,
|
||||
y: layer.transform.position.y,
|
||||
w: asset.intrinsicSize.w * layer.transform.scale.x,
|
||||
h: asset.intrinsicSize.h * layer.transform.scale.y,
|
||||
x: layer.transform.position.x + source.x * layer.transform.scale.x,
|
||||
y: layer.transform.position.y + source.y * layer.transform.scale.y,
|
||||
w: source.w * layer.transform.scale.x,
|
||||
h: source.h * layer.transform.scale.y,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user