{"version":3,"file":"SprayBrush.mjs","names":[],"sources":["../../../src/brushes/SprayBrush.ts"],"sourcesContent":["import type { Point } from '../Point';\nimport { Group } from '../shapes/Group';\nimport { Shadow } from '../Shadow';\nimport { Rect } from '../shapes/Rect';\nimport { getRandomInt } from '../util/internals/getRandomInt';\nimport type { Canvas } from '../canvas/Canvas';\nimport { BaseBrush } from './BaseBrush';\nimport type { SprayBrushPoint } from './typedefs';\nimport { CENTER } from '../constants';\n\n/**\n *\n * @param rects\n * @returns\n */\nfunction getUniqueRects(rects: Rect[]) {\n  const uniqueRects: Record<string, boolean> = {};\n  const uniqueRectsArray: Rect[] = [];\n\n  for (let i = 0, key: string; i < rects.length; i++) {\n    key = `${rects[i].left}${rects[i].top}`;\n    if (!uniqueRects[key]) {\n      uniqueRects[key] = true;\n      uniqueRectsArray.push(rects[i]);\n    }\n  }\n\n  return uniqueRectsArray;\n}\n\nexport class SprayBrush extends BaseBrush {\n  /**\n   * Width of a spray\n   * @type Number\n   */\n  width = 10;\n\n  /**\n   * Density of a spray (number of dots per chunk)\n   * @type Number\n   */\n  density = 20;\n\n  /**\n   * Width of spray dots\n   * @type Number\n   */\n  dotWidth = 1;\n\n  /**\n   * Width variance of spray dots\n   * @type Number\n   */\n  dotWidthVariance = 1;\n\n  /**\n   * Whether opacity of a dot should be random\n   * @type Boolean\n   */\n  randomOpacity = false;\n\n  /**\n   * Whether overlapping dots (rectangles) should be removed (for performance reasons)\n   * @type Boolean\n   */\n  optimizeOverlapping = true;\n\n  declare private sprayChunks: SprayBrushPoint[][];\n\n  declare private sprayChunk: SprayBrushPoint[];\n\n  /**\n   * Constructor\n   * @param {Canvas} canvas\n   * @return {SprayBrush} Instance of a spray brush\n   */\n  constructor(canvas: Canvas) {\n    super(canvas);\n    this.sprayChunks = [];\n    this.sprayChunk = [];\n  }\n\n  /**\n   * Invoked on mouse down\n   * @param {Point} pointer\n   */\n  onMouseDown(pointer: Point) {\n    this.sprayChunks = [];\n    this.canvas.clearContext(this.canvas.contextTop);\n    this._setShadow();\n\n    this.addSprayChunk(pointer);\n    this.renderChunck(this.sprayChunk);\n  }\n\n  /**\n   * Invoked on mouse move\n   * @param {Point} pointer\n   */\n  onMouseMove(pointer: Point) {\n    if (this.limitedToCanvasSize === true && this._isOutSideCanvas(pointer)) {\n      return;\n    }\n    this.addSprayChunk(pointer);\n    this.renderChunck(this.sprayChunk);\n  }\n\n  /**\n   * Invoked on mouse up\n   */\n  onMouseUp() {\n    const originalRenderOnAddRemove = this.canvas.renderOnAddRemove;\n    this.canvas.renderOnAddRemove = false;\n\n    const rects: Rect[] = [];\n\n    for (let i = 0; i < this.sprayChunks.length; i++) {\n      const sprayChunk = this.sprayChunks[i];\n      for (let j = 0; j < sprayChunk.length; j++) {\n        const chunck = sprayChunk[j];\n        const rect = new Rect({\n          width: chunck.width,\n          height: chunck.width,\n          left: chunck.x + 1,\n          top: chunck.y + 1,\n          originX: CENTER,\n          originY: CENTER,\n          fill: this.color,\n        });\n        rects.push(rect);\n      }\n    }\n\n    const group = new Group(\n      this.optimizeOverlapping ? getUniqueRects(rects) : rects,\n      {\n        objectCaching: true,\n        subTargetCheck: false,\n        interactive: false,\n      },\n    );\n    this.shadow && group.set('shadow', new Shadow(this.shadow));\n    this.canvas.fire('before:path:created', { path: group });\n    this.canvas.add(group);\n    this.canvas.fire('path:created', { path: group });\n\n    this.canvas.clearContext(this.canvas.contextTop);\n    this._resetShadow();\n    this.canvas.renderOnAddRemove = originalRenderOnAddRemove;\n    this.canvas.requestRenderAll();\n  }\n\n  renderChunck(sprayChunck: SprayBrushPoint[]) {\n    const ctx = this.canvas.contextTop;\n    ctx.fillStyle = this.color;\n\n    this._saveAndTransform(ctx);\n\n    for (let i = 0; i < sprayChunck.length; i++) {\n      const point = sprayChunck[i];\n      ctx.globalAlpha = point.opacity;\n      ctx.fillRect(point.x, point.y, point.width, point.width);\n    }\n\n    ctx.restore();\n  }\n\n  /**\n   * Render all spray chunks\n   */\n  _render() {\n    const ctx = this.canvas.contextTop;\n    ctx.fillStyle = this.color;\n\n    this._saveAndTransform(ctx);\n\n    for (let i = 0; i < this.sprayChunks.length; i++) {\n      this.renderChunck(this.sprayChunks[i]);\n    }\n    ctx.restore();\n  }\n\n  /**\n   * @param {Point} pointer\n   */\n  addSprayChunk(pointer: Point) {\n    this.sprayChunk = [];\n    const radius = this.width / 2;\n\n    for (let i = 0; i < this.density; i++) {\n      this.sprayChunk.push({\n        x: getRandomInt(pointer.x - radius, pointer.x + radius),\n        y: getRandomInt(pointer.y - radius, pointer.y + radius),\n        width: this.dotWidthVariance\n          ? getRandomInt(\n              // bottom clamp width to 1\n              Math.max(1, this.dotWidth - this.dotWidthVariance),\n              this.dotWidth + this.dotWidthVariance,\n            )\n          : this.dotWidth,\n        opacity: this.randomOpacity ? getRandomInt(0, 100) / 100 : 1,\n      });\n    }\n\n    this.sprayChunks.push(this.sprayChunk);\n  }\n}\n"],"mappings":"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"}