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