Browse Source

Merge branch 'master' of https://github.com/molstar/molstar into input-controls

Alexander Rose 2 years ago
parent
commit
00ff1a1eae

+ 12 - 4
CHANGELOG.md

@@ -6,16 +6,24 @@ Note that since we don't clearly distinguish between a public and private interf
 
 ## [Unreleased]
 
+- Input/controls improvements
+    - Move or fly around the scene using keys
+    - Pointer lock to look around scene
+    - Toggle spin/rock animation using keys
+
+## [v3.32.0] - 2023-03-20
+
+- Avoid rendering of fully transparent renderables
 - Add occlusion color parameter
 - Fix issue with outlines and orthographic camera
 - Reduce over-blurring occlusion at larger view distances
 - Fix occlusion artefact with non-canvas viewport and pixel-ratio > 1
 - Update nodejs-shims conditionals to handle polyfilled document object in NodeJS environment.
 - Ensure marking edges are at least one pixel wide
-- Input/controls improvements
-    - Move or fly around the scene using keys
-    - Pointer lock to look around scene
-    - Toggle spin/rock animation using keys
+- Add exposure parameter to renderer
+- Only trigger marking when mouse is directly over canvas
+- Fix blurry occlusion in screenshots
+- [Breaking] Add `setFSModule` to `mol-util/data-source` instead of trying to trick WebPack
 
 ## [v3.31.4] - 2023-02-24
 

File diff suppressed because it is too large
+ 231 - 291
package-lock.json


+ 20 - 20
package.json

@@ -1,6 +1,6 @@
 {
   "name": "molstar",
-  "version": "3.31.4",
+  "version": "3.32.0",
   "description": "A comprehensive macromolecular library.",
   "homepage": "https://github.com/molstar/molstar#readme",
   "repository": {
@@ -102,46 +102,46 @@
   "license": "MIT",
   "devDependencies": {
     "@graphql-codegen/add": "^4.0.1",
-    "@graphql-codegen/cli": "^3.2.1",
+    "@graphql-codegen/cli": "^3.2.2",
     "@graphql-codegen/time": "^4.0.0",
-    "@graphql-codegen/typescript": "^3.0.1",
+    "@graphql-codegen/typescript": "^3.0.2",
     "@graphql-codegen/typescript-graphql-files-modules": "^2.2.1",
     "@graphql-codegen/typescript-graphql-request": "^4.5.8",
-    "@graphql-codegen/typescript-operations": "^3.0.1",
+    "@graphql-codegen/typescript-operations": "^3.0.2",
     "@types/cors": "^2.8.13",
     "@types/gl": "^6.0.2",
     "@types/jpeg-js": "^0.3.7",
     "@types/pngjs": "^6.0.1",
-    "@types/jest": "^29.4.0",
+    "@types/jest": "^29.5.0",
     "@types/react": "^18.0.28",
     "@types/react-dom": "^18.0.11",
-    "@typescript-eslint/eslint-plugin": "^5.53.0",
-    "@typescript-eslint/parser": "^5.53.0",
+    "@typescript-eslint/eslint-plugin": "^5.55.0",
+    "@typescript-eslint/parser": "^5.55.0",
     "benchmark": "^2.1.4",
     "concurrently": "^7.6.0",
-    "cpx2": "^4.2.0",
+    "cpx2": "^4.2.2",
     "crypto-browserify": "^3.12.0",
     "css-loader": "^6.7.3",
-    "eslint": "^8.34.0",
+    "eslint": "^8.36.0",
     "extra-watch-webpack-plugin": "^1.0.3",
     "file-loader": "^6.2.0",
     "fs-extra": "^11.1.0",
     "graphql": "^16.6.0",
     "http-server": "^14.1.1",
-    "jest": "^29.4.3",
-    "mini-css-extract-plugin": "^2.7.2",
+    "jest": "^29.5.0",
+    "mini-css-extract-plugin": "^2.7.5",
     "path-browserify": "^1.0.1",
     "raw-loader": "^4.0.2",
     "react": "^18.2.0",
     "react-dom": "^18.2.0",
-    "sass": "^1.58.3",
-    "sass-loader": "^13.2.0",
-    "simple-git": "^3.16.1",
+    "sass": "^1.59.3",
+    "sass-loader": "^13.2.1",
+    "simple-git": "^3.17.0",
     "stream-browserify": "^3.0.0",
-    "style-loader": "^3.3.1",
+    "style-loader": "^3.3.2",
     "ts-jest": "^29.0.5",
-    "typescript": "^4.9.5",
-    "webpack": "^5.75.0",
+    "typescript": "^5.0.2",
+    "webpack": "^5.76.2",
     "webpack-cli": "^5.0.1"
   },
   "dependencies": {
@@ -149,7 +149,7 @@
     "@types/benchmark": "^2.1.2",
     "@types/compression": "1.7.2",
     "@types/express": "^4.17.17",
-    "@types/node": "^16.18.12",
+    "@types/node": "^16.18.16",
     "@types/node-fetch": "^2.6.2",
     "@types/swagger-ui-dist": "3.30.1",
     "argparse": "^2.0.1",
@@ -159,10 +159,10 @@
     "express": "^4.18.2",
     "h264-mp4-encoder": "^1.0.12",
     "immer": "^9.0.19",
-    "immutable": "^4.2.4",
+    "immutable": "^4.3.0",
     "node-fetch": "^2.6.9",
     "rxjs": "^7.8.0",
-    "swagger-ui-dist": "^4.16.1",
+    "swagger-ui-dist": "^4.18.1",
     "tslib": "^2.5.0",
     "util.promisify": "^1.1.1",
     "xhr2": "^0.2.1"

+ 3 - 0
src/examples/image-renderer/index.ts

@@ -19,8 +19,11 @@ import { StructureRepresentation3D } from '../../mol-plugin-state/transforms/rep
 import { HeadlessPluginContext } from '../../mol-plugin/headless-plugin-context';
 import { DefaultPluginSpec } from '../../mol-plugin/spec';
 import { STYLIZED_POSTPROCESSING } from '../../mol-plugin/util/headless-screenshot';
+import { setFSModule } from '../../mol-util/data-source';
 
 
+setFSModule(fs);
+
 interface Args {
     pdbId: string,
     outDirectory: string

+ 1 - 2
src/extensions/zenodo/ui.tsx

@@ -202,7 +202,7 @@ export class ZenodoImportUI extends CollapsableControls<{}, State> {
                 }));
             } else if (t.name === 'trajectory') {
                 const [topologyUrl, topologyFormat, topologyIsBinary] = t.params.topology.split('|');
-                const [coordinatesUrl, coordinatesFormat, coordinatesIsBinary] = t.params.coordinates.split('|');
+                const [coordinatesUrl, coordinatesFormat] = t.params.coordinates.split('|');
 
                 await this.plugin.runTask(this.plugin.state.data.applyAction(LoadTrajectory, {
                     source: {
@@ -216,7 +216,6 @@ export class ZenodoImportUI extends CollapsableControls<{}, State> {
                             coordinates: {
                                 url: coordinatesUrl,
                                 format: coordinatesFormat as any,
-                                isBinary: coordinatesIsBinary === 'true',
                             },
                         }
                     }

+ 5 - 1
src/mol-canvas3d/helper/interaction-events.ts

@@ -197,8 +197,12 @@ export class Canvas3dInteractionHelper {
             this.drag(x, y, buttons, button, modifiers);
         });
 
-        input.move.subscribe(({ x, y, inside, buttons, button, modifiers }) => {
+        input.move.subscribe(({ x, y, inside, buttons, button, modifiers, onElement }) => {
             if (!inside || this.isInteracting) return;
+            if (!onElement) {
+                this.leave();
+                return;
+            }
             // console.log('move');
             this.move(x, y, buttons, button, modifiers);
         });

+ 19 - 19
src/mol-gl/_spec/gl.shim.ts

@@ -16,7 +16,7 @@ const c = {
 
     MAX_TEXTURE_MAX_ANISOTROPY_EXT: 0x84FF,
     MAX_TEXTURE_IMAGE_UNITS_NV: 0x8872
-};
+} as const;
 
 const gl = {
     ACTIVE_ATTRIBUTES: 35721,
@@ -316,7 +316,7 @@ const gl = {
     VERTEX_SHADER: 35633,
     VIEWPORT: 2978,
     ZERO: 0
-};
+} as const;
 type gl = typeof gl
 
 export function createGl(width: number, height: number, contextAttributes: WebGLContextAttributes): WebGLRenderingContext {
@@ -371,66 +371,66 @@ export function createGl(width: number, height: number, contextAttributes: WebGL
                 case 'EXT_blend_minmax': return {
                     MAX_EXT: 0,
                     MIN_EXT: 0
-                } as EXT_blend_minmax;
+                } as unknown as EXT_blend_minmax;
                 case 'EXT_texture_filter_anisotropic': return {
                     MAX_TEXTURE_MAX_ANISOTROPY_EXT: 0,
                     TEXTURE_MAX_ANISOTROPY_EXT: 0
-                } as EXT_texture_filter_anisotropic;
+                } as unknown as EXT_texture_filter_anisotropic;
                 case 'EXT_frag_depth': return {} as EXT_frag_depth;
-                case 'EXT_shader_texture_lod': return {} as EXT_shader_texture_lod;
+                case 'EXT_shader_texture_lod': return {} as unknown as EXT_shader_texture_lod;
                 case 'EXT_sRGB': return {
                     FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT: 0,
                     SRGB8_ALPHA8_EXT: 0,
                     SRGB_ALPHA_EXT: 0,
                     SRGB_EXT: 0
-                } as EXT_sRGB;
+                } as unknown as EXT_sRGB;
                 case 'OES_vertex_array_object': return {
                     VERTEX_ARRAY_BINDING_OES: 0,
                     bindVertexArrayOES: function (arrayObject: WebGLVertexArrayObjectOES) { },
                     createVertexArrayOES: function (): WebGLVertexArrayObjectOES { return {}; },
                     deleteVertexArrayOES: function (arrayObject: WebGLVertexArrayObjectOES) { },
                     isVertexArrayOES: function (value: any) { return true; }
-                } as OES_vertex_array_object;
+                } as unknown as OES_vertex_array_object;
                 case 'WEBGL_color_buffer_float': return {
                     FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT: 0,
                     RGB32F_EXT: 0,
                     RGBA32F_EXT: 0,
                     UNSIGNED_NORMALIZED_EXT: 0
-                } as WEBGL_color_buffer_float;
+                } as unknown as WEBGL_color_buffer_float;
                 case 'WEBGL_compressed_texture_astc': return null;
                 case 'WEBGL_compressed_texture_s3tc_srgb': return null;
                 case 'WEBGL_debug_shaders': return {
                     getTranslatedShaderSource(shader: WebGLShader) { return ''; }
-                } as WEBGL_debug_shaders;
+                } as unknown as WEBGL_debug_shaders;
                 case 'WEBGL_draw_buffers': return null;
                 case 'WEBGL_lose_context': return {
                     loseContext: function () { },
                     restoreContext: function () { },
-                } as WEBGL_lose_context;
+                } as unknown as WEBGL_lose_context;
                 case 'WEBGL_depth_texture': return {
                     UNSIGNED_INT_24_8_WEBGL: 0
-                } as WEBGL_depth_texture;
+                } as unknown as WEBGL_depth_texture;
                 case 'WEBGL_debug_renderer_info': return {
                     UNMASKED_RENDERER_WEBGL: 0,
                     UNMASKED_VENDOR_WEBGL: 0
-                } as WEBGL_debug_renderer_info;
+                } as unknown as WEBGL_debug_renderer_info;
                 case 'WEBGL_compressed_texture_s3tc': return null;
-                case 'OES_texture_half_float_linear': return {} as OES_texture_half_float_linear;
+                case 'OES_texture_half_float_linear': return {} as unknown as OES_texture_half_float_linear;
                 case 'OES_texture_half_float': return {
                     HALF_FLOAT_OES: 0
-                } as OES_texture_half_float;
-                case 'OES_texture_float_linear': return {} as OES_texture_float_linear;
-                case 'OES_texture_float': return {} as OES_texture_float;
+                } as unknown as OES_texture_half_float;
+                case 'OES_texture_float_linear': return {} as unknown as OES_texture_float_linear;
+                case 'OES_texture_float': return {} as unknown as OES_texture_float;
                 case 'OES_standard_derivatives': return {
                     FRAGMENT_SHADER_DERIVATIVE_HINT_OES: 0
-                } as OES_standard_derivatives;
-                case 'OES_element_index_uint': return {} as OES_element_index_uint;
+                } as unknown as OES_standard_derivatives;
+                case 'OES_element_index_uint': return {} as unknown as OES_element_index_uint;
                 case 'ANGLE_instanced_arrays': return {
                     drawArraysInstancedANGLE: function (mode: number, first: number, count: number, primcount: number) {},
                     drawElementsInstancedANGLE: function (mode: number, count: number, type: number, offset: number, primcount: number) {},
                     vertexAttribDivisorANGLE: function (index: number, divisor: number) {},
                     VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE: 0
-                } as ANGLE_instanced_arrays;
+                } as unknown as ANGLE_instanced_arrays;
             }
             return null;
         },

+ 1 - 0
src/mol-gl/renderable/schema.ts

@@ -160,6 +160,7 @@ export const GlobalUniformSchema = {
     uMarkerAverage: UniformSpec('f'),
 
     uXrayEdgeFalloff: UniformSpec('f'),
+    uExposure: UniformSpec('f'),
 
     uRenderMask: UniformSpec('i'),
     uMarkingDepthTest: UniformSpec('b'),

+ 8 - 2
src/mol-gl/renderer.ts

@@ -104,6 +104,7 @@ export const RendererParams = {
     markerPriority: PD.Select(1, [[1, 'Highlight'], [2, 'Select']]),
 
     xrayEdgeFalloff: PD.Numeric(1, { min: 0.0, max: 3.0, step: 0.1 }),
+    exposure: PD.Numeric(1, { min: 0.0, max: 3.0, step: 0.01 }),
 
     light: PD.ObjectList({
         inclination: PD.Numeric(150, { min: 0, max: 180, step: 1 }),
@@ -242,6 +243,7 @@ namespace Renderer {
             uMarkerAverage: ValueCell.create(0),
 
             uXrayEdgeFalloff: ValueCell.create(p.xrayEdgeFalloff),
+            uExposure: ValueCell.create(p.exposure),
         };
         const globalUniformList = Object.entries(globalUniforms);
 
@@ -607,7 +609,7 @@ namespace Renderer {
                 // TODO: simplify, handle in renderable.state???
                 // uAlpha is updated in "render" so we need to recompute it here
                 const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1);
-                if (alpha < 1 || r.values.transparencyAverage.ref.value > 0 || r.values.dGeometryType.ref.value === 'directVolume' || r.values.dPointStyle?.ref.value === 'fuzzy' || r.values.dGeometryType.ref.value === 'text' || r.values.dXrayShaded?.ref.value) {
+                if ((alpha < 1 && alpha !== 0) || r.values.transparencyAverage.ref.value > 0 || r.values.dGeometryType.ref.value === 'directVolume' || r.values.dPointStyle?.ref.value === 'fuzzy' || r.values.dGeometryType.ref.value === 'text' || r.values.dXrayShaded?.ref.value) {
                     renderObject(r, 'colorWboit', Flag.None);
                 }
             }
@@ -655,7 +657,7 @@ namespace Renderer {
                 // TODO: simplify, handle in renderable.state???
                 // uAlpha is updated in "render" so we need to recompute it here
                 const alpha = clamp(r.values.alpha.ref.value * r.state.alphaFactor, 0, 1);
-                if (alpha < 1 || r.values.transparencyAverage.ref.value > 0 || r.values.dPointStyle?.ref.value === 'fuzzy' || !!r.values.uBackgroundColor || r.values.dXrayShaded?.ref.value) {
+                if ((alpha < 1 && alpha !== 0) || r.values.transparencyAverage.ref.value > 0 || r.values.dPointStyle?.ref.value === 'fuzzy' || r.values.dGeometryType.ref.value === 'text' || r.values.dXrayShaded?.ref.value) {
                     renderObject(r, 'colorDpoit', Flag.None);
                 }
             }
@@ -787,6 +789,10 @@ namespace Renderer {
                     p.xrayEdgeFalloff = props.xrayEdgeFalloff;
                     ValueCell.update(globalUniforms.uXrayEdgeFalloff, p.xrayEdgeFalloff);
                 }
+                if (props.exposure !== undefined && props.exposure !== p.exposure) {
+                    p.exposure = props.exposure;
+                    ValueCell.update(globalUniforms.uExposure, p.exposure);
+                }
 
                 if (props.light !== undefined && !deepEqual(props.light, p.light)) {
                     p.light = props.light;

+ 3 - 1
src/mol-gl/shader/chunks/apply-light-color.glsl.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2017-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  *
@@ -65,4 +65,6 @@ export const apply_light_color = `
 #ifdef dXrayShaded
     gl_FragColor.a *= 1.0 - pow(abs(dot(normal, vec3(0.0, 0.0, 1.0))), uXrayEdgeFalloff);
 #endif
+
+gl_FragColor.rgb *= uExposure;
 `;

+ 1 - 0
src/mol-gl/shader/chunks/common-frag-params.glsl.ts

@@ -72,6 +72,7 @@ uniform vec3 uInteriorColor;
 bool interior;
 
 uniform float uXrayEdgeFalloff;
+uniform float uExposure;
 
 uniform mat4 uProjection;
 

+ 1 - 0
src/mol-gl/shader/direct-volume.frag.ts

@@ -75,6 +75,7 @@ uniform vec3 uFogColor;
 uniform float uAlpha;
 uniform bool uTransparentBackground;
 uniform float uXrayEdgeFalloff;
+uniform float uExposure;
 
 uniform int uRenderMask;
 

+ 1 - 1
src/mol-plugin-state/transforms/data.ts

@@ -282,7 +282,7 @@ const ParseCif = PluginStateTransform.BuiltIn({
 })({
     apply({ a }) {
         return Task.create('Parse CIF', async ctx => {
-            const parsed = await (SO.Data.String.is(a) ? CIF.parse(a.data) : CIF.parseBinary(a.data)).runInContext(ctx);
+            const parsed = await (typeof a.data === 'string' ? CIF.parse(a.data) : CIF.parseBinary(a.data)).runInContext(ctx);
             if (parsed.isError) throw new Error(parsed.message);
             return new SO.Format.Cif(parsed.result);
         });

+ 2 - 7
src/mol-plugin-ui/controls.tsx

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -240,14 +240,9 @@ export class SelectionViewportControls extends PluginUIComponent {
         this.subscribe(this.plugin.behaviors.interaction.selectionMode, () => this.forceUpdate());
     }
 
-    onMouseMove = (e: React.MouseEvent) => {
-        // ignore mouse moves when no button is held
-        if (e.buttons === 0) e.stopPropagation();
-    };
-
     render() {
         if (!this.plugin.selectionMode) return null;
-        return <div className='msp-selection-viewport-controls' onMouseMove={this.onMouseMove}>
+        return <div className='msp-selection-viewport-controls'>
             <StructureSelectionActionsControls />
         </div>;
     }

+ 2 - 2
src/mol-plugin-ui/controls/slider.tsx

@@ -626,9 +626,9 @@ export class SliderBase extends React.Component<SliderBaseProps, SliderBaseState
         const value = bounds[handle];
 
         let direction = 0;
-        if (bounds[handle + 1] - value < threshold!) {
+        if (bounds[handle + 1] - value < +threshold!) {
             direction = +1;
-        } else if (value - bounds[handle - 1] < threshold!) {
+        } else if (value - bounds[handle - 1] < +threshold!) {
             direction = -1;
         }
 

+ 2 - 7
src/mol-plugin-ui/viewport.tsx

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author David Sehnal <david.sehnal@gmail.com>
@@ -76,13 +76,8 @@ export class ViewportControls extends PluginUIComponent<ViewportControlsProps, V
         return <IconButton svg={icon} toggleState={isOn} onClick={onClick} title={title} style={{ background: 'transparent' }} />;
     }
 
-    onMouseMove = (e: React.MouseEvent) => {
-        // ignore mouse moves when no button is held
-        if (e.buttons === 0) e.stopPropagation();
-    };
-
     render() {
-        return <div className={'msp-viewport-controls'} onMouseMove={this.onMouseMove}>
+        return <div className={'msp-viewport-controls'}>
             <div className='msp-viewport-controls-buttons'>
                 <div>
                     <div className='msp-semi-transparent-background' />

+ 2 - 2
src/mol-plugin/util/viewport-screenshot.ts

@@ -119,7 +119,7 @@ class ViewportScreenshotHelper extends PluginComponent {
             postprocessing: {
                 ...c.props.postprocessing,
                 occlusion: aoProps.name === 'on'
-                    ? { name: 'on', params: { ...aoProps.params, samples: 128, resolutionScale: 1 } }
+                    ? { name: 'on', params: { ...aoProps.params, samples: 128, resolutionScale: c.webgl.pixelRatio } }
                     : aoProps
             },
             marking: { ...c.props.marking }
@@ -143,7 +143,7 @@ class ViewportScreenshotHelper extends PluginComponent {
                 postprocessing: {
                     ...c.props.postprocessing,
                     occlusion: aoProps.name === 'on'
-                        ? { name: 'on', params: { ...aoProps.params, samples: 128, resolutionScale: 1 } }
+                        ? { name: 'on', params: { ...aoProps.params, samples: 128, resolutionScale: c.webgl.pixelRatio } }
                         : aoProps
                 },
                 marking: { ...c.props.marking }

+ 1 - 1
src/mol-state/object.ts

@@ -35,7 +35,7 @@ namespace StateObject {
     export function create<Data, T extends Type>(type: T) {
         return class O implements StateObject<Data, T> {
             static type = type;
-            static is(obj?: StateObject): obj is O { return !!obj && type === obj.type; }
+            static is(obj?: StateObject): obj is StateObject<Data, T> { return !!obj && type === obj.type; }
             id = UUID.create22();
             type = type;
             label: string;

+ 9 - 6
src/mol-util/data-source.ts

@@ -300,14 +300,17 @@ function ajaxGetInternal<T extends DataType>(title: string | undefined, url: str
     });
 }
 
-// NOTE: lazy imports cannot be used here because WebPack complains since this
-// is part of the "browser" build.
+// NOTE: a workaround for using this in Node.js
 let _fs: (typeof import ('fs')) | undefined = undefined;
 function getFS() {
-    if (_fs) return _fs!;
-    const req = require; // To fool webpack
-    _fs = req('fs');
-    return _fs!;
+    if (!_fs) {
+        throw new Error('When running in Node.js and reading from files, call mol-util/data-source\'s setFSModule function first.');
+    }
+    return _fs;
+}
+
+export function setFSModule(fs: typeof import ('fs')) {
+    _fs = fs;
 }
 
 /** Alternative implementation of ajaxGetInternal (because xhr2 does not support file:// protocol) */

+ 7 - 2
src/mol-util/input/input-observer.ts

@@ -168,6 +168,9 @@ export type MoveInput = {
     movementX?: number,
     movementY?: number,
     inside: boolean,
+    // Move is subscribed to window element
+    // This indicates that the event originated from the element the InputObserver was created on
+    onElement: boolean
 } & BaseInput
 
 export type PinchInput = {
@@ -220,6 +223,7 @@ type PointerEvent = {
     pageY: number
     movementX?: number
     movementY?: number
+    target: EventTarget | null
 
     preventDefault?: () => void
 }
@@ -503,7 +507,8 @@ namespace InputObserver {
                 clientX: (t0.clientX + t1.clientX) / 2,
                 clientY: (t0.clientY + t1.clientY) / 2,
                 pageX: (t0.pageX + t1.pageX) / 2,
-                pageY: (t0.pageY + t1.pageY) / 2
+                pageY: (t0.pageY + t1.pageY) / 2,
+                target: ev.target
             };
         }
 
@@ -658,7 +663,7 @@ namespace InputObserver {
             }
             isInside = inside;
 
-            move.next({ x, y, pageX, pageY, movementX, movementY, buttons, button, modifiers: getModifierKeys(), inside });
+            move.next({ x, y, pageX, pageY, movementX, movementY, buttons, button, modifiers: getModifierKeys(), inside, onElement: ev.target === element });
 
             if (dragging === DraggingState.Stopped) return;
 

Some files were not shown because too many files changed in this diff