[dasher] Added DasherSpaceLine to draw lines on screen that are straight in Dasher-space



commit 05c4d21fe28272110c5b76ad44fca7313a8b5f00
Author: Alan Lawrence <acl33 inf phy cam ac uk>
Date:   Wed Jan 6 21:24:27 2010 +0000

    Added DasherSpaceLine to draw lines on screen that are straight in Dasher-space
    
    (and hence curved on the screen if nonlinearity is enabled.) These can be used
    for the mouse line according to new BP_CURVE_MOUSE_LINE (off by default).
    
    Also fix DasherViewSq & IntfBase to handle runtime changes in BP_NONLINEAR_Y

 Src/DasherCore/DasherInterfaceBase.cpp |    1 +
 Src/DasherCore/DasherView.cpp          |   50 +++++++++++++++++++++++
 Src/DasherCore/DasherView.h            |   20 +++++++++-
 Src/DasherCore/DasherViewSquare.cpp    |   68 ++++++++++++++++++++++++++++++--
 Src/DasherCore/DasherViewSquare.h      |    4 ++
 Src/DasherCore/DefaultFilter.cpp       |    6 ++-
 Src/DasherCore/Parameters.h            |    3 +-
 7 files changed, 144 insertions(+), 8 deletions(-)
---
diff --git a/Src/DasherCore/DasherInterfaceBase.cpp b/Src/DasherCore/DasherInterfaceBase.cpp
index dbd7881..d8b785f 100644
--- a/Src/DasherCore/DasherInterfaceBase.cpp
+++ b/Src/DasherCore/DasherInterfaceBase.cpp
@@ -326,6 +326,7 @@ void CDasherInterfaceBase::InterfaceEventHandler(Dasher::CEvent *pEvent) {
       ScheduleRedraw();
       break;
     case LP_MARGIN_WIDTH:
+    case BP_NONLINEAR_Y:
         ScheduleRedraw();
         break;
     case LP_NODE_BUDGET:
diff --git a/Src/DasherCore/DasherView.cpp b/Src/DasherCore/DasherView.cpp
index 1c83283..08b9f6a 100644
--- a/Src/DasherCore/DasherView.cpp
+++ b/Src/DasherCore/DasherView.cpp
@@ -29,6 +29,7 @@
 #include "DasherScreen.h"
 
 using namespace Dasher;
+using std::vector;
 
 // Track memory leaks on Windows to the line that new'd the memory
 #ifdef _WIN32
@@ -105,6 +106,55 @@ void CDasherView::Display() {
   m_pScreen->Display();
 }
 
+void CDasherView::DasherSpaceLine(myint x1, myint y1, myint x2, myint y2, int iWidth, int iColor) {
+  if (!ClipLineToVisible(x1, y1, x2, y2)) return;
+  vector<CDasherScreen::point> vPoints;
+  CDasherScreen::point p;
+  Dasher2Screen(x1, y1, p.x, p.y);
+  vPoints.push_back(p);
+  DasherLine2Screen(x1,y1,x2,y2,vPoints);
+  CDasherScreen::point *pts = new CDasherScreen::point[vPoints.size()];
+  for (int i = vPoints.size(); i-->0; ) pts[i] = vPoints[i];
+  Screen()->Polyline(pts, vPoints.size(), iWidth, iColor);
+}
+
+bool CDasherView::ClipLineToVisible(myint &x1, myint &y1, myint &x2, myint &y2) {
+  if (x1 > x2) return ClipLineToVisible(x2,y2,x1,y1);
+  //ok. have x1 <= x2...
+  myint iDasherMinX, iDasherMinY, iDasherMaxX, iDasherMaxY;
+  VisibleRegion(iDasherMinX, iDasherMinY, iDasherMaxX, iDasherMaxY);
+  if (x1 > iDasherMaxX) {
+    DASHER_ASSERT(x2>iDasherMaxX);
+    return false; //entirely offscreen!
+  }
+  if (x2 < iDasherMinX) {
+    DASHER_ASSERT(x1<iDasherMinX);
+    return false;
+  }
+  if (y1 < iDasherMinY && y2 < iDasherMinY) return false;
+  if (y1 > iDasherMaxY && y2 > iDasherMaxY) return false;
+  if (x1 < iDasherMinX) {
+    y1 = y2+((y1-y2)*(iDasherMinX-x2)/(x1 - x2));
+    x1 = iDasherMinX;
+  }
+  if (x2 > iDasherMaxX) {
+    y2 = y1 + (y2-y1)*(iDasherMaxX-x1)/(x2-x1);
+    x2 = iDasherMaxX;
+  }
+  for (int i=0; i<2; i++) {
+    myint &y(i ? y2 : y1), &oy(i ? y1 : y2);
+    myint &x(i ? x2 : x1), &ox(i ? x1 : x2);
+    if (y<iDasherMinY) {
+      x = ox- (ox-x)*(oy-iDasherMinY)/(oy-y);
+      y = iDasherMinY;
+    } else if (y>iDasherMaxY) {
+      x = ox-(ox-x)*(oy-iDasherMaxY)/(oy-y);
+      y = iDasherMaxY;
+    }
+  }
+  return true;
+}
+
 /// Draw a polyline specified in Dasher co-ordinates
 
 void CDasherView::DasherPolyline(myint *x, myint *y, int n, int iWidth, int iColour) {
diff --git a/Src/DasherCore/DasherView.h b/Src/DasherCore/DasherView.h
index 9d45981..28416f7 100644
--- a/Src/DasherCore/DasherView.h
+++ b/Src/DasherCore/DasherView.h
@@ -6,7 +6,6 @@
 #define __DasherView_h_
 
 namespace Dasher {
-  class CDasherScreen;
   class CDasherModel;
   class CDasherInput; // Why does DasherView care about input? - pconlon
   class CDasherComponent;
@@ -17,6 +16,7 @@ namespace Dasher {
 #include "DasherTypes.h"
 #include "DasherComponent.h"
 #include "ExpansionPolicy.h"
+#include "DasherScreen.h"
 
 /// \defgroup View Visualisation of the model
 /// @{
@@ -136,6 +136,9 @@ public:
   /// Basic drawing primitives specified in Dasher coordinates.
   /// @{
 
+  ///Draw a straight line in Dasher-space - which may be curved on the screen...
+  void DasherSpaceLine(myint x1, myint y1, myint x2, myint y2, int iWidth, int iColour);
+  
   ///
   /// Draw a polyline specified in Dasher co-ordinates
   ///
@@ -168,6 +171,21 @@ public:
   /// @}
 
 protected:
+  /// Clips a line (specified in Dasher co-ordinates) to the visible region
+  /// by intersecting with all boundaries.
+  /// \return true if any part of the line was within the visible region; in this case, (x1,y1)-(x2,y2) delineate exactly that part
+  /// false if the line would be entirely outside the visible region; x1, y1, x2, y2 unaffected.
+  bool ClipLineToVisible(myint &x1, myint &y1, myint &x2, myint &y2); 
+  
+  ///Convert a straight line in Dasher-space, to coordinates for a corresponding polyline on the screen
+  /// (because of nonlinearity, this may require multiple line segments)
+  /// \param x1,y1 Dasher co-ordinates of start of line segment; note that these are guaranteed within VisibleRegion.
+  /// \param x2,y2 Dasher co-ordinates of end of line segment; also guaranteed within VisibleRegion.
+  /// \param vPoints vector to which to add screen points. Note that at the point that DasherLine2Screen is called,
+  /// the screen coordinates of the first point should already have been added to this vector; DasherLine2Screen
+  /// will then add exactly one CDasherScreen::point for each line segment required.
+  virtual void DasherLine2Screen(myint x1, myint y1, myint x2, myint y2, std::vector<CDasherScreen::point> &vPoints)=0;
+  
   // Orientation of Dasher Screen
 /*   inline void MapScreen(screenint * DrawX, screenint * DrawY); */
 /*   inline void UnMapScreen(screenint * DrawX, screenint * DrawY); */
diff --git a/Src/DasherCore/DasherViewSquare.cpp b/Src/DasherCore/DasherViewSquare.cpp
index 309da14..52f0c0a 100644
--- a/Src/DasherCore/DasherViewSquare.cpp
+++ b/Src/DasherCore/DasherViewSquare.cpp
@@ -27,7 +27,6 @@
 //#include "DasherGameMode.h"
 #include "DasherViewSquare.h"
 #include "DasherModel.h"
-#include "DasherScreen.h"
 #include "DasherView.h"
 #include "DasherTypes.h"
 #include "Event.h"
@@ -92,9 +91,10 @@ void CDasherViewSquare::HandleEvent(Dasher::CEvent *pEvent) {
       m_bVisibleRegionValid = false;
       break;
     case LP_MARGIN_WIDTH:
-        m_bVisibleRegionValid = false;
-        SetScaleFactor();
-        break;
+    case BP_NONLINEAR_Y:
+      m_bVisibleRegionValid = false;
+      SetScaleFactor();
+      break;
     default:
       break;
     }
@@ -796,6 +796,66 @@ void CDasherViewSquare::Dasher2Polar(myint iDasherX, myint iDasherY, double &r,
     r = sqrt(x * x + y * y);
 }
 
+void CDasherViewSquare::DasherLine2Screen(myint x1, myint y1, myint x2, myint y2, vector<CDasherScreen::point> &vPoints) {
+  if (x1!=x2 && y1!=y2 && GetBoolParameter(BP_NONLINEAR_Y)) {
+    //only diagonal lines ever get changed...
+    if ((y1 < m_Y3 && y2 > m_Y3) ||(y2 < m_Y3 && y1 > m_Y3)) {
+      //crosses bottom non-linearity border
+      int x_mid = x1+(x2-x1) * (m_Y3-y1)/(y2-y1);
+      DasherLine2Screen(x1, y1, x_mid, m_Y3, vPoints);
+      x1=x_mid; y1=m_Y3;
+    }//else //no, a single line might cross _both_ borders!
+    if ((y1 > m_Y2 && y2 < m_Y2) || (y2 > m_Y2 && y1 < m_Y2)) {
+      //crosses top non-linearity border
+      int x_mid = x1 + (x2-x1) * (m_Y2-y1)/(y2-y1);
+      DasherLine2Screen(x1, y1, x_mid, m_Y2, vPoints);
+      x1=x_mid; y1=m_Y2;
+    }
+    double dMax(static_cast<double>(GetLongParameter(LP_MAX_Y)));
+    if (GetBoolParameter(BP_NONLINEAR_Y) && (x1 / dMax > m_dXmpb || x2 / dMax > m_dXmpb)) {
+      //into logarithmic section
+      CDasherScreen::point pStart, pScreenMid, pEnd;
+      Dasher2Screen(x2, y2, pEnd.x, pEnd.y);
+      for(;;) {
+        Dasher2Screen(x1, y1, pStart.x, pStart.y);
+        //a straight line on the screen between pStart and pEnd passes through pScreenMid:
+        pScreenMid.x = (pStart.x + pEnd.x)/2;
+        pScreenMid.y = (pStart.y + pEnd.y)/2;
+        //whereas a straight line _in_Dasher_space_ passes through pDasherMid:
+        int xMid=(x1+x2)/2, yMid=(y1+y2)/2;
+        CDasherScreen::point pDasherMid;
+        Dasher2Screen(xMid, yMid, pDasherMid.x, pDasherMid.y);
+        
+        //since we know both endpoints are in the same section of the screen wrt. Y nonlinearity,
+        //the midpoint along the DasherY axis of both lines should be the same.
+        const Dasher::Opts::ScreenOrientations orient(Dasher::Opts::ScreenOrientations(GetLongParameter(LP_REAL_ORIENTATION)));
+        if (orient==Dasher::Opts::LeftToRight || orient==Dasher::Opts::RightToLeft) {
+          DASHER_ASSERT(abs(pDasherMid.y - pScreenMid.y)<=1);//allow for rounding error
+          if (abs(pDasherMid.x - pScreenMid.x)<=1) break; //call a straight line accurate enough
+        } else {
+          DASHER_ASSERT(abs(pDasherMid.x - pScreenMid.x)<=1);
+          if (abs(pDasherMid.y - pScreenMid.y)<=1) break;
+        }
+        //line should appear bent. Subdivide!
+        DasherLine2Screen(x1,y1,xMid,yMid,vPoints); //recurse for first half (to Dasher-space midpoint)
+        x1=xMid; y1=yMid; //& loop round for second half
+      }
+      //broke out of loop. a straight line (x1,y1)-(x2,y2) on the screen is an accurate portrayal of a straight line in Dasher-space.
+      vPoints.push_back(pEnd);
+      return;
+    }
+    //ok, not in x nonlinear section; fall through.
+  }
+#ifdef DEBUG
+  CDasherScreen::point pTest;
+  Dasher2Screen(x1, y1, pTest.x, pTest.y);
+  DASHER_ASSERT(vPoints.back().x == pTest.x && vPoints.back().y == pTest.y);
+#endif
+  CDasherScreen::point p;
+  Dasher2Screen(x2, y2, p.x, p.y);
+  vPoints.push_back(p);
+}
+
 void CDasherViewSquare::VisibleRegion( myint &iDasherMinX, myint &iDasherMinY, myint &iDasherMaxX, myint &iDasherMaxY ) {
   // TODO: Change output parameters to pointers and allow NULL to mean
   // 'I don't care'. Need to be slightly careful about this as it will
diff --git a/Src/DasherCore/DasherViewSquare.h b/Src/DasherCore/DasherViewSquare.h
index 156383c..81ca815 100644
--- a/Src/DasherCore/DasherViewSquare.h
+++ b/Src/DasherCore/DasherViewSquare.h
@@ -6,6 +6,7 @@
 #define __DasherViewSquare_h__
 #include "DasherView.h"
 #include "DelayedDraw.h"
+#include "DasherScreen.h"
 #include <deque>
 #include "Alphabet/GroupInfo.h"
 
@@ -97,6 +98,7 @@ public:
 
   /// @}
 
+  void DasherSpaceLine(myint x1, myint x2, myint y1, myint y2, int iColor, int iWidth);
 
 private:
   ///
@@ -170,6 +172,8 @@ private:
   
   inline myint CustomIDiv(myint iNumerator, myint iDenominator);
 
+  void DasherLine2Screen(myint x1, myint y1, myint x2, myint y2, vector<CDasherScreen::point> &vPoints);
+  
   // Called on screen size changes
   void SetScaleFactor();
 
diff --git a/Src/DasherCore/DefaultFilter.cpp b/Src/DasherCore/DefaultFilter.cpp
index 80a7550..b5fdffe 100644
--- a/Src/DasherCore/DefaultFilter.cpp
+++ b/Src/DasherCore/DefaultFilter.cpp
@@ -159,8 +159,10 @@ void CDefaultFilter::DrawMouseLine(CDasherView *pView) {
   ApplyTransform(x[1], y[1]);
 
   // Actually plot the line
-
-  pView->DasherPolyline(x, y, 2, GetLongParameter(LP_LINE_WIDTH), 1);
+  if (GetBoolParameter(BP_CURVE_MOUSE_LINE))
+    pView->DasherSpaceLine(x[0],y[0],x[1],y[1], GetLongParameter(LP_LINE_WIDTH), 1);
+  else
+    pView->DasherPolyline(x, y, 2, GetLongParameter(LP_LINE_WIDTH), 1);
 
   /*  // Plot a brachistochrone
 
diff --git a/Src/DasherCore/Parameters.h b/Src/DasherCore/Parameters.h
index 7727a8e..a7436a2 100644
--- a/Src/DasherCore/Parameters.h
+++ b/Src/DasherCore/Parameters.h
@@ -30,7 +30,7 @@
 // All parameters go into the enums here
 // They are unique across the different types
 enum { 
-  BP_DRAW_MOUSE_LINE, BP_DRAW_MOUSE,
+  BP_DRAW_MOUSE_LINE, BP_DRAW_MOUSE, BP_CURVE_MOUSE_LINE,
   BP_SHOW_SLIDER, BP_START_MOUSE,
   BP_START_SPACE, BP_START_STYLUS, BP_STOP_IDLE, BP_KEY_CONTROL, BP_CONTROL_MODE, 
   BP_COLOUR_MODE, BP_MOUSEPOS_MODE,
@@ -124,6 +124,7 @@ struct sp_table {
 static bp_table boolparamtable[] = {
   {BP_DRAW_MOUSE_LINE, "DrawMouseLine", PERS, true, "Draw Mouse Line"},
   {BP_DRAW_MOUSE, "DrawMouse", PERS, false, "Draw Mouse Position"},
+  {BP_CURVE_MOUSE_LINE, "CurveMouseLine", PERS, false, "Curve mouse line according to screen nonlinearity"},
 #ifdef WITH_MAEMO
   {BP_SHOW_SLIDER, "ShowSpeedSlider", PERS, false, "ShowSpeedSlider"},
 #else



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]