From f8c3f2a930ecbd583e4c7373f25230fffad4bf4c Mon Sep 17 00:00:00 2001
From: Bruce Toll <btoll@dhsus.com>
Date: Tue, 28 Jul 2015 11:12:08 -0400
Subject: [PATCH 3/5] Upgrade precision of AbstractArea subclasses.

The interactive WCartesianChart / WPaintedWidget / WImage classes need
to be able to represent areas with additional precision to support
zooming.

This update changes the internal representation from int to double but
leaves most of the external API (which favors int) unchanged.

This update also modifies the WCircleArea marker support in
WCartesianChart.C to take advantage of the extra precision.

PLEASE NOTE:

1. This patch has only had very limited testing.

2. The API probably should be updated to provide more access to the
   new double value representations.
---
 src/Wt/Chart/WCartesianChart.C |  6 +++---
 src/Wt/WCircleArea             |  2 +-
 src/Wt/WCircleArea.C           | 18 +++++++++++-----
 src/Wt/WPolygonArea            | 13 +++++++++---
 src/Wt/WPolygonArea.C          | 47 ++++++++++++++++++++++++++++--------------
 src/Wt/WRectArea               |  2 +-
 src/Wt/WRectArea.C             | 33 ++++++++++++++++++-----------
 7 files changed, 80 insertions(+), 41 deletions(-)

diff --git a/src/Wt/Chart/WCartesianChart.C b/src/Wt/Chart/WCartesianChart.C
index 7740731..3ba37c5 100644
--- a/src/Wt/Chart/WCartesianChart.C
+++ b/src/Wt/Chart/WCartesianChart.C
@@ -734,9 +734,9 @@ public:
 
 	  p = t.map(hv(p));
 
-	  WCircleArea *circleArea
-	    = new WCircleArea(static_cast<int>(p.x()),
-			      static_cast<int>(p.y()), 5);
+	  WCircleArea *circleArea = new WCircleArea();
+	  circleArea->setCenter(WPointF(p.x(), p.y()));
+	  circleArea->setRadius(5);
 	  circleArea->setToolTip(asString(toolTip));
 
 	  const_cast<WCartesianChart&>(chart_)
diff --git a/src/Wt/WCircleArea b/src/Wt/WCircleArea
index 7d8d2df..f9d3ed5 100644
--- a/src/Wt/WCircleArea
+++ b/src/Wt/WCircleArea
@@ -79,7 +79,7 @@ public:
   int radius() const { return r_; }
 
 private:
-  int x_, y_, r_;
+  double x_, y_, r_;
 
 protected:
   virtual bool updateDom(DomElement& element, bool all);
diff --git a/src/Wt/WCircleArea.C b/src/Wt/WCircleArea.C
index 7589124..346f75b 100644
--- a/src/Wt/WCircleArea.C
+++ b/src/Wt/WCircleArea.C
@@ -9,6 +9,7 @@
 #include "Wt/WPointF"
 
 #include "DomElement.h"
+#include "WebUtils.h"
 
 namespace Wt {
 
@@ -27,8 +28,10 @@ void WCircleArea::setCenter(const WPoint& point)
 
 void WCircleArea::setCenter(const WPointF& point)
 {
-  setCenter(static_cast<int>(point.x()),
-	    static_cast<int>(point.y()));
+  x_ = point.x();
+  y_ = point.y();
+
+  repaint();
 }
 
 void WCircleArea::setCenter(int x, int y)
@@ -51,7 +54,9 @@ bool WCircleArea::updateDom(DomElement& element, bool all)
   element.setAttribute("shape", "circle");
 
   std::stringstream coords;
-  coords << x_ << ',' << y_ << ',' << r_;
+  coords << static_cast<int>(x_) << ','
+         << static_cast<int>(y_) << ','
+         << static_cast<int>(r_);
   element.setAttribute("coords", coords.str());
 
   return WAbstractArea::updateDom(element, all);
@@ -60,9 +65,12 @@ bool WCircleArea::updateDom(DomElement& element, bool all)
 std::string WCircleArea::updateAreaCoordsJS()
 {
   std::stringstream coords;
+  char buf[30];
 
-  coords << "[" << jsRef() << ",["
-    << x_ << ',' << y_ << ',' << r_ << "]]";
+  coords << "[" << jsRef() << ",[";
+  coords << Utils::round_js_str(x_, 2, buf) << ',';
+  coords << Utils::round_js_str(y_, 2, buf) << ',';
+  coords << Utils::round_js_str(r_, 2, buf) << "]]";
 
   return coords.str();
 }
diff --git a/src/Wt/WPolygonArea b/src/Wt/WPolygonArea
index 2a33f3f..e16e383 100644
--- a/src/Wt/WPolygonArea
+++ b/src/Wt/WPolygonArea
@@ -103,12 +103,19 @@ public:
 
   /*! \brief Returns the polygon vertices.
    *
-   * \sa setPoints()
+   * \sa setPoints(), points()
    */
-  const std::vector<WPoint>& points() const { return points_; }
+  const std::vector<WPointF>& pointFs() const { return points_; }
+
+  /*! \brief Returns the polygon vertices.
+   *
+   * \sa setPoints(), pointFs()
+   */
+  const std::vector<WPoint>& points() const;
 
 private:
-  std::vector<WPoint> points_;
+  std::vector<WPointF> points_;
+  mutable std::vector<WPoint> pointsIntCompatibility_;
 
 protected:
   virtual bool updateDom(DomElement& element, bool all);
diff --git a/src/Wt/WPolygonArea.C b/src/Wt/WPolygonArea.C
index 0ef9074..d0388b5 100644
--- a/src/Wt/WPolygonArea.C
+++ b/src/Wt/WPolygonArea.C
@@ -8,6 +8,7 @@
 #include "Wt/WPointF"
 
 #include "DomElement.h"
+#include "WebUtils.h"
 
 namespace Wt {
 
@@ -15,48 +16,49 @@ WPolygonArea::WPolygonArea()
 { }
 
 WPolygonArea::WPolygonArea(const std::vector<WPoint>& points)
- : points_(points)
-{ }
-
-#ifndef WT_TARGET_JAVA
-WPolygonArea::WPolygonArea(const std::vector<WPointF>& points)
 {
   setPoints(points);
 }
+
+#ifndef WT_TARGET_JAVA
+WPolygonArea::WPolygonArea(const std::vector<WPointF>& points)
+ : points_(points)
+{ }
 #endif // WT_TARGET_JAVA
 
 void WPolygonArea::addPoint(int x, int y)
 {
-  points_.push_back(WPoint(x, y));
+  points_.push_back(WPointF(x, y));
   
   repaint();
 }
 
 void WPolygonArea::addPoint(double x, double y)
 {
-  points_.push_back(WPoint(static_cast<int>(x), static_cast<int>(y)));
+  points_.push_back(WPointF(x, y));
   
   repaint();
 }
 
 void WPolygonArea::addPoint(const WPoint& point)
 {
-  points_.push_back(point);
+  points_.push_back(WPointF(point.x(), point.y()));
   
   repaint();
 }
 
 void WPolygonArea::addPoint(const WPointF& point)
 {
-  points_.push_back(WPoint(static_cast<int>(point.x()),
-			   static_cast<int>(point.y())));
+  points_.push_back(point);
                
   repaint();
 }
 
 void WPolygonArea::setPoints(const std::vector<WPoint>& points)
 {
-  points_ = points;
+  points_.clear();
+  for (unsigned i = 0; i < points.size(); ++i)
+    addPoint(points[i]);
   
   repaint();
 }
@@ -64,9 +66,7 @@ void WPolygonArea::setPoints(const std::vector<WPoint>& points)
 #ifndef WT_TARGET_JAVA
 void WPolygonArea::setPoints(const std::vector<WPointF>& points)
 {
-  points_.clear();
-  for (unsigned i = 0; i < points.size(); ++i)
-    addPoint(points[i]);
+  points_ = points;
 }
 #endif // WT_TARGET_JAVA
 
@@ -78,7 +78,8 @@ bool WPolygonArea::updateDom(DomElement& element, bool all)
   for (unsigned i = 0; i < points_.size(); ++i) {
     if (i != 0)
       coords << ',';
-    coords << points_[i].x() << ',' << points_[i].y();
+    coords << static_cast<int>(points_[i].x()) << ','
+           << static_cast<int>(points_[i].y());
   }
   element.setAttribute("coords", coords.str());
 
@@ -88,16 +89,30 @@ bool WPolygonArea::updateDom(DomElement& element, bool all)
 std::string WPolygonArea::updateAreaCoordsJS()
 {
   std::stringstream coords;
+  char buf[30];
 
   coords << "[" << jsRef() << ",[";
   for (unsigned i = 0; i < points_.size(); ++i) {
     if (i != 0)
       coords << ',';
-    coords << points_[i].x() << ',' << points_[i].y();
+    coords << Utils::round_js_str(points_[i].x(), 2, buf) << ',';
+    coords << Utils::round_js_str(points_[i].y(), 2, buf);
   }
   coords << "]]";
 
   return coords.str();
 }
 
+/*
+ * NOTE: points() does not receive updates, unlike pointsF()
+ */
+const std::vector<WPoint>& WPolygonArea::points() const
+{
+  pointsIntCompatibility_.clear();
+  for (unsigned i = 0; i < points_.size(); ++i)
+    pointsIntCompatibility_.push_back(WPoint(points_[i].x(), points_[i].y()));
+
+  return pointsIntCompatibility_;
+}
+
 }
diff --git a/src/Wt/WRectArea b/src/Wt/WRectArea
index 0329d42..55ac816 100644
--- a/src/Wt/WRectArea
+++ b/src/Wt/WRectArea
@@ -92,7 +92,7 @@ public:
   int height() const { return height_; }
 
 private:
-  int x_, y_, width_, height_;
+  double x_, y_, width_, height_;
 
 protected:
   virtual bool updateDom(DomElement& element, bool all);
diff --git a/src/Wt/WRectArea.C b/src/Wt/WRectArea.C
index f5dc12f..0386968 100644
--- a/src/Wt/WRectArea.C
+++ b/src/Wt/WRectArea.C
@@ -7,6 +7,7 @@
 #include "Wt/WRectArea"
 
 #include "DomElement.h"
+#include "WebUtils.h"
 
 namespace Wt {
 
@@ -19,17 +20,17 @@ WRectArea::WRectArea(int x, int y, int width, int height)
 { }
 
 WRectArea::WRectArea(double x, double y, double width, double height)
-  : x_(static_cast<int>(x)),
-    y_(static_cast<int>(y)),
-    width_(static_cast<int>(width)),
-    height_(static_cast<int>(height))
+  : x_(x),
+    y_(y),
+    width_(width),
+    height_(height)
 { }
 
 WRectArea::WRectArea(const WRectF& rect)
-  : x_(static_cast<int>(rect.x())),
-    y_(static_cast<int>(rect.y())),
-    width_(static_cast<int>(rect.width())),
-    height_(static_cast<int>(rect.height()))
+  : x_(rect.x()),
+    y_(rect.y()),
+    width_(rect.width()),
+    height_(rect.height())
 { }
 
 void WRectArea::setX(int x)
@@ -66,10 +67,14 @@ bool WRectArea::updateDom(DomElement& element, bool all)
 
   std::stringstream coords;
 
-  if (x_ == 0 && y_ == 0 && width_ == 0 && height_ == 0)
+  int x = x_;
+  int y = y_;
+  int width = width_;
+  int height = height_;
+  if (x == 0 && y == 0 && width == 0 && height == 0)
     coords << "0%,0%,100%,100%";
   else
-    coords << x_ << ',' << y_ << ',' << (x_ + width_) << ',' << (y_ + height_);
+    coords << x << ',' << y << ',' << (x + width) << ',' << (y + height);
   element.setAttribute("coords", coords.str());
 
   return WAbstractArea::updateDom(element, all);
@@ -78,9 +83,13 @@ bool WRectArea::updateDom(DomElement& element, bool all)
 std::string WRectArea::updateAreaCoordsJS()
 {
   std::stringstream coords;
+  char buf[30];
 
-  coords << "[" << jsRef() << ",["
-    << x_ << ',' << y_ << ',' << (x_ + width_) << ',' << (y_ + height_) << "]]";
+  coords << "[" << jsRef() << ",[";
+  coords << Utils::round_js_str(x_, 2, buf) << ',';
+  coords << Utils::round_js_str(y_, 2, buf) << ',';
+  coords << Utils::round_js_str((x_ + width_), 2, buf) << ',';
+  coords << Utils::round_js_str((y_ + height_), 2, buf) << "]]";
 
   return coords.str();
 }
-- 
1.9.1

