Mercurial > projects > doodle
diff doodle/tk/screen_model.d @ 81:d92b9f04b1e8
Bed time
author | "David Bryant <bagnose@gmail.com>" |
---|---|
date | Mon, 16 Aug 2010 00:04:27 +0930 |
parents | doodle/tk/pixel_model.d@535bae7a7305 |
children | 100dd23c7bdf |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doodle/tk/screen_model.d Mon Aug 16 00:04:27 2010 +0930 @@ -0,0 +1,69 @@ +module doodle.tk.screen_model; + +public { + import doodle.tk.geometry; +} + +private { + import doodle.core.misc; +} + +// This class manages the relationship between screen space and model space. +// Screen is defined as the current window/viewport into the model +// It provides convenient high-level operations. +// +// x and y run right and up respectively for screen and model space + +class ScreenModel { + this(in double zoom, in Rectangle canvasBoundsModel, in Rectangle viewBoundsScreen) { + _zoom = zoom; + _viewBoundsScreen = viewBoundsScreen; + _canvasBoundsModel = canvasBoundsModel; + + // Choose the centre of the canvas as the centre of the view + _viewCentreModel = _canvasBoundsModel.centre; + } + + void consolidateCanvasBounds(in Rectangle requiredCanvasBounds) { _canvasBoundsModel = screenToModel(_viewBoundsScreen) | requiredCanvasBounds; } + void canvasAccommodate(in Rectangle bounds) { _canvasBoundsModel = _canvasBoundsModel | bounds; } + + void zoomRelative(in double factor, in Point screenDatum) { + // Work out screen distance from current centre to datum, + // Do the zoom, then work out the new centre that keeps the + // screen distance the same + + Point oldModelDatum = screenToModel(screenDatum); + Vector screenDistance = modelToScreen(oldModelDatum - _viewCentreModel); + _zoom = clampZoom(zoom * factor); + _viewCentreModel = oldModelDatum - screenToModel(screenDistance); + } + + void panRelativeScreen(in Vector screenDisplacement) { _viewCentreModel = _viewCentreModel + screenToModel(screenDisplacement); } + void panRelativeModel(in Vector modelDisplacement) { _viewCentreModel = _viewCentreModel + modelDisplacement; } + + // For userZoom 1.0 -> 100% means the presentation on the screen is one-to-one with real-life + double userZoom(in double pixelsPerMillimetre) const { return _zoom / pixelsPerMillimetre; } + double zoom() const { return _zoom; } + Rectangle viewBoundsScreen() const { return _viewBoundsScreen; } + Rectangle viewBoundsModel() const { return screenToModel(_viewBoundsScreen); } + Rectangle canvasBoundsModel() const { return _canvasBoundsModel; } + Rectangle canvasBoundsScreen() const { return modelToScreen(_canvasBoundsModel); } + + Point modelToScreen(in Point model) const { return _viewBoundsScreen.centre + _zoom * (model - _viewCentreModel); } + Point screenToModel(in Point screen) const { return _viewCentreModel + (screen - _viewBoundsScreen.centre) / _zoom; } + Vector modelToScreen(in Vector model) const { return _zoom * model; } + Vector screenToModel(in Vector screen) const { return screen / _zoom; } + Rectangle modelToScreen(in Rectangle model) const { return Rectangle(modelToScreen(model.position), modelToScreen(model.size)); } + Rectangle screenToModel(in Rectangle model) const { return Rectangle(screenToModel(model.position), screenToModel(model.size)); } + + private { + static double clampZoom(in double zoom) { return clamp(zoom, 1e-1, 1e2); } + + // Screen units are pixels + // Model units are millimetres + double _zoom; // pixels-per-millimetre + Rectangle _viewBoundsScreen; // bounds of the viewport in screen space + Point _viewCentreModel; // where in the model is the centre of our screen + Rectangle _canvasBoundsModel; // the bounds of the canvas in model space + } +}