blob: 4dc1bae2fcde534f0606590053346be7666e593d [file] [log] [blame]
Chris Craikb122baf2015-03-05 13:58:42 -08001<!DOCTYPE html>
2<!--
3Copyright (c) 2013 The Chromium Authors. All rights reserved.
4Use of this source code is governed by a BSD-style license that can be
5found in the LICENSE file.
6-->
7
8<link rel="stylesheet" href="/core/tracks/object_instance_track.css">
9
10<link rel="import" href="/base/extension_registry.html">
11<link rel="import" href="/core/trace_model/event.html">
12<link rel="import" href="/core/tracks/heading_track.html">
13<link rel="import" href="/core/event_presenter.html">
14<link rel="import" href="/base/sorted_array_utils.html">
15<link rel="import" href="/base/ui.html">
16
17<script>
18'use strict';
19
20tv.exportTo('tv.c.tracks', function() {
21
22 var SelectionState = tv.c.trace_model.SelectionState;
23 var EventPresenter = tv.c.EventPresenter;
24
25 /**
26 * A track that displays an array of Slice objects.
27 * @constructor
28 * @extends {HeadingTrack}
29 */
30
31 var ObjectInstanceTrack = tv.b.ui.define(
32 'object-instance-track', tv.c.tracks.HeadingTrack);
33
34 ObjectInstanceTrack.prototype = {
35 __proto__: tv.c.tracks.HeadingTrack.prototype,
36
37 decorate: function(viewport) {
38 tv.c.tracks.HeadingTrack.prototype.decorate.call(this, viewport);
39 this.classList.add('object-instance-track');
40 this.objectInstances_ = [];
41 this.objectSnapshots_ = [];
42 },
43
44 get objectInstances() {
45 return this.objectInstances_;
46 },
47
48 set objectInstances(objectInstances) {
49 if (!objectInstances || objectInstances.length == 0) {
50 this.heading = '';
51 this.objectInstances_ = [];
52 this.objectSnapshots_ = [];
53 return;
54 }
55 this.heading = objectInstances[0].typeName;
56 this.objectInstances_ = objectInstances;
57 this.objectSnapshots_ = [];
58 this.objectInstances_.forEach(function(instance) {
59 this.objectSnapshots_.push.apply(
60 this.objectSnapshots_, instance.snapshots);
61 }, this);
62 this.objectSnapshots_.sort(function(a, b) {
63 return a.ts - b.ts;
64 });
65 },
66
67 get height() {
68 return window.getComputedStyle(this).height;
69 },
70
71 set height(height) {
72 this.style.height = height;
73 },
74
75 get snapshotRadiusView() {
76 return 7 * (window.devicePixelRatio || 1);
77 },
78
79 draw: function(type, viewLWorld, viewRWorld) {
80 switch (type) {
Chris Craik19832152015-04-16 15:43:38 -070081 case tv.c.tracks.DrawType.GENERAL_EVENT:
82 this.drawLetterDots_(viewLWorld, viewRWorld);
Chris Craikb122baf2015-03-05 13:58:42 -080083 break;
84 }
85 },
86
Chris Craik19832152015-04-16 15:43:38 -070087 drawLetterDots_: function(viewLWorld, viewRWorld) {
Chris Craikb122baf2015-03-05 13:58:42 -080088 var ctx = this.context();
89 var pixelRatio = window.devicePixelRatio || 1;
90
91 var bounds = this.getBoundingClientRect();
92 var height = bounds.height * pixelRatio;
93 var halfHeight = height * 0.5;
94 var twoPi = Math.PI * 2;
95
96 // Culling parameters.
97 var dt = this.viewport.currentDisplayTransform;
98 var snapshotRadiusView = this.snapshotRadiusView;
99 var snapshotRadiusWorld = dt.xViewVectorToWorld(height);
100 var loI;
101
102 // Begin rendering in world space.
103 ctx.save();
104 dt.applyTransformToCanvas(ctx);
105
106 // Instances
107 var objectInstances = this.objectInstances_;
108 var loI = tv.b.findLowIndexInSortedArray(
109 objectInstances,
110 function(instance) {
111 return instance.deletionTs;
112 },
113 viewLWorld);
114 ctx.strokeStyle = 'rgb(0,0,0)';
115 for (var i = loI; i < objectInstances.length; ++i) {
116 var instance = objectInstances[i];
117 var x = instance.creationTs;
118 if (x > viewRWorld)
119 break;
120
121 var right = instance.deletionTs == Number.MAX_VALUE ?
122 viewRWorld : instance.deletionTs;
123 ctx.fillStyle = EventPresenter.getObjectInstanceColor(instance);
124 ctx.fillRect(x, pixelRatio, right - x, height - 2 * pixelRatio);
125 }
126 ctx.restore();
127
128 // Snapshots. Has to run in worldspace because ctx.arc gets transformed.
129 var objectSnapshots = this.objectSnapshots_;
130 loI = tv.b.findLowIndexInSortedArray(
131 objectSnapshots,
132 function(snapshot) {
133 return snapshot.ts + snapshotRadiusWorld;
134 },
135 viewLWorld);
136 for (var i = loI; i < objectSnapshots.length; ++i) {
137 var snapshot = objectSnapshots[i];
138 var x = snapshot.ts;
139 if (x - snapshotRadiusWorld > viewRWorld)
140 break;
141 var xView = dt.xWorldToView(x);
142
143 ctx.fillStyle = EventPresenter.getObjectSnapshotColor(snapshot);
144 ctx.beginPath();
145 ctx.arc(xView, halfHeight, snapshotRadiusView, 0, twoPi);
146 ctx.fill();
147 if (snapshot.selected) {
148 ctx.lineWidth = 5;
149 ctx.strokeStyle = 'rgb(100,100,0)';
150 ctx.stroke();
151
152 ctx.beginPath();
153 ctx.arc(xView, halfHeight, snapshotRadiusView - 1, 0, twoPi);
154 ctx.lineWidth = 2;
155 ctx.strokeStyle = 'rgb(255,255,0)';
156 ctx.stroke();
157 } else {
158 ctx.lineWidth = 1;
159 ctx.strokeStyle = 'rgb(0,0,0)';
160 ctx.stroke();
161 }
162 }
163 ctx.lineWidth = 1;
164
165 // For performance reasons we only check the SelectionState of the first
166 // instance. If it's DIMMED we assume that all are DIMMED.
167 // TODO(egraether): Allow partial highlight.
168 var selectionState = SelectionState.NONE;
169 if (objectInstances.length &&
170 objectInstances[0].selectionState === SelectionState.DIMMED) {
171 selectionState = SelectionState.DIMMED;
172 }
173
174 // Dim the track when there is an active highlight.
175 if (selectionState === SelectionState.DIMMED) {
176 var width = bounds.width * pixelRatio;
177 ctx.fillStyle = 'rgba(255,255,255,0.5)';
178 ctx.fillRect(0, 0, width, height);
179 ctx.restore();
180 }
181 },
182
183 addEventsToTrackMap: function(eventToTrackMap) {
184 if (this.objectInstance_ !== undefined) {
185 this.objectInstance_.forEach(function(obj) {
186 eventToTrackMap.addEvent(obj, this);
187 }, this);
188 }
189
190 if (this.objectSnapshots_ !== undefined) {
191 this.objectSnapshots_.forEach(function(obj) {
192 eventToTrackMap.addEvent(obj, this);
193 }, this);
194 }
195 },
196
Chris Craik44c28202015-05-12 17:25:16 -0700197 addIntersectingEventsInRangeToSelectionInWorldSpace: function(
Chris Craikb122baf2015-03-05 13:58:42 -0800198 loWX, hiWX, viewPixWidthWorld, selection) {
199 // Pick snapshots first.
200 var foundSnapshot = false;
201 function onSnapshot(snapshot) {
202 selection.push(snapshot);
203 foundSnapshot = true;
204 }
205 var snapshotRadiusView = this.snapshotRadiusView;
206 var snapshotRadiusWorld = viewPixWidthWorld * snapshotRadiusView;
207 tv.b.iterateOverIntersectingIntervals(
208 this.objectSnapshots_,
209 function(x) { return x.ts - snapshotRadiusWorld; },
210 function(x) { return 2 * snapshotRadiusWorld; },
211 loWX, hiWX,
212 onSnapshot);
213 if (foundSnapshot)
214 return;
215
216 // Try picking instances.
217 tv.b.iterateOverIntersectingIntervals(
218 this.objectInstances_,
219 function(x) { return x.creationTs; },
220 function(x) { return x.deletionTs - x.creationTs; },
221 loWX, hiWX,
222 selection.push.bind(selection));
223 },
224
225 /**
226 * Add the item to the left or right of the provided event, if any, to the
227 * selection.
228 * @param {event} The current event item.
229 * @param {Number} offset Number of slices away from the event to look.
230 * @param {Selection} selection The selection to add an event to,
231 * if found.
232 * @return {boolean} Whether an event was found.
233 * @private
234 */
Chris Craik44c28202015-05-12 17:25:16 -0700235 addEventNearToProvidedEventToSelection: function(event, offset, selection) {
Chris Craikb122baf2015-03-05 13:58:42 -0800236 var events;
237 if (event instanceof tv.c.trace_model.ObjectSnapshot)
238 events = this.objectSnapshots_;
239 else if (event instanceof tv.c.trace_model.ObjectInstance)
240 events = this.objectInstances_;
241 else
242 throw new Error('Unrecognized event');
243
244 var index = events.indexOf(event);
245 var newIndex = index + offset;
246 if (newIndex >= 0 && newIndex < events.length) {
247 selection.push(events[newIndex]);
248 return true;
249 }
250 return false;
251 },
252
Chris Craik44c28202015-05-12 17:25:16 -0700253 addAllEventsMatchingFilterToSelection: function(filter, selection) {
Chris Craikb122baf2015-03-05 13:58:42 -0800254 },
255
256 addClosestEventToSelection: function(worldX, worldMaxDist, loY, hiY,
257 selection) {
258 var snapshot = tv.b.findClosestElementInSortedArray(
259 this.objectSnapshots_,
260 function(x) { return x.ts; },
261 worldX,
262 worldMaxDist);
263
264 if (!snapshot)
265 return;
266
267 selection.push(snapshot);
268
269 // TODO(egraether): Search for object instances as well, which was not
270 // implemented because it makes little sense with the current visual and
271 // needs to take care of overlapping intervals.
272 }
273 };
274
275
276 var options = new tv.b.ExtensionRegistryOptions(
277 tv.b.TYPE_BASED_REGISTRY_MODE);
278 tv.b.decorateExtensionRegistry(ObjectInstanceTrack, options);
279
280 return {
281 ObjectInstanceTrack: ObjectInstanceTrack
282 };
283});
284</script>