12da489cd246702bee5938545b18a6f710ed214bcJamie Gennis// Copyright (c) 2012 The Chromium Authors. All rights reserved.
22da489cd246702bee5938545b18a6f710ed214bcJamie Gennis// Use of this source code is governed by a BSD-style license that can be
32da489cd246702bee5938545b18a6f710ed214bcJamie Gennis// found in the LICENSE file.
42da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
52da489cd246702bee5938545b18a6f710ed214bcJamie Gennis'use strict';
62da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennisbase.requireStylesheet('tracing.tracks.ruler_track');
82da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
96833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennisbase.require('tracing.constants');
1066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennisbase.require('tracing.tracks.track');
116833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennisbase.require('tracing.tracks.heading_track');
122da489cd246702bee5938545b18a6f710ed214bcJamie Gennisbase.require('ui');
132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1488448d9ae4dfff1805045790ef5f32495d62abccJeff Brownbase.exportTo('tracing.tracks', function() {
152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  /**
174849cead45edf85cf2a61526c0b716eb637f74baJeff Brown   * A track that displays the ruler.
182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   * @constructor
196833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis   * @extends {HeadingTrack}
202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis   */
212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
226833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis  var RulerTrack = ui.define('ruler-track', tracing.tracks.HeadingTrack);
232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  var logOf10 = Math.log(10);
252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  function log10(x) {
262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    return Math.log(x) / logOf10;
272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  }
282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
294849cead45edf85cf2a61526c0b716eb637f74baJeff Brown  RulerTrack.prototype = {
306833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis    __proto__: tracing.tracks.HeadingTrack.prototype,
312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    decorate: function(viewport) {
336833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis      tracing.tracks.HeadingTrack.prototype.decorate.call(this, viewport);
344849cead45edf85cf2a61526c0b716eb637f74baJeff Brown      this.classList.add('ruler-track');
352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.strings_secs_ = [];
362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.strings_msecs_ = [];
372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.addEventListener('mousedown', this.onMouseDown);
3866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
3966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      this.viewportMarkersChange_ = this.viewportMarkersChange_.bind(this);
4066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      viewport.addEventListener('markersChange', this.viewportMarkersChange_);
4166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
4266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    },
4366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
4466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    detach: function() {
456833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis      tracing.tracks.HeadingTrack.prototype.detach.call(this);
4666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      this.viewport.removeEventListener('markersChange',
4766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis                                        this.viewportMarkersChange_);
4866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    },
4966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
5066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis    viewportMarkersChange_: function() {
5166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      if (this.viewport.markers.length < 2)
5266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis        this.classList.remove('ruler-track-with-distance-measurements');
5366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      else
5466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis        this.classList.add('ruler-track-with-distance-measurements');
552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    onMouseDown: function(e) {
586833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis      if (e.button !== 0)
592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        return;
602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.placeAndBeginDraggingMarker(e.clientX);
612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    placeAndBeginDraggingMarker: function(clientX) {
6488448d9ae4dfff1805045790ef5f32495d62abccJeff Brown      var pixelRatio = window.devicePixelRatio || 1;
656833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis
666833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis      var viewX =
676833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis          (clientX - this.offsetLeft - tracing.constants.HEADING_WIDTH) *
686833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis              pixelRatio;
6966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      var worldX = this.viewport.xViewToWorld(viewX);
7066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      var marker = this.viewport.findMarkerNear(worldX, 6);
716833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis
722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var createdMarker = false;
732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var movedMarker = false;
742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (!marker) {
7566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis        marker = this.viewport.addMarker(worldX);
762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        createdMarker = true;
772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      marker.selected = true;
792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var onMouseMove = function(e) {
816833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis        var viewX =
826833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis            (e.clientX - this.offsetLeft - tracing.constants.HEADING_WIDTH) *
836833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis                pixelRatio;
846833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis        var worldX = this.viewport.xViewToWorld(viewX);
856833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis
862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        marker.positionWorld = worldX;
872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        movedMarker = true;
886833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis      }.bind(this);
892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var onMouseUp = function(e) {
912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        marker.selected = false;
922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if (!movedMarker && !createdMarker)
936833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis          this.viewport.removeMarker(marker);
946833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis
952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        document.removeEventListener('mouseup', onMouseUp);
962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        document.removeEventListener('mousemove', onMouseMove);
976833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis      }.bind(this);
982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      document.addEventListener('mouseup', onMouseUp);
1002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      document.addEventListener('mousemove', onMouseMove);
1012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
1022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    drawLine_: function(ctx, x1, y1, x2, y2, color) {
1042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.beginPath();
1052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.moveTo(x1, y1);
1062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.lineTo(x2, y2);
1072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.closePath();
1082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.strokeStyle = color;
1092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.stroke();
1102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
1112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    drawArrow_: function(ctx, x1, y1, x2, y2, arrowWidth, color) {
1132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      this.drawLine_(ctx, x1, y1, x2, y2, color);
1142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var dx = x2 - x1;
1162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var dy = y2 - y1;
1172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var len = Math.sqrt(dx * dx + dy * dy);
1182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var perc = (len - 10) / len;
1192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var bx = x1 + perc * dx;
1202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var by = y1 + perc * dy;
1212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var ux = dx / len;
1222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var uy = dy / len;
1232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var ax = uy * arrowWidth;
1242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var ay = -ux * arrowWidth;
1252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.beginPath();
1272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.fillStyle = color;
1282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.moveTo(bx + ax, by + ay);
1292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.lineTo(x2, y2);
1302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.lineTo(bx - ax, by - ay);
1312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.lineTo(bx + ax, by + ay);
1322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.closePath();
1332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.fill();
1342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
1352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1366833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis    draw: function(type, viewLWorld, viewRWorld) {
1376833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis      switch (type) {
1386833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis        case tracing.tracks.DrawType.SLICE:
1396833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis          this.drawSlices_(viewLWorld, viewRWorld);
1406833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis          break;
1416833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis      }
1426833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis    },
1436833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis
1446833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis    drawSlices_: function(viewLWorld, viewRWorld) {
14566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      var ctx = this.context();
14666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      var pixelRatio = window.devicePixelRatio || 1;
1472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
14866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      var bounds = this.getBoundingClientRect();
14966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      var width = bounds.width * pixelRatio;
15066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      var height = bounds.height * pixelRatio;
1512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1524849cead45edf85cf2a61526c0b716eb637f74baJeff Brown      var measurements = this.classList.contains(
1534849cead45edf85cf2a61526c0b716eb637f74baJeff Brown          'ruler-track-with-distance-measurements');
1542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
15566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      var rulerHeight = measurements ? height / 2 : height;
1562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
15766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      var vp = this.viewport;
15866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      vp.drawMarkerArrows(ctx, viewLWorld, viewRWorld, rulerHeight);
1592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
16088448d9ae4dfff1805045790ef5f32495d62abccJeff Brown      var idealMajorMarkDistancePix = 150 * pixelRatio;
1612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var idealMajorMarkDistanceWorld =
1622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          vp.xViewVectorToWorld(idealMajorMarkDistancePix);
1632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var majorMarkDistanceWorld;
1652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var unit;
1662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var unitDivisor;
1672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var tickLabels;
1682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // The conservative guess is the nearest enclosing 0.1, 1, 10, 100, etc.
1702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var conservativeGuess =
1712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          Math.pow(10, Math.ceil(log10(idealMajorMarkDistanceWorld)));
1722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // Once we have a conservative guess, consider things that evenly add up
1742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // to the conservative guess, e.g. 0.5, 0.2, 0.1 Pick the one that still
1752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // exceeds the ideal mark distance.
1762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var divisors = [10, 5, 2, 1];
1772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      for (var i = 0; i < divisors.length; ++i) {
1782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var tightenedGuess = conservativeGuess / divisors[i];
1792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if (vp.xWorldVectorToView(tightenedGuess) < idealMajorMarkDistancePix)
1802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          continue;
1812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        majorMarkDistanceWorld = conservativeGuess / divisors[i - 1];
1822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        break;
1832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
18466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis
1852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var tickLabels = undefined;
1862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (majorMarkDistanceWorld < 100) {
1872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        unit = 'ms';
1882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        unitDivisor = 1;
1892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        tickLabels = this.strings_msecs_;
1902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      } else {
1912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        unit = 's';
1922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        unitDivisor = 1000;
1932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        tickLabels = this.strings_secs_;
1942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
1952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
1962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var numTicksPerMajor = 5;
1972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var minorMarkDistanceWorld = majorMarkDistanceWorld / numTicksPerMajor;
1982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var minorMarkDistancePx = vp.xWorldVectorToView(minorMarkDistanceWorld);
1992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var firstMajorMark =
2012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          Math.floor(viewLWorld / majorMarkDistanceWorld) *
2022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              majorMarkDistanceWorld;
2032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
20466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis      var minorTickH = Math.floor(height * 0.25);
2052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.fillStyle = 'rgb(0, 0, 0)';
2072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.strokeStyle = 'rgb(0, 0, 0)';
2082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.textAlign = 'left';
2092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.textBaseline = 'top';
2102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      var pixelRatio = window.devicePixelRatio || 1;
2122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      ctx.font = (9 * pixelRatio) + 'px sans-serif';
2132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // Each iteration of this loop draws one major mark
2152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // and numTicksPerMajor minor ticks.
2162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      //
2172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // Rendering can't be done in world space because canvas transforms
2182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // affect line width. So, do the conversions manually.
2192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      for (var curX = firstMajorMark;
2202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis           curX < viewRWorld;
2212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis           curX += majorMarkDistanceWorld) {
2222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var curXView = Math.floor(vp.xWorldToView(curX));
2242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var unitValue = curX / unitDivisor;
2262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var roundedUnitValue = Math.floor(unitValue * 100000) / 100000;
2272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        if (!tickLabels[roundedUnitValue])
2292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          tickLabels[roundedUnitValue] = roundedUnitValue + ' ' + unit;
2302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.fillText(tickLabels[roundedUnitValue],
2312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                     curXView + 2 * pixelRatio, 0);
2322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.beginPath();
2332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        // Major mark
2352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.moveTo(curXView, 0);
2362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.lineTo(curXView, rulerHeight);
2372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        // Minor marks
2392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        for (var i = 1; i < numTicksPerMajor; ++i) {
2402da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var xView = Math.floor(curXView + minorMarkDistancePx * i);
2412da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          ctx.moveTo(xView, rulerHeight - minorTickH);
2422da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          ctx.lineTo(xView, rulerHeight);
2432da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        }
2442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        ctx.stroke();
2462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
2476833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis      // Draw bottom bar.
2486833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis      ctx.moveTo(0, rulerHeight);
2496833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis      ctx.lineTo(width, rulerHeight);
2506833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis      ctx.stroke();
2512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // Give distance between directly adjacent markers.
2532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      if (measurements) {
2542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        // Obtain a sorted array of markers
2552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var sortedMarkers = vp.markers.slice();
2562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        sortedMarkers.sort(function(a, b) {
2572da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          return a.positionWorld_ - b.positionWorld_;
2582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        });
2592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        // Distance Variables.
2612da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var displayDistance;
2622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var unitDivisor;
2632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var displayTextColor = 'rgb(0,0,0)';
2642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var measurementsPosY = rulerHeight + 2;
2652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        // Arrow Variables.
2672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var arrowSpacing = 10;
2682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var arrowColor = 'rgb(128,121,121)';
2692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var arrowPosY = measurementsPosY + 4;
2702da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var arrowWidthView = 3;
2712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        var spaceForArrowsView = 2 * (arrowWidthView + arrowSpacing);
2722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2732da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        for (i = 0; i < sortedMarkers.length - 1; i++) {
2742da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var rightMarker = sortedMarkers[i + 1];
2752da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var leftMarker = sortedMarkers[i];
2762da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var distanceBetweenMarkers =
2772da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              rightMarker.positionWorld - leftMarker.positionWorld;
2782da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var distanceBetweenMarkersView =
2792da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              vp.xWorldVectorToView(distanceBetweenMarkers);
2802da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2812da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var positionInMiddleOfMarkers = leftMarker.positionWorld +
2822da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                              distanceBetweenMarkers / 2;
2832da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var positionInMiddleOfMarkersView =
2842da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              vp.xWorldToView(positionInMiddleOfMarkers);
2852da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
2862da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          // Determine units.
2872da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          if (distanceBetweenMarkers < 100) {
2882da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            unit = 'ms';
2892da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            unitDivisor = 1;
2902da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          } else {
2912da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            unit = 's';
2922da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            unitDivisor = 1000;
2932da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          }
2942da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          // Calculate display value to print.
2952da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          displayDistance = distanceBetweenMarkers / unitDivisor;
2962da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var roundedDisplayDistance =
2972da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              Math.abs((Math.floor(displayDistance * 1000) / 1000));
2982da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var textToDraw = roundedDisplayDistance + ' ' + unit;
2992da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var textWidthView = ctx.measureText(textToDraw).width;
3002da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var textWidthWorld = vp.xViewVectorToWorld(textWidthView);
3012da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var spaceForArrowsAndTextView = textWidthView +
3022da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                                          spaceForArrowsView + arrowSpacing;
3032da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3042da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          // Set text positions.
3052da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var textLeft = leftMarker.positionWorld +
3062da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              (distanceBetweenMarkers / 2) - (textWidthWorld / 2);
3072da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var textRight = textLeft + textWidthWorld;
3082da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var textPosY = measurementsPosY;
3092da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var textLeftView = vp.xWorldToView(textLeft);
3102da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var textRightView = vp.xWorldToView(textRight);
3112da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var leftMarkerView = vp.xWorldToView(leftMarker.positionWorld);
3122da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var rightMarkerView = vp.xWorldToView(rightMarker.positionWorld);
3132da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          var textDrawn = false;
3142da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3152da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          if (spaceForArrowsAndTextView <= distanceBetweenMarkersView) {
3162da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            // Print the display distance text.
3172da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            ctx.fillStyle = displayTextColor;
3182da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            ctx.fillText(textToDraw, textLeftView, textPosY);
3192da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            textDrawn = true;
3202da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          }
3212da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3222da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          if (spaceForArrowsView <= distanceBetweenMarkersView) {
3232da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            var leftArrowStart;
3242da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            var rightArrowStart;
3252da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            if (textDrawn) {
3262da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              leftArrowStart = textLeftView - arrowSpacing;
3272da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              rightArrowStart = textRightView + arrowSpacing;
3282da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            } else {
3292da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              leftArrowStart = positionInMiddleOfMarkersView;
3302da489cd246702bee5938545b18a6f710ed214bcJamie Gennis              rightArrowStart = positionInMiddleOfMarkersView;
3312da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            }
3322da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            // Draw left arrow.
3332da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            this.drawArrow_(ctx, leftArrowStart, arrowPosY,
3342da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                leftMarkerView, arrowPosY, arrowWidthView, arrowColor);
3352da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            // Draw right arrow.
3362da489cd246702bee5938545b18a6f710ed214bcJamie Gennis            this.drawArrow_(ctx, rightArrowStart, arrowPosY,
3372da489cd246702bee5938545b18a6f710ed214bcJamie Gennis                rightMarkerView, arrowPosY, arrowWidthView, arrowColor);
3382da489cd246702bee5938545b18a6f710ed214bcJamie Gennis          }
3392da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        }
3406833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis        // Draw bottom bar.
3416833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis        ctx.moveTo(0, rulerHeight * 2);
3426833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis        ctx.lineTo(width, rulerHeight * 2);
3436833e18b1d4077bf3a727b4422cc2acdbeee35a7Jamie Gennis        ctx.stroke();
3442da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      }
3452da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
3462da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3472da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    /**
3482da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * Adds items intersecting the given range to a selection.
3492da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {number} loVX Lower X bound of the interval to search, in
3502da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     *     viewspace.
3512da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {number} hiVX Upper X bound of the interval to search, in
3522da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     *     viewspace.
3532da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {number} loVY Lower Y bound of the interval to search, in
3542da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     *     viewspace.
3552da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     * @param {number} hiVY Upper Y bound of the interval to search, in
3562da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     *     viewspace.
35788448d9ae4dfff1805045790ef5f32495d62abccJeff Brown     * @param {Selection} selection Selection to which to add hits.
3582da489cd246702bee5938545b18a6f710ed214bcJamie Gennis     */
3592da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    addIntersectingItemsInRangeToSelection: function(
3602da489cd246702bee5938545b18a6f710ed214bcJamie Gennis        loVX, hiVX, loY, hiY, selection) {
3614849cead45edf85cf2a61526c0b716eb637f74baJeff Brown      // Does nothing. There's nothing interesting to pick on the ruler
3622da489cd246702bee5938545b18a6f710ed214bcJamie Gennis      // track.
3632da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    },
3642da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3652da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    addAllObjectsMatchingFilterToSelection: function(filter, selection) {
3662da489cd246702bee5938545b18a6f710ed214bcJamie Gennis    }
3672da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  };
3682da489cd246702bee5938545b18a6f710ed214bcJamie Gennis
3692da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  return {
3704849cead45edf85cf2a61526c0b716eb637f74baJeff Brown    RulerTrack: RulerTrack
3712da489cd246702bee5938545b18a6f710ed214bcJamie Gennis  };
3722da489cd246702bee5938545b18a6f710ed214bcJamie Gennis});
373