spritejs/spritejs

View on GitHub
src/node/path.js

Summary

Maintainability
C
1 day
Test Coverage
F
3%
import {Figure2D, Mesh2D} from '@mesh.js/core';
import pasition from 'pasition';
import Node from './node';
import Attr from '../attribute/path';
import {setFillColor, setStrokeColor} from '../utils/color';
import {applyTexture, drawTexture} from '../utils/texture';
import ownerDocument from '../document';
import getBoundingBox from '../utils/bounding_box';

const _mesh = Symbol('mesh');

export default class Path extends Node {
  static Attr = Attr;

  constructor(attrs = {}) {
    if(typeof attrs === 'string') attrs = {d: attrs};
    super(attrs);
    this.effects = {
      d(from, to, p, s, e) {
        const ep = (p - s) / (e - s);
        if(ep <= 0) return from;
        if(ep >= 1) return to;
        const shapes = pasition._preprocessing(pasition.path2shapes(from), pasition.path2shapes(to));
        const shape = pasition._lerp(...shapes, ep)[0];
        const path = shape.reduce((str, c) => {
          return `${str}${c.slice(2).join(' ')} `;
        }, `M${shape[0][0]} ${shape[0][1]}C`).trim();
        return path;
      },
    };
  }

  /* override */
  get isVisible() {
    return !!this.d;
  }

  get mesh() {
    if(this.attributes.display === 'none') return null;
    const path = this.path;
    if(path) {
      let mesh = this[_mesh];
      if(!mesh) {
        mesh = new Mesh2D(this.path);
        mesh.path = path;
        const fillColor = this.attributes.fillColor;
        const fillRule = this.attributes.fillRule;
        if(fillColor) {
          setFillColor(mesh, {color: fillColor, rule: fillRule});
        }
        const lineWidth = this.attributes.lineWidth;
        const strokeColor = this.attributes.strokeColor;
        if(strokeColor && lineWidth > 0) {
          const {lineCap, lineJoin, miterLimit, lineDash, lineDashOffset, roundSegments} = this.attributes;
          setStrokeColor(mesh, {
            color: strokeColor,
            lineWidth,
            lineCap,
            lineJoin,
            miterLimit,
            lineDash,
            lineDashOffset,
            roundSegments,
          });
        }
        // mesh.setOpacity(this.attributes.opacity);
        this[_mesh] = mesh;
        const clipPath = this.attributes.clipPath;
        if(clipPath) {
          this[_mesh].setClipPath(clipPath);
        }
      } else if(mesh.path !== path) {
        mesh.contours = path.contours;
        mesh.path = path;
      }
      const opacity = this.opacity;
      if(mesh.getOpacity() !== opacity) {
        mesh.setOpacity(opacity);
      }
      mesh.setTransform(...this.renderMatrix);
      return mesh;
    }
    return null;
  }

  get originalContentRect() {
    if(this.path) {
      const boundingBox = this.path.boundingBox;
      return [boundingBox[0][0], boundingBox[0][1], boundingBox[1][0] - boundingBox[0][0], boundingBox[1][1] - boundingBox[0][1]];
    }
    return [0, 0, 0, 0];
  }

  get originalClientRect() {
    if(this.mesh) {
      const boundingBox = this.mesh.boundingBox;
      return [boundingBox[0][0], boundingBox[0][1], boundingBox[1][0] - boundingBox[0][0], boundingBox[1][1] - boundingBox[0][1]];
    }
    return [0, 0, 0, 0];
  }

  get originalClientCenter() {
    if(this.mesh) {
      return this.mesh.boundingCenter;
    }
    return [0, 0];
  }

  set d(value) {
    this.attributes.d = value;
  }

  get d() {
    return this.attributes.d;
  }

  /* override */
  draw(meshes = []) {
    super.draw(meshes);
    const mesh = this.mesh;
    if(mesh) {
      drawTexture(this, mesh);
    }

    return meshes;
  }

  getBoundingClientRect() {
    let boundingBox = null;
    if(this.mesh) boundingBox = this.mesh.boundingBox;
    return getBoundingBox(boundingBox, this.renderMatrix);
  }

  getPathLength() {
    if(this.mesh) {
      return this.mesh.getTotalLength();
    }
    return 0;
  }

  getPointAtLength(len) {
    if(this.mesh) {
      const point = this.mesh.getPointAtLength(len);
      if(point) {
        return [point.x, point.y];
      }
    }
    return [0, 0];
  }

  /* override */
  onPropertyChange(key, newValue, oldValue) {
    super.onPropertyChange(key, newValue, oldValue);
    if(key === 'd' || key === 'normalize') {
      this.updateContours();
    }
    // if(key === 'opacity') {
    //   if(this[_mesh]) this[_mesh].setOpacity(this.opacity);
    // }
    if(this[_mesh] && (key === 'fillColor' || key === 'fillRule')) {
      const {fillColor, fillRule} = this.attributes;
      setFillColor(this[_mesh], {color: fillColor, rule: fillRule});
    }
    if(this[_mesh] && (key === 'strokeColor' || key === 'lineWidth' || key === 'lineCap' || key === 'lineJoin'
      || key === 'lineDash' || key === 'lineDashOffset' || key === 'roundSegments')) {
      const {strokeColor, lineWidth} = this.attributes;
      if(strokeColor && lineWidth > 0) {
        const {lineCap, lineJoin, lineDash, lineDashOffset, miterLimit, roundSegments} = this.attributes;
        setStrokeColor(this[_mesh], {
          color: strokeColor, lineCap, lineJoin, lineWidth, lineDash, lineDashOffset, miterLimit, roundSegments});
      }
    }
    if(this[_mesh] && key === 'clipPath') {
      this[_mesh].setClipPath(newValue);
    }
    if(key === 'texture') {
      applyTexture(this, newValue);
    }
  }

  /* override */
  updateContours() {
    this.path = new Figure2D();
    this.path.addPath(this.attributes.d);
    if(this.attributes.normalize) {
      this.path.normalize(...this.path.boundingCenter);
    }
  }
}

ownerDocument.registerNode(Path, 'path');