Skip to content

Commit e166da6

Browse files
committed
rotsprite finished
1 parent 8cbaafe commit e166da6

File tree

6 files changed

+170
-145
lines changed

6 files changed

+170
-145
lines changed

CHANGELOG

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Changelog
22

33
## [unreleased]
4+
- Sprite Rotation
45
- Add zoom slider in sprites tab
56

67
## [1.1.3]

TODO

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,17 @@
1212

1313
ROADMAP
1414
look at issues related to crashes
15-
rotsprite
1615
drawing mode bugs
1716
improve undo/redo when drawing pixels / moving things
1817
animation editor
1918
export to gif
19+
20+
21+
rotsprite
22+
==
23+
better alg
24+
going back when making mappings
25+
2026
==
2127
BUGS
2228

@@ -29,15 +35,14 @@ set colour of art tiles
2935
make decompression threaded
3036
ctrl+wheel for horiz scroll
3137
==
32-
ROTSPRITE
33-
rotsprite https://crates.io/crates/rotsprite
34-
https://forums.sonicretro.org/index.php?threads/sprite-rotation-utility.8848/#post-159754
35-
==
3638
select multiple sprites at once to reorder
3739
copy / paste mappings
3840
time travel for undo/redo
3941
import palette from spritesheet https://yarnpkg.com/en/package/node-vibrant
4042
==
43+
Speaking of Flex2, will there be a way for sprite sheets to not load already compressed tiles? It seems like every time I add new sprites, it makes duplicates of the same tiles leading me to manually remove it myself or overlap the mappings.
44+
45+
4146
22:04 <+dIRCord> <M​ainMemory> > Art is easy ␤> Each byte encodes one pixel, both nybbles have the color index ␤> And then each column is stored all in one go, no breaking into tiles ␤> In other words, it's pretty much a headerless 8bpp bitmap ␤> I think the color index is duplicated over both nybbles for speed, but I never looked at the algorithm
4247
21:08 <+dIRCord> <R​andomName> Not this. For example look at ArtScaled_EggRoboFly, which is art that's used by s3k art scaler. It's uncompressed art,tho it looks transformed in some way, but not like random mess of pixels.
4348
uses a grayscale copy of another frame that you selected separately from the current one as a "background" frame.

app/components/import/state.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class ImportState {
2525

2626
reset = () => {
2727
this.path = undefined;
28+
this.rotCanvas = undefined;
2829
this.canvas = undefined;
2930
this.ctx = undefined;
3031
this.spriteIndex = 0;
@@ -82,12 +83,10 @@ class ImportState {
8283

8384
} else if (this.rotCanvas) {
8485
this.ctx.drawImage(this.rotCanvas, 0, 0);
85-
delete this.rotCanvas;
8686
requestAnimationFrame(() => {
8787
this.loaded();
8888
importState.getBBoxes();
8989
importState.importSprites();
90-
importState.importAll();
9190
});
9291
}
9392
};

app/components/import/ui-import.js

Lines changed: 156 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -5,145 +5,165 @@ import { importState } from './state';
55
import { Item, Select, Input } from '#ui';
66
import { zoomAssert, paletteLineAssert } from '#util/assertions';
77

8-
export const ImportSprites = observer(class ImportSprites extends React.Component {
9-
10-
render() {
11-
const { scale, sprites, spriteIndex, mappings, importWidth, importHeight } = importState;
12-
13-
return <div className="importer">
14-
15-
<div className="menu">
16-
17-
<div className="menu-section">
18-
<Item
19-
color="blue"
20-
inverted
21-
onClick={importState.importOne}
22-
>
23-
Import Sprite
24-
</Item>
25-
<Item
26-
color="magenta"
27-
inverted
28-
onClick={importState.importAll}
29-
>
30-
Import All
31-
</Item>
32-
<Item
33-
color="yellow"
34-
inverted
35-
onClick={importState.next}
36-
>
37-
Next Sprite
38-
</Item>
39-
<Item
40-
color="yellow"
41-
inverted
42-
onClick={importState.prev}
43-
>
44-
Prev Sprite
45-
</Item>
46-
47-
<Select
48-
options={[
49-
{label: 'Reduce Mappings', value: 'mappings'},
50-
{label: 'Reduce Tiles', value: 'tiles'},
51-
]}
52-
store={importState}
53-
accessor="type"
54-
onChange={importState.changeType}
55-
/>
56-
57-
<div className="input">
58-
<span>Palette</span>
59-
<Input
60-
store={importState}
61-
accessor="paletteLine"
62-
assert={paletteLineAssert}
63-
onChange={importState.changePalette}
64-
isNumber
65-
/>
8+
export const ImportSprites = observer(
9+
class ImportSprites extends React.Component {
10+
render() {
11+
const {
12+
scale,
13+
sprites,
14+
spriteIndex,
15+
mappings,
16+
importWidth,
17+
importHeight,
18+
} = importState;
19+
20+
return (
21+
<div className="importer">
22+
<div className="menu">
23+
<div className="menu-section">
24+
<Item
25+
color="blue"
26+
inverted
27+
onClick={importState.importOne}
28+
>
29+
Import Sprite
30+
</Item>
31+
{sprites.length > 1 && (
32+
<>
33+
<Item
34+
color="magenta"
35+
inverted
36+
onClick={importState.importAll}
37+
>
38+
Import All
39+
</Item>
40+
<Item
41+
color="yellow"
42+
inverted
43+
onClick={importState.next}
44+
>
45+
Next Sprite
46+
</Item>
47+
<Item
48+
color="yellow"
49+
inverted
50+
onClick={importState.prev}
51+
>
52+
Prev Sprite
53+
</Item>
54+
</>
55+
)}
56+
57+
<Select
58+
options={[
59+
{
60+
label: 'Reduce Mappings',
61+
value: 'mappings',
62+
},
63+
{ label: 'Reduce Tiles', value: 'tiles' },
64+
]}
65+
store={importState}
66+
accessor="type"
67+
onChange={importState.changeType}
68+
/>
69+
70+
<div className="input">
71+
<span>Palette</span>
72+
<Input
73+
store={importState}
74+
accessor="paletteLine"
75+
assert={paletteLineAssert}
76+
onChange={importState.changePalette}
77+
isNumber
78+
/>
79+
</div>
80+
81+
<div className="input">
82+
<span>Zoom</span>
83+
<Input
84+
store={importState}
85+
accessor="scale"
86+
assert={zoomAssert}
87+
isNumber
88+
/>
89+
</div>
90+
91+
<div className="input">
92+
<span>Mappings</span>
93+
<span>{mappings.length}</span>
94+
</div>
95+
96+
<div className="input">
97+
<span>Tiles</span>
98+
<span>{importState.tileQty}</span>
99+
</div>
100+
101+
{sprites.length > 1 && (
102+
<div className="input">
103+
<span>Sprite</span>
104+
<span>
105+
{spriteIndex + 1} / {sprites.length}
106+
</span>
107+
</div>
108+
)}
109+
</div>
110+
111+
<div className="menu-section">
112+
{importState.path &&
113+
<Item
114+
color="orange"
115+
inverted
116+
onClick={importState.backToDetect}
117+
>
118+
Back
119+
</Item>}
120+
<Item
121+
color="red"
122+
inverted
123+
onClick={importState.cancel}
124+
>
125+
Cancel
126+
</Item>
127+
</div>
66128
</div>
67129

68-
<div className="input">
69-
<span>Zoom</span>
70-
<Input
71-
store={importState}
72-
accessor="scale"
73-
assert={zoomAssert}
74-
isNumber
75-
/>
76-
</div>
77-
78-
<div className="input">
79-
<span>Mappings</span>
80-
<span>{mappings.length}</span>
81-
</div>
82-
83-
<div className="input">
84-
<span>Tiles</span>
85-
<span>{importState.tileQty}</span>
86-
</div>
87-
88-
<div className="input">
89-
<span>Sprite</span>
90-
<span>{spriteIndex+1} / {sprites.length}</span>
91-
</div>
92-
93-
</div>
94-
95-
<div className="menu-section">
96-
<Item
97-
color="orange"
98-
inverted
99-
onClick={importState.backToDetect}
100-
>
101-
Back
102-
</Item>
103-
<Item
104-
color="red"
105-
inverted
106-
onClick={importState.cancel}
107-
>
108-
Cancel
109-
</Item>
110-
</div>
111-
112-
</div>
113-
114-
115-
<div className="container">
116-
<div
117-
className="workspace"
118-
style={{
119-
width: 0,
120-
height: 0,
121-
left: '50%',
122-
top: '50%',
123-
transform: `
124-
scale(${scale})
125-
translate(-${importWidth/2}px,-${importHeight/2}px)
126-
`,
127-
}}>
128-
<canvas
129-
key={`import-${spriteIndex}`}
130-
ref={importState.canvasRefImport}
131-
className="import-canvas"
132-
/>
133-
{mappings.map(({x, y, width, height}, i) => (
130+
<div className="container">
134131
<div
132+
className="workspace"
135133
style={{
136-
top: y,
137-
left: x,
138-
width: (8*width)-1,
139-
height: (8*height)-1,
134+
width: 0,
135+
height: 0,
136+
left: '50%',
137+
top: '50%',
138+
transform: `
139+
scale(${scale})
140+
translate(-${importWidth / 2}px,-${
141+
importHeight / 2
142+
}px)
143+
`,
140144
}}
141-
key={i}
142-
className="import-mapping"
143-
/>
144-
))}
145+
>
146+
<canvas
147+
key={`import-${spriteIndex}`}
148+
ref={importState.canvasRefImport}
149+
className="import-canvas"
150+
/>
151+
{mappings.map(({ x, y, width, height }, i) => (
152+
<div
153+
style={{
154+
top: y,
155+
left: x,
156+
width: 8 * width - 1,
157+
height: 8 * height - 1,
158+
}}
159+
key={i}
160+
className="import-mapping"
161+
/>
162+
))}
163+
</div>
164+
</div>
145165
</div>
146-
</div>
147-
</div>;
148-
}
149-
});
166+
);
167+
}
168+
},
169+
);

app/components/mappings/rotate.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export const Rotate = observer(() => {
9494
<Button color="magenta" onClick={mappingState.toggleRotate}>
9595
close
9696
</Button>
97-
<Button color="red" onClick={reImport}>Reimport</Button>
97+
<Button color="red" onClick={reImport}>Import</Button>
9898
</div>
9999
</Modal>
100100
);

app/main.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import './components/import';
1010
import { configure } from 'mobx';
1111

1212
configure({
13-
enforceActions: 'never',
13+
enforceActions: 'never', // legacy design choice
1414
});
1515

1616
render(

0 commit comments

Comments
 (0)