# HG changeset patch # User "David Bryant " # Date 1281784721 -34200 # Node ID 0f7cf6c6f206cd9311b1fb3d8e759dc17bb0b3d0 # Parent 0e61702c6ea6fd25f6f98d475099be7e19601854 Reimplemented gtk.canvas in terms of tk.pixel_model but needs a lot of consolidation. diff -r 0e61702c6ea6 -r 0f7cf6c6f206 doodle/gtk/canvas.d --- a/doodle/gtk/canvas.d Sat Aug 14 20:05:55 2010 +0930 +++ b/doodle/gtk/canvas.d Sat Aug 14 20:48:41 2010 +0930 @@ -10,6 +10,7 @@ import doodle.core.logging; import doodle.cairo.routines; import doodle.gtk.conversions; + import doodle.tk.pixel_model; import cairo.Surface; @@ -36,8 +37,6 @@ import core.stdc.string : strlen; } -// x and y run right and up respectively - final class Canvas : Table, private IViewport { this(in Layer[] layers, IEventHandler eventHandler, IGrid grid, in double pixelsPerMillimetre) { super(3, 3, 0); @@ -140,25 +139,18 @@ // IViewport overrides: void zoomRelative(in Point pixelDatum, in double factor) { - // Work out pixel distance from current centre to datum, - // Do the zoom, then work out the new centre that keeps the - // pixel distance the same - - Point oldModelDatum = pixelToModel(pixelDatum); - Vector pixelDistance = modelToPixel(oldModelDatum - _viewCentre); - _zoom = clampZoom(factor * _zoom); - _viewCentre = oldModelDatum - pixelToModel(pixelDistance); + _pixelModel.zoomRelative(factor, pixelDatum); consolidateBounds; updateAdjustments; updateRulers; - _grid.zoomChanged(_zoom); + _grid.zoomChanged(_pixelModel.zoom); queueDraw; } void panRelative(in Vector pixelDisplacement) { - _viewCentre = _viewCentre + pixelToModel(pixelDisplacement); + _pixelModel.panRelativePixel(pixelDisplacement); consolidateBounds; @@ -191,7 +183,7 @@ } void damageModel(in Rectangle area) { - _damage = _damage | modelToPixel(area); + _damage = _damage | _pixelModel.modelToPixel(area); } void damagePixel(in Rectangle area) { @@ -208,22 +200,16 @@ return bounds; } - void initialiseBounds() { + void initialiseBounds(in Rectangle viewBounds) { 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(lb, - lb.size), 2.0 * lb.size); - // - - // FIXME 0.25 - _zoom = 0.25 * _pixelsPerMillimetre; // ie 0.25 pixels represents a millimetre + _pixelModel = new PixelModel(0.25 * _pixelsPerMillimetre, paddedLayerBounds, viewBounds); - _canvasBounds = paddedLayerBounds; - _viewCentre = _canvasBounds.centre; - - _grid.zoomChanged(_zoom); + _grid.zoomChanged(_pixelModel.zoom); updateAdjustments; updateRulers; @@ -235,8 +221,7 @@ // FIXME likewise as above Rectangle paddedLayerBounds = expand(move(lb, - lb.size), 2.0 * lb.size); - Rectangle r = pixelToModel(_viewBounds); - _canvasBounds = r | paddedLayerBounds; + _pixelModel.consolidateCanvasBounds(paddedLayerBounds); updateAdjustments; updateRulers; @@ -245,11 +230,10 @@ bool onConfigure(GdkEventConfigure * event, Widget widget) { assert(widget is _drawingArea); - _viewBounds = Rectangle(Point(0.0, 0.0), Vector(cast(double)event.width, cast(double)event.height)); + Rectangle viewBounds = Rectangle(Point(0.0, 0.0), Vector(cast(double)event.width, cast(double)event.height)); - if (!_boundsValid) { - initialiseBounds; - _boundsValid = true; + if (_pixelModel is null) { + initialiseBounds(viewBounds); } else { consolidateBounds; @@ -271,24 +255,22 @@ scope pixelCr = new Context(dr); Rectangle pixelDamage = - event is null ? - _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)), + event is null ? _pixelModel.viewBounds : + // FIXME next line sucks + Rectangle(_pixelModel.viewBounds.position + Vector(cast(double)event.area.x, _pixelModel.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); + Rectangle modelDamage = _pixelModel.pixelToModel(pixelDamage); //trace("Pixel damage: %s, model damage: %s", pixelDamage, modelDamage); modelCr.save; pixelCr.save; { { // Setup model context and clip - modelCr.translate(0.0, _viewBounds.h); - modelCr.scale(_zoom, -_zoom); + modelCr.translate(0.0, _pixelModel.viewBounds.h); + modelCr.scale(_pixelModel.zoom, -_pixelModel.zoom); - // XXX revisit - immutable Vector modelSize = pixelToModel(_viewBounds.size); - immutable Point viewLeftBottom = _viewCentre - modelSize / 2.0; + immutable Point viewLeftBottom = _pixelModel.pixelToModel(Point(0.0, 0.0)); modelCr.translate(-viewLeftBottom.x, -viewLeftBottom.y); rectangle(modelCr, modelDamage); @@ -297,7 +279,7 @@ { // Setup pixel context and clip - pixelCr.translate(0.0, _viewBounds.h); + pixelCr.translate(0.0, _pixelModel.viewBounds.h); pixelCr.scale(1.0, -1.0); rectangle(pixelCr, pixelDamage); @@ -324,8 +306,8 @@ assert(widget is _drawingArea); //trace("Got button event\n"); - Point pixelPoint = Point(event.x + 0.5, _viewBounds.h - (event.y + 0.5)); - Point modelPoint = pixelToModel(pixelPoint); + Point pixelPoint = Point(event.x + 0.5, _pixelModel.viewBounds.h - (event.y + 0.5)); + Point modelPoint = _pixelModel.pixelToModel(pixelPoint); auto buttonEvent = new ButtonEvent(gtk2tkButtonAction(event.type), gtk2tkButtonName(event.button), @@ -343,8 +325,8 @@ bool onButtonRelease(GdkEventButton * event, Widget widget) { assert(widget is _drawingArea); - Point pixelPoint = Point(event.x + 0.5, _viewBounds.h - (event.y + 0.5)); - Point modelPoint = pixelToModel(pixelPoint); + Point pixelPoint = Point(event.x + 0.5, _pixelModel.viewBounds.h - (event.y + 0.5)); + Point modelPoint = _pixelModel.pixelToModel(pixelPoint); auto buttonEvent = new ButtonEvent(gtk2tkButtonAction(event.type), gtk2tkButtonName(event.button), @@ -409,8 +391,8 @@ gtk_widget_event(_hRuler.getWidgetStruct(), cast(GdkEvent *)event); gtk_widget_event(_vRuler.getWidgetStruct(), cast(GdkEvent *)event); - Point pixelPoint = Point(event.x + 0.5, _viewBounds.h - (event.y + 0.5)); - Point modelPoint = pixelToModel(pixelPoint); + Point pixelPoint = Point(event.x + 0.5, _pixelModel.viewBounds.h - (event.y + 0.5)); + Point modelPoint = _pixelModel.pixelToModel(pixelPoint); auto motionEvent = new MotionEvent(pixelPoint, modelPoint, @@ -427,8 +409,8 @@ assert(widget is _drawingArea); //writefln("Got scroll\n"); - Point pixelPoint = Point(event.x + 0.5, _viewBounds.h - (event.y + 0.5)); - Point modelPoint = pixelToModel(pixelPoint); + Point pixelPoint = Point(event.x + 0.5, _pixelModel.viewBounds.h - (event.y + 0.5)); + Point modelPoint = _pixelModel.pixelToModel(pixelPoint); auto scrollEvent = new ScrollEvent(gtk2tkDirection(event.direction), pixelPoint, @@ -472,8 +454,8 @@ bool onEnterNotify(GdkEventCrossing * event, Widget widget) { assert(widget is _drawingArea); - Point pixelPoint = Point(event.x + 0.5, _viewBounds.h - (event.y + 0.5)); - Point modelPoint = pixelToModel(pixelPoint); + Point pixelPoint = Point(event.x + 0.5, _pixelModel.viewBounds.h - (event.y + 0.5)); + Point modelPoint = _pixelModel.pixelToModel(pixelPoint); auto crossingEvent = new CrossingEvent(gtk2tkCrossingMode(event.mode), pixelPoint, @@ -492,8 +474,8 @@ bool onLeaveNotify(GdkEventCrossing * event, Widget widget) { assert(widget is _drawingArea); - Point pixelPoint = Point(event.x + 0.5, _viewBounds.h - (event.y + 0.5)); - Point modelPoint = pixelToModel(pixelPoint); + Point pixelPoint = Point(event.x + 0.5, _pixelModel.viewBounds.h - (event.y + 0.5)); + Point modelPoint = _pixelModel.pixelToModel(pixelPoint); auto crossingEvent = new CrossingEvent(gtk2tkCrossingMode(event.mode), pixelPoint, @@ -548,21 +530,19 @@ GtkAdjustment * hGtkAdjustment = _hAdjustment.getAdjustmentStruct; GtkAdjustment * vGtkAdjustment = _vAdjustment.getAdjustmentStruct; - Point viewLeftBottom = Point(gtk_adjustment_get_value(hGtkAdjustment), - gtk_adjustment_get_value(vGtkAdjustment)); + Point oldViewLeftBottom = _pixelModel.pixelToModel(Point(0.0, 0.0)); + Point newViewLeftBottom = Point(gtk_adjustment_get_value(hGtkAdjustment), + gtk_adjustment_get_value(vGtkAdjustment)); - Vector modelSize = pixelToModel(_viewBounds.size); // XXX - - _viewCentre = viewLeftBottom + modelSize / 2.0; + _pixelModel.panRelativeModel(newViewLeftBottom - oldViewLeftBottom); updateRulers; queueDraw; } void updateRulers() { - immutable Vector modelSize = pixelToModel(_viewBounds.size); // XXX - immutable Point viewLeftBottom = _viewCentre - modelSize / 2.0; - immutable Point viewRightTop = _viewCentre + modelSize / 2.0; + immutable Point viewLeftBottom = _pixelModel.pixelToModel(Point(0.0, 0.0)); + immutable Point viewRightTop = _pixelModel.pixelToModel(_pixelModel.viewBounds.corner1); // Define these just to obtain the position // below and we can preserve it @@ -572,42 +552,43 @@ _hRuler.setRange(viewLeftBottom.x, viewRightTop.x, position, - _zoom * 50.0); + _pixelModel.zoom * 50.0); _vRuler.getRange(lower, upper, position, maxSize); _vRuler.setRange(viewRightTop.y, viewLeftBottom.y, position, - _zoom * 50.0); + _pixelModel.zoom * 50.0); } void updateAdjustments() { - immutable Vector modelSize = pixelToModel(_viewBounds.size); // XXX - immutable Point viewLeftBottom = _viewCentre - modelSize / 2.0; - immutable Point viewRightTop = _viewCentre + modelSize / 2.0; + immutable Point viewLeftBottom = _pixelModel.pixelToModel(Point(0.0, 0.0)); + immutable Point viewRightTop = _pixelModel.pixelToModel(_pixelModel.viewBounds.corner1); - // Adjust the canvas size if necessary - _canvasBounds = Rectangle(minExtents(_canvasBounds.corner0, viewLeftBottom), - maxExtents(_canvasBounds.corner1, viewRightTop)); + // Adjust the canvas size if necessary FIXME is this required?? + _pixelModel.canvasAccommodate(Rectangle(viewLeftBottom, viewRightTop)); + + // FIXME + Rectangle modelSize = _pixelModel.pixelToModel(_pixelModel.viewBounds); // Update the adjustments GtkAdjustment * hGtkAdjustment = _hAdjustment.getAdjustmentStruct; GtkAdjustment * vGtkAdjustment = _vAdjustment.getAdjustmentStruct; - gtk_adjustment_set_lower(hGtkAdjustment, _canvasBounds.corner0.x); - gtk_adjustment_set_upper(hGtkAdjustment, _canvasBounds.corner1.x); + gtk_adjustment_set_lower(hGtkAdjustment, _pixelModel.canvasBounds.x0); + gtk_adjustment_set_upper(hGtkAdjustment, _pixelModel.canvasBounds.x1); 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_step_increment(hGtkAdjustment, _pixelModel.canvasBounds.w / 16.0); + gtk_adjustment_set_page_increment(hGtkAdjustment, _pixelModel.canvasBounds.w / 4.0); + gtk_adjustment_set_page_size(hGtkAdjustment, modelSize.w); - gtk_adjustment_set_lower(vGtkAdjustment, _canvasBounds.corner0.y); - gtk_adjustment_set_upper(vGtkAdjustment, _canvasBounds.corner1.y); + gtk_adjustment_set_lower(vGtkAdjustment, _pixelModel.canvasBounds.y0); + gtk_adjustment_set_upper(vGtkAdjustment, _pixelModel.canvasBounds.y1); 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); - gtk_adjustment_set_page_size(vGtkAdjustment, modelSize.y); + gtk_adjustment_set_step_increment(vGtkAdjustment, _pixelModel.canvasBounds.h / 16.0); + gtk_adjustment_set_page_increment(vGtkAdjustment, _pixelModel.canvasBounds.h / 4.0); + gtk_adjustment_set_page_size(vGtkAdjustment, modelSize.h); _hAdjustment.changed; _hAdjustment.valueChanged; @@ -619,52 +600,19 @@ if (_damage.valid) { int x, y, w, h; _damage.getQuantised(x, y, w, h); - _drawingArea.queueDrawArea(x, cast(int)_viewBounds.h - (y + h), w, h); + _drawingArea.queueDrawArea(x, cast(int)_pixelModel.viewBounds.h - (y + h), w, h); _damage = Rectangle.DEFAULT; } } - static double clampZoom(in double zoom) { return clamp(zoom, 0.2, 10.0); } - - 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)); - } - void onRealize(Widget widget) { assert(widget is _drawingArea); //writefln("Got realize\n"); _drawingArea.grabFocus(); } - bool _boundsValid; Rectangle _damage; // pixels - - // Model units are millimetres - // Screen units are pixels - double _zoom; // pixels-per-model-unit - 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: bounds of the canvas in millimetres + PixelModel _pixelModel; // Child widgets: HRuler _hRuler; diff -r 0e61702c6ea6 -r 0f7cf6c6f206 doodle/tk/pixel_model.d --- a/doodle/tk/pixel_model.d Sat Aug 14 20:05:55 2010 +0930 +++ b/doodle/tk/pixel_model.d Sat Aug 14 20:48:41 2010 +0930 @@ -4,14 +4,24 @@ import doodle.tk.geometry; } +private { + import doodle.core.misc; +} + // FIXME consider using the term Screen instead of Pixel... +// This class manages the relationship between pixel space and model space. +// It provides convenient high-level operations. +// +// x and y run right and up respectively for pixel and model space + class PixelModel { this(in double zoom, in Rectangle canvasBounds, in Rectangle viewBounds) { _zoom = zoom; _viewBounds = viewBounds; _canvasBounds = canvasBounds; + // Choose the centre of the canvas as the centre of the view _viewCentre = _canvasBounds.centre; } @@ -20,9 +30,35 @@ _canvasBounds = r | requiredCanvasBounds; } + void canvasAccommodate(in Rectangle bounds) { + _canvasBounds = _canvasBounds | bounds; + } + + void zoomRelative(in double factor, in Point pixelDatum) { + // Work out pixel distance from current centre to datum, + // Do the zoom, then work out the new centre that keeps the + // pixel distance the same + + Point oldModelDatum = pixelToModel(pixelDatum); + Vector pixelDistance = modelToPixel(oldModelDatum - _viewCentre); + _zoom = clampZoom(zoom * factor); + _viewCentre = oldModelDatum - pixelToModel(pixelDistance); + } + + void panRelativePixel(in Vector pixelDisplacement) { + _viewCentre = _viewCentre + pixelToModel(pixelDisplacement); + } + + void panRelativeModel(in Vector modelDisplacement) { + _viewCentre = _viewCentre + modelDisplacement; + } + // 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; } + double normalZoom(in double pixelsPerMillimetre) const { return _zoom / pixelsPerMillimetre; } + double zoom() const { return _zoom; } + Rectangle viewBounds() const { return _viewBounds; } + Rectangle canvasBounds() const { return _canvasBounds; } Point modelToPixel(in Point model) const { return _viewBounds.centre + _zoom * (model - _viewCentre); } Point pixelToModel(in Point pixel) const { return _viewCentre + (pixel - _viewBounds.centre) / _zoom; } @@ -32,6 +68,8 @@ Rectangle pixelToModel(in Rectangle model) const { return Rectangle(pixelToModel(model.position), pixelToModel(model.size)); } private { + static double clampZoom(in double zoom) { return clamp(zoom, 0.1, 10.0); } + // Screen units are pixels // Model units are millimetres double _zoom; // pixels-per-millimetre