Files
AI-Video/frontend/src/components/inpainting/tools/lasso-tool.ts
empty 79a6c2ef3e feat: Add inpainting (局部重绘) feature for timeline editor
- Add canvas-based mask drawing tools (brush, eraser, rect, lasso)
- Add undo/redo history support for mask editing
- Integrate inpainting UI into preview player
- Add backend API endpoint for inpainting requests
- Add MediaService.inpaint method with ComfyUI workflow support
- Add Flux inpainting workflows for selfhost and RunningHub

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 23:44:51 +08:00

108 lines
2.5 KiB
TypeScript

/**
* Lasso Tool - Free polygon selection for mask
*/
import { Point } from './brush-tool'
export interface LassoToolOptions {
color: string
}
export class LassoTool {
private ctx: CanvasRenderingContext2D
private isDrawing: boolean = false
private points: Point[] = []
private options: LassoToolOptions
private previewCanvas: HTMLCanvasElement | null = null
constructor(ctx: CanvasRenderingContext2D, options: LassoToolOptions) {
this.ctx = ctx
this.options = options
}
setColor(color: string) {
this.options.color = color
}
setPreviewCanvas(canvas: HTMLCanvasElement) {
this.previewCanvas = canvas
}
// Add point on click
addPoint(point: Point) {
if (!this.isDrawing) {
this.isDrawing = true
this.points = []
}
this.points.push(point)
this.drawPreview()
}
// Draw preview polygon
private drawPreview() {
if (!this.previewCanvas || this.points.length === 0) return
const previewCtx = this.previewCanvas.getContext('2d')
if (!previewCtx) return
previewCtx.clearRect(0, 0, this.previewCanvas.width, this.previewCanvas.height)
// Draw polygon outline
previewCtx.strokeStyle = this.options.color
previewCtx.lineWidth = 2
previewCtx.setLineDash([5, 5])
previewCtx.beginPath()
previewCtx.moveTo(this.points[0].x, this.points[0].y)
for (let i = 1; i < this.points.length; i++) {
previewCtx.lineTo(this.points[i].x, this.points[i].y)
}
previewCtx.stroke()
// Draw points
this.points.forEach((p, i) => {
previewCtx.fillStyle = i === 0 ? '#00ff00' : this.options.color
previewCtx.beginPath()
previewCtx.arc(p.x, p.y, 4, 0, Math.PI * 2)
previewCtx.fill()
})
}
// Close and fill polygon on double-click
closePath() {
if (!this.isDrawing || this.points.length < 3) {
this.cancel()
return
}
// Fill polygon on mask
this.ctx.fillStyle = this.options.color
this.ctx.beginPath()
this.ctx.moveTo(this.points[0].x, this.points[0].y)
for (let i = 1; i < this.points.length; i++) {
this.ctx.lineTo(this.points[i].x, this.points[i].y)
}
this.ctx.closePath()
this.ctx.fill()
this.cancel()
}
cancel() {
this.isDrawing = false
this.points = []
if (this.previewCanvas) {
const ctx = this.previewCanvas.getContext('2d')
ctx?.clearRect(0, 0, this.previewCanvas.width, this.previewCanvas.height)
}
}
isActive(): boolean {
return this.isDrawing
}
getPointCount(): number {
return this.points.length
}
}