# HG changeset patch # User "David Bryant " # Date 1281782155 -34200 # Node ID 0e61702c6ea6fd25f6f98d475099be7e19601854 # Parent d540f7e4af9ed1bb323ffbd515fb298c96ef0776 Checkpoint diff -r d540f7e4af9e -r 0e61702c6ea6 doodle/dia/grid_layer.d --- a/doodle/dia/grid_layer.d Sat Aug 14 19:39:58 2010 +0930 +++ b/doodle/dia/grid_layer.d Sat Aug 14 20:05:55 2010 +0930 @@ -46,10 +46,10 @@ { // vertical grid lines - double x = start(modelDamage.minCorner.x, _spacing); + double x = start(modelDamage.corner0.x, _spacing); for (;;) { - vline(modelCr, x, modelDamage.minCorner.y, modelDamage.maxCorner.y); + vline(modelCr, x, modelDamage.corner0.y, modelDamage.corner1.y); // Ensure 1 pixel wide FIXME is this naughty? We are sneaking // through cairo to mix model and pixel coordinates... @@ -58,7 +58,7 @@ modelCr.stroke(); } modelCr.restore(); - if (x > modelDamage.maxCorner.x) { + if (x > modelDamage.corner1.x) { break; } @@ -68,10 +68,10 @@ { // horizontal grid lines - double y = start(modelDamage.minCorner.y, _spacing); + double y = start(modelDamage.corner0.y, _spacing); for (;;) { - hline(modelCr, y, modelDamage.minCorner.x, modelDamage.maxCorner.x); + hline(modelCr, y, modelDamage.corner0.x, modelDamage.corner1.x); // FIXME? modelCr.save(); { @@ -79,7 +79,7 @@ modelCr.stroke(); } modelCr.restore(); - if (y > modelDamage.maxCorner.y) { + if (y > modelDamage.corner1.y) { break; } diff -r d540f7e4af9e -r 0e61702c6ea6 doodle/gtk/canvas.d --- a/doodle/gtk/canvas.d Sat Aug 14 19:39:58 2010 +0930 +++ b/doodle/gtk/canvas.d Sat Aug 14 20:05:55 2010 +0930 @@ -37,18 +37,6 @@ } // x and y run right and up respectively -// -// Model units are millimetres. -// -// _zoom -> pixels-per-model-unit -// _viewSize -> size of view window in pixels -// _viewCentre -> location in model corresponding to centre of view -// _canvasBounds -> size of the virtual canvas in model coordinates -// -// User operations: -// pan (middle click and drag) -// zoom about a point (hold control and move scroll wheel) -// resize the widget final class Canvas : Table, private IViewport { this(in Layer[] layers, IEventHandler eventHandler, IGrid grid, in double pixelsPerMillimetre) { @@ -213,21 +201,23 @@ private { - void initialiseBounds() { - Rectangle layerBounds = Rectangle.DEFAULT; + Rectangle layerBounds() { + Rectangle bounds = Rectangle.DEFAULT; + foreach (layer; _layers) { bounds = bounds | layer.bounds; } + assert(bounds.valid); + return bounds; + } - foreach (layer; _layers) { - layerBounds = layerBounds | layer.bounds; - } - - assert(layerBounds.valid); + void initialiseBounds() { + Rectangle lb = layerBounds; // FIXME use a function that grows a rectangle about its centre // and change 2.0 to a class-level constant - Rectangle paddedLayerBounds = expand(move(layerBounds, - layerBounds.size), 2.0 * layerBounds.size); + Rectangle paddedLayerBounds = expand(move(lb, - lb.size), 2.0 * lb.size); // + // FIXME 0.25 _zoom = 0.25 * _pixelsPerMillimetre; // ie 0.25 pixels represents a millimetre _canvasBounds = paddedLayerBounds; @@ -240,18 +230,12 @@ } void consolidateBounds() { - Rectangle layerBounds = Rectangle.DEFAULT; - - foreach (layer; _layers) { - layerBounds = layerBounds | layer.bounds; - } + Rectangle lb = layerBounds; - assert(layerBounds.valid); + // FIXME likewise as above + Rectangle paddedLayerBounds = expand(move(lb, - lb.size), 2.0 * lb.size); - Rectangle paddedLayerBounds = expand(move(layerBounds, - layerBounds.size), 2.0 * layerBounds.size); - - Vector z = _viewSize / _zoom; - Rectangle r = Rectangle(_viewCentre - z / 2.0, z); + Rectangle r = pixelToModel(_viewBounds); _canvasBounds = r | paddedLayerBounds; updateAdjustments; @@ -261,7 +245,7 @@ bool onConfigure(GdkEventConfigure * event, Widget widget) { assert(widget is _drawingArea); - _viewSize = Vector(cast(double)event.width, cast(double)event.height); + _viewBounds = Rectangle(Point(0.0, 0.0), Vector(cast(double)event.width, cast(double)event.height)); if (!_boundsValid) { initialiseBounds; @@ -288,8 +272,8 @@ Rectangle pixelDamage = event is null ? - Rectangle(Point(0.0, 0.0), _viewSize) : - Rectangle(Point(cast(double)event.area.x, _viewSize.y - cast(double)(event.area.y + event.area.height)), + _viewBounds : // XXX can we do something nice with the next line? + Rectangle(_viewBounds.position + Vector(cast(double)event.area.x, _viewBounds.h - cast(double)(event.area.y + event.area.height)), Vector(cast(double)event.area.width, cast(double)event.area.height)); Rectangle modelDamage = pixelToModel(pixelDamage); @@ -299,10 +283,11 @@ modelCr.save; pixelCr.save; { { // Setup model context and clip - modelCr.translate(0.0, _viewSize.y); + modelCr.translate(0.0, _viewBounds.h); modelCr.scale(_zoom, -_zoom); - immutable Vector modelSize = pixelToModel(_viewSize); + // XXX revisit + immutable Vector modelSize = pixelToModel(_viewBounds.size); immutable Point viewLeftBottom = _viewCentre - modelSize / 2.0; modelCr.translate(-viewLeftBottom.x, -viewLeftBottom.y); @@ -312,7 +297,7 @@ { // Setup pixel context and clip - pixelCr.translate(0.0, _viewSize.y); + pixelCr.translate(0.0, _viewBounds.h); pixelCr.scale(1.0, -1.0); rectangle(pixelCr, pixelDamage); @@ -339,7 +324,7 @@ assert(widget is _drawingArea); //trace("Got button event\n"); - Point pixelPoint = Point(event.x + 0.5, _viewSize.y - (event.y + 0.5)); + Point pixelPoint = Point(event.x + 0.5, _viewBounds.h - (event.y + 0.5)); Point modelPoint = pixelToModel(pixelPoint); auto buttonEvent = new ButtonEvent(gtk2tkButtonAction(event.type), @@ -358,7 +343,7 @@ bool onButtonRelease(GdkEventButton * event, Widget widget) { assert(widget is _drawingArea); - Point pixelPoint = Point(event.x + 0.5, _viewSize.y - (event.y + 0.5)); + Point pixelPoint = Point(event.x + 0.5, _viewBounds.h - (event.y + 0.5)); Point modelPoint = pixelToModel(pixelPoint); auto buttonEvent = new ButtonEvent(gtk2tkButtonAction(event.type), @@ -424,7 +409,7 @@ gtk_widget_event(_hRuler.getWidgetStruct(), cast(GdkEvent *)event); gtk_widget_event(_vRuler.getWidgetStruct(), cast(GdkEvent *)event); - Point pixelPoint = Point(event.x + 0.5, _viewSize.y - (event.y + 0.5)); + Point pixelPoint = Point(event.x + 0.5, _viewBounds.h - (event.y + 0.5)); Point modelPoint = pixelToModel(pixelPoint); auto motionEvent = new MotionEvent(pixelPoint, @@ -442,7 +427,7 @@ assert(widget is _drawingArea); //writefln("Got scroll\n"); - Point pixelPoint = Point(event.x + 0.5, _viewSize.y - (event.y + 0.5)); + Point pixelPoint = Point(event.x + 0.5, _viewBounds.h - (event.y + 0.5)); Point modelPoint = pixelToModel(pixelPoint); auto scrollEvent = new ScrollEvent(gtk2tkDirection(event.direction), @@ -487,7 +472,7 @@ bool onEnterNotify(GdkEventCrossing * event, Widget widget) { assert(widget is _drawingArea); - Point pixelPoint = Point(event.x + 0.5, _viewSize.y - (event.y + 0.5)); + Point pixelPoint = Point(event.x + 0.5, _viewBounds.h - (event.y + 0.5)); Point modelPoint = pixelToModel(pixelPoint); auto crossingEvent = new CrossingEvent(gtk2tkCrossingMode(event.mode), @@ -507,7 +492,7 @@ bool onLeaveNotify(GdkEventCrossing * event, Widget widget) { assert(widget is _drawingArea); - Point pixelPoint = Point(event.x + 0.5, _viewSize.y - (event.y + 0.5)); + Point pixelPoint = Point(event.x + 0.5, _viewBounds.h - (event.y + 0.5)); Point modelPoint = pixelToModel(pixelPoint); auto crossingEvent = new CrossingEvent(gtk2tkCrossingMode(event.mode), @@ -566,7 +551,7 @@ Point viewLeftBottom = Point(gtk_adjustment_get_value(hGtkAdjustment), gtk_adjustment_get_value(vGtkAdjustment)); - Vector modelSize = pixelToModel(_viewSize); + Vector modelSize = pixelToModel(_viewBounds.size); // XXX _viewCentre = viewLeftBottom + modelSize / 2.0; @@ -575,7 +560,7 @@ } void updateRulers() { - immutable Vector modelSize = pixelToModel(_viewSize); + immutable Vector modelSize = pixelToModel(_viewBounds.size); // XXX immutable Point viewLeftBottom = _viewCentre - modelSize / 2.0; immutable Point viewRightTop = _viewCentre + modelSize / 2.0; @@ -597,28 +582,28 @@ } void updateAdjustments() { - immutable Vector modelSize = pixelToModel(_viewSize); + immutable Vector modelSize = pixelToModel(_viewBounds.size); // XXX immutable Point viewLeftBottom = _viewCentre - modelSize / 2.0; immutable Point viewRightTop = _viewCentre + modelSize / 2.0; // Adjust the canvas size if necessary - _canvasBounds = Rectangle(minExtents(_canvasBounds.minCorner, viewLeftBottom), - maxExtents(_canvasBounds.maxCorner, viewRightTop)); + _canvasBounds = Rectangle(minExtents(_canvasBounds.corner0, viewLeftBottom), + maxExtents(_canvasBounds.corner1, viewRightTop)); // Update the adjustments GtkAdjustment * hGtkAdjustment = _hAdjustment.getAdjustmentStruct; GtkAdjustment * vGtkAdjustment = _vAdjustment.getAdjustmentStruct; - gtk_adjustment_set_lower(hGtkAdjustment, _canvasBounds.minCorner.x); - gtk_adjustment_set_upper(hGtkAdjustment, _canvasBounds.maxCorner.x); + gtk_adjustment_set_lower(hGtkAdjustment, _canvasBounds.corner0.x); + gtk_adjustment_set_upper(hGtkAdjustment, _canvasBounds.corner1.x); gtk_adjustment_set_value(hGtkAdjustment, viewLeftBottom.x); gtk_adjustment_set_step_increment(hGtkAdjustment, _canvasBounds.size.x / 16.0); gtk_adjustment_set_page_increment(hGtkAdjustment, _canvasBounds.size.x / 4.0); gtk_adjustment_set_page_size(hGtkAdjustment, modelSize.x); - gtk_adjustment_set_lower(vGtkAdjustment, _canvasBounds.minCorner.y); - gtk_adjustment_set_upper(vGtkAdjustment, _canvasBounds.maxCorner.y); + gtk_adjustment_set_lower(vGtkAdjustment, _canvasBounds.corner0.y); + gtk_adjustment_set_upper(vGtkAdjustment, _canvasBounds.corner1.y); gtk_adjustment_set_value(vGtkAdjustment, viewLeftBottom.y); gtk_adjustment_set_step_increment(vGtkAdjustment, _canvasBounds.size.y / 16.0); gtk_adjustment_set_page_increment(vGtkAdjustment, _canvasBounds.size.y / 4.0); @@ -634,7 +619,7 @@ if (_damage.valid) { int x, y, w, h; _damage.getQuantised(x, y, w, h); - _drawingArea.queueDrawArea(x, cast(int)_viewSize.y - (y + h), w, h); + _drawingArea.queueDrawArea(x, cast(int)_viewBounds.h - (y + h), w, h); _damage = Rectangle.DEFAULT; } } @@ -642,11 +627,11 @@ static double clampZoom(in double zoom) { return clamp(zoom, 0.2, 10.0); } Point modelToPixel(in Point model) const { - return Point.DEFAULT + _viewSize / 2.0 + _zoom * (model - _viewCentre); + return _viewBounds.centre + _zoom * (model - _viewCentre); } Point pixelToModel(in Point pixel) const { - return _viewCentre + (pixel - _viewSize / 2.0 - Point.DEFAULT) / _zoom; + return _viewCentre + (pixel - _viewBounds.centre) / _zoom; } Vector modelToPixel(in Vector model) const { @@ -677,9 +662,9 @@ // Model units are millimetres // Screen units are pixels double _zoom; // pixels-per-model-unit - Vector _viewSize; // pixel: size of view window in pixels + Rectangle _viewBounds; // pixel: bounds of the viewport in pixels Point _viewCentre; // model: where in the model is the centre of our view - Rectangle _canvasBounds; // model: + Rectangle _canvasBounds; // model: bounds of the canvas in millimetres // Child widgets: HRuler _hRuler; diff -r d540f7e4af9e -r 0e61702c6ea6 doodle/tk/geometry.d --- a/doodle/tk/geometry.d Sat Aug 14 19:39:58 2010 +0930 +++ b/doodle/tk/geometry.d Sat Aug 14 20:05:55 2010 +0930 @@ -152,12 +152,19 @@ this(corner1.x, corner1.y, corner.x - corner1.x, corner.y - corner1.y); } - alias position minCorner; + double x0() { return _position.x; } + double y0() { return _position.y; } + double w() { return _size.x; } + double h() { return _size.y; } + double x1() { return x0 + w; } + double y1() { return y0 + h; } + + alias position corner0; Point position() const { return _position; } Vector size() const { return _size; } - Point maxCorner() const { return _position + _size; } + Point corner1() const { return _position + _size; } bool valid() const { return _size.x > 0.0 && _size.y > 0.0; } @@ -171,8 +178,8 @@ return DEFAULT; } else { - Point max = minExtents(maxCorner(), r.maxCorner()); - Point min = maxExtents(minCorner(), r.minCorner()); + Point max = minExtents(corner1(), r.corner1()); + Point min = maxExtents(corner0(), r.corner0()); if (max.x < min.x || max.y < min.y) { return DEFAULT; @@ -192,8 +199,8 @@ return this; } else { - return Rectangle(minExtents(minCorner(), r.minCorner()), - maxExtents(maxCorner(), r.maxCorner())); + return Rectangle(minExtents(corner0(), r.corner0()), + maxExtents(corner1(), r.corner1())); } } diff -r d540f7e4af9e -r 0e61702c6ea6 doodle/tk/pixel_model.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doodle/tk/pixel_model.d Sat Aug 14 20:05:55 2010 +0930 @@ -0,0 +1,42 @@ +module doodle.tk.pixel_model; + +public { + import doodle.tk.geometry; +} + +// FIXME consider using the term Screen instead of Pixel... + +class PixelModel { + this(in double zoom, in Rectangle canvasBounds, in Rectangle viewBounds) { + _zoom = zoom; + _viewBounds = viewBounds; + _canvasBounds = canvasBounds; + + _viewCentre = _canvasBounds.centre; + } + + void consolidateCanvasBounds(in Rectangle requiredCanvasBounds) { + Rectangle r = pixelToModel(_viewBounds); + _canvasBounds = r | requiredCanvasBounds; + } + + // For normalZoom 1.0 -> 100% means the presentation on the screen is + // one-to-one with real-life + double normalZoom(in double pixelsPerMillimetre) { return _zoom / pixelsPerMillimetre; } + + Point modelToPixel(in Point model) const { return _viewBounds.centre + _zoom * (model - _viewCentre); } + Point pixelToModel(in Point pixel) const { return _viewCentre + (pixel - _viewBounds.centre) / _zoom; } + Vector modelToPixel(in Vector model) const { return _zoom * model; } + Vector pixelToModel(in Vector pixel) const { return pixel / _zoom; } + Rectangle modelToPixel(in Rectangle model) const { return Rectangle(modelToPixel(model.position), modelToPixel(model.size)); } + Rectangle pixelToModel(in Rectangle model) const { return Rectangle(pixelToModel(model.position), pixelToModel(model.size)); } + + private { + // Screen units are pixels + // Model units are millimetres + double _zoom; // pixels-per-millimetre + Rectangle _viewBounds; // pixel: bounds of the viewport in pixels + Point _viewCentre; // model: where in the model is the centre of our view + Rectangle _canvasBounds; // model: the bounds of the canvas in millimetres + } +}