|
import {
|
|
Container,
|
|
Graphics,
|
|
Color,
|
|
type ColorSource,
|
|
type IRenderer,
|
|
RenderTexture,
|
|
Sprite
|
|
} from "pixi.js";
|
|
|
|
import type { LayerScene } from "../layers/utils";
|
|
import { type Command } from "../utils/commands";
|
|
import { make_graphics } from "../utils/pixi";
|
|
|
|
export interface DrawCommand extends Command {
|
|
|
|
|
|
|
|
|
|
|
|
start: (options: DrawOptions) => void;
|
|
|
|
|
|
|
|
|
|
|
|
continue: (options: Points) => void;
|
|
|
|
|
|
|
|
|
|
stop: () => void;
|
|
|
|
|
|
|
|
drawing: boolean;
|
|
}
|
|
|
|
|
|
|
|
|
|
interface Points {
|
|
|
|
|
|
|
|
x: number;
|
|
|
|
|
|
|
|
y: number;
|
|
}
|
|
|
|
|
|
|
|
|
|
interface DrawOptions extends Points {
|
|
|
|
|
|
|
|
size: number;
|
|
|
|
|
|
|
|
color?: ColorSource;
|
|
|
|
|
|
|
|
opacity: number;
|
|
|
|
|
|
|
|
set_initial_texture?: boolean;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function drawCircle(
|
|
graphics: Graphics,
|
|
x: number,
|
|
y: number,
|
|
brush_color: ColorSource = new Color("black"),
|
|
brush_size: number
|
|
): void {
|
|
const color = new Color(brush_color);
|
|
graphics.beginFill(color);
|
|
graphics.drawCircle(x, y, brush_size);
|
|
graphics.endFill();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function interpolate(
|
|
point1: { x: number; y: number },
|
|
point2: { x: number; y: number }
|
|
): Points[] {
|
|
let points: Points[] = [];
|
|
const dx = point2.x - point1.x;
|
|
const dy = point2.y - point1.y;
|
|
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
const steps = Math.ceil(distance / 2);
|
|
const stepX = dx / steps;
|
|
const stepY = dy / steps;
|
|
|
|
for (let j = 0; j < steps; j++) {
|
|
const x = point1.x + j * stepX;
|
|
const y = point1.y + j * stepY;
|
|
points.push({ x, y });
|
|
}
|
|
|
|
return points;
|
|
}
|
|
|
|
export function draw_path(
|
|
renderer: IRenderer,
|
|
stage: Container,
|
|
layer: LayerScene,
|
|
mode: "draw" | "erase"
|
|
): DrawCommand {
|
|
const paths: Points[] = [];
|
|
let initial_path: DrawOptions;
|
|
let graphics: Graphics;
|
|
let InitialTexture: RenderTexture;
|
|
|
|
let has_drawn = false;
|
|
let id = 0;
|
|
|
|
return {
|
|
drawing: false,
|
|
start: function ({
|
|
x,
|
|
y,
|
|
size,
|
|
color = new Color("black"),
|
|
opacity,
|
|
set_initial_texture = true
|
|
}: DrawOptions) {
|
|
if (set_initial_texture) {
|
|
InitialTexture = RenderTexture.create({
|
|
width: layer.draw_texture.width,
|
|
height: layer.draw_texture.height
|
|
});
|
|
renderer.render(layer.composite, {
|
|
renderTexture: InitialTexture
|
|
});
|
|
}
|
|
initial_path = { x, y, size, color, opacity };
|
|
paths.push({ x, y });
|
|
graphics = make_graphics(id++);
|
|
|
|
drawCircle(graphics, x, y, color, size);
|
|
|
|
renderer.render(graphics, {
|
|
renderTexture:
|
|
mode === "draw" ? layer.draw_texture : layer.erase_texture,
|
|
clear: false
|
|
});
|
|
|
|
this.drawing = true;
|
|
},
|
|
continue: function ({ x, y }: Points) {
|
|
const last_point = paths[paths.length - 1];
|
|
const new_points = interpolate(last_point, { x, y });
|
|
|
|
for (let i = 0; i < new_points.length; i++) {
|
|
const { x, y } = new_points[i];
|
|
drawCircle(graphics, x, y, initial_path.color, initial_path.size);
|
|
paths.push({ x, y });
|
|
}
|
|
|
|
renderer.render(graphics, {
|
|
renderTexture:
|
|
mode === "draw" ? layer.draw_texture : layer.erase_texture,
|
|
clear: false
|
|
});
|
|
graphics.clear();
|
|
},
|
|
stop: function () {
|
|
const current_sketch = RenderTexture.create({
|
|
width: layer.draw_texture.width,
|
|
height: layer.draw_texture.height
|
|
});
|
|
|
|
renderer.render(layer.composite, {
|
|
renderTexture: current_sketch
|
|
});
|
|
|
|
renderer.render(new Sprite(current_sketch), {
|
|
renderTexture: layer.draw_texture
|
|
});
|
|
|
|
const clear_graphics = new Graphics()
|
|
.beginFill(0x000000, 0)
|
|
.drawRect(0, 0, layer.erase_texture.width, layer.erase_texture.height)
|
|
.endFill();
|
|
|
|
renderer.render(clear_graphics, {
|
|
renderTexture: layer.erase_texture,
|
|
clear: true
|
|
});
|
|
|
|
has_drawn = true;
|
|
this.drawing = false;
|
|
},
|
|
execute: function () {
|
|
if (!has_drawn) {
|
|
for (let i = 1; i < paths.length; i++) {
|
|
const { x, y } = paths[i];
|
|
drawCircle(graphics, x, y, initial_path.color, initial_path.size);
|
|
}
|
|
|
|
renderer.render(graphics, {
|
|
renderTexture:
|
|
mode === "draw" ? layer.draw_texture : layer.erase_texture,
|
|
clear: false
|
|
});
|
|
|
|
this.stop!();
|
|
}
|
|
},
|
|
undo: function () {
|
|
const clear_graphics = new Graphics()
|
|
.beginFill(0x000000, 0)
|
|
.drawRect(0, 0, layer.erase_texture.width, layer.erase_texture.height)
|
|
.endFill();
|
|
renderer.render(new Sprite(InitialTexture), {
|
|
renderTexture: layer.draw_texture
|
|
});
|
|
renderer.render(clear_graphics, {
|
|
renderTexture: layer.erase_texture,
|
|
clear: true
|
|
});
|
|
|
|
this.stop!();
|
|
has_drawn = false;
|
|
}
|
|
};
|
|
}
|
|
|