gl2d
Version:
2D graphics package for WebGL
338 lines • 13.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var point_1 = require("../struct/point");
/**
* A rendering surface linked to an HTMLCanvasElement (the drawing buffer).
*/
var Surface = (function () {
/**
* Creates a new rendering surface.
* @param drawingBuffer the canvas that serves as the drawing buffer for this surface.
* @param renderer the renderer responsible for rendering the surface.
*/
function Surface(drawingBuffer, renderer) {
/**
* True if a request has been made to re-render this surface.
*/
this.hasRenderRequest = false;
this.drawingBuffer = drawingBuffer;
this.clientRect = drawingBuffer.getBoundingClientRect();
this.renderer = renderer;
this.renderer.onSurfaceCreated();
}
/**
* Requests that this surface be re-rendered.
*/
Surface.prototype.requestRender = function () {
this.hasRenderRequest = true;
};
/**
* Re-renders this surface if it has a render request.
*/
Surface.prototype.onAnimationFrame = function () {
if (this.hasRenderRequest) {
this.renderer.onDrawFrame();
this.hasRenderRequest = false;
}
};
/**
* Resizes this surface if the specified width and height differ from the current width and height.
* @param width the width to set on the surface. Defaults to the client width of the drawing buffer.
* @param height the height to set on the surface. Defaults to the client height of the drawing buffer.
*/
Surface.prototype.resize = function (width, height) {
if (width === void 0) { width = this.drawingBuffer.clientWidth; }
if (height === void 0) { height = this.drawingBuffer.clientHeight; }
// If width or height has changed
if (this.drawingBuffer.width !== width || this.drawingBuffer.height !== height) {
// Resize canvas to specified dimensions
this.drawingBuffer.width = width;
this.drawingBuffer.height = height;
// Get new bounding box
this.clientRect = this.drawingBuffer.getBoundingClientRect();
// Notify renderer of surface change
this.renderer.onSurfaceChanged(width, height);
// Request render to show changes
this.requestRender();
}
};
/**
* Sends a request to offset the image displayed by this surface.
* @param desiredOffset the desired offset, which is automatically adjusted according to the renderer's camera settings.
* @returns the actual offset.
*/
Surface.prototype.offset = function (desiredOffset) {
var camera = this.renderer.camera;
var actual = camera.offset(desiredOffset);
if (!actual.equalsScalar(0)) {
camera.updateMatrix();
this.requestRender();
}
return actual;
};
/**
* Sends a request to zoom into the image displayed by this surface.
* @param desiredScaleFactor the desired scale factor, which is automatically adjusted according to the renderer's camera settings.
* @returns the actual scale factor.
*/
Surface.prototype.zoomIn = function (desiredScaleFactor) {
var camera = this.renderer.camera;
var actual = camera.zoomIn(desiredScaleFactor);
if (actual !== 1) {
camera.updateMatrix();
this.requestRender();
}
return actual;
};
/**
* Sends a request to zoom out of the image displayed by this surface.
* @param desiredScaleFactor the desired scale factor, which is automatically adjusted according to the renderer's camera settings.
* @returns the actual scale factor.
*/
Surface.prototype.zoomOut = function (desiredScaleFactor) {
return this.zoomIn(1 / desiredScaleFactor);
};
/**
* Sends a request to zoom into the image displayed by this surface while fixing the specified focus point.
* @param desiredScaleFactor the desired scale factor, which is automatically adjusted according to the renderer's camera settings.
* @param focus the focus point.
* @returns the actual scale factor and camera offset.
*/
Surface.prototype.zoomToPoint = function (desiredScaleFactor, focus) {
var camera = this.renderer.camera;
var actual = camera.zoomToPoint(desiredScaleFactor, focus);
if (actual.scaleFactor !== 1 || !actual.offset.equalsScalar(0)) {
camera.updateMatrix();
this.requestRender();
}
return actual;
};
/**
* Maps a screen cordinate to canvas space.
* @param screen the screen coordinate.
* @param dst where to store the result.
*/
Surface.prototype.screenToCanvas = function (screenPoint, dst) {
if (dst === void 0) { dst = new point_1.Point(); }
dst.x = screenPoint.clientX - this.clientRect.left;
dst.y = screenPoint.clientY - this.clientRect.top;
return dst;
};
/**
* Maps a screen coordinate to NDC space [0,1].
* @param screen the screen coordinate.
* @param dst where to store the result.
*/
Surface.prototype.screenToNdc = function (screenPoint, dst) {
if (dst === void 0) { dst = new point_1.Point(); }
this.screenToCanvas(screenPoint, dst);
this.canvasToNdc(dst, dst);
return dst;
};
/**
* Maps a screen coordinate to clip space.
* @param screen the screen coordinate.
* @param dst where to store the result.
*/
Surface.prototype.screenToWorld = function (screenPoint, dst) {
if (dst === void 0) { dst = new point_1.Point(); }
this.screenToCanvas(screenPoint, dst);
this.canvasToNdc(dst, dst);
this.ndcToWorld(dst, dst);
return dst;
};
/**
* Maps a canvas coordinate to NDC space [0,1].
* @param p the canvas coordinate.
* @param dst where to store the result.
*/
Surface.prototype.canvasToNdc = function (canvasPoint, dst) {
if (dst === void 0) { dst = new point_1.Point(); }
// Normalize the coordinate by width and height of canvas
var width = this.clientRect.width;
var height = this.clientRect.height;
dst.x = canvasPoint.x / width;
dst.y = (height - canvasPoint.y) / height; // Flip in y axis
return dst;
};
/**
* Maps a normalized device coordinate (NDC) to world space.
* @param p the normalized device coordinate.
* @param dst where to store the result.
*/
Surface.prototype.ndcToWorld = function (ndc, dst) {
if (dst === void 0) { dst = new point_1.Point(); }
// Depends on what is currently in view
var view = this.renderer.camera.view;
dst.x = view.left + (ndc.x * view.width());
dst.y = view.bottom + (ndc.y * view.height());
return dst;
};
/**
* Invokes the specified callback whenever a mouse action occurs on this surface.
*/
Surface.prototype.onMouseEvent = function (callback) {
var _this = this;
/**
* Whether or not a mouse button is currently pressed.
*/
var isPressed = false;
// Listen for mouse events
window.addEventListener("mousedown", function (e) {
// Return if not inside element
if (e.target !== _this.drawingBuffer)
return;
// A mouse button is pressed
isPressed = true;
// Tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// Get action and pass to callback
callback(_this.getSurfaceMouseEvent(e, 0 /* Start */));
}, false);
window.addEventListener("mousemove", function (e) {
// If mouse is pressed
if (isPressed) {
// Tell browser we're handling this event
e.preventDefault();
e.stopPropagation();
// Process as a drag action
callback(_this.getSurfaceMouseEvent(e, 2 /* Drag */));
}
else {
// Otherwise process as a move action
callback(_this.getSurfaceMouseEvent(e, 1 /* Move */));
}
}, false);
window.addEventListener("mouseout", function (e) {
// Ignore if mouse not pressed
if (!isPressed) {
return;
}
// Tell browser we're handling this event
e.preventDefault();
e.stopPropagation();
// Get action and pass to callback
callback(_this.getSurfaceMouseEvent(e, 3 /* Leave */));
}, false);
window.addEventListener("mouseup", function (e) {
// Ignore if mouse not pressed
if (!isPressed) {
return;
}
// Mouse is no longer pressed
isPressed = false;
// Tell browser we're handling this event
e.preventDefault();
e.stopPropagation();
// Get action and pass to callback
callback(_this.getSurfaceMouseEvent(e, 4 /* End */));
}, false);
};
Surface.prototype.getSurfaceMouseEvent = function (event, status) {
return {
target: this,
src: event,
status: status,
cursor: this.screenToWorld(event)
};
};
/**
* Invokes the specified callback whenever a touch action occurs on this surface.
*/
Surface.prototype.onTouchEvent = function (callback) {
var _this = this;
/**
* Whether or no a touch event is currently active.
*/
var isActive = false;
// Listen for touch events
window.addEventListener("touchstart", function (e) {
// Return if not inside element
if (e.target !== _this.drawingBuffer)
return;
// Mark touch event active
isActive = true;
// Tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// Get action and pass to callback
callback(_this.getSurfaceTouchEvent(e, 0 /* Start */));
}, false);
window.addEventListener("touchmove", function (e) {
// Don't send callback if touch action did not originate inside the element
if (!isActive)
return;
// Tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// Get action and pass to callback
callback(_this.getSurfaceTouchEvent(e, 2 /* Drag */));
}, false);
window.addEventListener("touchleave", function (e) {
// Don't send callback if touch action did not originate inside the element
if (!isActive)
return;
// Tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// Get action and pass to callback
callback(_this.getSurfaceTouchEvent(e, 3 /* Leave */));
}, false);
window.addEventListener("touchend", function (e) {
// Don't send callback if touch action did not originate inside the element
if (!isActive)
return;
// Touch event is no longer active
isActive = false;
// Tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// Get action and pass to callback
callback(_this.getSurfaceTouchEvent(e, 4 /* End */));
}, false);
};
Surface.prototype.getSurfaceTouchEvent = function (event, status) {
return {
target: this,
src: event,
status: status,
pointers: this.getPointers(event)
};
};
Surface.prototype.getPointers = function (event) {
var pointers = [];
var touches = event.touches;
// Convert each of the touches to world space and add to array
for (var i = 0; i < touches.length; i++) {
pointers[i] = this.screenToWorld(touches[i]);
}
return pointers;
};
/**
* Invokes the specified callback whenever a scroll action occurs on this surface.
*/
Surface.prototype.onWheelEvent = function (callback) {
var _this = this;
document.addEventListener(this.getScrollSupport(), function (e) {
if (e.target === _this.drawingBuffer) {
e.preventDefault();
e.stopPropagation();
var isUpward = e.deltaY < 0 || e.detail < 0 || e.wheelDelta > 0;
callback({ isUpward: isUpward, cursor: _this.screenToWorld(e), target: _this, src: e });
}
});
};
/**
* From https://developer.mozilla.org/en-US/docs/Web/Events/wheel
*/
Surface.prototype.getScrollSupport = function () {
return "onwheel" in document.createElement("div") ? "wheel" :
document.onmousewheel !== undefined ? "mousewheel" :
"DOMMouseScroll"; // let's assume that remaining browsers are older Firefox
};
return Surface;
}());
exports.Surface = Surface;
;
//# sourceMappingURL=surface.js.map