changeset 79:535bae7a7305

Checkpoint
author "David Bryant <bagnose@gmail.com>"
date Sun, 15 Aug 2010 23:18:05 +0930
parents 024a5608087f
children b759414d2b72
files doodle/fig/select_tool.d doodle/gtk/cairo.d doodle/gtk/canvas.d doodle/gtk/opengl.d doodle/tk/color.d doodle/tk/drawing.d doodle/tk/geometry.d doodle/tk/pixel_model.d
diffstat 8 files changed, 169 insertions(+), 107 deletions(-) [+]
line wrap: on
line diff
--- a/doodle/fig/select_tool.d	Sun Aug 15 15:19:14 2010 +0930
+++ b/doodle/fig/select_tool.d	Sun Aug 15 23:18:05 2010 +0930
@@ -28,7 +28,7 @@
     override bool handleButtonRelease(scope IViewport viewport, in ButtonEvent event) {
         if (event.buttonName == ButtonName.LEFT && _active) {
             _active = false;
-            viewport.damageScreen(feather(Rectangle(_anchorPoint, _currentPoint), LINE_WIDTH / 2.0));
+            viewport.damageScreen(growCentre(Rectangle(_anchorPoint, _currentPoint), LINE_WIDTH));
             viewport.setCursor(Cursor.DEFAULT);
             return true;
         }
@@ -39,9 +39,9 @@
 
     override bool handleMotion(scope IViewport viewport, in MotionEvent event) {
         if (_active) {
-            viewport.damageScreen(feather(Rectangle(_anchorPoint, _currentPoint), LINE_WIDTH / 2.0));
+            viewport.damageScreen(growCentre(Rectangle(_anchorPoint, _currentPoint), LINE_WIDTH));
             _currentPoint = event.pixelPoint;
-            viewport.damageScreen(feather(Rectangle(_anchorPoint, _currentPoint), LINE_WIDTH / 2.0));
+            viewport.damageScreen(growCentre(Rectangle(_anchorPoint, _currentPoint), LINE_WIDTH));
         }
 
         return false;
--- a/doodle/gtk/cairo.d	Sun Aug 15 15:19:14 2010 +0930
+++ b/doodle/gtk/cairo.d	Sun Aug 15 23:18:05 2010 +0930
@@ -2,9 +2,9 @@
 
 public {
     import doodle.tk.drawing;
+    import cairo.Context;
 }
 
-/*
 final class CairoDrawable : Drawable {
     this(Context cr) {
         assert(cr);
@@ -13,11 +13,84 @@
 
     // Drawing overrides:
 
+    void setLineStyle(LineStyle style) {
+        switch (style) {
+        case LineStyle.SOLID:
+            _cr.setDash([ 4.0, 4.0 ], 0.0);
+            break;
+        case LineStyle.DASHED:
+            _cr.setDash([ 4.0, 4.0 ], 0.0);
+            break;
+        case LineStyle.DOTTED:
+            _cr.setDash([ 4.0, 4.0 ], 0.0);
+            break;
+        default:
+            assert(0);
+        }
+    }
+
+    void setLineWidth(in double width) { _cr.setLineWidth(width); }
+
+    void setColor(in Color color) { _cr.setSourceRgba(color.r, color.g, color.b, color.a); }
+
+    void translate(in Point p) { _cr.translate(p.x, p.y); }
+    void scale(in double s) { _cr.scale(s, s); }
+
     void pushState() { _cr.save; }
     void popState() { _cr.restore; }
 
+    void drawRectangle(in Rectangle rectangle, bool fill) {
+        _cr.rectangle(rectangle.position.x, rectangle.position.y,
+                      rectangle.size.x, rectangle.size.y);
+    }
+
+    void drawEllipse(in Rectangle rectangle, bool fill) {
+        // NYI
+    }
+
+    void drawSegment(in Segment segment) {
+        _cr.moveTo(segment.begin.x, segment.begin.y);
+        _cr.lineTo(segment.end.x, segment.end.y);
+        _cr.stroke;
+    }
+
+    void drawHLine(in double y, in double x0, in double x1) {
+        _cr.moveTo(x0, y);
+        _cr.lineTo(x1, y);
+        _cr.stroke;
+    }
+
+    void drawVLine(in double x, in double y0, in double y1) {
+        _cr.moveTo(x, y0);
+        _cr.lineTo(x, y1);
+    }
+
+    void drawPoly(in Point[] points, bool fill) {
+        assert(points.length >= 2);
+        foreach(i, p; points) {
+            if (i == 0) { _cr.moveTo(p.x, p.y); }
+            else { _cr.lineTo(p.x, p.y); }
+        }
+        if (fill) { _cr.fill; } else { _cr.stroke; }
+    }
+
+    void setFontFace(in FontFace face) {
+        // NYI
+    }
+
+    void setFontSize(in double size) {
+        // NYI
+    }
+
+    void drawText(in string text) {
+        // NYI
+    }
+
+    void measureText(in string text, out Rectangle logicalBounds, out Rectangle totalBounds) const {
+        // NYI
+    }
+
     private {
         Context _cr;
     }
 }
-*/
--- a/doodle/gtk/canvas.d	Sun Aug 15 15:19:14 2010 +0930
+++ b/doodle/gtk/canvas.d	Sun Aug 15 23:18:05 2010 +0930
@@ -35,23 +35,18 @@
     import std.stdio;
 }
 
-//
-// TODO
-// Pass out a scope Drawing reference to the layers for drawing on instead of cairo context.
-// Create a class called LayerStack so we don't have to provide that non-gtk behaviour here.
-//
-
 final class Canvas : Table, private IViewport {
     this(in Layer[] layers, IEventHandler eventHandler, IGrid grid, in double pixelsPerMillimetre) {
         super(3, 3, 0);
 
         _damageScreen = Rectangle.DEFAULT;
 
-        _layerStack = new LayerStack(layers);
         _eventHandler = eventHandler;
         _grid = grid;
         _pixelsPerMillimetre = pixelsPerMillimetre;
 
+        _layerStack = new LayerStack(layers);
+
         // Create our child widgets and register callbacks
 
         _hRuler = new HRuler;
@@ -136,6 +131,13 @@
                AttachOptions.SHRINK,
                AttachOptions.FILL | AttachOptions.EXPAND,
                0, 0);
+
+        _cursors = [
+            Cursor.DEFAULT   : CursorType.ARROW,
+            Cursor.HAND      : CursorType.HAND1,
+            Cursor.CROSSHAIR : CursorType.CROSSHAIR,
+            Cursor.PENCIL    : CursorType.PENCIL
+            ];
     }
 
     protected {         // XXX the compiler complains about unimplemented methods if this is private
@@ -144,9 +146,7 @@
 
         void zoomRelative(in Point screenDatum, in double factor) {
             _screenModel.zoomRelative(factor, screenDatum);
-
             consolidateBounds;
-
             updateAdjustments;
             updateRulers;
             _grid.zoomChanged(_screenModel.zoom);
@@ -155,36 +155,14 @@
 
         void panRelative(in Vector screenDisplacement) {
             _screenModel.panRelativeScreen(screenDisplacement);
-
             consolidateBounds;
-
             updateAdjustments;
             updateRulers;
             queueDraw;
         }
 
         void setCursor(in Cursor cursor) {
-            // FIXME how about an associative array instead of a switch statement?
-            CursorType cursorType;
-
-            switch (cursor) {
-            case Cursor.DEFAULT:
-                cursorType = CursorType.ARROW;
-                break;
-            case Cursor.HAND:
-                cursorType = CursorType.HAND1;
-                break;
-            case Cursor.CROSSHAIR:
-                cursorType = CursorType.CROSSHAIR;
-                break;
-            case Cursor.PENCIL:
-                cursorType = CursorType.PENCIL;
-                break;
-            default:
-                assert(0);
-            }
-
-            _drawingArea.setCursor(new gdk.Cursor.Cursor(cursorType));
+            _drawingArea.setCursor(new gdk.Cursor.Cursor(_cursors[cursor]));
         }
 
         void damageModel(in Rectangle area) {
@@ -199,14 +177,9 @@
     private {
 
         void initialiseBounds(in Rectangle viewBoundsScreen) {
-            Rectangle lb = _layerStack.bounds;
-
-            // 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);
-
+            Rectangle layerBounds = _layerStack.bounds;
+            Rectangle paddedLayerBounds = growCentre(layerBounds, 2 * layerBounds.size);
             _screenModel = new ScreenModel(0.25 * _pixelsPerMillimetre, paddedLayerBounds, viewBoundsScreen);
-
             _grid.zoomChanged(_screenModel.zoom);
 
             updateAdjustments;
@@ -214,11 +187,8 @@
         }
 
         void consolidateBounds() {
-            Rectangle lb = _layerStack.bounds;
-
-            // FIXME likewise as above
-            Rectangle paddedLayerBounds = expand(move(lb, - lb.size), 2.0 * lb.size);
-
+            Rectangle layerBounds = _layerStack.bounds;
+            Rectangle paddedLayerBounds = growCentre(layerBounds, 2 * layerBounds.size);
             _screenModel.consolidateCanvasBounds(paddedLayerBounds);
 
             updateAdjustments;
@@ -229,13 +199,8 @@
             assert(widget is _drawingArea);
 
             Rectangle viewBoundsScreen = Rectangle(Point(0.0, 0.0), Vector(cast(double)event.width, cast(double)event.height));
-
-            if (_screenModel is null) {
-                initialiseBounds(viewBoundsScreen);
-            }
-            else {
-                consolidateBounds;
-            }
+            if (_screenModel is null) { initialiseBounds(viewBoundsScreen); }
+            else { consolidateBounds; }
 
             return true;
         }
@@ -254,14 +219,11 @@
 
             Rectangle screenDamage =
                 event is null ? _screenModel.viewBoundsScreen :
-                // FIXME next line sucks
                 Rectangle(_screenModel.viewBoundsScreen.position + Vector(cast(double)event.area.x, _screenModel.viewBoundsScreen.h - cast(double)(event.area.y + event.area.height)),
                           Vector(cast(double)event.area.width, cast(double)event.area.height));
 
             Rectangle modelDamage = _screenModel.screenToModel(screenDamage);
 
-            //trace("Screen damage: %s, model damage: %s", screenDamage, modelDamage);
-
             modelCr.save; screenCr.save; {
                 {
                     // Setup model context and clip
@@ -400,7 +362,7 @@
         }
 
         void updateRulers() {
-            immutable Point viewLeftBottom = _screenModel.screenToModel(_screenModel.viewBoundsScreen.corner1);
+            immutable Point viewLeftBottom = _screenModel.screenToModel(_screenModel.viewBoundsScreen.corner0);
             immutable Point viewRightTop = _screenModel.screenToModel(_screenModel.viewBoundsScreen.corner1);
 
             // Define these just to obtain the position
@@ -424,11 +386,10 @@
             immutable Point viewLeftBottom = _screenModel.screenToModel(Point(0.0, 0.0));
             immutable Point viewRightTop = _screenModel.screenToModel(_screenModel.viewBoundsScreen.corner1);
 
-            // Adjust the canvas size if necessary FIXME is this required??
+            // Adjust the canvas size if necessary
             _screenModel.canvasAccommodate(Rectangle(viewLeftBottom, viewRightTop));
 
-            // FIXME
-            Rectangle modelSize = _screenModel.screenToModel(_screenModel.viewBoundsScreen);
+            Rectangle viewBoundsModel = _screenModel.viewBoundsModel;
 
             // Update the adjustments
 
@@ -440,14 +401,14 @@
             gtk_adjustment_set_value(hGtkAdjustment, viewLeftBottom.x);
             gtk_adjustment_set_step_increment(hGtkAdjustment, _screenModel.canvasBoundsModel.w / 16.0);
             gtk_adjustment_set_page_increment(hGtkAdjustment, _screenModel.canvasBoundsModel.w / 4.0);
-            gtk_adjustment_set_page_size(hGtkAdjustment, modelSize.w);
+            gtk_adjustment_set_page_size(hGtkAdjustment, viewBoundsModel.w);
 
             gtk_adjustment_set_lower(vGtkAdjustment, _screenModel.canvasBoundsModel.y0);
             gtk_adjustment_set_upper(vGtkAdjustment, _screenModel.canvasBoundsModel.y1);
             gtk_adjustment_set_value(vGtkAdjustment, viewLeftBottom.y);
             gtk_adjustment_set_step_increment(vGtkAdjustment, _screenModel.canvasBoundsModel.h / 16.0);
             gtk_adjustment_set_page_increment(vGtkAdjustment, _screenModel.canvasBoundsModel.h / 4.0);
-            gtk_adjustment_set_page_size(vGtkAdjustment, modelSize.h);
+            gtk_adjustment_set_page_size(vGtkAdjustment, viewBoundsModel.h);
 
             _hAdjustment.changed;
             _hAdjustment.valueChanged;
@@ -466,14 +427,15 @@
 
         void onRealize(Widget widget) {
             assert(widget is _drawingArea);
-            //writefln("Got realize\n");
             _drawingArea.grabFocus();
         }
 
-        LayerStack    _layerStack;
         IEventHandler _eventHandler;
         IGrid         _grid;
         double        _pixelsPerMillimetre;
+        LayerStack    _layerStack;
+
+        immutable CursorType[Cursor] _cursors;
 
         // Child widgets:
         HRuler        _hRuler;
@@ -484,7 +446,7 @@
         Adjustment    _vAdjustment;
         VScrollbar    _vScrollbar;
 
-        Rectangle     _damageScreen;          // in screens
+        Rectangle     _damageScreen;
         ScreenModel   _screenModel;
     }
 }
--- a/doodle/gtk/opengl.d	Sun Aug 15 15:19:14 2010 +0930
+++ b/doodle/gtk/opengl.d	Sun Aug 15 23:18:05 2010 +0930
@@ -5,6 +5,11 @@
 }
 
 /*
-final class OpenGLDrawing : Drawing {
+final class OpenGLDrawing : Drawable {
+
+    // Drawing overrides:
+
+    void setLineStyle(LineStyle style) {
+    }
 }
 */
--- a/doodle/tk/color.d	Sun Aug 15 15:19:14 2010 +0930
+++ b/doodle/tk/color.d	Sun Aug 15 23:18:05 2010 +0930
@@ -14,6 +14,11 @@
     // TODO
     // hsv, grey, etc.
 
+    double r() const { return _r; }
+    double g() const { return _g; }
+    double b() const { return _b; }
+    double a() const { return _a; }
+
     private {
         double _r, _g, _b, _a;
     }
--- a/doodle/tk/drawing.d	Sun Aug 15 15:19:14 2010 +0930
+++ b/doodle/tk/drawing.d	Sun Aug 15 23:18:05 2010 +0930
@@ -2,6 +2,7 @@
 
 public {
     import doodle.tk.geometry;
+    import doodle.tk.color;
 }
 
 interface Drawable {
@@ -11,32 +12,36 @@
         DOTTED
     }
 
-    void setLineStyle(LineStyle style);
-    void setLineThickness(in double thickness);
-    //void setLineColor(in Color color);
-    //void setFillColor(in Color color);
+    enum FontFace {
+        NORMAL
+    }
+
+    // Low-level state manipulation
 
-    void drawRectangle(in Rectangle rectangle, bool fill);
-    void drawEllipse(in Rectangle rectangle, bool fill);
-    void drawSegment(in Segment segment);
-    void drawHLine(in double y0, in double x0, in double x1);
-    void drawVLine(in double x0, in double y0, in double y1);
-    void drawPoly(in Point[] points, bool fill);
+    void setLineStyle(LineStyle style);
+    void setLineWidth(in double width);
+    void setColor(in Color color);
+
+    void translate(in Point p);
+    void scale(in double s);
 
     void pushState();           // Copies all of current state
     void popState();            // Restores all of previous state
 
-    void translate(in Vector v);
-    void translate(in Point p);
-    void scale(in Vector v);
-    void zoom(in double z);
+    // High-level drawing routines
 
-    //void setFontFace;
-    //void setFontSize;
+    void drawRectangle(in Rectangle rectangle, bool fill);
+    void drawEllipse(in Rectangle rectangle, bool fill);
+    void drawSegment(in Segment segment);
+    void drawHLine(in double y, in double x0, in double x1);
+    void drawVLine(in double x, in double y0, in double y1);
+    void drawPoly(in Point[] points, bool fill);
+
+    // Text routines
+
+    void setFontFace(in FontFace face);
+    void setFontSize(in double size);
     void drawText(in string text);
 
-    void measureText(in string text, out Rectangle logicalBounds, out Rectangle totalBounds);
-
-    // How to fit fonts, metrics, etc in here?
-    // Initially let's just support one font
+    void measureText(in string text, out Rectangle logicalBounds, out Rectangle totalBounds) const;
 }
--- a/doodle/tk/geometry.d	Sun Aug 15 15:19:14 2010 +0930
+++ b/doodle/tk/geometry.d	Sun Aug 15 23:18:05 2010 +0930
@@ -113,7 +113,8 @@
     }
 }
 
-Vector normalise(in Vector v) {
+/*
+Vector normal(in Vector v) {
     double l = v.length;
 
     if (l < 1e-9) {         // TODO consolidate numerical stability constants
@@ -124,6 +125,7 @@
         return v / l;
     }
 }
+*/
 
 //
 // A rectangle in 2D space.
@@ -152,12 +154,12 @@
         this(corner1.x, corner1.y, corner.x - corner1.x, corner.y - corner1.y);
     }
 
-    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; }
+    double x0() const { return _position.x; }
+    double y0() const { return _position.y; }
+    double w()  const { return _size.x; }
+    double h()  const { return _size.y; }
+    double x1() const { return x0 + w; }
+    double y1() const { return y0 + h; }
 
     alias position corner0;
     Point position() const { return _position; }
@@ -236,9 +238,18 @@
     }
 }
 
+Rectangle growCentre(in Rectangle r, in Vector amount) {
+    return Rectangle(r.x0 - amount.x / 2, r.y0 - amount.y / 2, r.w + amount.x, r.h + amount.y);
+}
+
+Rectangle growCentre(in Rectangle r, in double amount) {
+    return Rectangle(r.x0 - amount / 2, r.y0 - amount / 2, r.w + amount, r.h + amount);
+}
+
 // TODO review these functions.
 // Want a clear and simple set.
 
+/+
 Rectangle move(in Rectangle r, in Vector displacement) {
     return Rectangle(r.position + displacement, r.size);
 }
@@ -260,15 +271,18 @@
 Rectangle shrink(in Rectangle r, in Vector shrink_amount) {
     return Rectangle(r.position, r.size - shrink_amount);
 }
++/
 
 // Operations about the centre
 
+/+
 Rectangle feather(in Rectangle r, double amount) {          // feather isn't the right name
     assert(amount >= 0.0);
     assert(!isnan(amount));
     return Rectangle(Point(r.position.x - amount, r.position.y - amount),
                      Vector(r.size.x + 2.0 * amount, r.size.y + 2.0 * amount));
 }
++/
 
 private {
     // This function computes the intersection of two lines.
@@ -388,9 +402,11 @@
     }
 }
 
+/*
 Segment reverse(in Segment s) {
     return Segment(s.end, s.begin);
 }
+*/
 
 bool intersection(in Segment a, in Segment b, out Point p) {
     Point pa1 = a.begin;
--- a/doodle/tk/pixel_model.d	Sun Aug 15 15:19:14 2010 +0930
+++ b/doodle/tk/pixel_model.d	Sun Aug 15 23:18:05 2010 +0930
@@ -24,13 +24,8 @@
         _viewCentreModel = _canvasBoundsModel.centre;
     }
 
-    void consolidateCanvasBounds(in Rectangle requiredCanvasBounds) {
-        _canvasBoundsModel = screenToModel(_viewBoundsScreen) | requiredCanvasBounds;
-    }
-
-    void canvasAccommodate(in Rectangle bounds) {
-        _canvasBoundsModel = _canvasBoundsModel | bounds;
-    }
+    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,
@@ -46,12 +41,13 @@
     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 screensPerMillimetre) const { return _zoom / screensPerMillimetre; }
+    // 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; }
@@ -61,7 +57,7 @@
     Rectangle screenToModel(in Rectangle model) const { return Rectangle(screenToModel(model.position), screenToModel(model.size)); }
 
     private {
-        static double clampZoom(in double zoom) { return clamp(zoom, 0.1, 10.0); }
+        static double clampZoom(in double zoom) { return clamp(zoom, 1e-1, 1e2); }
 
         // Screen units are pixels
         // Model units are millimetres