diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/devtools/front_end/timeline/Layers3DView.js')
-rw-r--r-- | chromium/third_party/WebKit/Source/devtools/front_end/timeline/Layers3DView.js | 750 |
1 files changed, 750 insertions, 0 deletions
diff --git a/chromium/third_party/WebKit/Source/devtools/front_end/timeline/Layers3DView.js b/chromium/third_party/WebKit/Source/devtools/front_end/timeline/Layers3DView.js new file mode 100644 index 00000000000..90dd25d0be6 --- /dev/null +++ b/chromium/third_party/WebKit/Source/devtools/front_end/timeline/Layers3DView.js @@ -0,0 +1,750 @@ +/* + * 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._emptyView = new WebInspector.EmptyView(WebInspector.UIString("Not in the composited mode.\nConsider forcing composited mode in Settings.")); + this._canvasElement = this.element.createChild("canvas"); + 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; + WebInspector.settings.showPaintRects.addChangeListener(this._update, this); +} + +/** @typedef {{layer: !WebInspector.Layer, scrollRectIndex: number}|{layer: !WebInspector.Layer}} */ +WebInspector.Layers3DView.ActiveObject; + +/** @typedef {{color: !Array.<number>, borderColor: !Array.<number>, borderWidth: number}} */ +WebInspector.Layers3DView.LayerStyle; + +/** @typedef {{layerId: string, rect: !Array.<number>, imageURL: string}} */ +WebInspector.Layers3DView.Tile; + +/** + * @enum {string} + */ +WebInspector.Layers3DView.OutlineType = { + Hovered: "hovered", + Selected: "selected" +} + +/** + * @enum {string} + */ +WebInspector.Layers3DView.Events = { + ObjectHovered: "ObjectHovered", + ObjectSelected: "ObjectSelected", + LayerSnapshotRequested: "LayerSnapshotRequested" +} + +/** + * @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 {function(!Array.<!WebInspector.KeyboardShortcut.Descriptor>, function(?Event=))} registerShortcutDelegate + */ + registerShortcuts: function(registerShortcutDelegate) + { + this._transformController.registerShortcuts(registerShortcutDelegate); + }, + + onResize: function() + { + this._update(); + }, + + willHide: function() + { + }, + + 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 {!WebInspector.Layer} layer + * @param {string=} imageURL + */ + showImageForLayer: function(layer, imageURL) + { + this.setTiles([{layerId: layer.id(), rect: [0, 0, layer.width(), layer.height()], imageURL: imageURL}]) + }, + + /** + * @param {!Array.<!WebInspector.Layers3DView.Tile>} tiles + */ + setTiles: function(tiles) + { + this._picturesForLayer = {}; + tiles.forEach(this._setTile, this); + }, + + /** + * @param {!WebInspector.Layers3DView.Tile} tile + */ + _setTile: function(tile) + { + var texture = this._gl.createTexture(); + texture.image = new Image(); + texture.image.addEventListener("load", this._handleLoadedTexture.bind(this, texture, tile.layerId, tile.rect), false); + texture.image.src = tile.imageURL; + }, + + /** + * @param {!Element} canvas + * @return {!Object} + */ + _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); + }, + + /** + * @param {string} attributeName + * @param {string} glName + */ + _enableVertexAttribArray: function(attributeName, glName) + { + this._shaderProgram[attributeName] = this._gl.getAttribLocation(this._shaderProgram, glName); + this._gl.enableVertexAttribArray(this._shaderProgram[attributeName]); + }, + + _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); + }, + + _initProjectionMatrix: function() + { + this._pMatrix = new WebKitCSSMatrix().scale(1, -1, -1).translate(-1, -1, 0) + .scale(2 / this._canvasElement.width, 2 / this._canvasElement.height, 1 / 1000000).multiply(this._calculateProjectionMatrix()); + this._gl.uniformMatrix4fv(this._shaderProgram.pMatrixUniform, false, this._arrayFromMatrix(this._pMatrix)); + }, + + /** + * @param {!Object} texture + * @param {string} layerId + * @param {!Array.<number>} rect + */ + _handleLoadedTexture: function(texture, layerId, rect) + { + 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); + if (!this._picturesForLayer[layerId]) + this._picturesForLayer[layerId] = []; + this._picturesForLayer[layerId].push({texture: texture, rect: rect}); + this._update(); + }, + + _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(); + return this._gl; + }, + + /** + * @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]); + }, + + /** + * @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 {!Array.<number>} color + * @param {!Object} glMode + * @param {!Object=} texture + */ + _drawRectangle: function(vertices, color, glMode, texture) + { + this._setVertexAttribute(this._shaderProgram.vertexPositionAttribute, vertices, 3); + this._setVertexAttribute(this._shaderProgram.textureCoordAttribute, [0, 1, 1, 1, 1, 0, 0, 0], 2); + + if (texture) { + var white = [255, 255, 255, 1]; + this._setVertexAttribute(this._shaderProgram.vertexColorAttribute, this._makeColorsArray(white), white.length); + this._gl.activeTexture(this._gl.TEXTURE0); + this._gl.bindTexture(this._gl.TEXTURE_2D, texture); + this._gl.uniform1i(this._shaderProgram.samplerUniform, 0); + } else { + this._setVertexAttribute(this._shaderProgram.vertexColorAttribute, this._makeColorsArray(color), color.length); + this._gl.bindTexture(this._gl.TEXTURE_2D, this._whiteTexture); + } + + var numberOfVertices = 4; + this._gl.drawArrays(glMode, 0, numberOfVertices); + }, + + /** + * @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 color = isSelected ? WebInspector.Layers3DView.SelectedBackgroundColor : WebInspector.Layers3DView.BackgroundColor; + 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 {color: color, borderColor: borderColor, borderWidth: borderWidth}; + }, + + /** + * @param {!Array.<number>} quad + * @param {number} z + * @return {!Array.<number>} + */ + _calculateVerticesForQuad: function(quad, z) + { + return [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 + * @return {!Array.<number>} + */ + _calculateRectQuad: function(layer, rect) + { + 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(); + return this._calculatePointOnQuad(quad, rx1, ry1).concat(this._calculatePointOnQuad(quad, rx2, ry1)) + .concat(this._calculatePointOnQuad(quad, rx2, ry2)).concat(this._calculatePointOnQuad(quad, rx1, ry2)); + }, + + /** + * @param {!WebInspector.Layer} layer + * @return {!Array.<!Array.<number>>} + */ + _calculateScrollRectQuadsForLayer: function(layer) + { + var quads = []; + for (var i = 0; i < layer.scrollRects().length; ++i) + quads.push(this._calculateRectQuad(layer, layer.scrollRects()[i].rect)); + return quads; + }, + + /** + * @param {!WebInspector.Layer} layer + * @param {number} index + * @return {number} + */ + _calculateScrollRectDepth: function(layer, index) + { + return this._depthByLayerId[layer.id()] * WebInspector.Layers3DView.LayerSpacing + index * WebInspector.Layers3DView.ScrollRectSpacing + 1; + }, + + /** + * @param {!WebInspector.Layer} layer + */ + _drawLayer: function(layer) + { + var gl = this._gl; + var vertices; + var style = this._styleForLayer(layer); + var layerDepth = this._depthByLayerId[layer.id()] * WebInspector.Layers3DView.LayerSpacing; + if (this._isVisible[layer.id()]) { + vertices = this._calculateVerticesForQuad(layer.quad(), layerDepth); + gl.lineWidth(style.borderWidth); + this._drawRectangle(vertices, style.borderColor, gl.LINE_LOOP); + gl.lineWidth(1); + } + this._scrollRectQuadsForLayer[layer.id()] = this._calculateScrollRectQuadsForLayer(layer); + var scrollRectQuads = this._scrollRectQuadsForLayer[layer.id()]; + for (var i = 0; i < scrollRectQuads.length; ++i) { + vertices = this._calculateVerticesForQuad(scrollRectQuads[i], this._calculateScrollRectDepth(layer, i)); + var isSelected = this._isObjectActive(WebInspector.Layers3DView.OutlineType.Selected, layer, i); + var color = isSelected ? WebInspector.Layers3DView.SelectedScrollRectBackgroundColor : WebInspector.Layers3DView.ScrollRectBackgroundColor; + this._drawRectangle(vertices, color, gl.TRIANGLE_FAN); + this._drawRectangle(vertices, WebInspector.Layers3DView.ScrollRectBorderColor, gl.LINE_LOOP); + } + var tiles = this._picturesForLayer[layer.id()] || []; + for (var i = 0; i < tiles.length; ++i) { + var tile = tiles[i]; + var quad = this._calculateRectQuad(layer, {x: tile.rect[0], y: tile.rect[1], width: tile.rect[2] - tile.rect[0], height: tile.rect[3] - tile.rect[1]}); + vertices = this._calculateVerticesForQuad(quad, layerDepth); + this._drawRectangle(vertices, style.color, gl.TRIANGLE_FAN, tile.texture); + } + }, + + _drawViewport: function() + { + var viewport = this._layerTree.viewportSize(); + var vertices = [0, 0, 0, viewport.width, 0, 0, viewport.width, viewport.height, 0, 0, viewport.height, 0]; + var color = [0, 0, 0, 1]; + this._gl.lineWidth(3.0); + this._drawRectangle(vertices, color, this._gl.LINE_LOOP); + this._gl.lineWidth(1.0); + }, + + _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]); + } + } + }, + + + /** + * @param {?WebInspector.LayerTreeBase} layerTree + */ + setLayerTree: function(layerTree) + { + this._layerTree = layerTree; + this._update(); + }, + + _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(); + + gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + + if (this._layerTree.viewportSize()) + this._drawViewport(); + this._layerTree.forEachLayer(this._drawLayer.bind(this)); + }, + + /** + * Intersects quad with given transform matrix and line l(t) = (x0, y0, t) + * @param {!Array.<number>} vertices + * @param {!CSSMatrix} matrix + * @param {!number} x0 + * @param {!number} y0 + * @return {(number|undefined)} + */ + _intersectLineAndRect: function(vertices, 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(vertices[i * 3], vertices[i * 3 + 1], 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; + }, + + /** + * @param {?Event} event + * @return {?WebInspector.Layers3DView.ActiveObject} + */ + _activeObjectFromEventPoint: function(event) + { + if (!this._layerTree) + return null; + var closestIntersectionPoint = Infinity; + var closestLayer = 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.Layer} layer + * @this {WebInspector.Layers3DView} + */ + function checkIntersection(layer) + { + var t; + if (this._isVisible[layer.id()]) { + t = this._intersectLineAndRect(this._calculateVerticesForQuad(layer.quad(), this._depthByLayerId[layer.id()] * WebInspector.Layers3DView.LayerSpacing), projectionMatrix, x0, y0); + if (t < closestIntersectionPoint) { + closestIntersectionPoint = t; + closestLayer = {layer: layer}; + } + } + var scrollRectQuads = this._scrollRectQuadsForLayer[layer.id()]; + for (var i = 0; i < scrollRectQuads.length; ++i) { + t = this._intersectLineAndRect(this._calculateVerticesForQuad(scrollRectQuads[i], this._calculateScrollRectDepth(layer, i)), projectionMatrix, x0, y0); + if (t < closestIntersectionPoint) { + closestIntersectionPoint = t; + closestLayer = {layer: layer, scrollRectIndex: i}; + } + } + } + + this._layerTree.forEachLayer(checkIntersection.bind(this)); + return closestLayer; + }, + + /** + * @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("Reset view", this._transformController._resetAndNotify.bind(this._transformController), 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 && object.layer) + this.dispatchEventToListeners(WebInspector.Layers3DView.Events.LayerSnapshotRequested, object.layer); + event.stopPropagation(); + }, + + __proto__: WebInspector.VBox.prototype +} |