Home Manual Reference Source

src/model/Symbol.js

import { MusicClefs } from '../renderer/canvas/symbols/MusicSymbolCanvasRenderer';
import Constants from '../configuration/Constants';

function mergeBounds(boundsA, boundsB) {
  return {
    minX: Math.min(boundsA.minX, boundsB.minX),
    maxX: Math.max(boundsA.maxX, boundsB.maxX),
    minY: Math.min(boundsA.minY, boundsB.minY),
    maxY: Math.max(boundsA.maxY, boundsB.maxY)
  };
}

function getLineBounds(line) {
  return {
    minX: Math.min(line.firstPoint.x, line.lastPoint.x),
    maxX: Math.max(line.firstPoint.x, line.lastPoint.x),
    minY: Math.min(line.firstPoint.y, line.lastPoint.y),
    maxY: Math.max(line.firstPoint.y, line.lastPoint.y)
  };
}

function getEllipseBounds(ellipse) {
  const angleStep = 0.02; // angle delta between interpolated points on the arc, in radian

  let z1 = Math.cos(ellipse.orientation);
  let z3 = Math.sin(ellipse.orientation);
  let z2 = z1;
  let z4 = z3;
  z1 *= ellipse.maxRadius;
  z2 *= ellipse.minRadius;
  z3 *= ellipse.maxRadius;
  z4 *= ellipse.minRadius;

  const n = Math.abs(ellipse.sweepAngle) / angleStep;

  const x = [];
  const y = [];

  for (let i = 0; i <= n; i++) {
    const angle = ellipse.startAngle + ((i / n) * ellipse.sweepAngle);
    const alpha = Math.atan2(Math.sin(angle) / ellipse.minRadius, Math.cos(angle) / ellipse.maxRadius);

    const cosAlpha = Math.cos(alpha);
    const sinAlpha = Math.sin(alpha);

    x.push(ellipse.center.x + ((z1 * cosAlpha) - (z4 * sinAlpha)));
    y.push(ellipse.center.y + ((z2 * sinAlpha) + (z3 * cosAlpha)));
  }

  return {
    minX: Math.min(...x),
    maxX: Math.max(...x),
    minY: Math.min(...y),
    maxY: Math.max(...y)
  };
}

function getTextLineBounds(textLine) {
  return {
    minX: textLine.data.topLeftPoint.x,
    maxX: textLine.data.topLeftPoint.x + textLine.data.width,
    minY: textLine.data.topLeftPoint.y,
    maxY: textLine.data.topLeftPoint.y + textLine.data.height
  };
}

function getClefBounds(clef) {
  return {
    minX: clef.boundingBox.x,
    maxX: clef.boundingBox.x + clef.boundingBox.width,
    minY: clef.boundingBox.y,
    maxY: clef.boundingBox.y + clef.boundingBox.height
  };
}

function getStrokeBounds(stroke) {
  return {
    minX: Math.min(...stroke.x),
    maxX: Math.max(...stroke.x),
    minY: Math.min(...stroke.y),
    maxY: Math.max(...stroke.y)
  };
}

/**
 * Get the box enclosing the given symbols
 * @param {Array} symbols Symbols to extract bounds from
 * @param {Bounds} [bounds] Starting bounds for recursion
 * @return {Bounds} Bounding box enclosing symbols
 */
export function getSymbolsBounds(symbols, bounds = { minX: Number.MAX_VALUE, maxX: Number.MIN_VALUE, minY: Number.MAX_VALUE, maxY: Number.MIN_VALUE }) {
  let boundsRef = bounds;
  boundsRef = symbols
      .filter(symbol => symbol.type === 'stroke')
      .map(getStrokeBounds)
      .reduce(mergeBounds, boundsRef);
  boundsRef = symbols
      .filter(symbol => symbol.type === 'clef')
      .map(getClefBounds)
      .reduce(mergeBounds, boundsRef);
  boundsRef = symbols
      .filter(symbol => symbol.type === 'line')
      .map(getLineBounds)
      .reduce(mergeBounds, boundsRef);
  boundsRef = symbols
      .filter(symbol => symbol.type === 'ellipse')
      .map(getEllipseBounds)
      .reduce(mergeBounds, boundsRef);
  boundsRef = symbols
      .filter(symbol => symbol.type === 'textLine')
      .map(getTextLineBounds)
      .reduce(mergeBounds, boundsRef);
  return boundsRef;
}

function getDefaultMusicSymbols(configuration) {
  const defaultStaff = Object.assign({}, { type: 'staff' }, configuration.recognitionParams.v3.musicParameter.staff);
  const defaultClef = {
    type: 'clef',
    value: Object.assign({}, configuration.recognitionParams.v3.musicParameter.clef)
  };
  defaultClef.value.yAnchor = defaultStaff.top + (defaultStaff.gap * (defaultStaff.count - defaultClef.value.line));
  delete defaultClef.value.line;
  defaultClef.boundingBox = MusicClefs[defaultClef.value.symbol].getBoundingBox(defaultStaff.gap, 0, defaultClef.value.yAnchor);
  return [defaultStaff, defaultClef];
}

/**
 * Get the default symbols for the current recognition type
 * @param {Configuration} configuration Current recognition parameters from which extract default symbols
 * @return {Array} Symbols matching configuration
 */
export function getDefaultSymbols(configuration) {
  switch (configuration.recognitionParams.type) {
    case Constants.RecognitionType.MUSIC:
      return getDefaultMusicSymbols(configuration);
    default:
      return [];
  }
}