Skip to content

Commit b2c0677

Browse files
committed
base implementation
1 parent 98f120e commit b2c0677

File tree

2 files changed

+101
-56
lines changed

2 files changed

+101
-56
lines changed

app/components/mappings/rotate.js

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, { useEffect, useRef } from 'react';
22
import { environment } from '#store/environment';
33
import { observer } from 'mobx-react';
44
import { exportSprite } from '#formats/image';
5-
import { rotateImageData } from '#util/rotsprite';
5+
import { rotateImageData, getRotateDiagonal } from '#util/rotsprite';
66
import { Input, Slider, Item, Button, Modal } from '#ui';
77
import { mappingState } from './state';
88

@@ -11,14 +11,58 @@ import { importState } from '../import/state';
1111
function rotateCurrentSprite(canvas, angle) {
1212
const spriteCanv = exportSprite(environment.currentSprite);
1313
const spriteCtx = spriteCanv.getContext('2d');
14-
const { width, height } = spriteCanv;
15-
const imageData = spriteCtx.getImageData(0, 0, width, height);
16-
const rotatedData = rotateImageData(imageData, angle, width, height);
14+
// const { width, height } = spriteCanv;
15+
// const imageData = spriteCtx.getImageData(0, 0, width, height);
16+
// const rotatedData = rotateImageData(imageData, angle, width, height);
17+
18+
// const ctx = canvas.getContext('2d');
19+
// canvas.width = rotatedData.width;
20+
// canvas.height = rotatedData.height;
21+
// ctx.putImageData(rotatedData, 0, 0);
22+
23+
24+
// add padding
25+
const { diagonal, xMargin, yMargin } = getRotateDiagonal(spriteCanv.width, spriteCanv.height);
26+
const width = diagonal;
27+
const height = diagonal;
28+
29+
const copy = spriteCtx.getImageData(0, 0, spriteCanv.width, spriteCanv.height);
30+
31+
spriteCanv.width = diagonal;
32+
spriteCanv.height = diagonal;
33+
34+
spriteCtx.putImageData(copy, xMargin, yMargin);
1735

1836
const ctx = canvas.getContext('2d');
19-
canvas.width = rotatedData.width;
20-
canvas.height = rotatedData.height;
21-
ctx.putImageData(rotatedData, 0, 0);
37+
canvas.width = width;
38+
canvas.height = height;
39+
40+
// rotate
41+
42+
const theta = angle * Math.PI / 180;
43+
const alpha = -Math.tan(theta/2);
44+
const beta = Math.sin(theta);
45+
46+
47+
48+
for (let y = 0; y < height; ++y) {
49+
const shear = Math.round((y - height/2) * alpha);
50+
ctx.drawImage(spriteCanv, 0, y, width, 1, shear, y, width, 1);
51+
}
52+
spriteCtx.clearRect(0, 0, width, height);
53+
spriteCtx.drawImage(canvas, 0, 0);
54+
ctx.clearRect(0, 0, width, height);
55+
for (let x = 0; x < width; ++x) {
56+
const shear = Math.round((x - width/2) * beta);
57+
ctx.drawImage(spriteCanv, x, 0, 1, height, x, shear, 1, height);
58+
}
59+
spriteCtx.clearRect(0, 0, width, height);
60+
spriteCtx.drawImage(canvas, 0, 0);
61+
ctx.clearRect(0, 0, width, height);
62+
for (let y = 0; y < height; ++y) {
63+
const shear = Math.round((y - height/2) * alpha);
64+
ctx.drawImage(spriteCanv, 0, y, width, 1, shear, y, width, 1);
65+
}
2266
}
2367

2468
export const Rotate = observer(() => {

app/util/rotsprite.js

Lines changed: 50 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,57 @@
1+
export function getRotateDiagonal(width, height) {
2+
const diagonal =
3+
1 + (0 | (2 * Math.sqrt(width ** 2 / 4 + height ** 2 / 4)));
4+
5+
const xMargin = Math.round((diagonal - width) / 2);
6+
const yMargin = Math.round((diagonal - height) / 2);
7+
8+
return { diagonal: xMargin * 2 + width, xMargin, yMargin };
9+
}
10+
11+
export function rotateImageData(imageData, angle, width, height) {
12+
const { diagonal, xMargin, yMargin } = getRotateDiagonal(width, height);
13+
14+
const spriteData = addMarginToImageData(
15+
imageData,
16+
xMargin,
17+
yMargin,
18+
);
19+
20+
const data = new Uint32Array(diagonal ** 2);
21+
22+
for (let i = 0; i < spriteData.data.length; i += 4) {
23+
const r = spriteData.data[i];
24+
const g = spriteData.data[i + 1];
25+
const b = spriteData.data[i + 2];
26+
const a = spriteData.data[i + 3];
27+
data[i / 4] = (a << 24) + (r << 16) + (g << 8) + b;
28+
}
29+
30+
const rotated = rotSprite(
31+
new Pixels(diagonal, diagonal, data),
32+
(angle * Math.PI) / 180,
33+
).pixels;
34+
35+
const pixelData = new Uint8ClampedArray(data.length * 4);
36+
37+
for (let i = 0; i < data.length; i++) {
38+
const value = rotated[i];
39+
40+
pixelData[i * 4] = (value >> 16) & 0xff;
41+
pixelData[i * 4 + 1] = (value >> 8) & 0xff;
42+
pixelData[i * 4 + 2] = value & 0xff;
43+
pixelData[i * 4 + 3] = (value >> 24) & 0xff;
44+
}
45+
46+
return new ImageData(pixelData, diagonal, diagonal);
47+
}
48+
49+
// rotsprite
150
// original algorithm Xenowhirl
251
// stole code from ChaseMor/pxt-arcade-rotsprite
352
// some generated with chatGPT
453

5-
export default function rotSprite(image, angle) {
54+
function rotSprite(image, angle) {
655
image = scale2xImage(image);
756
image = scale2xImage(image);
857
image = scale2xImage(image);
@@ -169,51 +218,3 @@ export function addMarginToImageData(imageData, xMargin, yMargin) {
169218

170219
return newImageData;
171220
}
172-
173-
export function getRotateDiagonal(width, height) {
174-
const diagonal =
175-
1 + (0 | (2 * Math.sqrt(width ** 2 / 4 + height ** 2 / 4)));
176-
177-
const xMargin = Math.round((diagonal - width) / 2);
178-
const yMargin = Math.round((diagonal - height) / 2);
179-
180-
return { diagonal: xMargin * 2 + width, xMargin, yMargin };
181-
}
182-
183-
export function rotateImageData(imageData, angle, width, height) {
184-
const { diagonal, xMargin, yMargin } = getRotateDiagonal(width, height);
185-
186-
const spriteData = addMarginToImageData(
187-
imageData,
188-
xMargin,
189-
yMargin,
190-
);
191-
192-
const data = new Uint32Array(diagonal ** 2);
193-
194-
for (let i = 0; i < spriteData.data.length; i += 4) {
195-
const r = spriteData.data[i];
196-
const g = spriteData.data[i + 1];
197-
const b = spriteData.data[i + 2];
198-
const a = spriteData.data[i + 3];
199-
data[i / 4] = (a << 24) + (r << 16) + (g << 8) + b;
200-
}
201-
202-
const rotated = rotSprite(
203-
new Pixels(diagonal, diagonal, data),
204-
(angle * Math.PI) / 180,
205-
).pixels;
206-
207-
const pixelData = new Uint8ClampedArray(data.length * 4);
208-
209-
for (let i = 0; i < data.length; i++) {
210-
const value = rotated[i];
211-
212-
pixelData[i * 4] = (value >> 16) & 0xff;
213-
pixelData[i * 4 + 1] = (value >> 8) & 0xff;
214-
pixelData[i * 4 + 2] = value & 0xff;
215-
pixelData[i * 4 + 3] = (value >> 24) & 0xff;
216-
}
217-
218-
return new ImageData(pixelData, diagonal, diagonal);
219-
}

0 commit comments

Comments
 (0)