VexFlow - Music Engraving for HTML5 Copyright Mohit Muthanna 2010

This class implements curves (for slurs)

Vex.Flow.Curve = (function() {

from: Start note to: End note options: cps: List of control points x_shift: pixels to shift y_shift: pixels to shift

  function Curve(from, to, options) {
    if (arguments.length > 0) this.init(from, to, options);
  }

  Curve.Position = {
    NEAR_HEAD: 1,
    NEAR_TOP: 2
  };

  Curve.DEBUG = true;

  Curve.prototype = {
    init: function(from, to, options) {
      this.render_options = {
        spacing: 2,
        thickness: 2,
        x_shift: 0,
        y_shift: 10,
        position: Curve.Position.NEAR_HEAD,
        invert: false,
        cps: [{x: 0, y: 10}, {x: 0, y: 10}]
      };

      Vex.Merge(this.render_options, options);
      this.setNotes(from, to);
    },

    setContext: function(context) { this.context = context; return this; },
    setNotes: function(from, to) {
      if (!from && !to)
        throw new Vex.RuntimeError("BadArguments",
            "Curve needs to have either first_note or last_note set.");

      this.from = from;
      this.to = to;
      return this;
    },

    /**
     * @return {boolean} Returns true if this is a partial bar.
     */
    isPartial: function() {
      return (!this.from || !this.to);
    },

    renderCurve: function(params) {
      var ctx = this.context;
      var cps = this.render_options.cps;

      var x_shift = this.render_options.x_shift;
      var y_shift = this.render_options.y_shift * params.direction;

      var first_x = params.first_x + x_shift;
      var first_y = params.first_y + y_shift;
      var last_x = params.last_x - x_shift;
      var last_y = params.last_y + y_shift;
      var thickness = this.render_options.thickness;

      var cp_spacing = (last_x - first_x) / (cps.length + 2);

      ctx.beginPath();
      ctx.moveTo(first_x, first_y);
      ctx.bezierCurveTo(first_x + cp_spacing + cps[0].x,
                        first_y + (cps[0].y * params.direction),
                        last_x - cp_spacing + cps[1].x,
                        last_y + (cps[1].y * params.direction),
                        last_x, last_y);
      ctx.bezierCurveTo(last_x - cp_spacing + cps[1].x,
                        last_y + ((cps[1].y + thickness) * params.direction),
                        first_x + cp_spacing + cps[0].x,
                        first_y + ((cps[0].y + thickness) * params.direction),
                        first_x, first_y);
      ctx.stroke();
      ctx.closePath();
      ctx.fill();
    },

    draw: function() {
      if (!this.context)
        throw new Vex.RERR("NoContext", "No context to render tie.");
      var first_note = this.from;
      var last_note = this.to;
      var first_x, last_x, first_y, last_y, stem_direction;

      var metric = "baseY";
      var end_metric = "baseY";
      var position = this.render_options.position;
      var position_end = this.render_options.position_end;

      if (position === Curve.Position.NEAR_TOP) {
        metric = "topY";
        end_metric = "topY";
      }

      if (position_end == Curve.Position.NEAR_HEAD) {
        end_metric = "baseY";
      } else if (position_end == Curve.Position.NEAR_TOP) {
        end_metric = "topY";
      }

      if (first_note) {
        first_x = first_note.getTieRightX();
        stem_direction = first_note.getStemDirection();
        first_y = first_note.getStemExtents()[metric];
      } else {
        first_x = last_note.getStave().getTieStartX();
        first_y = last_note.getStemExtents()[metric];
      }

      if (last_note) {
        last_x = last_note.getTieLeftX();
        stem_direction = last_note.getStemDirection();
        last_y = last_note.getStemExtents()[end_metric];
      } else {
        last_x = first_note.getStave().getTieEndX();
        last_y = first_note.getStemExtents()[end_metric];
      }

      this.renderCurve({
        first_x: first_x,
        last_x: last_x,
        first_y: first_y,
        last_y: last_y,
        direction: stem_direction *
          (this.render_options.invert === true ? -1 : 1)
      });
      return true;
    }
  };

  return Curve;
}());
h