Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Endpoint] Animate camera, add sidebar #55590

Merged
merged 76 commits into from
Feb 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
af1264a
refactor camera stuff (partial) and add sidebar
Jan 22, 2020
54f658a
fix panning tests
Jan 22, 2020
762296a
refactor useCamera
Jan 22, 2020
45f6f62
reorganizing and commenting useCamera
Jan 22, 2020
bc4e40b
hooks are hard
Jan 22, 2020
d66edfa
animation is working good.
Jan 22, 2020
5115325
cleanup
Jan 22, 2020
35f84d0
memoize clipping planes
Jan 22, 2020
b430a97
ref to isAnimating is not needed
Jan 22, 2020
36c571e
position resolver children via styled components
Jan 23, 2020
8ebdef4
rename Panel component
Jan 23, 2020
606d1d7
fixing the camera speed is weird
Jan 23, 2020
f46b813
projection matrix isnt handling camemra position right
Jan 23, 2020
921fe88
cleanup
Jan 23, 2020
3cee938
almost fix pedro code
Jan 23, 2020
8ef190e
cleanup
Jan 23, 2020
8412b74
able to animate scale, but for what
Jan 24, 2020
b9e1fcf
GO TEAM
Jan 24, 2020
e257080
dabes
Jan 24, 2020
9392f5f
clenaup
Jan 24, 2020
2451947
who knows
Jan 24, 2020
93b1b3c
tweaking
Jan 27, 2020
be5c556
cool
Jan 27, 2020
54d28a3
cleanup
Jan 29, 2020
545259c
comments
Jan 29, 2020
26cee99
cleanup
Jan 30, 2020
ec8f541
remove junk
Jan 30, 2020
fae5069
i18n
Jan 30, 2020
4d0b088
refactor scale selector
Jan 30, 2020
4407e89
remove useless ref
Jan 30, 2020
9784f7b
comments
Jan 30, 2020
0118822
comments
Jan 30, 2020
fb73f6c
cleanup
Feb 3, 2020
ae429a2
comments
Feb 3, 2020
53aeab2
comments
Feb 3, 2020
99b3178
comments
Feb 3, 2020
94d281c
spelling
Feb 4, 2020
4d2063c
remove magic dates
Feb 4, 2020
0016871
cleanup
Feb 4, 2020
08e4399
cleanup
Feb 4, 2020
1892436
testing
Feb 5, 2020
bb6128f
fix projectionMatrix when dimensions are 0x0
Feb 5, 2020
1a21aab
fix inverse projection matrix and improve comments
Feb 5, 2020
7ed841f
refactor mock resize observer
Feb 5, 2020
c61f104
refactor test helper
Feb 5, 2020
51c8cb6
BROKEN
Feb 6, 2020
2770599
Revert "BROKEN"
Feb 6, 2020
487935e
refactor simulator
Feb 6, 2020
4d427bc
more refactor
Feb 6, 2020
4b353a7
refactor
Feb 6, 2020
9cb9ab0
start mocking rAF
Feb 7, 2020
991cb79
broken, refactoring tests and resolver
Feb 7, 2020
301e9ce
refactor to DI side effectors
Feb 7, 2020
c6dd982
factoring
Feb 7, 2020
20ba0b3
rewrote tests
Feb 7, 2020
852dd3b
fix tests. use real lib for integration react dev tools. more tests f…
Feb 7, 2020
fe6ab61
too cool
Feb 10, 2020
1fc862c
factorin
Feb 11, 2020
897da30
rename files
Feb 11, 2020
de5b6b4
refactor view tests
Feb 11, 2020
2c78529
cleanup
Feb 11, 2020
eaf4b95
cleanup
Feb 11, 2020
d95ff44
cleanup
Feb 11, 2020
6a4e617
animation tests
Feb 11, 2020
a2a2a27
Delete substate_middleware_factory.ts
Feb 11, 2020
40a0c71
Delete mock_resize_observer.ts
Feb 11, 2020
551853c
types
Feb 11, 2020
712c83c
THATS IT
Feb 11, 2020
86eb4b0
fix i18n ids
Feb 12, 2020
527ccd2
maybe fix build by moving redux dev tools to regular deps
Feb 12, 2020
316b5ce
try using redux-devtools-extension in development only
Feb 13, 2020
aee6769
handle invalid dates in table
Feb 13, 2020
bd98e7e
Use a badge to show invalid date
Feb 13, 2020
f8d1bb4
Merge branch 'master' into card
elasticmachine Feb 13, 2020
7aa451f
Merge branch 'master' into card
elasticmachine Feb 13, 2020
bac80ed
Merge branch 'master' into card
elasticmachine Feb 14, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion x-pack/plugins/endpoint/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"react-redux": "^7.1.0"
},
"devDependencies": {
"@types/react-redux": "^7.1.0"
"@types/react-redux": "^7.1.0",
"redux-devtools-extension": "^2.13.8"
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Introduction

Resolver renders a map in a DOM element. Items on the map are placed in 2 dimensions using arbitrary units. Like other mapping software, the map can show things at different scales. The 'camera' determines what is shown on the map.

The camera is positioned. When the user clicks-and-drags the map, the camera's position is changed. This allows the user to pan around the map and see things that would otherwise be out of view, at a given scale.

The camera determines the scale. If the scale is smaller, the viewport of the map is larger and more is visible. This allows the user to zoom in an out. On screen controls and gestures (trackpad-pinch, or CTRL-mousewheel) change the scale.

# Concepts

## Scaling
The camera scale is controlled both by the user and programatically by Resolver. There is a maximum and minimum scale value (at the time of this writing they are 0.5 and 6.) This means that the map, and things on the map, will be rendered at between 0.5 and 6 times their instrinsic dimensions.

A range control is provided so that the user can change the scale. The user can also pinch-to-zoom on Mac OS X (or use ctrl-mousewheel otherwise) to change the scale. These interactions change the `scalingFactor`. This number is between 0 and 1. It represents how zoomed-in things should be. When the `scalingFactor` is 1, the scale will be the maximum scale value. When `scalingFactor` is 0, the scale will be the minimum scale value. Otherwise we interpolate between the minimum and maximum scale factor. The rate that the scale increases between the two is controlled by `scalingFactor**zoomCurveRate` The zoom curve rate is 4 at the time of this writing. This makes it so that the change in scale is more pronounced when the user is zoomed in.

```
renderScale = minimumScale * (1 - scalingFactor**curveRate) + maximumScale * scalingFactor**curveRate;
```

## Panning
When the user clicks and drags the map, the camera is 'moved' around. This allows the user to see different things on the map. The on-screen controls provide 4 directional buttons which nudge the camera, as well as a reset button. The reset button brings the camera back where it started (0, 0).

Resolver may programatically change the position of the camera in order to bring some interesting elements into view.

## Animation
The camera can animate changes to its position. Animations usually have a short, fixed duration, such as 1 second. If the camera is moving a great deal during the animation, then things could end up moving across the screen too quickly. In this case, looking at Resolver might be disorienting. In order to combat this, Resolver may temporarily decrease the scale. By decreasing the scale, objects look futher away. Far away objects appear to move slower.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

import ReactDOM from 'react-dom';
import React from 'react';
import { AppRoot } from './view';
import { Provider } from 'react-redux';
import { Resolver } from './view';
import { storeFactory } from './store';
import { Embeddable } from '../../../../../../src/plugins/embeddable/public';

Expand All @@ -20,7 +21,12 @@ export class ResolverEmbeddable extends Embeddable {
}
this.lastRenderTarget = node;
const { store } = storeFactory();
ReactDOM.render(<AppRoot store={store} />, node);
ReactDOM.render(
<Provider store={store}>
<Resolver />
</Provider>,
node
);
}

public reload(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,10 @@
export function clamp(value: number, minimum: number, maximum: number) {
return Math.max(Math.min(value, maximum), minimum);
}

/**
* linearly interpolate between `a` and `b` at a ratio of `ratio`. If `ratio` is `0`, return `a`, if ratio is `1`, return `b`.
*/
export function lerp(a: number, b: number, ratio: number): number {
return a * (1 - ratio) + b * ratio;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,32 @@ export function inverseOrthographicProjection(
bottom: number,
left: number
): Matrix3 {
const m11 = (right - left) / 2;
const m13 = (right + left) / (right - left);
let m11: number;
let m13: number;
let m22: number;
let m23: number;

const m22 = (top - bottom) / 2;
const m23 = (top + bottom) / (top - bottom);
/**
* If `right - left` is 0, the width is 0, so scale everything to 0
*/
if (right - left === 0) {
m11 = 0;
m13 = 0;
} else {
m11 = (right - left) / 2;
m13 = (right + left) / (right - left);
}

/**
* If `top - bottom` is 0, the height is 0, so scale everything to 0
*/
if (top - bottom === 0) {
m22 = 0;
m23 = 0;
} else {
m22 = (top - bottom) / 2;
m23 = (top + bottom) / (top - bottom);
}

return [m11, 0, m13, 0, m22, m23, 0, 0, 0];
}
Expand All @@ -37,11 +58,32 @@ export function orthographicProjection(
bottom: number,
left: number
): Matrix3 {
const m11 = 2 / (right - left); // adjust x scale to match ndc (-1, 1) bounds
const m13 = -((right + left) / (right - left));
let m11: number;
let m13: number;
let m22: number;
let m23: number;

/**
* If `right - left` is 0, the width is 0, so scale everything to 0
*/
if (right - left === 0) {
m11 = 0;
m13 = 0;
} else {
m11 = 2 / (right - left); // adjust x scale to match ndc (-1, 1) bounds
m13 = -((right + left) / (right - left));
}

const m22 = 2 / (top - bottom); // adjust y scale to match ndc (-1, 1) bounds
const m23 = -((top + bottom) / (top - bottom));
/**
* If `top - bottom` is 0, the height is 0, so scale everything to 0
*/
if (top - bottom === 0) {
m22 = 0;
m23 = 0;
} else {
m22 = top - bottom === 0 ? 0 : 2 / (top - bottom); // adjust y scale to match ndc (-1, 1) bounds
m23 = top - bottom === 0 ? 0 : -((top + bottom) / (top - bottom));
}

return [m11, 0, m13, 0, m22, m23, 0, 0, 0];
}
Expand All @@ -68,6 +110,6 @@ export function translationTransformation([x, y]: Vector2): Matrix3 {
return [
1, 0, x,
0, 1, y,
0, 0, 1
0, 0, 0
]
}
37 changes: 37 additions & 0 deletions x-pack/plugins/endpoint/public/embeddables/resolver/lib/vector2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ export function divide(a: Vector2, b: Vector2): Vector2 {
return [a[0] / b[0], a[1] / b[1]];
}

/**
* Return `[ a[0] * b[0], a[1] * b[1] ]`
*/
export function multiply(a: Vector2, b: Vector2): Vector2 {
return [a[0] * b[0], a[1] * b[1]];
}

/**
* Returns a vector which is the result of applying a 2D transformation matrix to the provided vector.
*/
Expand All @@ -50,3 +57,33 @@ export function angle(a: Vector2, b: Vector2) {
const deltaY = b[1] - a[1];
return Math.atan2(deltaY, deltaX);
}

/**
* Clamp `vector`'s components.
*/
export function clamp([x, y]: Vector2, [minX, minY]: Vector2, [maxX, maxY]: Vector2): Vector2 {
return [Math.max(minX, Math.min(maxX, x)), Math.max(minY, Math.min(maxY, y))];
}

/**
* Scale vector by number
*/
export function scale(a: Vector2, n: number): Vector2 {
return [a[0] * n, a[1] * n];
}

/**
* Linearly interpolate between `a` and `b`.
* `t` represents progress and:
* 0 <= `t` <= 1
*/
export function lerp(a: Vector2, b: Vector2, t: number): Vector2 {
return add(scale(a, 1 - t), scale(b, t));
}

/**
* The length of the vector
*/
export function length([x, y]: Vector2): number {
return Math.sqrt(x * x + y * y);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export function mockProcessEvent(
machine_id: '',
...parts,
data_buffer: {
timestamp_utc: '2019-09-24 01:47:47Z',
event_subtype_full: 'creation_event',
event_type_full: 'process_event',
process_name: '',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { ProcessEvent } from '../types';
import { CameraAction } from './camera';
import { DataAction } from './data';

/**
* When the user wants to bring a process node front-and-center on the map.
*/
interface UserBroughtProcessIntoView {
readonly type: 'userBroughtProcessIntoView';
readonly payload: {
/**
* Used to identify the process node that should be brought into view.
*/
readonly process: ProcessEvent;
/**
* The time (since epoch in milliseconds) when the action was dispatched.
*/
readonly time: number;
};
}

export type ResolverAction = CameraAction | DataAction | UserBroughtProcessIntoView;
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { Vector2, PanDirection } from '../../types';
import { Vector2 } from '../../types';

interface TimestampedPayload {
/**
* Time (since epoch in milliseconds) when this action was dispatched.
*/
readonly time: number;
}

interface UserSetZoomLevel {
readonly type: 'userSetZoomLevel';
Expand All @@ -24,11 +31,13 @@ interface UserClickedZoomIn {

interface UserZoomed {
readonly type: 'userZoomed';
/**
* A value to zoom in by. Should be a fraction of `1`. For a `'wheel'` event when `event.deltaMode` is `'pixel'`,
* pass `event.deltaY / -renderHeight` where `renderHeight` is the height of the Resolver element in pixels.
*/
readonly payload: number;
readonly payload: {
/**
* A value to zoom in by. Should be a fraction of `1`. For a `'wheel'` event when `event.deltaMode` is `'pixel'`,
* pass `event.deltaY / -renderHeight` where `renderHeight` is the height of the Resolver element in pixels.
*/
readonly zoomChange: number;
} & TimestampedPayload;
}

interface UserSetRasterSize {
Expand All @@ -40,7 +49,7 @@ interface UserSetRasterSize {
}

/**
* This is currently only used in tests. The 'back to center' button will use this action, and more tests around its behavior will need to be added.
* When the user warps the camera to an exact point instantly.
*/
interface UserSetPositionOfCamera {
readonly type: 'userSetPositionOfCamera';
Expand All @@ -52,33 +61,45 @@ interface UserSetPositionOfCamera {

interface UserStartedPanning {
readonly type: 'userStartedPanning';
/**
* A vector in screen coordinates (each unit is a pixel and the Y axis increases towards the bottom of the screen)
* relative to the Resolver component.
* Represents a starting position during panning for a pointing device.
*/
readonly payload: Vector2;

readonly payload: {
/**
* A vector in screen coordinates (each unit is a pixel and the Y axis increases towards the bottom of the screen)
* relative to the Resolver component.
* Represents a starting position during panning for a pointing device.
*/
readonly screenCoordinates: Vector2;
} & TimestampedPayload;
}

interface UserStoppedPanning {
readonly type: 'userStoppedPanning';

readonly payload: TimestampedPayload;
}

interface UserClickedPanControl {
readonly type: 'userClickedPanControl';
interface UserNudgedCamera {
readonly type: 'userNudgedCamera';
/**
* String that represents the direction in which Resolver can be panned
*/
readonly payload: PanDirection;
readonly payload: {
/**
* A cardinal direction to move the users perspective in.
*/
readonly direction: Vector2;
} & TimestampedPayload;
}

interface UserMovedPointer {
readonly type: 'userMovedPointer';
/**
* A vector in screen coordinates relative to the Resolver component.
* The payload should be contain clientX and clientY minus the client position of the Resolver component.
*/
readonly payload: Vector2;
readonly payload: {
/**
* A vector in screen coordinates relative to the Resolver component.
* The payload should be contain clientX and clientY minus the client position of the Resolver component.
*/
screenCoordinates: Vector2;
} & TimestampedPayload;
}

export type CameraAction =
Expand All @@ -91,4 +112,4 @@ export type CameraAction =
| UserMovedPointer
| UserClickedZoomOut
| UserClickedZoomIn
| UserClickedPanControl;
| UserNudgedCamera;
Loading