/**
 * fabric.brushes - A collection of brushes for fabric.js (version 4 and up).
 *
 * Made by Arjan Haverkamp, https://www.webgear.nl
 * Copyright 2021 Arjan Haverkamp
 * MIT Licensed
 * @version 1.0 - 2021-06-02
 * @url https://github.com/av01d/fabric-brushes
 *
 * Inspiration sources:
 * - https://github.com/tennisonchan/fabric-brush
 * - https://mrdoob.com/projects/harmony/
 * - http://perfectionkills.com/exploring-canvas-drawing-techniques/
 */
import { fabric } from "fabric-with-all";
import { sprayBrushDataUrl } from "./data/sprayBrushData.js";

(function (fabric) {
  /**
   * Trim a canvas. Returns the left-top coordinate where trimming began.
   * @param {canvas} canvas A canvas element to trim. This element will be trimmed (reference).
   * @returns {Object} Left-top coordinate of trimmed area. Example: {x:65, y:104}
   * @see: https://stackoverflow.com/a/22267731/3360038
   */
  fabric.util.trimCanvas = function (canvas) {
    var ctx = canvas.getContext("2d"),
      w = canvas.width,
      h = canvas.height,
      pix = { x: [], y: [] },
      n,
      imageData = ctx.getImageData(0, 0, w, h),
      fn = function (a, b) {
        return a - b;
      };
    for (var y = 0; y < h; y++) {
      for (var x = 0; x < w; x++) {
        if (imageData.data[(y * w + x) * 4 + 3] > 0) {
          pix.x.push(x);
          pix.y.push(y);
        }
      }
    }
    pix.x.sort(fn);
    pix.y.sort(fn);
    n = pix.x.length - 1;
    //if (n == -1) {
    //	// Nothing to trim... empty canvas?
    //}

    w = pix.x[n] - pix.x[0];
    h = pix.y[n] - pix.y[0];
    if (!w) {
      return;
    }
    var cut = ctx.getImageData(pix.x[0], pix.y[0], w, h);

    canvas.width = w;
    canvas.height = h;
    ctx.putImageData(cut, 0, 0);

    return { x: pix.x[0], y: pix.y[0] };
  };

  /**
   * Extract r,g,b,a components from any valid color.
   * Returns {undefined} when color cannot be parsed.
   *
   * @param {number} color Any color string (named, hex, rgb, rgba)
   * @returns {(Array|undefined)} Example: [0,128,255,1]
   * @see https://gist.github.com/oriadam/396a4beaaad465ca921618f2f2444d49
   */
  fabric.util.colorValues = function (color) {
    if (!color) {
      return;
    }
    if (color.toLowerCase() === "transparent") {
      return [0, 0, 0, 0];
    }
    if (color[0] === "#") {
      if (color.length < 7) {
        // convert #RGB and #RGBA to #RRGGBB and #RRGGBBAA
        color =
          "#" +
          color[1] +
          color[1] +
          color[2] +
          color[2] +
          color[3] +
          color[3] +
          (color.length > 4 ? color[4] + color[4] : "");
      }
      return [
        parseInt(color.substr(1, 2), 16),
        parseInt(color.substr(3, 2), 16),
        parseInt(color.substr(5, 2), 16),
        color.length > 7 ? parseInt(color.substr(7, 2), 16) / 255 : 1,
      ];
    }
    if (color.indexOf("rgb") === -1) {
      // convert named colors
      var tempElem = document.body.appendChild(document.createElement("fictum")); // intentionally use unknown tag to lower chances of css rule override with !important
      var flag = "rgb(1, 2, 3)"; // this flag tested on chrome 59, ff 53, ie9, ie10, ie11, edge 14
      tempElem.style.color = flag;
      if (tempElem.style.color !== flag) {
        return; // color set failed - some monstrous css rule is probably taking over the color of our object
      }
      tempElem.style.color = color;
      if (tempElem.style.color === flag || tempElem.style.color === "") {
        return; // color parse failed
      }
      color = getComputedStyle(tempElem).color;
      document.body.removeChild(tempElem);
    }
    if (color.indexOf("rgb") === 0) {
      if (color.indexOf("rgba") === -1) {
        color += ",1"; // convert 'rgb(R,G,B)' to 'rgb(R,G,B)A' which looks awful but will pass the regxep below
      }
      return color.match(/[.\d]+/g).map(function (a) {
        return +a;
      });
    }
  };

  fabric.Point.prototype.angleBetween = function (that) {
    return Math.atan2(this.x - that.x, this.y - that.y);
  };

  fabric.Point.prototype.normalize = function (thickness) {
    if (null === thickness || undefined === thickness) {
      thickness = 1;
    }

    var length = this.distanceFrom({ x: 0, y: 0 });

    if (length > 0) {
      this.x = (this.x / length) * thickness;
      this.y = (this.y / length) * thickness;
    }

    return this;
  };

  /**
   * Convert a brush drawing on the upperCanvas to an image on the fabric canvas.
   * This makes the drawing editable, it can be moved, rotated, scaled, skewed etc.
   */
  fabric.BaseBrush.prototype.convertToPath = function () {
    var pixelRatio = this.canvas.getRetinaScaling(),
      c = fabric.util.copyCanvasElement(this.canvas.upperCanvasEl),
      xy = fabric.util.trimCanvas(c),
      path = this._points
        .map((arr) => {
          arr[1] = arr[1] / this.canvas.getZoom();
          arr[2] = arr[2] / this.canvas.getZoom();
          return arr.join(" ");
        })
        .join(" "),
      pathElemetn = new fabric.Path(path);
    if (!xy) {
      return;
    }
    let pointerX = this.canvas.viewportTransform[4];
    let pointerY = this.canvas.viewportTransform[5];
    pathElemetn
      .set({
        strokeDashArray: [this._width * 3, this._width * 3],
        strokeWidth: this._width,
        stroke: "black",
        fill: "transparent",
        custom: {
          dashed: true,
        },
      })
      .setCoords();
    let group = new fabric.Group([pathElemetn], {
      left: (xy.x / pixelRatio - pointerX) / this.canvas.getZoom(),
      top: (xy.y / pixelRatio - pointerY) / this.canvas.getZoom(),
      custom: {
        dashed: true,
      },
    });

    this.canvas.add(group).clearContext(this.canvas.contextTop);
    this.canvas.clearContext(this.canvas.contextTop);
  };

  fabric.BaseBrush.prototype.convertToImg = function () {
    var pixelRatio = this.canvas.getRetinaScaling(),
      c = fabric.util.copyCanvasElement(this.canvas.upperCanvasEl),
      xy = fabric.util.trimCanvas(c),
      img = new fabric.Image(c);
    if (!xy) {
      return;
    }
    let pointerX = this.canvas.viewportTransform[4];
    let pointerY = this.canvas.viewportTransform[5];
    img
      .set({
        left: (xy.x / pixelRatio - pointerX) / this.canvas.getZoom(),
        top: (xy.y / pixelRatio - pointerY) / this.canvas.getZoom(),
        scaleX: 1 / pixelRatio / this.canvas.getZoom(),
        scaleY: 1 / pixelRatio / this.canvas.getZoom(),
        custom: {
          type: "pencil",
        },
      })
      .setCoords();
    // 检查是否有图像处理回调函数
    const result = this.canvas?.onBrushImageConverted?.(img);
    !result && this.canvas.add(img);
    this.canvas.clearContext(this.canvas.contextTop);
  };

  fabric.util.getRandom = function (max, min) {
    min = min ? min : 0;
    return Math.random() * ((max ? max : 1) - min) + min;
  };

  fabric.util.clamp = function (n, max, min) {
    if (typeof min !== "number") {
      min = 0;
    }
    return n > max ? max : n < min ? min : n;
  };

  fabric.Stroke = fabric.util.createClass(fabric.Object, {
    color: null,
    inkAmount: null,
    lineWidth: null,

    _point: null,
    _lastPoint: null,
    _currentLineWidth: null,

    initialize: function (ctx, pointer, range, color, lineWidth, inkAmount) {
      var rx = fabric.util.getRandom(range),
        c = fabric.util.getRandom(Math.PI * 2),
        c0 = fabric.util.getRandom(Math.PI * 2),
        x0 = rx * Math.sin(c0),
        y0 = (rx / 2) * Math.cos(c0),
        cos = Math.cos(c),
        sin = Math.sin(c);

      this.ctx = ctx;
      this.color = color;
      this._point = new fabric.Point(
        pointer.x + x0 * cos - y0 * sin,
        pointer.y + x0 * sin + y0 * cos
      );
      this.lineWidth = lineWidth;
      this.inkAmount = inkAmount;
      this._currentLineWidth = lineWidth;

      ctx.lineCap = "round";
    },

    update: function (pointer, subtractPoint, distance) {
      this._lastPoint = fabric.util.object.clone(this._point);
      this._point = this._point.addEquals({
        x: subtractPoint.x,
        y: subtractPoint.y,
      });
      var n = this.inkAmount / (distance + 1),
        per = n > 0.3 ? 0.2 : n < 0 ? 0 : n;
      this._currentLineWidth = this.lineWidth * per;
    },

    draw: function () {
      var ctx = this.ctx;
      ctx.save();
      this.line(ctx, this._lastPoint, this._point, this.color, this._currentLineWidth);
      ctx.restore();
    },

    line: function (ctx, point1, point2, color, lineWidth) {
      ctx.strokeStyle = color;
      ctx.lineWidth = lineWidth;
      ctx.beginPath();
      ctx.moveTo(point1.x, point1.y);
      ctx.lineTo(point2.x, point2.y);
      ctx.stroke();
    },
  });

  /**
   * CrayonBrush
   * Based on code by Tennison Chan.
   */
  fabric.CrayonBrush = fabric.util.createClass(fabric.BaseBrush, {
    color: "#000",
    opacity: 0.6,
    width: 30,

    _baseWidth: 15,
    _inkAmount: 10,
    _latestStrokeLength: 0,
    _point: null,
    _sep: 3,
    _size: 0,
    _latest: null,
    _drawn: false,

    initialize: function (canvas, opt) {
      opt = opt || {};

      this.canvas = canvas;
      this.width = opt.width || this.width;
      this.color = opt.color || this.color;
      this.opacity = opt.opacity || this.opacity;
      this._baseWidth = this.width / 2;
      this._point = new fabric.Point(0, 0);
    },

    onMouseDown: function (pointer) {
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
      this.canvas.contextTop.globalAlpha = this.opacity;
      this._size = this.width / 2 + this._baseWidth;
      this._drawn = false;
      this.set(pointer);
    },

    onMouseMove: function (pointer) {
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
      this.update(pointer);
      this.draw(this.canvas.contextTop);
    },

    onMouseUp: function () {
      if (this._drawn) {
        this.convertToImg();
      }
      this._latest = null;
      this._latestStrokeLength = 0;
      this.canvas.contextTop.globalAlpha = 1;
    },

    set: function (p) {
      if (this._latest) {
        this._latest.setFromPoint(this._point);
      } else {
        this._latest = new fabric.Point(p.x, p.y);
      }
      fabric.Point.prototype.setFromPoint.call(this._point, p);
    },

    update: function (p) {
      this.set(p);
      this._latestStrokeLength = this._point.subtract(this._latest).distanceFrom({ x: 0, y: 0 });
    },

    draw: function (ctx) {
      var i, j, p, r, c, x, y, w, h, v, s, stepNum, dotSize, dotNum, range;

      v = this._point.subtract(this._latest);
      s = Math.ceil(this._size / 2);
      stepNum = Math.floor(v.distanceFrom({ x: 0, y: 0 }) / s) + 1;
      v.normalize(s);

      dotSize =
        this._sep * fabric.util.clamp((this._inkAmount / this._latestStrokeLength) * 3, 1, 0.5);
      dotNum = Math.ceil(this._size * this._sep);

      range = this._size / 2;

      ctx.save();
      ctx.fillStyle = this.color;
      ctx.beginPath();
      for (i = 0; i < dotNum; i++) {
        for (j = 0; j < stepNum; j++) {
          p = this._latest.add(v.multiply(j));
          r = fabric.util.getRandom(range);
          c = fabric.util.getRandom(Math.PI * 2);
          w = fabric.util.getRandom(dotSize, dotSize / 2);
          h = fabric.util.getRandom(dotSize, dotSize / 2);
          x = p.x + r * Math.sin(c) - w / 2;
          y = p.y + r * Math.cos(c) - h / 2;
          ctx.rect(x, y, w, h);
        }
      }
      ctx.fill();
      ctx.restore();
      this._drawn = true;
    },

    _render: function () {},
  }); // End CrayonBrush

  /**
   * FurBrush
   * Based on code by Mr. Doob.
   */
  fabric.FurBrush = fabric.util.createClass(fabric.BaseBrush, {
    color: "#000",
    opacity: 1,
    width: 1,

    _count: 0,
    _points: [],

    initialize: function (canvas, opt) {
      opt = opt || {};

      this.canvas = canvas;
      this.width = opt.width || this.width;
      this.color = opt.color || this.color;
      this.opacity = opt.opacity || 1;
    },

    onMouseDown: function (pointer) {
      // 添加坐标转换处理画布缩放和偏移
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];

      this._points = [pointer];
      this._count = 0;

      var ctx = this.canvas.contextTop,
        color = fabric.util.colorValues(this.color);

      ctx.strokeStyle =
        "rgba(" + color[0] + "," + color[1] + "," + color[2] + "," + 0.1 * this.opacity + ")";
      ctx.lineWidth = this.width;

      this._points.push(pointer);
    },

    onMouseMove: function (pointer) {
      // 添加坐标转换处理画布缩放和偏移
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];

      this._points.push(pointer);

      var i,
        dx,
        dy,
        d,
        ctx = this.canvas.contextTop,
        points = this._points,
        lastPoint = points[points.length - 2];

      ctx.beginPath();
      ctx.moveTo(lastPoint.x, lastPoint.y);
      ctx.lineTo(pointer.x, pointer.y);
      ctx.stroke();

      for (i = 0; i < this._points.length; i++) {
        dx = this._points[i].x - this._points[this._count].x;
        dy = this._points[i].y - this._points[this._count].y;
        d = dx * dx + dy * dy;

        if (d < 2000 && Math.random() > d / 2000) {
          ctx.beginPath();
          ctx.moveTo(pointer.x + dx * 0.5, pointer.y + dy * 0.5);
          ctx.lineTo(pointer.x - dx * 0.5, pointer.y - dy * 0.5);
          ctx.stroke();
        }
      }

      this._count++;
    },

    onMouseUp: function (pointer) {
      if (this._count > 0) {
        this.convertToImg();
      }
    },

    _render: function () {},
  }); // End FurBrush

  /**
   * InkBrush
   * Based on code by Tennison Chan.
   */
  fabric.InkBrush = fabric.util.createClass(fabric.BaseBrush, {
    color: "#000",
    opacity: 1,
    width: 30,

    _baseWidth: 15,
    _inkAmount: 7,
    _lastPoint: null,
    _point: null,
    _range: 10,
    _strokes: null,

    initialize: function (canvas, opt) {
      opt = opt || {};
      this.canvas = canvas;
      this.width = opt.width || this.width;
      this.color = opt.color || this.color;
      this.opacity = opt.opacity || this.opacity;
      this._baseWidth = this.width;
      this._point = new fabric.Point();
    },

    _render: function (pointer) {
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
      var len,
        i,
        point = this.setPointer(pointer),
        subtractPoint = point.subtract(this._lastPoint),
        distance = point.distanceFrom(this._lastPoint),
        stroke;
      for (i = 0, len = this._strokes.length; i < len; i++) {
        stroke = this._strokes[i];
        stroke.update(point, subtractPoint, distance);
        stroke.draw();
      }

      if (distance > 30) {
        this.drawSplash(point, this._inkAmount);
      }
    },

    onMouseDown: function (pointer) {
      this.canvas.contextTop.globalAlpha = this.opacity;
      this._resetTip(pointer);
    },

    onMouseMove: function (pointer) {
      if (this.canvas._isCurrentlyDrawing) {
        this._render(pointer);
      }
    },

    onMouseUp: function () {
      this.convertToImg();
      this.canvas.contextTop.globalAlpha = 1;
    },
    hexToRgba: function (hex, alpha) {},
    drawSplash: function (pointer, maxSize) {
      var c,
        r,
        i,
        point,
        ctx = this.canvas.contextTop,
        num = fabric.util.getRandom(12),
        range = maxSize * 10,
        color = this.color;

      ctx.save();
      for (i = 0; i < num; i++) {
        r = fabric.util.getRandom(range, 1);
        c = fabric.util.getRandom(Math.PI * 2);
        point = new fabric.Point(pointer.x + r * Math.sin(c), pointer.y + r * Math.cos(c));

        ctx.fillStyle = color;
        ctx.beginPath();
        ctx.arc(point.x, point.y, fabric.util.getRandom(maxSize) / 2, 0, Math.PI * 2, false);
        ctx.fill();
      }
      ctx.restore();
    },

    setPointer: function (pointer) {
      var point = new fabric.Point(pointer.x, pointer.y);

      this._lastPoint = fabric.util.object.clone(this._point);
      this._point = point;

      return point;
    },

    _resetTip: function (pointer) {
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
      var len,
        i,
        point = this.setPointer(pointer);

      this._strokes = [];
      this.size = this.width;
      this._range = this.size / 2;
      for (i = 0, len = this.size; i < len; i++) {
        this._strokes[i] = new fabric.Stroke(
          this.canvas.contextTop,
          point,
          this._range,
          this.color,
          this.width + 10,
          this._inkAmount
        );
      }
    },
  }); // End InkBrush

  /**
   * LongfurBrush
   * Based on code by Mr. Doob.
   */
  fabric.LongfurBrush = fabric.util.createClass(fabric.BaseBrush, {
    color: "#000",
    opacity: 1,
    width: 1,

    _count: 0,
    _points: [],

    initialize: function (canvas, opt) {
      opt = opt || {};

      this.canvas = canvas;
      this.width = opt.width || this.width;
      this.color = opt.color || this.color;
      this.opacity = opt.opacity || 1;
    },

    onMouseDown: function (pointer) {
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
      this._points = [pointer];
      this._count = 0;

      var ctx = this.canvas.contextTop,
        color = fabric.util.colorValues(this.color);

      //ctx.globalCompositeOperation = 'source-over';
      ctx.strokeStyle =
        "rgba(" + color[0] + "," + color[1] + "," + color[2] + "," + 0.05 * this.opacity + ")";
      ctx.lineWidth = this.width;
    },

    onMouseMove: function (pointer) {
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
      this._points.push(pointer);

      var i,
        dx,
        dy,
        d,
        size,
        ctx = this.canvas.contextTop,
        points = this._points;

      for (i = 0; i < this._points.length; i++) {
        size = -Math.random();

        dx = this._points[i].x - this._points[this._count].x;
        dy = this._points[i].y - this._points[this._count].y;
        d = dx * dx + dy * dy;

        if (d < 1000 && Math.random() > d / 1000) {
          ctx.beginPath();
          ctx.moveTo(
            this._points[this._count].x + dx * size,
            this._points[this._count].y + dy * size
          );
          ctx.lineTo(
            this._points[i].x - dx * size + Math.random() * 2,
            this._points[i].y - dy * size + Math.random() * 2
          );
          ctx.stroke();
        }
      }

      this._count++;
    },

    onMouseUp: function (pointer) {
      if (this._count > 0) {
        this.convertToImg();
      }
    },

    _render: function () {},
  }); // End LongfurBrush
  /**
   * WritingBrush
   * Based on code by Tennison Chan.
   */
  fabric.WritingBrush = fabric.util.createClass(fabric.BaseBrush, {
    color: "#000",
    opacity: 1,
    width: 30,

    _baseWidth: 15,
    _lastPoint: null,
    _lineWidth: 2,
    _point: null,
    _size: 0,

    initialize: function (canvas, opt) {
      opt = opt || {};

      this.canvas = canvas;
      this.width = opt.width || this.width;
      this.color = opt.color || this.color;
      this.opacity = opt.opacity || this.opacity;
      this._baseWidth = this.width;
      this.canvas.contextTop.globalAlpha = this.opacity;
      this._point = new fabric.Point();

      this.canvas.contextTop.lineJoin = "round";
      this.canvas.contextTop.lineCap = "round";
    },

    _render: function (pointer) {
      var ctx, lineWidthDiff, i, len;
      ctx = this.canvas.contextTop;
      ctx.beginPath();
      // let num = this._size / this._lineWidth / 2 / 1.2
      let num = this.width / 1.25 / 2;

      for (i = 0, len = this.width / 1.25; i < len; i++) {
        // for(i = 0, len = (this._size / this._lineWidth) / 1.2; i < len; i++) {
        lineWidthDiff = (this._lineWidth - 1) * i;

        ctx.globalAlpha = 0.8 * this.opacity;
        ctx.moveTo(
          this._lastPoint.x - lineWidthDiff + num,
          this._lastPoint.y + lineWidthDiff - num
        );
        ctx.lineTo(pointer.x - lineWidthDiff + num, pointer.y + lineWidthDiff - num);
        ctx.stroke();
      }

      this._lastPoint = new fabric.Point(pointer.x, pointer.y);
    },

    onMouseDown: function (pointer) {
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
      this._lastPoint = pointer;
      this.canvas.contextTop.strokeStyle = this.color;
      this.canvas.contextTop.lineWidth = this._lineWidth;
      this._size = this.width + this._baseWidth;
    },

    onMouseMove: function (pointer) {
      if (this.canvas._isCurrentlyDrawing) {
        pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
        pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
        this._render(pointer);
      }
    },

    onMouseUp: function () {
      this.canvas.contextTop.globalAlpha = this.opacity;
      this.canvas.contextTop.globalAlpha = 1;
      this.convertToImg();
    },
  }); // End WritingBrush
  /**
   * MarkerBrush
   * Based on code by Tennison Chan.
   */
  fabric.MarkerBrush = fabric.util.createClass(fabric.BaseBrush, {
    color: "#000",
    opacity: 1,
    width: 30,

    _baseWidth: 15,
    _lastPoint: null,
    _lineWidth: 2,
    _point: null,
    _size: 0,

    initialize: function (canvas, opt) {
      opt = opt || {};

      this.canvas = canvas;
      this.width = opt.width || this.width;
      this.color = opt.color || this.color;
      this.opacity = opt.opacity || this.opacity;
      this._baseWidth = this.width;
      this.canvas.contextTop.globalAlpha = this.opacity;
      this._point = new fabric.Point();

      this.canvas.contextTop.lineJoin = "round";
      this.canvas.contextTop.lineCap = "round";
    },

    _render: function (pointer) {
      var ctx, lineWidthDiff, i, len;
      ctx = this.canvas.contextTop;

      ctx.beginPath();
      let num = this.width / 1.25 / 2;
      for (i = 0, len = this.width / 1.25; i < len; i++) {
        lineWidthDiff = (this._lineWidth - 1) * i;
        ctx.globalAlpha = 0.8 * this.opacity;
        ctx.moveTo(
          this._lastPoint.x + lineWidthDiff - num,
          this._lastPoint.y + lineWidthDiff - num
        );
        ctx.lineTo(pointer.x + lineWidthDiff - num, pointer.y + lineWidthDiff - num);
        ctx.stroke();
      }

      this._lastPoint = new fabric.Point(pointer.x, pointer.y);
    },

    onMouseDown: function (pointer) {
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
      this._lastPoint = pointer;
      this.canvas.contextTop.strokeStyle = this.color;
      this.canvas.contextTop.lineWidth = this._lineWidth;
      this._size = this.width + this._baseWidth / 2;
    },

    onMouseMove: function (pointer) {
      if (this.canvas._isCurrentlyDrawing) {
        pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
        pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
        this._render(pointer);
      }
    },

    onMouseUp: function () {
      this.canvas.contextTop.globalAlpha = this.opacity;
      this.canvas.contextTop.globalAlpha = 1;
      this.convertToImg();
    },
  }); // End MarkerBrush
  /**
   * test
   * Based on code by Tennison Chan.
   */
  fabric.Test = fabric.util.createClass(fabric.BaseBrush, {
    color: "#000",
    opacity: 1,

    _points: [],

    _width: 2,
    initialize: function (canvas, opt) {
      opt = opt || {};
      this.canvas = canvas;
      this._width = opt._width || this._width;
      this.color = opt.color || this.color;
    },

    _render: function (pointer) {
      var ctx, lineWidthDiff, i, len;
      ctx = this.canvas.contextTop;

      this._points.push(["L", pointer.x, pointer.y]);

      // if(this._points.length % 10 < 5){
      let points = this._points;
      ctx.beginPath();
      ctx.moveTo(points[points.length - 2][1], points[points.length - 2][2]);
      ctx.lineTo(points[points.length - 1][1], points[points.length - 1][2]);
      ctx.stroke();
      // }
    },

    onMouseDown: function (pointer) {
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
      this._points = [];
      this._points.push(["M", pointer.x, pointer.y]);

      var ctx = this.canvas.contextTop;
      ctx.strokeStyle = "rgba(" + 0 + "," + 0 + "," + 0 + "," + 1 + ")";
      ctx.lineWidth = this._width * this.canvas.getZoom();
      ctx.lineJoin = ctx.lineCap = "round";
    },

    onMouseMove: function (pointer) {
      if (this.canvas._isCurrentlyDrawing) {
        pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
        pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
        this._render(pointer);
      }
    },

    onMouseUp: function () {
      this._points.push(["Z"]);
      this.convertToPath();
    },
  }); // End test

  /**
   * MarkerBrush
   * Based on code by Tennison Chan.
   */
  fabric.MarkerBrush1 = fabric.util.createClass(fabric.BaseBrush, {
    color: "#000",
    opacity: 1,
    width: 30,
    content: 0,

    _baseWidth: 15,
    _lastPoint: null,
    _lineWidth: 2,
    _point: null,
    _size: 0,

    initialize: function (canvas, opt) {
      opt = opt || {};

      this.canvas = canvas;
      this.width = opt.width || this.width;
      this.color = opt.color || this.color;
      this.opacity = opt.opacity || this.opacity;
      this._baseWidth = this.width;
      this.content = opt.content || canvas.width / 2;
      this.canvas.contextTop.globalAlpha = this.opacity;
      this._point = new fabric.Point();

      this.canvas.contextTop.lineJoin = "round";
      this.canvas.contextTop.lineCap = "round";
    },

    _render: function (pointer) {
      var ctx, lineWidthDiff, i, len;

      ctx = this.canvas.contextTop;

      ctx.beginPath();

      for (i = 0, len = this._size / this._lineWidth / 2; i < len; i++) {
        lineWidthDiff = (this._lineWidth - 1) * i;
        let half = this.canvas.width / 2;
        ctx.globalAlpha = 0.8 * this.opacity;
        ctx.moveTo(this._lastPoint.x, this._lastPoint.y);
        let x =
          this.content > this._lastPoint.x
            ? this.content - this._lastPoint.x + this.content
            : this.content * 2 - this._lastPoint.x;
        ctx.lineTo(x, this._lastPoint.y);
        // ctx.lineTo(pointer.y + lineWidthDiff,pointer.x + lineWidthDiff);
        ctx.stroke();
      }

      this._lastPoint = new fabric.Point(pointer.x, pointer.y);
    },

    onMouseDown: function (pointer) {
      // 添加坐标转换处理画布缩放和偏移
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];

      this._lastPoint = pointer;
      this.canvas.contextTop.strokeStyle = this.color;
      this.canvas.contextTop.lineWidth = this._lineWidth;
      this._size = this.width + this._baseWidth;
    },

    onMouseMove: function (pointer) {
      if (this.canvas._isCurrentlyDrawing) {
        // 添加坐标转换处理画布缩放和偏移
        pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
        pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];

        this._render(pointer);
      }
    },

    onMouseUp: function () {
      this.canvas.contextTop.globalAlpha = this.opacity;
      this.canvas.contextTop.globalAlpha = 1;
      this.convertToImg();
    },
  }); // End MarkerBrush1
  /**
   * PenBrush
   * Based on code by Tennison Chan.
   */
  fabric.PenBrush = fabric.util.createClass(fabric.BaseBrush, {
    color: "#000",
    opacity: 1,
    width: 30,

    _baseWidth: 15,
    _lastPoint: null,
    _lineWidth: 2,
    _point: null,
    _size: 0,

    initialize: function (canvas, opt) {
      opt = opt || {};

      this.canvas = canvas;
      this.width = opt.width || this.width;
      this.color = opt.color || this.color;
      this.opacity = opt.opacity || this.opacity;
      this._baseWidth = this.width;
      this.canvas.contextTop.globalAlpha = this.opacity;
      this._point = new fabric.Point();

      this.canvas.contextTop.lineJoin = "round";
      this.canvas.contextTop.lineCap = "round";
    },

    _render: function (pointer) {
      var ctx, lineWidthDiff, i, len;
      ctx = this.canvas.contextTop;
      ctx.beginPath();
      // let num = this._size / this._lineWidth / 2 / 1.2
      let num = this.width / 1.25 / 2;
      for (i = 0, len = this.width / 1.25; i < len; i++) {
        // for(i = 0, len = (this._size / this._lineWidth) / 1.2; i < len; i++) {
        var randomNum = Math.random() * (0.6 - 0.2) + 0.2;
        var color = this.color.replace(/1(?=\))/, randomNum);
        this.canvas.contextTop.strokeStyle = color;

        lineWidthDiff = (this._lineWidth - 1) * i;

        ctx.globalAlpha = 0.8 * this.opacity;
        ctx.moveTo(this._lastPoint.x, this._lastPoint.y + lineWidthDiff - num);
        ctx.lineTo(pointer.x, pointer.y + lineWidthDiff - num);
        ctx.stroke();
      }

      this._lastPoint = new fabric.Point(pointer.x, pointer.y);
    },

    onMouseDown: function (pointer) {
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
      this._lastPoint = pointer;
      this.canvas.contextTop.lineWidth = this._lineWidth;
      this._size = this.width + this._baseWidth;
    },

    onMouseMove: function (pointer) {
      if (this.canvas._isCurrentlyDrawing) {
        pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
        pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
        this._render(pointer);
      }
    },

    onMouseUp: function () {
      this.canvas.contextTop.globalAlpha = this.opacity;
      this.canvas.contextTop.globalAlpha = 1;
      this.convertToImg();
    },
  }); // End PenBrush
  /**
   * RibbonBrush
   * Based on code by Mr. Doob.
   */
  fabric.RibbonBrush = fabric.util.createClass(fabric.BaseBrush, {
    color: "#000",
    opacity: 1,
    width: 1,

    _nrPainters: 50,
    _painters: [],
    _lastPoint: null,
    _interval: null,

    initialize: function (canvas, opt) {
      opt = opt || {};
      this.canvas = canvas;
      this.width = opt.width || this.width;
      this._nrPainters = opt._nrPainters || this._nrPainters;
      this.color = opt.color || this.color;
      this.opacity = opt.opacity || this.opacity;
      for (var i = 0; i < this._nrPainters; i++) {
        this._painters.push({
          dx: this.canvas.width / 2,
          dy: this.canvas.height / 2,
          ax: 0,
          ay: 0,
          div: 0.1,
          ease: Math.random() * 0.2 + 0.6,
        });
      }
    },

    update: function () {
      var ctx = this.canvas.contextTop,
        painters = this._painters;
      for (var i = 0; i < painters.length; i++) {
        ctx.beginPath();
        ctx.moveTo(painters[i].dx, painters[i].dy);
        painters[i].dx -= painters[i].ax =
          (painters[i].ax + (painters[i].dx - this._lastPoint.x) * painters[i].div) *
          painters[i].ease;
        painters[i].dy -= painters[i].ay =
          (painters[i].ay + (painters[i].dy - this._lastPoint.y) * painters[i].div) *
          painters[i].ease;
        ctx.lineTo(painters[i].dx, painters[i].dy);
        ctx.stroke();
      }
    },

    onMouseDown: function (pointer) {
      var ctx = this.canvas.contextTop,
        color = fabric.util.colorValues(this.color);

      this._painters = [];
      for (var i = 0; i < this._nrPainters; i++) {
        this._painters.push({
          dx: this.canvas.width / 2,
          dy: this.canvas.height / 2,
          ax: 0,
          ay: 0,
          div: 0.1,
          ease: Math.random() * 0.2 + 0.6,
        });
      }
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
      this._lastPoint = pointer;

      //ctx.globalCompositeOperation = 'source-over';
      ctx.strokeStyle =
        "rgba(" + color[0] + "," + color[1] + "," + color[2] + "," + 0.05 * this.opacity + ")";
      ctx.lineWidth = this.width;

      for (let i = 0; i < this._nrPainters; i++) {
        this._painters[i].dx = pointer.x;
        this._painters[i].dy = pointer.y;
      }

      var self = this;
      this._interval = setInterval(function () {
        self.update();
      }, 1000 / 60);
    },

    onMouseMove: function (pointer) {
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
      this._lastPoint = pointer;
    },

    onMouseUp: function (pointer) {
      clearInterval(this._interval);
      this.convertToImg();
    },

    _render: function () {},
  }); // End RibbonBrush

  /**
   * ShadedBrush
   * Based on code by Mr. Doob.
   */
  fabric.ShadedBrush = fabric.util.createClass(fabric.BaseBrush, {
    color: "#000",
    opacity: 0.3,
    width: 1,
    shadeDistance: 1000,

    _points: [],

    initialize: function (canvas, opt) {
      opt = opt || {};

      this.canvas = canvas;
      this.width = opt.width || this.width;
      this.color = opt.color || this.color;
      this.opacity = opt.opacity || this.opacity;
      this.shadeDistance = opt.shadeDistance || 1000;
    },

    onMouseDown: function (pointer) {
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
      this._points = [pointer];

      var ctx = this.canvas.contextTop,
        color = fabric.util.colorValues(this.color);

      ctx.strokeStyle =
        "rgba(" + color[0] + "," + color[1] + "," + color[2] + "," + this.opacity + ")";
      ctx.lineWidth = this.width;
      ctx.lineJoin = ctx.lineCap = "round";
    },

    onMouseMove: function (pointer) {
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
      this._points.push(pointer);

      var ctx = this.canvas.contextTop,
        points = this._points,
        dx,
        dy,
        d; // 在此处声明变量

      ctx.beginPath();
      ctx.moveTo(points[points.length - 2].x, points[points.length - 2].y);
      ctx.lineTo(points[points.length - 1].x, points[points.length - 1].y);
      ctx.stroke();

      for (var i = 0, len = points.length; i < len; i++) {
        dx = points[i].x - points[points.length - 1].x;
        dy = points[i].y - points[points.length - 1].y;
        d = dx * dx + dy * dy;

        if (d < this.shadeDistance) {
          ctx.beginPath();
          ctx.moveTo(
            points[points.length - 1].x + dx * 0.2,
            points[points.length - 1].y + dy * 0.2
          );
          ctx.lineTo(points[i].x - dx * 0.2, points[i].y - dy * 0.2);
          ctx.stroke();
        }
      }
    },

    onMouseUp: function (pointer) {
      if (this._points.length > 1) {
        this.convertToImg();
      }
    },

    _render: function () {},
  }); // End ShadedBrush

  /**
   * SketchyBrush
   * Based on code by Mr. Doob.
   */
  fabric.SketchyBrush = fabric.util.createClass(fabric.BaseBrush, {
    color: "#000",
    opacity: 1,
    width: 1,

    _count: 0,
    _points: [],

    initialize: function (canvas, opt) {
      opt = opt || {};

      this.canvas = canvas;
      this.width = opt.width || this.width;
      this.color = opt.color || this.color;
      this.opacity = opt.opacity || this.opacity;
    },

    onMouseDown: function (pointer) {
      this.canvas.contextTop.globalAlpha = this.opacity;

      // 坐标转换
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];

      this._points = [pointer];
      this._count = 0;
      this._drawn = false;

      // 设置绘图样式
      var ctx = this.canvas.contextTop;
      var color = fabric.util.colorValues(this.color);

      ctx.lineWidth = this.width;
      ctx.strokeStyle =
        "rgba(" + color[0] + "," + color[1] + "," + color[2] + "," + 0.3 * this.opacity + ")";
    },

    onMouseMove: function (pointer) {
      // 坐标转换
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];

      this._points.push(pointer);

      var i, dx, dy, d;
      var factor = 0.3 * this.width;
      var ctx = this.canvas.contextTop;
      var points = this._points;
      var lastPoint = points.length > 1 ? points[points.length - 2] : points[0]; // 修复第一次无上一个点的问题

      // 绘制主线条
      ctx.beginPath();
      ctx.moveTo(lastPoint.x, lastPoint.y);
      ctx.lineTo(pointer.x, pointer.y);
      ctx.stroke();

      // 增加透明度设置
      var color = fabric.util.colorValues(this.color);
      ctx.strokeStyle =
        "rgba(" + color[0] + "," + color[1] + "," + color[2] + "," + 0.2 * this.opacity + ")";

      // 修改循环逻辑，确保在有点时能画出效果
      if (this._count > 0) {
        // 确保有历史点可用
        for (i = 0; i < points.length; i++) {
          dx = points[i].x - points[this._count].x;
          dy = points[i].y - points[this._count].y;
          d = dx * dx + dy * dy;

          if (d < 4000 && Math.random() > d / 2000) {
            ctx.beginPath();
            ctx.moveTo(points[this._count].x + dx * factor, points[this._count].y + dy * factor);
            ctx.lineTo(points[i].x - dx * factor, points[i].y - dy * factor);
            ctx.stroke();
            this._drawn = true;
          }
        }
      }

      this._count++;
    },

    onMouseUp: function (pointer) {
      if (this._count > 0 || this._drawn) {
        this.convertToImg();
      }
      this.canvas.contextTop.globalAlpha = 1;
    },

    _render: function () {},
  }); // End SketchyBrush

  /**
   * SpraypaintBrush
   * Based on code by Tennison Chan.
   */
  fabric.SpraypaintBrush = fabric.util.createClass(fabric.BaseBrush, {
    color: "#000",
    opacity: 1,
    width: 30,

    _baseWidth: 40,
    _inkAmount: 0,
    _interval: 20,
    _lastPoint: null,
    _point: null,
    brush: null,
    sprayBrushDataUrl: sprayBrushDataUrl,

    initialize: function (canvas, opt) {
      var self = this;
      opt = opt || {};

      this.canvas = canvas;
      this.width = opt.width || this.width;
      this.opacity = opt.opacity || this.opacity;
      this.color = opt.color || this.color;

      this.canvas.contextTop.lineJoin = "round";
      this.canvas.contextTop.lineCap = "round";

      this._reset();

      fabric.Image.fromURL(
        this.sprayBrushDataUrl,
        function (brush) {
          self.brush = brush;
          self.brush.filters = [];
          self._setColor(self.color || this.color);
        },
        { crossOrigin: "anonymous" }
      );
    },

    _setColor: function (color) {
      this.color = color;
      this.brush.filters[0] = new fabric.Image.filters.BlendColor({
        color: color,
        alpha: 1,
        mode: "tint",
      });
      this.brush.applyFilters();
    },

    onMouseDown: function (pointer) {
      this.canvas.contextTop.globalAlpha = this.opacity;
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
      this._point = new fabric.Point(pointer.x, pointer.y);
      this._lastPoint = this._point;

      // this.size = this.width + this._baseWidth;
      this.size = this.width + this.width / 2;
      this._inkAmount = 0;

      this._setColor(this.color);
      this._render();
    },

    onMouseMove: function (pointer) {
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
      this._lastPoint = this._point;
      this._point = new fabric.Point(pointer.x, pointer.y);
    },

    onMouseUp: function () {
      var self = this;
      setTimeout(function () {
        self.convertToImg();
        self._reset();
      }, this._interval);
    },

    _render: function () {
      var self = this;

      function draw() {
        var point, distance, angle, amount, x, y;

        point = new fabric.Point(self._point.x || 0, self._point.y || 0);
        distance = point.distanceFrom(self._lastPoint);
        angle = point.angleBetween(self._lastPoint);
        amount = 100 / self.size / (Math.pow(distance, 2) + 1);

        self._inkAmount += amount;
        self._inkAmount = Math.max(self._inkAmount - distance / 10, 0);

        x = self._lastPoint.x + Math.sin(angle) - self.size / 2;
        y = self._lastPoint.y + Math.cos(angle) - self.size / 2;
        self.canvas.contextTop.drawImage(self.brush._element, x, y, self.size, self.size);

        if (self.canvas._isCurrentlyDrawing) {
          setTimeout(draw, self._interval);
        } else {
          self._reset();
        }
      }

      draw();
    },

    _reset: function () {
      this._point = null;
      this._lastPoint = null;
      this.canvas.contextTop.globalAlpha = 1;
    },
  }); // End SpraypaintBrush

  /**
   * SquaresBrush
   * Based on code by Mr. Doob.
   */
  fabric.SquaresBrush = fabric.util.createClass(fabric.BaseBrush, {
    color: "#000",
    bgColor: "#fff",
    opacity: 1,
    width: 1,

    _lastPoint: null,
    _drawn: false,

    initialize: function (canvas, opt) {
      opt = opt || {};

      this.canvas = canvas;
      this.width = opt.width || this.width;
      this.color = opt.color || this.color;
      this.bgColor = opt.bgColor || "#fff";
      this.opacity = opt.opacity || this.opacity;
    },

    onMouseDown: function (pointer) {
      var ctx = this.canvas.contextTop,
        color = fabric.util.colorValues(this.color),
        bgColor = fabric.util.colorValues(this.bgColor);
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
      this._lastPoint = pointer;
      this._drawn = false;

      //ctx.globalCompositeOperation = 'source-over';
      this.canvas.contextTop.globalAlpha = this.opacity;
      ctx.fillStyle =
        "rgba(" + bgColor[0] + "," + bgColor[1] + "," + bgColor[2] + "," + bgColor[3] + ")";
      ctx.strokeStyle = "rgba(" + color[0] + "," + color[1] + "," + color[2] + "," + color[3] + ")";
      ctx.lineWidth = this.width;
    },

    onMouseMove: function (pointer) {
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
      var ctx = this.canvas.contextTop,
        dx = pointer.x - this._lastPoint.x,
        dy = pointer.y - this._lastPoint.y,
        angle = 1.57079633,
        px = Math.cos(angle) * dx - Math.sin(angle) * dy,
        py = Math.sin(angle) * dx + Math.cos(angle) * dy;

      ctx.beginPath();
      ctx.moveTo(this._lastPoint.x - px, this._lastPoint.y - py);
      ctx.lineTo(this._lastPoint.x + px, this._lastPoint.y + py);
      ctx.lineTo(pointer.x + px, pointer.y + py);
      ctx.lineTo(pointer.x - px, pointer.y - py);
      ctx.lineTo(this._lastPoint.x - px, this._lastPoint.y - py);
      ctx.fill();
      ctx.stroke();

      this._lastPoint = pointer;
      this._drawn = true;
    },

    onMouseUp: function (pointer) {
      if (this._drawn) {
        this.convertToImg();
      }
      this.canvas.contextTop.globalAlpha = 1;
    },

    _render: function () {},
  }); // End SquaresBrush

  /**
   * WebBrush
   * Based on code by Mr. Doob.
   */
  fabric.WebBrush = fabric.util.createClass(fabric.BaseBrush, {
    color: "#000",
    opacity: 1,
    width: 1,

    _count: 0,
    _points: [],

    initialize: function (canvas, opt) {
      opt = opt || {};

      this.canvas = canvas;
      this.width = opt.width || this.width;
      this.color = opt.color || this.color;
      this.opacity = opt.opacity || 1;
    },

    onMouseDown: function (pointer) {
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
      this._points = [pointer];
      this._count = 0;
      this._colorValues = fabric.util.colorValues(this.color);
    },

    onMouseMove: function (pointer) {
      pointer.x = pointer.x * this.canvas.getZoom() + this.canvas.viewportTransform[4];
      pointer.y = pointer.y * this.canvas.getZoom() + this.canvas.viewportTransform[5];
      this._points.push(pointer);

      var ctx = this.canvas.contextTop,
        points = this._points,
        lastPoint = points[points.length - 2],
        colorValues = this._colorValues,
        i,
        dx,
        dy,
        d;

      ctx.lineWidth = this.width;
      ctx.strokeStyle =
        "rgba(" +
        colorValues[0] +
        "," +
        colorValues[1] +
        "," +
        colorValues[2] +
        "," +
        0.5 * this.opacity +
        ")";

      ctx.beginPath();
      ctx.moveTo(lastPoint.x, lastPoint.y);
      ctx.lineTo(pointer.x, pointer.y);
      ctx.stroke();

      ctx.strokeStyle =
        "rgba(" +
        colorValues[0] +
        "," +
        colorValues[1] +
        "," +
        colorValues[2] +
        "," +
        0.1 * this.opacity +
        ")";

      for (i = 0; i < points.length; i++) {
        dx = points[i].x - points[this._count].x;
        dy = points[i].y - points[this._count].y;
        d = dx * dx + dy * dy;

        if (d < 2500 && Math.random() > 0.9) {
          ctx.beginPath();
          ctx.moveTo(points[this._count].x, points[this._count].y);
          ctx.lineTo(points[i].x, points[i].y);
          ctx.stroke();
        }
      }
      this._count++;
    },

    onMouseUp: function (pointer) {
      if (this._count > 0) {
        this.convertToImg();
      }
    },

    _render: function () {},
  }); // End WebBrush
})(typeof fabric !== "undefined" ? fabric : require("fabric").fabric);
