mirror of
https://github.com/Ku6epXBOCTuK/easy-png-tools.git
synced 2026-11-13 17:08:20 +00:00
fix: shapes offset calculate
This commit is contained in:
@@ -65,10 +65,6 @@
|
||||
строку для svg - форматирование или подсветку синтаксиса??? для bin\hex -
|
||||
форматирование по колонкам??? для hex\bytes\rgba - пример в placeholder
|
||||
- [ ] сделать загрузку картинки исходника по ctrl+v и drag-and-drop (dropzone)
|
||||
- [ ] square-mask-png — перепроверить баг-репорт: он устарел, в схеме больше нет
|
||||
параметров `width`/`height`/`x`/`y` (теперь `widthPct`/`heightPct` 10..100
|
||||
и один `offset` в диапазоне −50..50, значения 100 недостижимы). Либо
|
||||
подтвердить, что баг исправлен, либо найти новый repro
|
||||
- [ ] прикрутить кастомный color picker, добавить прозрачность (range?)
|
||||
- [ ] проверить настройки range slider на crop-png - слишком большие значения
|
||||
- [ ] для range slider field - добавить поле для ввода цифр и кнопку сброса
|
||||
|
||||
@@ -59,4 +59,32 @@ describe("renderShape", () => {
|
||||
expect(shifted.data[3]).toBe(0);
|
||||
expect(shifted.data[4 + 3]).toBe(255);
|
||||
});
|
||||
|
||||
// Регрессия старого бага square-mask: полный размер без смещения обязан
|
||||
// сохранять квадратную картинку целиком.
|
||||
it("квадрат 100% без смещения сохраняет всё квадратное изображение", () => {
|
||||
const out = renderShape(
|
||||
makeImage(4, 4, new Array(16).fill([255, 255, 255, 255])),
|
||||
boxTest(0.5, 0.5),
|
||||
);
|
||||
for (let i = 3; i < out.data.length; i += 4) expect(out.data[i]).toBe(255);
|
||||
});
|
||||
|
||||
// Смещение — доля свободного места: ±0.5 прижимает фигуру к краю при любом
|
||||
// её размере, фигура не уезжает за холст.
|
||||
it("максимальное смещение прижимает фигуру к краю", () => {
|
||||
const out = renderShape(
|
||||
makeImage(4, 4, new Array(16).fill([255, 255, 255, 255])),
|
||||
boxTest(0.25, 0.25),
|
||||
0.5,
|
||||
0.5,
|
||||
{ x: 0.25, y: 0.25 },
|
||||
);
|
||||
const opaqueAt = (x: number, y: number) =>
|
||||
out.data[(y * 4 + x) * 4 + 3] === 255;
|
||||
expect(opaqueAt(3, 3)).toBe(true);
|
||||
expect(opaqueAt(2, 2)).toBe(true);
|
||||
expect(opaqueAt(1, 1)).toBe(false);
|
||||
expect(opaqueAt(2, 1)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,6 +3,12 @@ import { createPixelImage } from "./types";
|
||||
|
||||
export type ShapeTest = (nx: number, ny: number) => boolean;
|
||||
|
||||
/** Полуразмер фигуры в нормированных координатах теста, по осям. */
|
||||
export interface ShapeExtent {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Координаты теста: нормированные к половине меньшей стороны изображения,
|
||||
* центр в (0,0), ось Y вниз как в пикселях.
|
||||
@@ -54,18 +60,24 @@ export function wavyTest(
|
||||
|
||||
/**
|
||||
* Вырезает фигуру из изображения: внутри фигуры сохраняются исходные пиксели
|
||||
* (с их альфой), снаружи альфа обнуляется. Смещение задаётся в долях меньшей стороны.
|
||||
* (с их альфой), снаружи альфа обнуляется. Смещение задаётся в долях свободного
|
||||
* места по каждой оси: ±0.5 прижимает фигуру к краю независимо от её размера,
|
||||
* поэтому фигура никогда не уезжает за холст. Свободное место считается от
|
||||
* extent (полуразмера фигуры); без extent смещение считается от точки.
|
||||
*/
|
||||
export function renderShape(
|
||||
img: PixelImage,
|
||||
test: ShapeTest,
|
||||
offsetXFrac = 0,
|
||||
offsetYFrac = 0,
|
||||
extent: ShapeExtent = { x: 0, y: 0 },
|
||||
): PixelImage {
|
||||
const out = createPixelImage(img.width, img.height);
|
||||
const minDim = Math.min(img.width, img.height);
|
||||
const cx = img.width / 2 + offsetXFrac * minDim;
|
||||
const cy = img.height / 2 + offsetYFrac * minDim;
|
||||
const freeX = Math.max(0, img.width / (2 * minDim) - extent.x);
|
||||
const freeY = Math.max(0, img.height / (2 * minDim) - extent.y);
|
||||
const cx = img.width / 2 + offsetXFrac * 2 * freeX * minDim;
|
||||
const cy = img.height / 2 + offsetYFrac * 2 * freeY * minDim;
|
||||
for (let y = 0; y < img.height; y++) {
|
||||
for (let x = 0; x < img.width; x++) {
|
||||
const di = (y * img.width + x) * 4;
|
||||
|
||||
@@ -139,6 +139,7 @@ const circleMask: Tool<CircleMaskParams> = {
|
||||
circleTest(p.size / 200),
|
||||
p.offset.x / 100,
|
||||
p.offset.y / 100,
|
||||
{ x: p.size / 200, y: p.size / 200 },
|
||||
),
|
||||
),
|
||||
};
|
||||
@@ -197,6 +198,7 @@ const squareMask: Tool<SquareMaskParams> = {
|
||||
boxTest(p.widthPct / 200, p.heightPct / 200),
|
||||
p.offset.x / 100,
|
||||
p.offset.y / 100,
|
||||
{ x: p.widthPct / 200, y: p.heightPct / 200 },
|
||||
),
|
||||
),
|
||||
};
|
||||
@@ -271,6 +273,7 @@ const starMask: Tool<StarMaskParams> = {
|
||||
starTest(p.points, p.innerRadius / 100, p.size / 200, p.rotation),
|
||||
p.offset.x / 100,
|
||||
p.offset.y / 100,
|
||||
{ x: p.size / 200, y: p.size / 200 },
|
||||
),
|
||||
),
|
||||
};
|
||||
@@ -345,6 +348,7 @@ const wavyMask: Tool<WavyMaskParams> = {
|
||||
wavyTest(p.size / 200, p.amplitude / 200, p.waves, p.phase),
|
||||
p.offset.x / 100,
|
||||
p.offset.y / 100,
|
||||
{ x: (p.size + p.amplitude) / 200, y: (p.size + p.amplitude) / 200 },
|
||||
),
|
||||
),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user