|
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; |
|
} |
|
}; |
|
} |
|
|