Home | History | Annotate | Download | only in tracks
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 'use strict';
      6 
      7 base.requireStylesheet('tracing.tracks.counter_track');
      8 
      9 base.require('tracing.tracks.heading_track');
     10 base.require('tracing.color_scheme');
     11 base.require('ui');
     12 
     13 base.exportTo('tracing.tracks', function() {
     14 
     15   var palette = tracing.getColorPalette();
     16 
     17   /**
     18    * A track that displays a Counter object.
     19    * @constructor
     20    * @extends {HeadingTrack}
     21    */
     22 
     23   var CounterTrack =
     24       ui.define('counter-track', tracing.tracks.HeadingTrack);
     25 
     26   CounterTrack.prototype = {
     27 
     28     __proto__: tracing.tracks.HeadingTrack.prototype,
     29 
     30     decorate: function(viewport) {
     31       tracing.tracks.HeadingTrack.prototype.decorate.call(this, viewport);
     32       this.classList.add('counter-track');
     33       this.selectedSamples_ = {};
     34       this.categoryFilter_ = new tracing.Filter();
     35     },
     36 
     37     /**
     38      * Called by all the addToSelection functions on the created selection
     39      * hit objects. Override this function on parent classes to add
     40      * context-specific information to the hit.
     41      */
     42     decorateHit: function(hit) {
     43     },
     44 
     45     get counter() {
     46       return this.counter_;
     47     },
     48 
     49     set counter(counter) {
     50       this.counter_ = counter;
     51       this.heading = counter.name + ': ';
     52     },
     53 
     54     get categoryFilter() {
     55       return this.categoryFilter_;
     56     },
     57 
     58     set categoryFilter(v) {
     59       this.categoryFilter_ = v;
     60     },
     61 
     62     /**
     63      * @return {Object} A sparse, mutable map from sample index to bool. Samples
     64      * indices the map that are true are drawn as selected.
     65      */
     66     get selectedSamples() {
     67       return this.selectedSamples_;
     68     },
     69 
     70     draw: function(type, viewLWorld, viewRWorld) {
     71       switch (type) {
     72         case tracing.tracks.DrawType.SLICE:
     73           this.drawSlices_(viewLWorld, viewRWorld);
     74           break;
     75       }
     76     },
     77 
     78     drawSlices_: function(viewLWorld, viewRWorld) {
     79       var ctx = this.context();
     80       var pixelRatio = window.devicePixelRatio || 1;
     81 
     82       var bounds = this.getBoundingClientRect();
     83       var height = bounds.height * pixelRatio;
     84 
     85       var counter = this.counter_;
     86 
     87       // Culling parametrs.
     88       var vp = this.viewport;
     89       var pixWidth = vp.xViewVectorToWorld(1);
     90 
     91       // Drop sampels that are less than skipDistancePix apart.
     92       var skipDistancePix = 1;
     93       var skipDistanceWorld = vp.xViewVectorToWorld(skipDistancePix);
     94 
     95       // Begin rendering in world space.
     96       ctx.save();
     97       vp.applyTransformToCanvas(ctx);
     98 
     99       // Figure out where drawing should begin.
    100       var numSeries = counter.numSeries;
    101       var numSamples = counter.numSamples;
    102       var startIndex = base.findLowIndexInSortedArray(
    103           counter.timestamps,
    104           function(x) { return x; },
    105           viewLWorld);
    106 
    107       startIndex = startIndex - 1 > 0 ? startIndex - 1 : 0;
    108       // Draw indices one by one until we fall off the viewRWorld.
    109       var yScale = height / counter.maxTotal;
    110       for (var seriesIndex = counter.numSeries - 1;
    111            seriesIndex >= 0; seriesIndex--) {
    112         var colorId = counter.series[seriesIndex].color;
    113         ctx.fillStyle = palette[colorId];
    114         ctx.beginPath();
    115 
    116         // Set iLast and xLast such that the first sample we draw is the
    117         // startIndex sample.
    118         var iLast = startIndex - 1;
    119         var xLast = iLast >= 0 ?
    120             counter.timestamps[iLast] - skipDistanceWorld : -1;
    121         var yLastView = height;
    122 
    123         // Iterate over samples from iLast onward until we either fall off the
    124         // viewRWorld or we run out of samples. To avoid drawing too much, after
    125         // drawing a sample at xLast, skip subsequent samples that are less than
    126         // skipDistanceWorld from xLast.
    127         var hasMoved = false;
    128 
    129         while (true) {
    130           var i = iLast + 1;
    131           if (i >= numSamples) {
    132             ctx.lineTo(xLast, yLastView);
    133             ctx.lineTo(xLast + 8 * pixWidth, yLastView);
    134             ctx.lineTo(xLast + 8 * pixWidth, height);
    135             break;
    136           }
    137 
    138           var x = counter.timestamps[i];
    139           var y = counter.totals[i * numSeries + seriesIndex];
    140           var yView = height - (yScale * y);
    141 
    142           if (x > viewRWorld) {
    143             ctx.lineTo(x, yLastView);
    144             ctx.lineTo(x, height);
    145             break;
    146           }
    147 
    148           if (i + 1 < numSamples) {
    149             var xNext = counter.timestamps[i + 1];
    150             if (xNext - xLast <= skipDistanceWorld && xNext < viewRWorld) {
    151               iLast = i;
    152               continue;
    153             }
    154           }
    155 
    156           if (!hasMoved) {
    157             ctx.moveTo(viewLWorld, height);
    158             hasMoved = true;
    159           }
    160 
    161           if (x - xLast < skipDistanceWorld) {
    162             // We know that xNext > xLast + skipDistanceWorld, so we can
    163             // safely move this sample's x over that much without passing
    164             // xNext.  This ensure that the previous sample is visible when
    165             // zoomed out very far.
    166             x = xLast + skipDistanceWorld;
    167           }
    168           ctx.lineTo(x, yLastView);
    169           ctx.lineTo(x, yView);
    170 
    171           iLast = i;
    172           xLast = x;
    173           yLastView = yView;
    174         }
    175         ctx.closePath();
    176         ctx.fill();
    177       }
    178 
    179       ctx.fillStyle = 'rgba(255, 0, 0, 1)';
    180       for (var i in this.selectedSamples_) {
    181         if (!this.selectedSamples_[i])
    182           continue;
    183 
    184         var x = counter.timestamps[i];
    185         for (var seriesIndex = counter.numSeries - 1;
    186              seriesIndex >= 0; seriesIndex--) {
    187           var y = counter.totals[i * numSeries + seriesIndex];
    188           var yView = height - (yScale * y);
    189           ctx.fillRect(x - pixWidth, yView - 1, 3 * pixWidth, 3);
    190         }
    191       }
    192       ctx.restore();
    193     },
    194 
    195     addIntersectingItemsInRangeToSelectionInWorldSpace: function(
    196         loWX, hiWX, viewPixWidthWorld, selection) {
    197 
    198       function getSampleWidth(x, i) {
    199         if (i === counter.timestamps.length - 1)
    200           return 0;
    201         return counter.timestamps[i + 1] - counter.timestamps[i];
    202       }
    203 
    204       var counter = this.counter_;
    205       var iLo = base.findLowIndexInSortedIntervals(counter.timestamps,
    206                                                    function(x) { return x; },
    207                                                    getSampleWidth,
    208                                                    loWX);
    209       var iHi = base.findLowIndexInSortedIntervals(counter.timestamps,
    210                                                    function(x) { return x; },
    211                                                    getSampleWidth,
    212                                                    hiWX);
    213 
    214       // Iterate over every sample intersecting..
    215       for (var i = iLo; i <= iHi; i++) {
    216         if (i < 0)
    217           continue;
    218         if (i >= counter.timestamps.length)
    219           continue;
    220 
    221         // TODO(nduca): Pick the seriesIndexHit based on the loY - hiY values.
    222         var hit = selection.addCounterSample(this, this.counter, i);
    223         this.decorateHit(hit);
    224       }
    225     },
    226 
    227     addAllObjectsMatchingFilterToSelection: function(filter, selection) {
    228     }
    229   };
    230 
    231   return {
    232     CounterTrack: CounterTrack
    233   };
    234 });
    235