3 Copyright (c) 2013 The Chromium Authors. All rights reserved.
4 Use of this source code is governed by a BSD-style license that can be
5 found in the LICENSE file.
8 <link rel="stylesheet" href="/tracing/tracks/counter_track.css">
10 <link rel="import" href="/tracing/trace_model/event.html">
11 <link rel="import" href="/tracing/tracks/heading_track.html">
12 <link rel="import" href="/tracing/color_scheme.html">
13 <link rel="import" href="/base/ui.html">
18 tv.exportTo('tracing.tracks', function() {
20 var SelectionState = tracing.trace_model.SelectionState;
21 var EventPresenter = tracing.EventPresenter;
22 var LAST_SAMPLE_PIXELS = 8;
25 var BACKGROUND_ALPHA_MULTIPLIER = 0.5;
26 var SQUARE_WIDTH = 3; // Unselected sample point.
27 var CIRCLE_RADIUS = 2; // Selected sample point.
29 var POINT_DENSITY_TRANSPARENT = 0.10;
30 var POINT_DENSITY_OPAQUE = 0.05;
31 var POINT_DENSITY_RANGE = POINT_DENSITY_TRANSPARENT - POINT_DENSITY_OPAQUE;
34 * A track that displays a Counter object.
36 * @extends {HeadingTrack}
40 tv.ui.define('counter-track', tracing.tracks.HeadingTrack);
42 CounterTrack.prototype = {
43 __proto__: tracing.tracks.HeadingTrack.prototype,
45 decorate: function(viewport) {
46 tracing.tracks.HeadingTrack.prototype.decorate.call(this, viewport);
47 this.classList.add('counter-track');
54 set counter(counter) {
55 this.counter_ = counter;
56 this.heading = counter.name + ': ';
59 draw: function(type, viewLWorld, viewRWorld) {
61 case tracing.tracks.DrawType.SLICE:
62 this.drawSlices_(viewLWorld, viewRWorld);
67 drawSlices_: function(viewLWorld, viewRWorld) {
68 var ctx = this.context();
69 var pixelRatio = window.devicePixelRatio || 1;
71 var bounds = this.getBoundingClientRect();
72 var height = bounds.height * pixelRatio;
74 var counter = this.counter_;
77 var vp = this.viewport;
78 var dt = vp.currentDisplayTransform;
79 var pixWidth = dt.xViewVectorToWorld(1);
81 // Drop samples that are less than skipDistancePix apart.
82 var skipDistancePix = 1;
83 var skipDistanceWorld = dt.xViewVectorToWorld(skipDistancePix);
85 // Figure out where drawing should begin.
86 var numSeries = counter.numSeries;
87 var numSamples = counter.numSamples;
88 var startIndex = tv.findLowIndexInSortedArray(
90 function(x) { return x; },
92 var timestamps = counter.timestamps;
94 startIndex = startIndex - 1 > 0 ? startIndex - 1 : 0;
95 // Draw indices one by one until we fall off the viewRWorld.
96 var yScale = height / counter.maxTotal;
98 for (var seriesIndex = counter.numSeries - 1;
99 seriesIndex >= 0; seriesIndex--) {
100 var series = counter.series[seriesIndex];
101 ctx.strokeStyle = EventPresenter.getCounterSeriesColor(
102 series.color, SelectionState.NONE);
104 // Draw the background and the line.
105 var drawSeries = function(background) {
106 var selectionStateLast = -1;
108 // Set i and x such that the first sample we draw is the
109 // startIndex sample.
110 var i = startIndex - 1;
112 timestamps[i] - skipDistanceWorld : -1;
113 var yLastView = height;
115 // Iterate over samples from i onward until we either fall off the
116 // viewRWorld or we run out of samples. To avoid drawing too much,
117 // after drawing a sample at xLast, skip subsequent samples that are
118 // less than skipDistanceWorld from xLast.
119 var hasMoved = false;
123 if (i >= numSamples) {
127 var x = timestamps[i];
128 var y = counter.totals[i * numSeries + seriesIndex];
129 var yView = height - (yScale * y);
131 // If the sample is to the right of the viewport, we add a fixed
132 // margin to reduce zooming clipping errors.
133 if (x > viewRWorld) {
135 xLast = x = viewRWorld;
136 ctx.lineTo(dt.xWorldToView(x), yLastView);
141 if (i + 1 < numSamples) {
142 var xNext = timestamps[i + 1];
143 if (xNext - xLast <= skipDistanceWorld && xNext < viewRWorld) {
147 // If the sample is to the left of the viewport, we add a fixed
148 // margin to reduce zooming clipping errors.
149 if (x < viewLWorld) {
154 if (x - xLast < skipDistanceWorld && xLast < x) {
155 // We know that xNext > xLast + skipDistanceWorld, so we can
156 // safely move this sample's x over that much without passing
157 // xNext. This ensure that the previous sample is visible when
158 // zoomed out very far.
159 x = xLast + skipDistanceWorld;
162 var selectionState = series.samples[i].selectionState;
165 ctx.lineTo(dt.xWorldToView(x), yLastView);
166 if (selectionState != selectionStateLast) {
168 ctx.lineTo(dt.xWorldToView(x), height);
172 ctx.lineTo(dt.xWorldToView(x), yView);
178 if (selectionState != selectionStateLast) {
179 ctx.fillStyle = EventPresenter.getCounterSeriesColor(
180 series.color, selectionState, BACKGROUND_ALPHA_MULTIPLIER);
181 ctx.lineWidth = LINE_WIDTH * pixelRatio;
185 ctx.moveTo(dt.xWorldToView(x), height);
187 ctx.moveTo(dt.xWorldToView(x), hasMoved ? yLastView : yView);
192 ctx.lineTo(dt.xWorldToView(x), yView);
194 ctx.lineTo(dt.xWorldToView(x), yView);
200 selectionStateLast = selectionState;
205 ctx.lineTo(dt.xWorldToView(xLast), height);
217 // Calculate point density and, consequently, opacity of sample points.
218 var endIndex = tv.findLowIndexInSortedArray(
219 counter.timestamps, function(x) { return x; }, viewRWorld);
220 if (counter.timestamps[endIndex] == viewRWorld) {
223 var minVisible = (startIndex >= counter.timestamps.length ?
224 viewLWorld : counter.timestamps[startIndex]);
225 var maxVisible = (endIndex < 1 ?
226 viewRWorld : counter.timestamps[endIndex - 1]);
227 var rangeVisible = (minVisible >= maxVisible ?
228 viewRWorld - viewLWorld : maxVisible - minVisible);
230 var density = (endIndex - startIndex) / (dt.scaleX * rangeVisible);
231 var clampedDensity = tv.clamp(density, POINT_DENSITY_OPAQUE,
232 POINT_DENSITY_TRANSPARENT);
234 (POINT_DENSITY_TRANSPARENT - clampedDensity) / POINT_DENSITY_RANGE;
236 // Draw sample points.
237 ctx.strokeStyle = EventPresenter.getCounterSeriesColor(
238 series.color, SelectionState.NONE);
239 for (var i = startIndex; timestamps[i] < viewRWorld; i++) {
240 var x = timestamps[i];
241 var y = counter.totals[i * numSeries + seriesIndex];
242 var yView = height - (yScale * y);
244 if (series.samples[i].selected) {
245 ctx.fillStyle = EventPresenter.getCounterSeriesColor(
246 series.color, series.samples[i].selectionState);
248 ctx.arc(dt.xWorldToView(x), yView, CIRCLE_RADIUS * pixelRatio, 0,
253 ctx.fillStyle = EventPresenter.getCounterSeriesColor(
254 series.color, series.samples[i].selectionState, opacity);
255 ctx.fillRect(dt.xWorldToView(x) - (SQUARE_WIDTH / 2) * pixelRatio,
256 yView - (SQUARE_WIDTH / 2) * pixelRatio,
257 SQUARE_WIDTH * pixelRatio, SQUARE_WIDTH * pixelRatio);
263 addEventsToTrackMap: function(eventToTrackMap) {
264 var allSeries = this.counter_.series;
265 for (var seriesIndex = 0; seriesIndex < allSeries.length; seriesIndex++) {
266 var samples = allSeries[seriesIndex].samples;
267 for (var i = 0; i < samples.length; i++)
268 eventToTrackMap.addEvent(samples[i], this);
272 addIntersectingItemsInRangeToSelectionInWorldSpace: function(
273 loWX, hiWX, viewPixWidthWorld, selection) {
275 function getSampleWidth(x, i) {
276 if (i === counter.timestamps.length - 1) {
277 var dt = this.viewport.currentDisplayTransform;
278 var pixWidth = dt.xViewVectorToWorld(1);
279 return LAST_SAMPLE_PIXELS * pixWidth;
281 return counter.timestamps[i + 1] - counter.timestamps[i];
284 var counter = this.counter_;
285 var iLo = tv.findLowIndexInSortedIntervals(counter.timestamps,
286 function(x) { return x; },
287 getSampleWidth.bind(this),
289 var iHi = tv.findLowIndexInSortedIntervals(counter.timestamps,
290 function(x) { return x; },
291 getSampleWidth.bind(this),
294 // Iterate over every sample intersecting..
295 for (var sampleIndex = iLo; sampleIndex <= iHi; sampleIndex++) {
298 if (sampleIndex >= counter.timestamps.length)
301 // TODO(nduca): Pick the seriesIndexHit based on the loY - hiY values.
302 for (var seriesIndex = 0;
303 seriesIndex < this.counter.numSeries;
305 var series = this.counter.series[seriesIndex];
306 selection.push(series.samples[sampleIndex]);
311 addItemNearToProvidedEventToSelection: function(sample, offset, selection) {
312 var index = sample.getSampleIndex();
313 var newIndex = index + offset;
314 if (newIndex < 0 || newIndex >= sample.series.samples.length)
317 selection.push(sample.series.samples[newIndex]);
321 addAllObjectsMatchingFilterToSelection: function(filter, selection) {
324 addClosestEventToSelection: function(worldX, worldMaxDist, loY, hiY,
326 var counter = this.counter;
327 if (!counter.numSeries)
332 for (var i = 0; i < counter.numSeries; i++) {
333 var counterSample = tv.findClosestElementInSortedArray(
334 counter.series_[i].samples_,
335 function(x) { return x.timestamp; },
342 selection.push(counterSample);
348 CounterTrack: CounterTrack