mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-09-18 22:22:21 +02:00
Squash Odysseus development history
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
import json
|
||||
import subprocess
|
||||
import textwrap
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
MODULE = (ROOT / "static/js/editor/multi-transform.js").as_uri()
|
||||
|
||||
|
||||
def run_node(body: str):
|
||||
script = textwrap.dedent(
|
||||
f"""
|
||||
import {{planLayerTransform,selectionBounds,transformedLayerGeometry,transformedSelectionBounds}} from {json.dumps(MODULE)};
|
||||
{body}
|
||||
"""
|
||||
)
|
||||
result = subprocess.run(
|
||||
["node", "--input-type=module", "-e", script], cwd=ROOT,
|
||||
text=True, capture_output=True, check=True,
|
||||
)
|
||||
return json.loads(result.stdout)
|
||||
|
||||
|
||||
def test_selection_bounds_cover_every_layer_and_preserve_relative_geometry_when_scaled():
|
||||
result = run_node(
|
||||
"""
|
||||
const layers=[{id:'a',canvas:{width:100,height:50}},{id:'b',canvas:{width:20,height:20}}];
|
||||
const state={layerOffsets:new Map([['a',{x:0,y:0}],['b',{x:180,y:80}]])};
|
||||
const bounds=selectionBounds(state,layers);
|
||||
const target={width:400,height:200,rotation:0,flipH:false,flipV:false,centerX:300,centerY:200};
|
||||
const a=transformedLayerGeometry({width:100,height:50,offset:{x:0,y:0}},bounds,target);
|
||||
const b=transformedLayerGeometry({width:20,height:20,offset:{x:180,y:80}},bounds,target);
|
||||
console.log(JSON.stringify({bounds,a,b,frame:transformedSelectionBounds(bounds,target)}));
|
||||
"""
|
||||
)
|
||||
assert result["bounds"] == {"x": 0, "y": 0, "width": 200, "height": 100, "centerX": 100, "centerY": 50}
|
||||
assert result["a"]["offset"] == {"x": 100, "y": 100}
|
||||
assert [result["a"]["width"], result["a"]["height"]] == [200, 100]
|
||||
assert result["b"]["offset"] == {"x": 460, "y": 260}
|
||||
assert [result["b"]["width"], result["b"]["height"]] == [40, 40]
|
||||
assert result["frame"] == {"x": 100, "y": 100, "width": 400, "height": 200, "centerX": 300, "centerY": 200}
|
||||
|
||||
|
||||
def test_rotation_and_flip_transform_layer_centers_around_the_shared_pivot():
|
||||
result = run_node(
|
||||
"""
|
||||
const bounds={x:0,y:0,width:200,height:100,centerX:100,centerY:50};
|
||||
const snapshot={width:100,height:50,offset:{x:0,y:0}};
|
||||
const target={width:400,height:200,rotation:90,flipH:true,flipV:false,centerX:300,centerY:200};
|
||||
console.log(JSON.stringify(transformedLayerGeometry(snapshot,bounds,target)));
|
||||
"""
|
||||
)
|
||||
assert [result["width"], result["height"]] == [100, 200]
|
||||
assert round(result["centerX"], 6) == 350
|
||||
assert round(result["centerY"], 6) == 300
|
||||
assert result["offset"] == {"x": 300, "y": 200}
|
||||
assert result["signedScaleX"] == -2
|
||||
assert result["signedScaleY"] == 2
|
||||
|
||||
|
||||
def test_transform_plan_counts_linked_masks_at_output_size_and_preserves_unlinked_surfaces():
|
||||
result = run_node(
|
||||
"""
|
||||
const linked={canvas:{width:100,height:50}};
|
||||
const unlinked={canvas:{width:30,height:20}};
|
||||
const layer={id:'a',canvas:{width:100,height:50},masks:[linked,unlinked]};
|
||||
const snapshot={
|
||||
layer,width:100,height:50,offset:{x:0,y:0},
|
||||
masks:[
|
||||
{mask:linked,linked:true,canvas:linked.canvas},
|
||||
{mask:unlinked,linked:false,canvas:unlinked.canvas},
|
||||
],
|
||||
};
|
||||
const state={
|
||||
layers:[layer,{id:'b',canvas:{width:10,height:10},masks:[]}],
|
||||
layerGroups:[{masks:[{canvas:{width:5,height:4}}]}],
|
||||
savedSelections:[{canvas:{width:8,height:6}}],
|
||||
wandMask:{width:7,height:3},
|
||||
};
|
||||
const bounds={x:0,y:0,width:100,height:50,centerX:50,centerY:25};
|
||||
const target={width:200,height:100,rotation:0,flipH:false,flipV:false,centerX:50,centerY:25};
|
||||
const plan=planLayerTransform(state,[snapshot],bounds,target,{maxDimension:1000,maxSurfacePixels:100000});
|
||||
console.log(JSON.stringify({ok:plan.ok,pixels:plan.surfacePixels,size:[plan.items[0].geometry.width,plan.items[0].geometry.height]}));
|
||||
"""
|
||||
)
|
||||
# layer 20k + linked mask 20k + unlinked mask 600 + other layer 100
|
||||
# + group mask 20 + saved selection 48 + active selection 21
|
||||
assert result == {"ok": True, "pixels": 40789, "size": [200, 100]}
|
||||
|
||||
|
||||
def test_transform_plan_rejects_dimension_and_total_surface_limits_before_allocation():
|
||||
result = run_node(
|
||||
"""
|
||||
const layer={id:'a',canvas:{width:100,height:50},masks:[]};
|
||||
const snapshot={layer,width:100,height:50,offset:{x:0,y:0},masks:[]};
|
||||
const state={layers:[layer],layerGroups:[],savedSelections:[],wandMask:null};
|
||||
const bounds={x:0,y:0,width:100,height:50,centerX:50,centerY:25};
|
||||
const base={rotation:0,flipH:false,flipV:false,centerX:50,centerY:25};
|
||||
const dimension=planLayerTransform(state,[snapshot],bounds,{...base,width:1001,height:20},{maxDimension:1000,maxSurfacePixels:100000});
|
||||
const surface=planLayerTransform(state,[snapshot],bounds,{...base,width:500,height:300},{maxDimension:1000,maxSurfacePixels:100000});
|
||||
const numeric=planLayerTransform(state,[snapshot],bounds,{...base,width:Infinity,height:20},{maxDimension:1000,maxSurfacePixels:100000});
|
||||
console.log(JSON.stringify({dimension,surface,numeric}));
|
||||
"""
|
||||
)
|
||||
assert result["dimension"]["ok"] is False
|
||||
assert "dimension limit" in result["dimension"]["reason"]
|
||||
assert result["surface"] == {
|
||||
"ok": False,
|
||||
"reason": "Transform would exceed the 300 megapixel surface budget.",
|
||||
}
|
||||
assert result["numeric"]["ok"] is False
|
||||
assert "finite positive" in result["numeric"]["reason"]
|
||||
Reference in New Issue
Block a user