UNPKG

strong-arc

Version:

A visual suite for the StrongLoop API Platform

1,063 lines (967 loc) 38.8 kB
/* * Copyright (C) 2014 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @constructor * @extends {WebInspector.VBox} */ WebInspector.Layers3DView = function() { WebInspector.VBox.call(this); this.element.classList.add("layers-3d-view"); this._initStatusBar(); this._emptyView = new WebInspector.EmptyView(WebInspector.UIString("Not in the composited mode.\nConsider forcing composited mode in Settings.")); this._canvasElement = this.element.createChild("canvas"); this._canvasElement.tabIndex = 0; this._transformController = new WebInspector.TransformController(this._canvasElement); this._transformController.addEventListener(WebInspector.TransformController.Events.TransformChanged, this._update, this); this._canvasElement.addEventListener("dblclick", this._onDoubleClick.bind(this), false); this._canvasElement.addEventListener("mousedown", this._onMouseDown.bind(this), false); this._canvasElement.addEventListener("mouseup", this._onMouseUp.bind(this), false); this._canvasElement.addEventListener("mouseout", this._onMouseMove.bind(this), false); this._canvasElement.addEventListener("mousemove", this._onMouseMove.bind(this), false); this._canvasElement.addEventListener("contextmenu", this._onContextMenu.bind(this), false); this._lastActiveObject = {}; this._picturesForLayer = {}; this._scrollRectQuadsForLayer = {}; this._isVisible = {}; this._layerTree = null; this._textureManager = new WebInspector.LayerTextureManager(); this._textureManager.addEventListener(WebInspector.LayerTextureManager.Events.TextureUpdated, this._update, this); WebInspector.settings.showPaintRects.addChangeListener(this._update, this); } /** @typedef {{borderColor: !Array.<number>, borderWidth: number}} */ WebInspector.Layers3DView.LayerStyle; /** @typedef {{layerId: string, rect: !Array.<number>, snapshot: !WebInspector.PaintProfilerSnapshot, traceEvent: !WebInspector.TracingModel.Event}} */ WebInspector.Layers3DView.PaintTile; /** * @enum {string} */ WebInspector.Layers3DView.OutlineType = { Hovered: "hovered", Selected: "selected" } /** * @enum {string} */ WebInspector.Layers3DView.Events = { ObjectHovered: "ObjectHovered", ObjectSelected: "ObjectSelected", LayerSnapshotRequested: "LayerSnapshotRequested", JumpToPaintEventRequested: "JumpToPaintEventRequested" } /** * @enum {string} */ WebInspector.Layers3DView.ScrollRectTitles = { RepaintsOnScroll: WebInspector.UIString("repaints on scroll"), TouchEventHandler: WebInspector.UIString("touch event listener"), WheelEventHandler: WebInspector.UIString("mousewheel event listener") } WebInspector.Layers3DView.FragmentShader = "\ precision mediump float;\ varying vec4 vColor;\ varying vec2 vTextureCoord;\ uniform sampler2D uSampler;\ void main(void)\ {\ gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t)) * vColor;\ }"; WebInspector.Layers3DView.VertexShader = "\ attribute vec3 aVertexPosition;\ attribute vec2 aTextureCoord;\ attribute vec4 aVertexColor;\ uniform mat4 uPMatrix;\ varying vec2 vTextureCoord;\ varying vec4 vColor;\ void main(void)\ {\ gl_Position = uPMatrix * vec4(aVertexPosition, 1.0);\ vColor = aVertexColor;\ vTextureCoord = aTextureCoord;\ }"; WebInspector.Layers3DView.SelectedBackgroundColor = [20, 40, 110, 0.66]; WebInspector.Layers3DView.BackgroundColor = [0, 0, 0, 0]; WebInspector.Layers3DView.HoveredBorderColor = [0, 0, 255, 1]; WebInspector.Layers3DView.SelectedBorderColor = [0, 255, 0, 1]; WebInspector.Layers3DView.BorderColor = [0, 0, 0, 1]; WebInspector.Layers3DView.ScrollRectBackgroundColor = [178, 0, 0, 0.4]; WebInspector.Layers3DView.SelectedScrollRectBackgroundColor = [178, 0, 0, 0.6]; WebInspector.Layers3DView.ScrollRectBorderColor = [178, 0, 0, 1]; WebInspector.Layers3DView.BorderWidth = 1; WebInspector.Layers3DView.SelectedBorderWidth = 2; WebInspector.Layers3DView.LayerSpacing = 20; WebInspector.Layers3DView.ScrollRectSpacing = 4; WebInspector.Layers3DView.prototype = { /** * @param {?WebInspector.LayerTreeBase} layerTree */ setLayerTree: function(layerTree) { this._layerTree = layerTree; this._textureManager.reset(); this._update(); }, /** * @param {?Array.<!WebInspector.Layers3DView.PaintTile>} tiles */ setTiles: function(tiles) { this._textureManager.setTiles(tiles); }, /** * @param {!WebInspector.Layer} layer * @param {string=} imageURL */ showImageForLayer: function(layer, imageURL) { this._textureManager.createTexture(onTextureCreated.bind(this), imageURL); /** * @this {WebInspector.Layers3DView} * @param {!WebGLTexture} texture */ function onTextureCreated(texture) { this._layerTexture = {layerId: layer.id(), texture: texture}; this._update(); } }, onResize: function() { this._update(); }, wasShown: function() { if (this._needsUpdate) this._update(); }, /** * @param {!WebInspector.Layers3DView.OutlineType} type * @param {?WebInspector.Layers3DView.ActiveObject} activeObject */ _setOutline: function(type, activeObject) { this._lastActiveObject[type] = activeObject; this._update(); }, /** * @param {?WebInspector.Layers3DView.ActiveObject} activeObject */ hoverObject: function(activeObject) { this._setOutline(WebInspector.Layers3DView.OutlineType.Hovered, activeObject); }, /** * @param {?WebInspector.Layers3DView.ActiveObject} activeObject */ selectObject: function(activeObject) { this._setOutline(WebInspector.Layers3DView.OutlineType.Hovered, null); this._setOutline(WebInspector.Layers3DView.OutlineType.Selected, activeObject); }, /** * @param {!Element} canvas * @return {!WebGLRenderingContext} */ _initGL: function(canvas) { var gl = canvas.getContext("webgl"); gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); gl.enable(gl.BLEND); gl.clearColor(0.0, 0.0, 0.0, 0.0); gl.enable(gl.DEPTH_TEST); return gl; }, /** * @param {!Object} type * @param {string} script */ _createShader: function(type, script) { var shader = this._gl.createShader(type); this._gl.shaderSource(shader, script); this._gl.compileShader(shader); this._gl.attachShader(this._shaderProgram, shader); }, _initShaders: function() { this._shaderProgram = this._gl.createProgram(); this._createShader(this._gl.FRAGMENT_SHADER, WebInspector.Layers3DView.FragmentShader); this._createShader(this._gl.VERTEX_SHADER, WebInspector.Layers3DView.VertexShader); this._gl.linkProgram(this._shaderProgram); this._gl.useProgram(this._shaderProgram); this._shaderProgram.vertexPositionAttribute = this._gl.getAttribLocation(this._shaderProgram, "aVertexPosition"); this._gl.enableVertexAttribArray(this._shaderProgram.vertexPositionAttribute); this._shaderProgram.vertexColorAttribute = this._gl.getAttribLocation(this._shaderProgram, "aVertexColor"); this._gl.enableVertexAttribArray(this._shaderProgram.vertexColorAttribute); this._shaderProgram.textureCoordAttribute = this._gl.getAttribLocation(this._shaderProgram, "aTextureCoord"); this._gl.enableVertexAttribArray(this._shaderProgram.textureCoordAttribute); this._shaderProgram.pMatrixUniform = this._gl.getUniformLocation(this._shaderProgram, "uPMatrix"); this._shaderProgram.samplerUniform = this._gl.getUniformLocation(this._shaderProgram, "uSampler"); }, _resizeCanvas: function() { this._canvasElement.width = this._canvasElement.offsetWidth * window.devicePixelRatio; this._canvasElement.height = this._canvasElement.offsetHeight * window.devicePixelRatio; this._gl.viewportWidth = this._canvasElement.width; this._gl.viewportHeight = this._canvasElement.height; }, /** * @return {!CSSMatrix} */ _calculateProjectionMatrix: function() { var scaleFactorForMargins = 1.2; var viewport = this._layerTree.viewportSize(); var baseWidth = viewport ? viewport.width : this._layerTree.contentRoot().width(); var baseHeight = viewport ? viewport.height : this._layerTree.contentRoot().height(); var canvasWidth = this._canvasElement.width; var canvasHeight = this._canvasElement.height; var scaleX = canvasWidth / baseWidth / scaleFactorForMargins; var scaleY = canvasHeight / baseHeight / scaleFactorForMargins; var viewScale = Math.min(scaleX, scaleY); var scale = this._transformController.scale(); var offsetX = this._transformController.offsetX() * window.devicePixelRatio; var offsetY = this._transformController.offsetY() * window.devicePixelRatio; var rotateX = this._transformController.rotateX(); var rotateY = this._transformController.rotateY(); return new WebKitCSSMatrix().translate(offsetX, offsetY, 0).scale(scale, scale, scale).translate(canvasWidth / 2, canvasHeight / 2, 0) .rotate(rotateX, rotateY, 0).scale(viewScale, viewScale, viewScale).translate(-baseWidth / 2, -baseHeight / 2, 0); }, /** * @param {!CSSMatrix} m * @return {!Float32Array} */ _arrayFromMatrix: function(m) { return new Float32Array([m.m11, m.m12, m.m13, m.m14, m.m21, m.m22, m.m23, m.m24, m.m31, m.m32, m.m33, m.m34, m.m41, m.m42, m.m43, m.m44]); }, _initProjectionMatrix: function() { var projectionMatrix = this._calculateProjectionMatrix(); this._pMatrix = new WebKitCSSMatrix().scale(1, -1, -1).translate(-1, -1, 0) .scale(2 / this._canvasElement.width, 2 / this._canvasElement.height, 1 / 1000000).multiply(projectionMatrix); this._gl.uniformMatrix4fv(this._shaderProgram.pMatrixUniform, false, this._arrayFromMatrix(this._pMatrix)); this._textureScale = Math.min(1, Math.max(projectionMatrix.m11, projectionMatrix.m22)); }, _initWhiteTexture: function() { this._whiteTexture = this._gl.createTexture(); this._gl.bindTexture(this._gl.TEXTURE_2D, this._whiteTexture); var whitePixel = new Uint8Array([255, 255, 255, 255]); this._gl.texImage2D(this._gl.TEXTURE_2D, 0, this._gl.RGBA, 1, 1, 0, this._gl.RGBA, this._gl.UNSIGNED_BYTE, whitePixel); }, _initGLIfNecessary: function() { if (this._gl) return this._gl; this._gl = this._initGL(this._canvasElement); this._initShaders(); this._initWhiteTexture(); this._textureManager.setContext(this._gl); return this._gl; }, _calculateDepths: function() { this._depthByLayerId = {}; this._isVisible = {}; var depth = 0; var root = this._layerTree.root(); var queue = [root]; this._depthByLayerId[root.id()] = 0; this._isVisible[root.id()] = false; while (queue.length > 0) { var layer = queue.shift(); var children = layer.children(); for (var i = 0; i < children.length; ++i) { this._depthByLayerId[children[i].id()] = ++depth; this._isVisible[children[i].id()] = children[i] === this._layerTree.contentRoot() || this._isVisible[layer.id()]; queue.push(children[i]); } } this._maxDepth = depth; }, /** * @param {!WebInspector.Layers3DView.OutlineType} type * @param {!WebInspector.Layer} layer * @param {number=} scrollRectIndex */ _isObjectActive: function(type, layer, scrollRectIndex) { var activeObject = this._lastActiveObject[type]; return activeObject && activeObject.layer && activeObject.layer.id() === layer.id() && (typeof scrollRectIndex !== "number" || activeObject.scrollRectIndex === scrollRectIndex); }, /** * @param {!WebInspector.Layer} layer * @return {!WebInspector.Layers3DView.LayerStyle} */ _styleForLayer: function(layer) { var isSelected = this._isObjectActive(WebInspector.Layers3DView.OutlineType.Selected, layer); var isHovered = this._isObjectActive(WebInspector.Layers3DView.OutlineType.Hovered, layer); var borderColor; if (isSelected) borderColor = WebInspector.Layers3DView.SelectedBorderColor; else if (isHovered) borderColor = WebInspector.Layers3DView.HoveredBorderColor; else borderColor = WebInspector.Layers3DView.BorderColor; var borderWidth = isSelected ? WebInspector.Layers3DView.SelectedBorderWidth : WebInspector.Layers3DView.BorderWidth; return {borderColor: borderColor, borderWidth: borderWidth}; }, /** * @param {!WebInspector.Layer} layer * @return {number} */ _depthForLayer: function(layer) { return this._depthByLayerId[layer.id()] * WebInspector.Layers3DView.LayerSpacing; }, /** * @param {!WebInspector.Layer} layer * @param {number} index * @return {number} */ _calculateScrollRectDepth: function(layer, index) { return this._depthForLayer(layer) + index * WebInspector.Layers3DView.ScrollRectSpacing + 1; }, /** * @param {!WebInspector.Layer} layer */ _calculateLayerRect: function(layer) { if (!this._isVisible[layer.id()]) return; var activeObject = WebInspector.Layers3DView.ActiveObject.createLayerActiveObject(layer); var rect = new WebInspector.Layers3DView.Rectangle(activeObject); var style = this._styleForLayer(layer); rect.setVertices(layer.quad(), this._depthForLayer(layer)); rect.lineWidth = style.borderWidth; rect.borderColor = style.borderColor; this._rects.push(rect); }, /** * @param {!WebInspector.Layer} layer */ _calculateLayerScrollRects: function(layer) { var scrollRects = layer.scrollRects(); for (var i = 0; i < scrollRects.length; ++i) { var activeObject = WebInspector.Layers3DView.ActiveObject.createScrollRectActiveObject(layer, i); var rect = new WebInspector.Layers3DView.Rectangle(activeObject); rect.calculateVerticesFromRect(layer, scrollRects[i].rect, this._calculateScrollRectDepth(layer, i)); var isSelected = this._isObjectActive(WebInspector.Layers3DView.OutlineType.Selected, layer, i); var color = isSelected ? WebInspector.Layers3DView.SelectedScrollRectBackgroundColor : WebInspector.Layers3DView.ScrollRectBackgroundColor; rect.fillColor = color; rect.borderColor = WebInspector.Layers3DView.ScrollRectBorderColor; this._rects.push(rect); } }, /** * @param {!WebInspector.Layer} layer */ _calculateLayerImageRect: function(layer) { var layerTexture = this._layerTexture; if (layer.id() !== layerTexture.layerId) return; var activeObject = WebInspector.Layers3DView.ActiveObject.createLayerActiveObject(layer); var rect = new WebInspector.Layers3DView.Rectangle(activeObject); rect.setVertices(layer.quad(), this._depthForLayer(layer)); rect.texture = layerTexture.texture; this._rects.push(rect); }, /** * @param {!WebInspector.Layer} layer */ _calculateLayerTileRects: function(layer) { var tiles = this._textureManager.tilesForLayer(layer.id()); for (var i = 0; i < tiles.length; ++i) { var tile = tiles[i]; if (!tile.texture) continue; var activeObject = WebInspector.Layers3DView.ActiveObject.createTileActiveObject(layer, tile.traceEvent); var rect = new WebInspector.Layers3DView.Rectangle(activeObject); rect.calculateVerticesFromRect(layer, {x: tile.rect[0], y: tile.rect[1], width: tile.rect[2], height: tile.rect[3]}, this._depthForLayer(layer) + 1); rect.texture = tile.texture; this._rects.push(rect); } }, _calculateViewportRect: function() { var rect = new WebInspector.Layers3DView.Rectangle(null); var viewport = this._layerTree.viewportSize(); var depth = (this._maxDepth + 1) * WebInspector.Layers3DView.LayerSpacing; var vertices = [0, 0, depth, viewport.width, 0, depth, viewport.width, viewport.height, depth, 0, viewport.height, depth]; rect.vertices = vertices; rect.borderColor = [0, 0, 0, 1]; rect.lineWidth = 3; this._rects.push(rect); }, _calculateRects: function() { this._rects = []; this._layerTree.forEachLayer(this._calculateLayerRect.bind(this)); if (this._showSlowScrollRectsSetting.get()) this._layerTree.forEachLayer(this._calculateLayerScrollRects.bind(this)); if (this._showPaintsSetting.get()) { if (this._layerTexture) this._layerTree.forEachLayer(this._calculateLayerImageRect.bind(this)); else this._layerTree.forEachLayer(this._calculateLayerTileRects.bind(this)); } if (this._layerTree.viewportSize() && this._showViewportSetting.get()) this._calculateViewportRect(); }, /** * @param {!Array.<number>} color * @return {!Array.<number>} */ _makeColorsArray: function(color) { var colors = []; var normalizedColor = [color[0] / 255, color[1] / 255, color[2] / 255, color[3]]; for (var i = 0; i < 4; i++) { colors = colors.concat(normalizedColor); } return colors; }, /** * @param {!Object} attribute * @param {!Array.<number>} array * @param {!number} length */ _setVertexAttribute: function(attribute, array, length) { var gl = this._gl; var buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, buffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(array), gl.STATIC_DRAW); gl.vertexAttribPointer(attribute, length, gl.FLOAT, false, 0, 0); }, /** * @param {!Array.<number>} vertices * @param {boolean} isBorder * @param {!Array.<number>=} color * @param {!Object=} texture */ _drawRectangle: function(vertices, isBorder, color, texture) { var gl = this._gl; var glMode = isBorder ? gl.LINE_LOOP : gl.TRIANGLE_FAN; var white = [255, 255, 255, 1]; this._setVertexAttribute(this._shaderProgram.vertexPositionAttribute, vertices, 3); this._setVertexAttribute(this._shaderProgram.textureCoordAttribute, [0, 1, 1, 1, 1, 0, 0, 0], 2); if (texture) { this._setVertexAttribute(this._shaderProgram.vertexColorAttribute, this._makeColorsArray(white), white.length); gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, texture); gl.uniform1i(this._shaderProgram.samplerUniform, 0); } else { this._setVertexAttribute(this._shaderProgram.vertexColorAttribute, this._makeColorsArray(color || white), color.length); gl.bindTexture(gl.TEXTURE_2D, this._whiteTexture); } var numberOfVertices = 4; gl.drawArrays(glMode, 0, numberOfVertices); }, /** * @param {!WebInspector.Layers3DView.Rectangle} rect */ _drawViewRect: function(rect) { var vertices = rect.vertices; if (rect.texture) this._drawRectangle(vertices, false, undefined, rect.texture); else if (rect.fillColor) this._drawRectangle(vertices, false, rect.fillColor); this._gl.lineWidth(rect.lineWidth); if (rect.borderColor) this._drawRectangle(vertices, true, rect.borderColor); }, _update: function() { if (!this.isShowing()) { this._needsUpdate = true; return; } var contentRoot = this._layerTree && this._layerTree.contentRoot(); if (!contentRoot || !this._layerTree.root()) { this._emptyView.show(this.element); return; } this._emptyView.detach(); var gl = this._initGLIfNecessary(); this._resizeCanvas(); this._initProjectionMatrix(); this._calculateDepths(); this._textureManager.setScale(this._textureScale); gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); this._calculateRects(); this._rects.forEach(this._drawViewRect.bind(this)); }, /** * @param {!Event} event * @return {?WebInspector.Layers3DView.ActiveObject} */ _activeObjectFromEventPoint: function(event) { if (!this._layerTree) return null; var closestIntersectionPoint = Infinity; var closestObject = null; var projectionMatrix = new WebKitCSSMatrix().scale(1, -1, -1).translate(-1, -1, 0).multiply(this._calculateProjectionMatrix()); var x0 = (event.clientX - this._canvasElement.totalOffsetLeft()) * window.devicePixelRatio; var y0 = -(event.clientY - this._canvasElement.totalOffsetTop()) * window.devicePixelRatio; /** * @param {!WebInspector.Layers3DView.Rectangle} rect */ function checkIntersection(rect) { if (!rect.relatedObject) return; var t = rect.intersectWithLine(projectionMatrix, x0, y0); if (t < closestIntersectionPoint) { closestIntersectionPoint = t; closestObject = rect.relatedObject; } } this._rects.forEach(checkIntersection); return closestObject; }, /** * @param {string} caption * @param {string} name * @param {boolean} value * @param {!Element} statusBarElement * @return {!WebInspector.Setting} */ _createVisibilitySetting: function(caption, name, value, statusBarElement) { var checkbox = new WebInspector.StatusBarCheckbox(WebInspector.UIString(caption)) statusBarElement.appendChild(checkbox.element); var setting = WebInspector.settings.createSetting(name, value) WebInspector.SettingsUI.bindCheckbox(checkbox.inputElement, setting); setting.addChangeListener(this._update, this); return setting; }, _initStatusBar: function() { this._panelStatusBarElement = this.element.createChild("div", "panel-status-bar"); this._showViewportSetting = this._createVisibilitySetting("Viewport", "showViewport", true, this._panelStatusBarElement); this._showSlowScrollRectsSetting = this._createVisibilitySetting("Slow scroll rects", "showSlowScrollRects", true, this._panelStatusBarElement); this._showPaintsSetting = this._createVisibilitySetting("Paints", "showPaints", true, this._panelStatusBarElement); }, /** * @param {!Event} event */ _onContextMenu: function(event) { var activeObject = this._activeObjectFromEventPoint(event); var node = activeObject && activeObject.layer && activeObject.layer.nodeForSelfOrAncestor(); var contextMenu = new WebInspector.ContextMenu(event); contextMenu.appendItem(WebInspector.UIString("Reset View"), this._transformController.resetAndNotify.bind(this._transformController), false); if (activeObject && activeObject.type() === WebInspector.Layers3DView.ActiveObject.Type.Tile) contextMenu.appendItem(WebInspector.UIString("Jump to Paint Event"), this.dispatchEventToListeners.bind(this, WebInspector.Layers3DView.Events.JumpToPaintEventRequested, activeObject.traceEvent), false); if (node) contextMenu.appendApplicableItems(node); contextMenu.show(); }, /** * @param {!Event} event */ _onMouseMove: function(event) { if (event.which) return; this.dispatchEventToListeners(WebInspector.Layers3DView.Events.ObjectHovered, this._activeObjectFromEventPoint(event)); }, /** * @param {!Event} event */ _onMouseDown: function(event) { this._mouseDownX = event.clientX; this._mouseDownY = event.clientY; }, /** * @param {!Event} event */ _onMouseUp: function(event) { const maxDistanceInPixels = 6; if (this._mouseDownX && Math.abs(event.clientX - this._mouseDownX) < maxDistanceInPixels && Math.abs(event.clientY - this._mouseDownY) < maxDistanceInPixels) this.dispatchEventToListeners(WebInspector.Layers3DView.Events.ObjectSelected, this._activeObjectFromEventPoint(event)); delete this._mouseDownX; delete this._mouseDownY; }, /** * @param {!Event} event */ _onDoubleClick: function(event) { var object = this._activeObjectFromEventPoint(event); if (object) { if (object.type() == WebInspector.Layers3DView.ActiveObject.Type.Tile) this.dispatchEventToListeners(WebInspector.Layers3DView.Events.JumpToPaintEventRequested, object.traceEvent); else if (object.layer) this.dispatchEventToListeners(WebInspector.Layers3DView.Events.LayerSnapshotRequested, object.layer); } event.stopPropagation(); }, __proto__: WebInspector.VBox.prototype } /** * @constructor * @extends {WebInspector.Object} */ WebInspector.LayerTextureManager = function() { WebInspector.Object.call(this); this.reset(); } WebInspector.LayerTextureManager.Events = { TextureUpdated: "TextureUpated" } WebInspector.LayerTextureManager.prototype = { reset: function() { /** @type {!Object.<string, !Array.<!WebInspector.LayerTextureManager.Tile>>} */ this._tilesByLayerId = {}; this._scale = 0; }, /** * @param {!WebGLRenderingContext} glContext */ setContext: function(glContext) { this._gl = glContext; if (this._scale) this._updateTextures(); }, /** * @param {?Array.<!WebInspector.Layers3DView.PaintTile>} paintTiles */ setTiles: function(paintTiles) { this._tilesByLayerId = {}; if (!paintTiles) return; for (var i = 0; i < paintTiles.length; ++i) { var layerId = paintTiles[i].layerId; var tilesForLayer = this._tilesByLayerId[layerId]; if (!tilesForLayer) { tilesForLayer = []; this._tilesByLayerId[layerId] = tilesForLayer; } var tile = new WebInspector.LayerTextureManager.Tile(paintTiles[i].snapshot, paintTiles[i].rect, paintTiles[i].traceEvent); tilesForLayer.push(tile); if (this._scale && this._gl) this._updateTile(tile); } }, /** * @param {number} scale */ setScale: function(scale) { if (this._scale && this._scale >= scale) return; this._scale = scale; this._updateTextures(); }, /** * @param {string} layerId * @return {!Array.<!WebInspector.LayerTextureManager.Tile>} */ tilesForLayer: function(layerId) { return this._tilesByLayerId[layerId] || []; }, _updateTextures: function() { if (!this._gl) return; if (!this._scale) return; for (var layerId in this._tilesByLayerId) { for (var i = 0; i < this._tilesByLayerId[layerId].length; ++i) { var tile = this._tilesByLayerId[layerId][i]; if (!tile.scale || tile.scale < this._scale) this._updateTile(tile); } } }, /** * @param {!WebInspector.LayerTextureManager.Tile} tile */ _updateTile: function(tile) { console.assert(this._scale && this._gl); tile.scale = this._scale; tile.snapshot.requestImage(null, null, tile.scale, onGotImage.bind(this)); /** * @this {WebInspector.LayerTextureManager} * @param {string=} imageURL */ function onGotImage(imageURL) { this.createTexture(onTextureCreated.bind(this), imageURL); } /** * @this {WebInspector.LayerTextureManager} * @param {!WebGLTexture} texture */ function onTextureCreated(texture) { tile.texture = texture; this.dispatchEventToListeners(WebInspector.LayerTextureManager.Events.TextureUpdated); } }, /** * @param {!function(!WebGLTexture)} textureCreatedCallback * @param {string=} imageURL */ createTexture: function(textureCreatedCallback, imageURL) { var image = new Image(); image.addEventListener("load", onImageLoaded.bind(this), false); image.src = imageURL; /** * @this {WebInspector.LayerTextureManager} */ function onImageLoaded() { textureCreatedCallback(this._createTextureForImage(image)); } }, /** * @param {!Image} image * @return {!WebGLTexture} texture */ _createTextureForImage: function(image) { var texture = this._gl.createTexture(); texture.image = image; this._gl.bindTexture(this._gl.TEXTURE_2D, texture); this._gl.pixelStorei(this._gl.UNPACK_FLIP_Y_WEBGL, true); this._gl.texImage2D(this._gl.TEXTURE_2D, 0, this._gl.RGBA, this._gl.RGBA, this._gl.UNSIGNED_BYTE, texture.image); this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MIN_FILTER, this._gl.LINEAR); this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_MAG_FILTER, this._gl.LINEAR); this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_WRAP_S, this._gl.CLAMP_TO_EDGE); this._gl.texParameteri(this._gl.TEXTURE_2D, this._gl.TEXTURE_WRAP_T, this._gl.CLAMP_TO_EDGE); this._gl.bindTexture(this._gl.TEXTURE_2D, null); return texture; }, __proto__: WebInspector.Object.prototype } /** * @constructor * @param {?WebInspector.Layers3DView.ActiveObject} relatedObject */ WebInspector.Layers3DView.Rectangle = function(relatedObject) { this.relatedObject = relatedObject; /** @type {number} */ this.lineWidth = 1; /** @type {?Array.<number>} */ this.borderColor = null; /** @type {?Array.<number>} */ this.fillColor = null; /** @type {?WebGLTexture} */ this.texture = null; } WebInspector.Layers3DView.Rectangle.prototype = { /** * @param {!Array.<number>} quad * @param {number} z */ setVertices: function(quad, z) { this.vertices = [quad[0], quad[1], z, quad[2], quad[3], z, quad[4], quad[5], z, quad[6], quad[7], z]; }, /** * Finds coordinates of point on layer quad, having offsets (ratioX * width) and (ratioY * height) * from the left corner of the initial layer rect, where width and heigth are layer bounds. * @param {!Array.<number>} quad * @param {number} ratioX * @param {number} ratioY * @return {!Array.<number>} */ _calculatePointOnQuad: function(quad, ratioX, ratioY) { var x0 = quad[0]; var y0 = quad[1]; var x1 = quad[2]; var y1 = quad[3]; var x2 = quad[4]; var y2 = quad[5]; var x3 = quad[6]; var y3 = quad[7]; // Point on the first quad side clockwise var firstSidePointX = x0 + ratioX * (x1 - x0); var firstSidePointY = y0 + ratioX * (y1 - y0); // Point on the third quad side clockwise var thirdSidePointX = x3 + ratioX * (x2 - x3); var thirdSidePointY = y3 + ratioX * (y2 - y3); var x = firstSidePointX + ratioY * (thirdSidePointX - firstSidePointX); var y = firstSidePointY + ratioY * (thirdSidePointY - firstSidePointY); return [x, y]; }, /** * @param {!WebInspector.Layer} layer * @param {!DOMAgent.Rect} rect * @param {number} z */ calculateVerticesFromRect: function(layer, rect, z) { var quad = layer.quad(); var rx1 = rect.x / layer.width(); var rx2 = (rect.x + rect.width) / layer.width(); var ry1 = rect.y / layer.height(); var ry2 = (rect.y + rect.height) / layer.height(); var rectQuad = this._calculatePointOnQuad(quad, rx1, ry1).concat(this._calculatePointOnQuad(quad, rx2, ry1)) .concat(this._calculatePointOnQuad(quad, rx2, ry2)).concat(this._calculatePointOnQuad(quad, rx1, ry2)); this.setVertices(rectQuad, z); }, /** * Intersects quad with given transform matrix and line l(t) = (x0, y0, t) * @param {!CSSMatrix} matrix * @param {!number} x0 * @param {!number} y0 * @return {(number|undefined)} */ intersectWithLine: function(matrix, x0, y0) { var epsilon = 1e-8; var i; // Vertices of the quad with transform matrix applied var points = []; for (i = 0; i < 4; ++i) points[i] = WebInspector.Geometry.multiplyVectorByMatrixAndNormalize(new WebInspector.Geometry.Vector(this.vertices[i * 3], this.vertices[i * 3 + 1], this.vertices[i * 3 + 2]), matrix); // Calculating quad plane normal var normal = WebInspector.Geometry.crossProduct(WebInspector.Geometry.subtract(points[1], points[0]), WebInspector.Geometry.subtract(points[2], points[1])); // General form of the equation of the quad plane: A * x + B * y + C * z + D = 0 var A = normal.x; var B = normal.y; var C = normal.z; var D = -(A * points[0].x + B * points[0].y + C * points[0].z); // Finding t from the equation var t = -(D + A * x0 + B * y0) / C; // Point of the intersection var pt = new WebInspector.Geometry.Vector(x0, y0, t); // Vectors from the intersection point to vertices of the quad var tVects = points.map(WebInspector.Geometry.subtract.bind(null, pt)); // Intersection point lies inside of the polygon if scalar products of normal of the plane and // cross products of successive tVects are all nonstrictly above or all nonstrictly below zero for (i = 0; i < tVects.length; ++i) { var product = WebInspector.Geometry.scalarProduct(normal, WebInspector.Geometry.crossProduct(tVects[i], tVects[(i + 1) % tVects.length])); if (product < 0) return undefined; } return t; } } /** * @constructor */ WebInspector.Layers3DView.ActiveObject = function() { } /** * @enum {string} */ WebInspector.Layers3DView.ActiveObject.Type = { Layer: "Layer", ScrollRect: "ScrollRect", Tile: "Tile", }; /** * @param {!WebInspector.Layer} layer * @return {!WebInspector.Layers3DView.ActiveObject} */ WebInspector.Layers3DView.ActiveObject.createLayerActiveObject = function(layer) { var activeObject = new WebInspector.Layers3DView.ActiveObject(); activeObject._type = WebInspector.Layers3DView.ActiveObject.Type.Layer; activeObject.layer = layer; return activeObject; } /** * @param {!WebInspector.Layer} layer * @param {number} scrollRectIndex * @return {!WebInspector.Layers3DView.ActiveObject} */ WebInspector.Layers3DView.ActiveObject.createScrollRectActiveObject = function(layer, scrollRectIndex) { var activeObject = new WebInspector.Layers3DView.ActiveObject(); activeObject._type = WebInspector.Layers3DView.ActiveObject.Type.ScrollRect; activeObject.layer = layer; activeObject.scrollRectIndex = scrollRectIndex; return activeObject; } /** * @param {!WebInspector.Layer} layer * @param {!WebInspector.TracingModel.Event} traceEvent * @return {!WebInspector.Layers3DView.ActiveObject} */ WebInspector.Layers3DView.ActiveObject.createTileActiveObject = function(layer, traceEvent) { var activeObject = new WebInspector.Layers3DView.ActiveObject(); activeObject._type = WebInspector.Layers3DView.ActiveObject.Type.Tile; activeObject.layer = layer; activeObject.traceEvent = traceEvent; return activeObject; } WebInspector.Layers3DView.ActiveObject.prototype = { /** * @return {!WebInspector.Layers3DView.ActiveObject.Type} */ type: function() { return this._type; } }; /** * @constructor * @param {!WebInspector.PaintProfilerSnapshot} snapshot * @param {!Array.<number>} rect * @param {!WebInspector.TracingModel.Event} traceEvent */ WebInspector.LayerTextureManager.Tile = function(snapshot, rect, traceEvent) { this.snapshot = snapshot; this.rect = rect; this.traceEvent = traceEvent; this.scale = 0; /** @type {?WebGLTexture} */ this.texture = null; }