feat: fill
This commit is contained in:
parent
649f5ce859
commit
ad0916727e
|
@ -1,10 +1,9 @@
|
||||||
export default function floodwalk2D(b, data, [x, y, w, h]) {
|
export default function floodwalk2D(eligible, data, [x, y, w, h]) {
|
||||||
const n = data.length;
|
const n = data.length;
|
||||||
let i = x + w * y;
|
let i = x + w * y;
|
||||||
if (i < 0 || i >= n) {
|
if (i < 0 || i >= n) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
// const b = data[i];
|
|
||||||
const points = [];
|
const points = [];
|
||||||
const seen = [];
|
const seen = [];
|
||||||
seen[-1] = true;
|
seen[-1] = true;
|
||||||
|
@ -13,7 +12,7 @@ export default function floodwalk2D(b, data, [x, y, w, h]) {
|
||||||
while (todo.length > 0) {
|
while (todo.length > 0) {
|
||||||
i = todo.pop();
|
i = todo.pop();
|
||||||
const v = data[i];
|
const v = data[i];
|
||||||
if (!b.has(v)) {
|
if (!eligible.has(v)) {
|
||||||
// eslint-disable-next-line no-continue
|
// eslint-disable-next-line no-continue
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,9 @@ import {
|
||||||
Stage,
|
Stage,
|
||||||
} from '@avocado/graphics';
|
} from '@avocado/graphics';
|
||||||
import {
|
import {
|
||||||
|
floodwalk2D,
|
||||||
|
mod,
|
||||||
|
randomNumber,
|
||||||
Rectangle,
|
Rectangle,
|
||||||
Vector,
|
Vector,
|
||||||
} from '@avocado/math';
|
} from '@avocado/math';
|
||||||
|
@ -61,6 +64,7 @@ function TilesPage({
|
||||||
const [mute, setMute] = useState(Array(layerCount).fill(false));
|
const [mute, setMute] = useState(Array(layerCount).fill(false));
|
||||||
const [selection, setSelection] = useState([0, 0, 16, 16]);
|
const [selection, setSelection] = useState([0, 0, 16, 16]);
|
||||||
const [tilesetMode, setTilesetMode] = useState(0);
|
const [tilesetMode, setTilesetMode] = useState(0);
|
||||||
|
const [fillMode, setFillMode] = useState(0);
|
||||||
const [viewport, setViewport] = useState([0, 0]);
|
const [viewport, setViewport] = useState([0, 0]);
|
||||||
const onRef = useCallback((stage) => {
|
const onRef = useCallback((stage) => {
|
||||||
setTilesetEvents(stage?.events());
|
setTilesetEvents(stage?.events());
|
||||||
|
@ -107,7 +111,7 @@ function TilesPage({
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const tileSize = [16, 16];
|
const tileSize = [16, 16];
|
||||||
const stamp = (translated) => {
|
const stamp = (origin) => {
|
||||||
const position = Vector.div([selection[0], selection[1]], tileSize);
|
const position = Vector.div([selection[0], selection[1]], tileSize);
|
||||||
const size = Vector.div([selection[2], selection[3]], tileSize);
|
const size = Vector.div([selection[2], selection[3]], tileSize);
|
||||||
let [x, y] = position;
|
let [x, y] = position;
|
||||||
|
@ -120,7 +124,7 @@ function TilesPage({
|
||||||
x -= size[0];
|
x -= size[0];
|
||||||
y += 1;
|
y += 1;
|
||||||
}
|
}
|
||||||
const where = Rectangle.compose(translated, size);
|
const where = Rectangle.compose(origin, size);
|
||||||
room.tiles[currentLayer].stampAt(where, stamp);
|
room.tiles[currentLayer].stampAt(where, stamp);
|
||||||
room.tiles[currentLayer].emit('update', where);
|
room.tiles[currentLayer].emit('update', where);
|
||||||
const {data} = room.tiles[currentLayer].toJSON();
|
const {data} = room.tiles[currentLayer].toJSON();
|
||||||
|
@ -137,25 +141,113 @@ function TilesPage({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const fill = (origin) => {
|
||||||
|
const position = Vector.div([selection[0], selection[1]], tileSize);
|
||||||
|
const size = Vector.div([selection[2], selection[3]], tileSize);
|
||||||
|
const {width} = room.tiles[currentLayer];
|
||||||
|
const {$$data} = room.tiles[currentLayer];
|
||||||
|
const updates = floodwalk2D(
|
||||||
|
new Set([$$data[width * origin[1] + origin[0]]]),
|
||||||
|
room.tiles[currentLayer].$$data,
|
||||||
|
Rectangle.compose(origin, room.tiles[currentLayer].area),
|
||||||
|
);
|
||||||
|
const [min, max] = [[Infinity, Infinity], [-Infinity, -Infinity]];
|
||||||
|
for (let i = 0; i < updates.length; i++) {
|
||||||
|
const update = updates[i];
|
||||||
|
const [x, y] = [update % width, Math.floor(update / width)];
|
||||||
|
const offset = Vector.sub([x, y], origin);
|
||||||
|
if (x < min[0]) {
|
||||||
|
min[0] = x;
|
||||||
|
}
|
||||||
|
else if (x > max[0]) {
|
||||||
|
max[0] = x;
|
||||||
|
}
|
||||||
|
if (y < min[1]) {
|
||||||
|
min[1] = y;
|
||||||
|
}
|
||||||
|
else if (y > max[1]) {
|
||||||
|
max[1] = y;
|
||||||
|
}
|
||||||
|
let tileLocation;
|
||||||
|
switch (fillMode) {
|
||||||
|
case 0: {
|
||||||
|
tileLocation = Vector.add(
|
||||||
|
position,
|
||||||
|
[
|
||||||
|
mod(offset[0], size[0]),
|
||||||
|
mod(offset[1], size[1]),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
tileLocation = Vector.add(
|
||||||
|
position,
|
||||||
|
[
|
||||||
|
Math.floor(randomNumber(0, size[0])),
|
||||||
|
Math.floor(randomNumber(0, size[1])),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
$$data[update] = (tileLocation[1] * (viewport[0] / tileSize[0])) + tileLocation[0];
|
||||||
|
}
|
||||||
|
const where = Rectangle.compose(min, Vector.add([1, 1], Vector.sub(max, min)));
|
||||||
|
room.tiles[currentLayer].emit('update', where);
|
||||||
|
const {data} = room.tiles[currentLayer].toJSON();
|
||||||
|
if (!data) {
|
||||||
|
patch({
|
||||||
|
op: 'remove',
|
||||||
|
path: `/tiles/${currentLayer}/data`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
patch({
|
||||||
|
path: `/tiles/${currentLayer}/data`,
|
||||||
|
value: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
const onValue = (
|
const onValue = (
|
||||||
{
|
{
|
||||||
position,
|
position,
|
||||||
type,
|
type,
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
|
const origin = Vector.floor(Vector.div(position, tileSize));
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'touchstart':
|
case 'touchstart':
|
||||||
case 'mousedown': {
|
case 'mousedown': {
|
||||||
const translated = Vector.floor(Vector.div(position, tileSize));
|
switch (tilesetMode) {
|
||||||
stamp(translated);
|
case 0: {
|
||||||
|
stamp(origin);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
fill(origin);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
setIsHoldingPaint(true);
|
setIsHoldingPaint(true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'touchmove':
|
case 'touchmove':
|
||||||
case 'mousemove': {
|
case 'mousemove': {
|
||||||
if (isHoldingPaint) {
|
if (isHoldingPaint) {
|
||||||
const translated = Vector.floor(Vector.div(position, tileSize));
|
switch (tilesetMode) {
|
||||||
stamp(translated);
|
case 0: {
|
||||||
|
stamp(origin);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1: {
|
||||||
|
fill(origin);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -291,9 +383,14 @@ function TilesPage({
|
||||||
<option value={1}>Fill</option>
|
<option value={1}>Fill</option>
|
||||||
</select>
|
</select>
|
||||||
{1 === tilesetMode && (
|
{1 === tilesetMode && (
|
||||||
<select>
|
<select
|
||||||
<option>Tiling</option>
|
defaultValue={fillMode}
|
||||||
<option>Random</option>
|
onChange={(event) => {
|
||||||
|
setFillMode(parseInt(event.target.value, 10));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<option value={0}>Tiling</option>
|
||||||
|
<option value={1}>Random</option>
|
||||||
</select>
|
</select>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user