camera-2d-simple
Advanced tools
Comparing version 1.2.0 to 2.0.0-rc1
@@ -0,1 +1,10 @@ | ||
**v2.0.0** | ||
- Add tests | ||
- Add `camera.translate()` and `camera.scale()` as synonyms for `camera.pan()` and `camera.zoom()` | ||
- Fixed rotation | ||
- Cleaned up code | ||
- Changed `camera.view()` to `camera.view` | ||
- Removed `camera.position` and `camera.transformation` as both are unnecessary. | ||
**v1.2.0** | ||
@@ -2,0 +11,0 @@ |
@@ -7,8 +7,13 @@ (function (global, factory) { | ||
var createCamera = function (ref) { | ||
if ( ref === void 0 ) ref = {}; | ||
var initTarget = ref.target; if ( initTarget === void 0 ) initTarget = [0, 0]; | ||
var initDistance = ref.distance; if ( initDistance === void 0 ) initDistance = 1.0; | ||
var initRotation = ref.rotation; if ( initRotation === void 0 ) initRotation = 0; | ||
var VIEW_CENTER = [0, 0, 0, 1]; | ||
var createCamera = function ( | ||
initTarget, | ||
initDistance, | ||
initRotation | ||
) { | ||
if ( initTarget === void 0 ) initTarget = [0, 0]; | ||
if ( initDistance === void 0 ) initDistance = 1; | ||
if ( initRotation === void 0 ) initRotation = 0; | ||
// Scratch variables | ||
@@ -19,73 +24,32 @@ var scratch0 = new Float32Array(16); | ||
var target = initTarget; | ||
var distance = initDistance; | ||
var rotation = initRotation; | ||
var view = glMatrix.mat4.create(); | ||
var center = glMatrix.vec3.create(); | ||
var getRotation = function () { return Math.acos(view[0]); }; | ||
var getDistance = function () { return distance; }; | ||
var setDistance = function (d) { | ||
distance = d; | ||
if (distance < 0.0) { distance = 0.0; } | ||
}; | ||
var getScaling = function () { return glMatrix.mat4.getScaling(scratch0, view)[0]; }; | ||
var getPosition = function () { return [center[0], center[1], distance]; }; | ||
var getDistance = function () { return 1 / getScaling(); }; | ||
var getTarget = function () { return center.slice(0, 2).map(function (x) { return -1 * x; }); }; | ||
var getTranslation = function () { return glMatrix.mat4.getTranslation(scratch0, view).slice(0, 2); }; | ||
var transformation = function () { | ||
var out = glMatrix.mat3.create(); | ||
glMatrix.mat3.fromTranslation(out, getTarget()); | ||
glMatrix.mat3.scale(out, out, [distance, distance]); | ||
return out; | ||
}; | ||
var getTarget = function () { return glMatrix.vec4.transformMat4(scratch0, VIEW_CENTER, glMatrix.mat4.invert(scratch2, view)); }; | ||
var view = function (v) { | ||
if (!v) { v = glMatrix.mat4.create(); } // eslint-disable-line no-param-reassign | ||
var getView = function () { return view; }; | ||
scratch0[0] = 1 / distance; | ||
scratch0[1] = scratch0[0]; // eslint-disable-line prefer-destructuring | ||
scratch0[2] = 1.0; | ||
// View matrix. First scale, then translate | ||
glMatrix.mat4.fromScaling(v, scratch0); | ||
glMatrix.mat4.translate(v, v, center); | ||
// Auxilliary frame around which we rotate | ||
// I.e., the center of the viewport | ||
var a = glMatrix.mat4.create(); | ||
glMatrix.mat4.fromTranslation(a, glMatrix.vec3.negate(scratch1, center)); | ||
// Rotation matrix | ||
var r = glMatrix.mat4.create(); | ||
glMatrix.mat4.fromRotation(r, rotation, [0, 0, 1]); | ||
// Finally, we rotate `v` around `a` (the viewport center) by `r` | ||
return glMatrix.mat4.multiply( | ||
v, | ||
a, | ||
glMatrix.mat4.multiply(v, r, glMatrix.mat4.multiply(v, glMatrix.mat4.invert(scratch2, a), v)) | ||
); | ||
}; | ||
var lookAt = function (ref, newDistance, newRotation) { | ||
if ( ref === void 0 ) ref = []; | ||
var x = ref[0]; | ||
var y = ref[1]; | ||
var x = ref[0]; if ( x === void 0 ) x = 0; | ||
var y = ref[1]; if ( y === void 0 ) y = 0; | ||
if ( newDistance === void 0 ) newDistance = 1; | ||
if ( newRotation === void 0 ) newRotation = 0; | ||
if (+x && +y) { | ||
target = [+x * -1 || 0, +y * -1 || 0]; | ||
center = [target[0], target[1], 0]; | ||
} | ||
// Reset the view | ||
view = glMatrix.mat4.create(); | ||
if (+newDistance >= 0) { | ||
distance = newDistance; | ||
} | ||
if (+newRotation) { | ||
rotation = newRotation; | ||
} | ||
translate([-x, -y]); | ||
rotate(newRotation); | ||
scale(1 / newDistance); | ||
}; | ||
var pan = function (ref) { | ||
var translate = function (ref) { | ||
if ( ref === void 0 ) ref = []; | ||
@@ -95,45 +59,79 @@ var x = ref[0]; if ( x === void 0 ) x = 0; | ||
center[0] += x * distance; | ||
center[1] -= y * distance; | ||
scratch0[0] = x; | ||
scratch0[1] = y; | ||
scratch0[2] = 0; | ||
target[0] -= x * distance; | ||
target[1] += y * distance; | ||
var t = glMatrix.mat4.fromTranslation(scratch1, scratch0); | ||
// Translate about the viewport center | ||
// This is identical to `i * t * i * view` where `i` is the identity matrix | ||
glMatrix.mat4.multiply(view, t, view); | ||
}; | ||
var zoom = function (d) { | ||
setDistance(distance * d); | ||
var scale = function (d, mousePos) { | ||
if (d <= 0) { return; } | ||
scratch0[0] = d; | ||
scratch0[1] = d; | ||
scratch0[2] = 1; | ||
var s = glMatrix.mat4.fromScaling(scratch1, scratch0); | ||
var scaleCenter = mousePos ? mousePos.concat( [0]) : VIEW_CENTER; | ||
var a = glMatrix.mat4.fromTranslation(scratch0, scaleCenter); | ||
// Translate about the scale center | ||
// I.e., the mouse position or the view center | ||
glMatrix.mat4.multiply( | ||
view, | ||
a, | ||
glMatrix.mat4.multiply( | ||
view, | ||
s, | ||
glMatrix.mat4.multiply(view, glMatrix.mat4.invert(scratch2, a), view) | ||
) | ||
); | ||
}; | ||
var rotate = function (rad) { | ||
rotation += rad; | ||
var r = glMatrix.mat4.create(); | ||
glMatrix.mat4.fromRotation(r, rad, [0, 0, 1]); | ||
// Rotate about the viewport center | ||
// This is identical to `i * r * i * view` where `i` is the identity matrix | ||
glMatrix.mat4.multiply(view, r, view); | ||
}; | ||
var reset = function () { | ||
lookAt(initTarget, initDistance); | ||
lookAt(initTarget, initDistance, initRotation); | ||
}; | ||
// Init | ||
lookAt(target, distance); | ||
lookAt(initTarget, initDistance, initRotation); | ||
return { | ||
get translation() { | ||
return getTranslation(); | ||
}, | ||
get target() { | ||
return getTarget(); | ||
}, | ||
get scaling() { | ||
return getScaling(); | ||
}, | ||
get distance() { | ||
return getDistance(); | ||
}, | ||
set distance(d) { | ||
setDistance(d); | ||
get rotation() { | ||
return getRotation(); | ||
}, | ||
get position() { | ||
return getPosition(); | ||
get view() { | ||
return getView(); | ||
}, | ||
get transformation() { | ||
return transformation(); | ||
}, | ||
view: view, | ||
lookAt: lookAt, | ||
pan: pan, | ||
translate: translate, | ||
pan: translate, | ||
rotate: rotate, | ||
zoom: zoom, | ||
scale: scale, | ||
zoom: scale, | ||
reset: reset | ||
@@ -140,0 +138,0 @@ }; |
@@ -1,1 +0,1 @@ | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("gl-matrix")):"function"==typeof define&&define.amd?define(["gl-matrix"],e):t.createCamera2d=e(t.glMatrix)}(this,function(t){"use strict";return function(e){void 0===e&&(e={});var r=e.target;void 0===r&&(r=[0,0]);var a=e.distance;void 0===a&&(a=1);var n=e.rotation;void 0===n&&(n=0);var o=new Float32Array(16),i=new Float32Array(16),m=new Float32Array(16),u=r,c=a,f=n,v=t.vec3.create(),l=function(t){(c=t)<0&&(c=0)},d=function(){return v.slice(0,2).map(function(t){return-1*t})},s=function(t,e,r){void 0===t&&(t=[]);var a=t[0],n=t[1];+a&&+n&&(v=[(u=[-1*+a||0,-1*+n||0])[0],u[1],0]),+e>=0&&(c=e),+r&&(f=r)};return s(u,c),{get target(){return d()},get distance(){return c},set distance(t){l(t)},get position(){return[v[0],v[1],c]},get transformation(){return e=t.mat3.create(),t.mat3.fromTranslation(e,d()),t.mat3.scale(e,e,[c,c]),e;var e},view:function(e){e||(e=t.mat4.create()),o[0]=1/c,o[1]=o[0],o[2]=1,t.mat4.fromScaling(e,o),t.mat4.translate(e,e,v);var r=t.mat4.create();t.mat4.fromTranslation(r,t.vec3.negate(i,v));var a=t.mat4.create();return t.mat4.fromRotation(a,f,[0,0,1]),t.mat4.multiply(e,r,t.mat4.multiply(e,a,t.mat4.multiply(e,t.mat4.invert(m,r),e)))},lookAt:s,pan:function(t){void 0===t&&(t=[]);var e=t[0];void 0===e&&(e=0);var r=t[1];void 0===r&&(r=0),v[0]+=e*c,v[1]-=r*c,u[0]-=e*c,u[1]+=r*c},rotate:function(t){f+=t},zoom:function(t){l(c*t)},reset:function(){s(r,a)}}}}); | ||
!function(t,a){"object"==typeof exports&&"undefined"!=typeof module?module.exports=a(require("gl-matrix")):"function"==typeof define&&define.amd?define(["gl-matrix"],a):t.createCamera2d=a(t.glMatrix)}(this,function(t){"use strict";var a=[0,0,0,1];return function(r,e,n){void 0===r&&(r=[0,0]),void 0===e&&(e=1),void 0===n&&(n=0);var o=new Float32Array(16),i=new Float32Array(16),m=new Float32Array(16),l=t.mat4.create(),u=function(){return t.mat4.getScaling(o,l)[0]},c=function(a,r,e){void 0===a&&(a=[]);var n=a[0];void 0===n&&(n=0);var o=a[1];void 0===o&&(o=0),void 0===r&&(r=1),void 0===e&&(e=0),l=t.mat4.create(),v([-n,-o]),d(e),f(1/r)},v=function(a){void 0===a&&(a=[]);var r=a[0];void 0===r&&(r=0);var e=a[1];void 0===e&&(e=0),o[0]=r,o[1]=e,o[2]=0;var n=t.mat4.fromTranslation(i,o);t.mat4.multiply(l,n,l)},f=function(r,e){if(!(r<=0)){o[0]=r,o[1]=r,o[2]=1;var n=t.mat4.fromScaling(i,o),u=e?e.concat([0]):a,c=t.mat4.fromTranslation(o,u);t.mat4.multiply(l,c,t.mat4.multiply(l,n,t.mat4.multiply(l,t.mat4.invert(m,c),l)))}},d=function(a){var r=t.mat4.create();t.mat4.fromRotation(r,a,[0,0,1]),t.mat4.multiply(l,r,l)};return c(r,e,n),{get translation(){return t.mat4.getTranslation(o,l).slice(0,2)},get target(){return t.vec4.transformMat4(o,a,t.mat4.invert(m,l))},get scaling(){return u()},get distance(){return 1/u()},get rotation(){return Math.acos(l[0])},get view(){return l},lookAt:c,translate:v,pan:v,rotate:d,scale:f,zoom:f,reset:function(){c(r,e,n)}}}}); |
{ | ||
"name": "camera-2d-simple", | ||
"version": "1.2.0", | ||
"version": "2.0.0-rc1", | ||
"description": "2D camera for WebGL", | ||
@@ -30,2 +30,3 @@ "author": "Fritz Lekschas", | ||
"eslint-config-prettier": "^v3.1.0", | ||
"esm": "^3.0.84", | ||
"husky": "^1.1.2", | ||
@@ -36,11 +37,14 @@ "prettier": "1.14.3", | ||
"rollup-plugin-buble": "^0.19.2", | ||
"rollup-plugin-terser": "^1.0.1" | ||
"rollup-plugin-terser": "^1.0.1", | ||
"tap-spec": "^5.0.0", | ||
"tape": "^4.9.1" | ||
}, | ||
"scripts": { | ||
"build": "rollup -c", | ||
"lint": "eslint src rollup.config.js", | ||
"precommit": "pretty-quick --staged", | ||
"lint": "eslint src tests rollup.config.js", | ||
"precommit": "pretty-quick --staged; npm run test", | ||
"prepublishOnly": "npm run lint", | ||
"prerelease": "rm -rf dist/*; npm run build; zip -r dist.zip dist", | ||
"pretest": "npm run lint", | ||
"test": "node -r esm tests/index.js | tap-spec", | ||
"watch": "rollup -cw" | ||
@@ -47,0 +51,0 @@ }, |
@@ -6,3 +6,3 @@ # 2D Camera | ||
[![build status](https://travis-ci.org/flekschas/camera-2d.svg?branch=master)](https://travis-ci.org/flekschas/camera-2d) | ||
[![gzipped size](https://img.shields.io/badge/gzipped%20size-0.9%20KB-6ae3c7.svg)](https://unpkg.com/camera-2d-simple) | ||
[![gzipped size](https://img.shields.io/badge/gzipped%20size-0.8%20KB-6ae3c7.svg)](https://unpkg.com/camera-2d-simple) | ||
[![code style prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier) | ||
@@ -30,3 +30,3 @@ [![demo](https://img.shields.io/badge/demo-online-6ae3c7.svg)](https://flekschas.github.io/regl-scatterplot/) | ||
### `const camera = createCamera(target = [0, 0], distance = 1)` | ||
### `const camera = createCamera(target = [0,0], distance = 1, rotation = 0)` | ||
@@ -37,27 +37,48 @@ Creates a 2d camera looking at `target` from a certain `distance`. | ||
- `distance` is the distance between the target and the camera. | ||
- `rotation` is angle in radiance around the z axis with respect to the viewport center. | ||
**Returns** A new 2d camera object | ||
### `camera.lookAt(target = [0, 0], distance = 1)` | ||
#### `camera.lookAt(target = [0,0], distance = 1, rotation = 0)` | ||
Move the camera to look at the new position. | ||
Move the camera to look at . | ||
### `camera.pan(translation)` | ||
#### `camera.pan([x,y])` or `camera.translate([x,y])` | ||
Moves the center of the camera by `translation`. Note that translation must be an array of length 2. | ||
Moves the center of the camera by `x` and `y` pixel. | ||
### `camera.zoom(delta)` | ||
#### `camera.zoom(delta, scaleCenter)` or `camera.scale(delta, scaleCenter)` | ||
Zooms in or out by some amount. I.e., the new distance is defined as `distance * delta`. | ||
Zooms in or out by `delta` with respect to `scaleCenter` in `[x,y]`. The new distance will be `distance * delta`. | ||
### `camera.view([out])` | ||
#### `camera.rotate(angle)` | ||
Returns the current view matrix associated to the camera. | ||
Rotate the camera by `angle` (in radians) around the z axis with respect to the viewport center. | ||
### `camera.position` | ||
#### `camera.reset()` | ||
Is an array of length 3 comprised of the target (x, y) and distance (z) of the camera. | ||
Reset the camera to the initial target, distance, and rotation. | ||
### `camera.transformation` | ||
#### `camera.view` | ||
Is the current transformation `mat3` associated to the camera. | ||
The current view matrix (`mat4`) of the camera. | ||
#### `camera.translation` | ||
The camera translation needed to look at the `target`. | ||
#### `camera.target` | ||
The camera center in normalized device coordinates. This is a shorthand for inverseOf(`camera.view`) \* `[0,0,0,1]`. | ||
#### `camera.scaling` | ||
The camera scaling. Larger scales are equivalent of a smaller `distance` to the target. | ||
#### `camera.distance` | ||
Distance of the camera to the target. This is a shorthand for the inverse `scaling`, i.e., `1 / scaling`. | ||
#### `camera.rotation` | ||
Rotation in radians around the z axis. |
150
src/index.js
@@ -1,8 +0,10 @@ | ||
import { mat3, mat4, vec3 } from "gl-matrix"; | ||
import { mat4, vec4 } from "gl-matrix"; | ||
const createCamera = ({ | ||
target: initTarget = [0, 0], | ||
distance: initDistance = 1.0, | ||
rotation: initRotation = 0 | ||
} = {}) => { | ||
const VIEW_CENTER = [0, 0, 0, 1]; | ||
const createCamera = ( | ||
initTarget = [0, 0], | ||
initDistance = 1, | ||
initRotation = 0 | ||
) => { | ||
// Scratch variables | ||
@@ -13,112 +15,104 @@ const scratch0 = new Float32Array(16); | ||
let target = initTarget; | ||
let distance = initDistance; | ||
let rotation = initRotation; | ||
let view = mat4.create(); | ||
let center = vec3.create(); | ||
const getRotation = () => Math.acos(view[0]); | ||
const getDistance = () => distance; | ||
const setDistance = d => { | ||
distance = d; | ||
if (distance < 0.0) distance = 0.0; | ||
}; | ||
const getScaling = () => mat4.getScaling(scratch0, view)[0]; | ||
const getPosition = () => [center[0], center[1], distance]; | ||
const getDistance = () => 1 / getScaling(); | ||
const getTarget = () => center.slice(0, 2).map(x => -1 * x); | ||
const getTranslation = () => mat4.getTranslation(scratch0, view).slice(0, 2); | ||
const transformation = () => { | ||
const out = mat3.create(); | ||
mat3.fromTranslation(out, getTarget()); | ||
mat3.scale(out, out, [distance, distance]); | ||
return out; | ||
}; | ||
const getTarget = () => | ||
vec4.transformMat4(scratch0, VIEW_CENTER, mat4.invert(scratch2, view)); | ||
const view = v => { | ||
if (!v) v = mat4.create(); // eslint-disable-line no-param-reassign | ||
const getView = () => view; | ||
scratch0[0] = 1 / distance; | ||
scratch0[1] = scratch0[0]; // eslint-disable-line prefer-destructuring | ||
scratch0[2] = 1.0; | ||
const lookAt = ([x = 0, y = 0] = [], newDistance = 1, newRotation = 0) => { | ||
// Reset the view | ||
view = mat4.create(); | ||
// View matrix. First scale, then translate | ||
mat4.fromScaling(v, scratch0); | ||
mat4.translate(v, v, center); | ||
translate([-x, -y]); | ||
rotate(newRotation); | ||
scale(1 / newDistance); | ||
}; | ||
// Auxilliary frame around which we rotate | ||
// I.e., the center of the viewport | ||
const a = mat4.create(); | ||
mat4.fromTranslation(a, vec3.negate(scratch1, center)); | ||
const translate = ([x = 0, y = 0] = []) => { | ||
scratch0[0] = x; | ||
scratch0[1] = y; | ||
scratch0[2] = 0; | ||
// Rotation matrix | ||
const r = mat4.create(); | ||
mat4.fromRotation(r, rotation, [0, 0, 1]); | ||
const t = mat4.fromTranslation(scratch1, scratch0); | ||
// Finally, we rotate `v` around `a` (the viewport center) by `r` | ||
return mat4.multiply( | ||
v, | ||
a, | ||
mat4.multiply(v, r, mat4.multiply(v, mat4.invert(scratch2, a), v)) | ||
); | ||
// Translate about the viewport center | ||
// This is identical to `i * t * i * view` where `i` is the identity matrix | ||
mat4.multiply(view, t, view); | ||
}; | ||
const lookAt = ([x, y] = [], newDistance, newRotation) => { | ||
if (+x && +y) { | ||
target = [+x * -1 || 0, +y * -1 || 0]; | ||
center = [target[0], target[1], 0]; | ||
} | ||
const scale = (d, mousePos) => { | ||
if (d <= 0) return; | ||
if (+newDistance >= 0) { | ||
distance = newDistance; | ||
} | ||
scratch0[0] = d; | ||
scratch0[1] = d; | ||
scratch0[2] = 1; | ||
if (+newRotation) { | ||
rotation = newRotation; | ||
} | ||
}; | ||
const s = mat4.fromScaling(scratch1, scratch0); | ||
const pan = ([x = 0, y = 0] = []) => { | ||
center[0] += x * distance; | ||
center[1] -= y * distance; | ||
const scaleCenter = mousePos ? [...mousePos, 0] : VIEW_CENTER; | ||
const a = mat4.fromTranslation(scratch0, scaleCenter); | ||
target[0] -= x * distance; | ||
target[1] += y * distance; | ||
// Translate about the scale center | ||
// I.e., the mouse position or the view center | ||
mat4.multiply( | ||
view, | ||
a, | ||
mat4.multiply( | ||
view, | ||
s, | ||
mat4.multiply(view, mat4.invert(scratch2, a), view) | ||
) | ||
); | ||
}; | ||
const zoom = d => { | ||
setDistance(distance * d); | ||
}; | ||
const rotate = rad => { | ||
const r = mat4.create(); | ||
mat4.fromRotation(r, rad, [0, 0, 1]); | ||
const rotate = rad => { | ||
rotation += rad; | ||
// Rotate about the viewport center | ||
// This is identical to `i * r * i * view` where `i` is the identity matrix | ||
mat4.multiply(view, r, view); | ||
}; | ||
const reset = () => { | ||
lookAt(initTarget, initDistance); | ||
lookAt(initTarget, initDistance, initRotation); | ||
}; | ||
// Init | ||
lookAt(target, distance); | ||
lookAt(initTarget, initDistance, initRotation); | ||
return { | ||
get translation() { | ||
return getTranslation(); | ||
}, | ||
get target() { | ||
return getTarget(); | ||
}, | ||
get scaling() { | ||
return getScaling(); | ||
}, | ||
get distance() { | ||
return getDistance(); | ||
}, | ||
set distance(d) { | ||
setDistance(d); | ||
get rotation() { | ||
return getRotation(); | ||
}, | ||
get position() { | ||
return getPosition(); | ||
get view() { | ||
return getView(); | ||
}, | ||
get transformation() { | ||
return transformation(); | ||
}, | ||
view, | ||
lookAt, | ||
pan, | ||
translate, | ||
pan: translate, | ||
rotate, | ||
zoom, | ||
scale, | ||
zoom: scale, | ||
reset | ||
@@ -125,0 +119,0 @@ }; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
14151
82
11
213
2
1