changeset 804:61ba4b504e9a

PolylineSceneLayer now has one color per chain
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 28 May 2019 15:58:21 +0200
parents f38c1fc08655
children 6a75f1c03c4e
files Framework/Scene2D/Color.h Framework/Scene2D/ColorSceneLayer.h Framework/Scene2D/Internals/CairoPolylineRenderer.cpp Framework/Scene2D/Internals/CairoTextRenderer.cpp Framework/Scene2D/Internals/OpenGLBasicPolylineRenderer.cpp Framework/Scene2D/Internals/OpenGLLinesProgram.cpp Framework/Scene2D/Internals/OpenGLLinesProgram.h Framework/Scene2D/Internals/OpenGLTextProgram.cpp Framework/Scene2D/PolylineSceneLayer.cpp Framework/Scene2D/PolylineSceneLayer.h Framework/Scene2D/TextSceneLayer.cpp Framework/Scene2DViewport/AngleMeasureTool.cpp Framework/Scene2DViewport/LineMeasureTool.cpp Framework/StoneInitialization.cpp Framework/Toolbox/DicomStructureSet.cpp Framework/Toolbox/DicomStructureSet.h Framework/Toolbox/LinearAlgebra.cpp Samples/Sdl/BasicScene.cpp Samples/Sdl/Loader.cpp Samples/Sdl/TrackerSampleApp.cpp
diffstat 20 files changed, 305 insertions(+), 176 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Scene2D/Color.h	Tue May 28 15:58:21 2019 +0200
@@ -0,0 +1,82 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include <stdint.h>
+
+namespace OrthancStone
+{
+  class Color
+  {
+  private:
+    uint8_t  red_;
+    uint8_t  green_;
+    uint8_t  blue_;
+
+  public:
+    Color() :
+      red_(255),
+      green_(255),
+      blue_(255)
+    {
+    }
+
+    Color(uint8_t red,
+          uint8_t green,
+          uint8_t blue) :
+      red_(red),
+      green_(green),
+      blue_(blue)
+    {
+    }
+
+    uint8_t GetRed() const
+    {
+      return red_;
+    }
+
+    uint8_t GetGreen() const
+    {
+      return green_;
+    }
+
+    uint8_t GetBlue() const
+    {
+      return blue_;
+    }
+
+    float GetRedAsFloat() const
+    {
+      return static_cast<float>(red_) / 255.0f;
+    }
+
+    float GetGreenAsFloat() const
+    {
+      return static_cast<float>(green_) / 255.0f;
+    }
+
+    float GetBlueAsFloat() const
+    {
+      return static_cast<float>(blue_) / 255.0f;
+    }
+  };
+}
--- a/Framework/Scene2D/ColorSceneLayer.h	Tue May 28 14:18:46 2019 +0200
+++ b/Framework/Scene2D/ColorSceneLayer.h	Tue May 28 15:58:21 2019 +0200
@@ -22,18 +22,17 @@
 #pragma once
 
 #include "ISceneLayer.h"
-#include <Core/Enumerations.h>
+#include "Color.h"
 
-#include <stdint.h>
+#include <Core/Enumerations.h>  // For ORTHANC_OVERRIDE
 
 namespace OrthancStone
 {
+  // TODO - Is this needed?
   class ColorSceneLayer : public ISceneLayer
   {
   private:
-    uint8_t  red_;
-    uint8_t  green_;
-    uint8_t  blue_;
+    Color    color_;
     uint64_t revision_;
 
   protected:
@@ -45,9 +44,6 @@
 
   public:
     ColorSceneLayer() :
-      red_(255),
-      green_(255),
-      blue_(255),
       revision_(0)
     {
     }
@@ -61,40 +57,19 @@
                   uint8_t green,
                   uint8_t blue)
     {
-      red_ = red;
-      green_ = green;
-      blue_ = blue;
+      color_ = Color(red, green, blue);
       BumpRevision();
     }
 
-    uint8_t GetRed() const
+    void SetColor(const Color& color)
     {
-      return red_;
-    }
-
-    uint8_t GetGreen() const
-    {
-      return green_;
+      color_ = color;
+      BumpRevision();
     }
 
-    uint8_t GetBlue() const
-    {
-      return blue_;
-    }
-
-    float GetRedAsFloat() const
+    const Color& GetColor() const
     {
-      return static_cast<float>(red_) / 255.0f;
-    }
-
-    float GetGreenAsFloat() const
-    {
-      return static_cast<float>(green_) / 255.0f;
-    }
-
-    float GetBlueAsFloat() const
-    {
-      return static_cast<float>(blue_) / 255.0f;
+      return color_;
     }
   };
 }
--- a/Framework/Scene2D/Internals/CairoPolylineRenderer.cpp	Tue May 28 14:18:46 2019 +0200
+++ b/Framework/Scene2D/Internals/CairoPolylineRenderer.cpp	Tue May 28 15:58:21 2019 +0200
@@ -33,11 +33,15 @@
       
       cairo_t* cr = GetCairoContext();
 
-      cairo_set_source_rgb(cr, layer.GetRedAsFloat(), layer.GetGreenAsFloat(), layer.GetBlueAsFloat());
       cairo_set_line_width(cr, layer.GetThickness());
       
       for (size_t i = 0; i < layer.GetChainsCount(); i++)
       {
+        const Color& color = layer.GetColor(i);
+        cairo_set_source_rgb(cr, color.GetRedAsFloat(),
+                             color.GetGreenAsFloat(),
+                             color.GetBlueAsFloat());
+
         const PolylineSceneLayer::Chain& chain = layer.GetChain(i);
 
         if (!chain.empty())
@@ -62,9 +66,9 @@
             cairo_line_to(cr, p.GetX(), p.GetY());
           }
         }
+
+        cairo_stroke(cr);
       }
-
-      cairo_stroke(cr);
     }
   }
 }
--- a/Framework/Scene2D/Internals/CairoTextRenderer.cpp	Tue May 28 14:18:46 2019 +0200
+++ b/Framework/Scene2D/Internals/CairoTextRenderer.cpp	Tue May 28 15:58:21 2019 +0200
@@ -48,9 +48,7 @@
         }
         
         const unsigned int width = source->GetWidth();
-        const unsigned int red = layer.GetRed();
-        const unsigned int green = layer.GetGreen();
-        const unsigned int blue = layer.GetBlue();
+        const Color& color = layer.GetColor();
 
         for (unsigned int y = 0; y < source->GetHeight(); y++)
         {
@@ -62,9 +60,9 @@
             unsigned int alpha = *p;
 
             // Premultiplied alpha
-            q[0] = static_cast<uint8_t>((blue * alpha) / 255);
-            q[1] = static_cast<uint8_t>((green * alpha) / 255);
-            q[2] = static_cast<uint8_t>((red * alpha) / 255);
+            q[0] = static_cast<uint8_t>((color.GetBlue() * alpha) / 255);
+            q[1] = static_cast<uint8_t>((color.GetGreen() * alpha) / 255);
+            q[2] = static_cast<uint8_t>((color.GetRed() * alpha) / 255);
             q[3] = *p;
             
             p++;
@@ -85,7 +83,9 @@
         const TextSceneLayer& layer = GetLayer<TextSceneLayer>();
       
         cairo_t* cr = GetCairoContext();
-        cairo_set_source_rgb(cr, layer.GetRedAsFloat(), layer.GetGreenAsFloat(), layer.GetBlueAsFloat());
+        cairo_set_source_rgb(cr, layer.GetColor().GetRedAsFloat(),
+                             layer.GetColor().GetGreenAsFloat(),
+                             layer.GetColor().GetBlueAsFloat());
 
         double dx, dy;  // In pixels
         ComputeAnchorTranslation(dx, dy, layer.GetAnchor(), text_.GetWidth(),
--- a/Framework/Scene2D/Internals/OpenGLBasicPolylineRenderer.cpp	Tue May 28 14:18:46 2019 +0200
+++ b/Framework/Scene2D/Internals/OpenGLBasicPolylineRenderer.cpp	Tue May 28 15:58:21 2019 +0200
@@ -28,7 +28,7 @@
   namespace Internals
   {
     OpenGLBasicPolylineRenderer::OpenGLBasicPolylineRenderer(OpenGL::IOpenGLContext& context,
-      const PolylineSceneLayer& layer) :
+                                                             const PolylineSceneLayer& layer) :
       context_(context)
     {
       layer_.Copy(layer);
@@ -42,12 +42,14 @@
         transform);
 
       glUseProgram(0);
-      glColor3ub(layer_.GetRed(), layer_.GetGreen(), layer_.GetBlue());
 
       glBegin(GL_LINES);
 
       for (size_t i = 0; i < layer_.GetChainsCount(); i++)
       {
+        const Color& color = layer_.GetColor(i);
+        glColor3ub(color.GetRed(), color.GetGreen(), color.GetBlue());
+
         const PolylineSceneLayer::Chain& chain = layer_.GetChain(i);
 
         if (chain.size() > 1)
--- a/Framework/Scene2D/Internals/OpenGLLinesProgram.cpp	Tue May 28 14:18:46 2019 +0200
+++ b/Framework/Scene2D/Internals/OpenGLLinesProgram.cpp	Tue May 28 15:58:21 2019 +0200
@@ -26,6 +26,7 @@
 
 
 static const unsigned int COMPONENTS_POSITION = 3;
+static const unsigned int COMPONENTS_COLOR = 3;
 static const unsigned int COMPONENTS_MITER = 2;
 
 
@@ -33,12 +34,15 @@
   ORTHANC_STONE_OPENGL_SHADER_VERSION_DIRECTIVE
   "attribute vec2 a_miter_direction; \n"
   "attribute vec4 a_position;        \n"
+  "attribute vec3 a_color;           \n"
   "uniform float u_thickness;        \n"
   "uniform mat4 u_matrix;            \n"
   "varying float v_distance;         \n"
+  "varying vec3 v_color;             \n"
   "void main()                       \n"
   "{                                 \n"
   "  v_distance = a_position.z;      \n"
+  "  v_color = a_color;              \n"
   "  gl_Position = u_matrix * vec4(a_position.xy + a_position.z * a_miter_direction * u_thickness, 0, 1); \n"
   "}";
 
@@ -47,20 +51,20 @@
   ORTHANC_STONE_OPENGL_SHADER_VERSION_DIRECTIVE
   "uniform bool u_antialiasing;           \n"
   "uniform float u_antialiasing_start;    \n"
-  "uniform vec3 u_color;                  \n"
   "varying float v_distance;              \n"   // Distance of the point to the segment
+  "varying vec3 v_color;                  \n"
   "void main()                            \n"
   "{                                      \n"
   "  float d = abs(v_distance);           \n"
   "  if (!u_antialiasing ||               \n"
   "      d <= u_antialiasing_start)       \n"
-  "    gl_FragColor = vec4(u_color, 1);   \n"
+  "    gl_FragColor = vec4(v_color, 1);   \n"
   "  else if (d >= 1.0)                   \n"
   "    gl_FragColor = vec4(0, 0, 0, 0);   \n"
   "  else                                 \n"
   "  {                                    \n"
   "    float alpha = 1.0 - smoothstep(u_antialiasing_start, 1.0, d); \n"
-  "    gl_FragColor = vec4(u_color * alpha, alpha); \n"
+  "    gl_FragColor = vec4(v_color * alpha, alpha); \n"
   "  }                                    \n"
   "}";
 
@@ -197,7 +201,9 @@
       }
 
       void AddTriangles(std::vector<float>& coords,
-                        std::vector<float>& miterDirections)
+                        std::vector<float>& miterDirections,
+                        std::vector<float>& colors,
+                        const Color& color)
       {
         if (isEmpty_)
         {
@@ -239,6 +245,14 @@
         miterDirections.push_back(static_cast<float>(miterY1_));
         miterDirections.push_back(static_cast<float>(miterX2_));
         miterDirections.push_back(static_cast<float>(miterY2_));
+
+        // Add the colors of the 2 triangles (leading to 2 * 3 values)
+        for (unsigned int i = 0; i < 6; i++)
+        {
+          colors.push_back(color.GetRedAsFloat());
+          colors.push_back(color.GetGreenAsFloat());
+          colors.push_back(color.GetBlueAsFloat());
+        }
       }        
     };
 
@@ -247,10 +261,7 @@
                                    const PolylineSceneLayer& layer) :
       context_(context),
       verticesCount_(0),
-      thickness_(static_cast<float>(layer.GetThickness())),
-      red_(layer.GetRedAsFloat()),
-      green_(layer.GetGreenAsFloat()),
-      blue_(layer.GetBlueAsFloat())
+      thickness_(static_cast<float>(layer.GetThickness()))
     {
       // High-level reference:
       // https://mattdesl.svbtle.com/drawing-lines-is-hard
@@ -271,8 +282,9 @@
         countVertices += countSegments * 2 * 3;
       }
 
-      std::vector<float>  coords, miterDirections;
+      std::vector<float>  coords, colors, miterDirections;
       coords.reserve(countVertices * COMPONENTS_POSITION);
+      colors.reserve(countVertices * COMPONENTS_COLOR);
       miterDirections.reserve(countVertices * COMPONENTS_MITER);
 
       for (size_t i = 0; i < layer.GetChainsCount(); i++)
@@ -307,24 +319,29 @@
           {
             if (!segments[j].IsEmpty())
             {
-              segments[j].AddTriangles(coords, miterDirections);
+              segments[j].AddTriangles(coords, miterDirections, colors, layer.GetColor(i));
             }
           }
         }
       }
 
+      assert(coords.size() == colors.size());
+
       if (!coords.empty())
       {
         verticesCount_ = coords.size() / COMPONENTS_POSITION;
 
         context_.MakeCurrent();
-        glGenBuffers(2, buffers_);
+        glGenBuffers(3, buffers_);
 
         glBindBuffer(GL_ARRAY_BUFFER, buffers_[0]);
         glBufferData(GL_ARRAY_BUFFER, sizeof(float) * coords.size(), &coords[0], GL_STATIC_DRAW);
 
         glBindBuffer(GL_ARRAY_BUFFER, buffers_[1]);
         glBufferData(GL_ARRAY_BUFFER, sizeof(float) * miterDirections.size(), &miterDirections[0], GL_STATIC_DRAW);
+
+        glBindBuffer(GL_ARRAY_BUFFER, buffers_[2]);
+        glBufferData(GL_ARRAY_BUFFER, sizeof(float) * colors.size(), &colors[0], GL_STATIC_DRAW);
       }
     }
 
@@ -334,7 +351,7 @@
       if (!IsEmpty())
       {
         context_.MakeCurrent();
-        glDeleteBuffers(2, buffers_);
+        glDeleteBuffers(3, buffers_);
       }
     }
 
@@ -365,10 +382,22 @@
     }
 
 
+    GLuint OpenGLLinesProgram::Data::GetColorsBuffer() const
+    {
+      if (IsEmpty())
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+      }
+      else
+      {
+        return buffers_[2];
+      }
+    }
+
+
     OpenGLLinesProgram::OpenGLLinesProgram(OpenGL::IOpenGLContext&  context) :
       context_(context)
     {
-
       context_.MakeCurrent();
 
       program_.reset(new OpenGL::OpenGLProgram);
@@ -388,13 +417,12 @@
 
         GLint locationPosition = program_->GetAttributeLocation("a_position");
         GLint locationMiterDirection = program_->GetAttributeLocation("a_miter_direction");
+        GLint locationColor = program_->GetAttributeLocation("a_color");
 
         float m[16];
         transform.ConvertToOpenGLMatrix(m, context_.GetCanvasWidth(), context_.GetCanvasHeight());
 
         glUniformMatrix4fv(program_->GetUniformLocation("u_matrix"), 1, GL_FALSE, m);
-        glUniform3f(program_->GetUniformLocation("u_color"), 
-                    data.GetRed(), data.GetGreen(), data.GetBlue());
 
         glBindBuffer(GL_ARRAY_BUFFER, data.GetVerticesBuffer());
         glEnableVertexAttribArray(locationPosition);
@@ -404,6 +432,10 @@
         glEnableVertexAttribArray(locationMiterDirection);
         glVertexAttribPointer(locationMiterDirection, COMPONENTS_MITER, GL_FLOAT, GL_FALSE, 0, 0);
 
+        glBindBuffer(GL_ARRAY_BUFFER, data.GetColorsBuffer());
+        glEnableVertexAttribArray(locationColor);
+        glVertexAttribPointer(locationColor, COMPONENTS_COLOR, GL_FLOAT, GL_FALSE, 0, 0);
+
         glUniform1i(program_->GetUniformLocation("u_antialiasing"), (antialiasing ? 1 : 0));
 
         const double zoom = transform.ComputeZoom();
@@ -464,6 +496,7 @@
 
         glDisableVertexAttribArray(locationPosition);
         glDisableVertexAttribArray(locationMiterDirection);
+        glDisableVertexAttribArray(locationColor);
       }
     }
   }
--- a/Framework/Scene2D/Internals/OpenGLLinesProgram.h	Tue May 28 14:18:46 2019 +0200
+++ b/Framework/Scene2D/Internals/OpenGLLinesProgram.h	Tue May 28 15:58:21 2019 +0200
@@ -39,12 +39,9 @@
         class Segment;
         
         OpenGL::IOpenGLContext&  context_;
-        GLuint                   buffers_[2];
+        GLuint                   buffers_[3];
         size_t                   verticesCount_;
         float                    thickness_;
-        float                    red_;
-        float                    green_;
-        float                    blue_;
 
       public:
         Data(OpenGL::IOpenGLContext& context,
@@ -66,25 +63,12 @@
 
         GLuint GetMiterDirectionsBuffer() const;
 
+        GLuint GetColorsBuffer() const;
+
         float GetThickness() const
         {
           return thickness_;
         }
-
-        float GetRed() const
-        {
-          return red_;
-        }
-
-        float GetGreen() const
-        {
-          return green_;
-        }
-
-        float GetBlue() const
-        {
-          return blue_;
-        }
       };
       
     private:
--- a/Framework/Scene2D/Internals/OpenGLTextProgram.cpp	Tue May 28 14:18:46 2019 +0200
+++ b/Framework/Scene2D/Internals/OpenGLTextProgram.cpp	Tue May 28 15:58:21 2019 +0200
@@ -75,9 +75,9 @@
                                   const GlyphTextureAlphabet& alphabet,
                                   const TextSceneLayer& layer) :
       context_(context),
-      red_(layer.GetRedAsFloat()),
-      green_(layer.GetGreenAsFloat()),
-      blue_(layer.GetBlueAsFloat()),
+      red_(layer.GetColor().GetRedAsFloat()),
+      green_(layer.GetColor().GetGreenAsFloat()),
+      blue_(layer.GetColor().GetBlueAsFloat()),
       x_(layer.GetX()),
       y_(layer.GetY()),
       border_(layer.GetBorder()),
--- a/Framework/Scene2D/PolylineSceneLayer.cpp	Tue May 28 14:18:46 2019 +0200
+++ b/Framework/Scene2D/PolylineSceneLayer.cpp	Tue May 28 15:58:21 2019 +0200
@@ -25,6 +25,14 @@
 
 namespace OrthancStone
 {
+  void PolylineSceneLayer::Copy(const PolylineSceneLayer& other)
+  {
+    items_ = other.items_;
+    thickness_ = other.thickness_;
+    revision_ ++;
+  }
+
+  
   ISceneLayer* PolylineSceneLayer::Clone() const
   {
     std::auto_ptr<PolylineSceneLayer> cloned(new PolylineSceneLayer);
@@ -42,52 +50,40 @@
     else
     {
       thickness_ = thickness;
-      BumpRevision();
+      revision_++;
     }
   }
 
 
-  void PolylineSceneLayer::Copy(const PolylineSceneLayer& from)
-  {
-    SetColor(from.GetRed(), from.GetGreen(), from.GetBlue());
-    chains_ = from.chains_;
-    closed_ = from.closed_;
-    thickness_ = from.thickness_;
-    BumpRevision();
-  }
-
-  
-  void PolylineSceneLayer::Reserve(size_t countChains)
-  {
-    chains_.reserve(countChains);
-    closed_.reserve(countChains);
-  }
-
-  
   void PolylineSceneLayer::AddChain(const Chain& chain,
-                                    bool isClosed)
+                                    bool isClosed,
+                                    uint8_t red,
+                                    uint8_t green,
+                                    uint8_t blue)
   {
     if (!chain.empty())
     {
-      chains_.push_back(chain);
-      closed_.push_back(isClosed);
-      BumpRevision();
+      items_.push_back(Item());
+      items_.back().chain_ = chain;
+      items_.back().closed_ = isClosed;
+      items_.back().color_ = Color(red, green, blue);
+
+      revision_++;
     }
   }
 
 
   void PolylineSceneLayer::ClearAllChains()
   {
-    chains_.clear();
-    closed_.clear();
-    BumpRevision();
+    items_.clear();
+    revision_++;
   }
 
-  const PolylineSceneLayer::Chain& PolylineSceneLayer::GetChain(size_t i) const
+  const PolylineSceneLayer::Item& PolylineSceneLayer::GetItem(size_t i) const
   {
-    if (i < chains_.size())
+    if (i < items_.size())
     {
-      return chains_[i];
+      return items_[i];
     }
     else
     {
@@ -96,28 +92,15 @@
   }
 
   
-  bool PolylineSceneLayer::IsClosedChain(size_t i) const
-  {
-    if (i < closed_.size())
-    {
-      return closed_[i];
-    }
-    else
-    {
-      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
-    }
-  }
-
-
   bool PolylineSceneLayer::GetBoundingBox(Extent2D& target) const
   {
     target.Reset();
 
-    for (size_t i = 0; i < chains_.size(); i++)
+    for (size_t i = 0; i < items_.size(); i++)
     {
-      for (size_t j = 0; j < chains_[i].size(); j++)
+      for (size_t j = 0; j < items_[i].chain_.size(); j++)
       {
-        const ScenePoint2D& p = chains_[i][j];
+        const ScenePoint2D& p = items_[i].chain_[j];
         target.AddPoint(p.GetX(), p.GetY());
       }
     }
--- a/Framework/Scene2D/PolylineSceneLayer.h	Tue May 28 14:18:46 2019 +0200
+++ b/Framework/Scene2D/PolylineSceneLayer.h	Tue May 28 15:58:21 2019 +0200
@@ -21,29 +21,47 @@
 
 #pragma once
 
-#include "ColorSceneLayer.h"
+#include "Color.h"
 #include "ScenePoint2D.h"
+#include "ISceneLayer.h"
 
 #include <vector>
 
 namespace OrthancStone
 {
-  class PolylineSceneLayer : public ColorSceneLayer
+  class PolylineSceneLayer : public ISceneLayer
   {
   public:
     typedef std::vector<ScenePoint2D>  Chain;
 
   private:
-    std::vector<Chain>  chains_;
-    std::vector<bool>   closed_;
-    double              thickness_;
+    struct Item
+    {
+      Chain  chain_;
+      bool   closed_;
+      Color  color_;
+    };
+    
+    std::vector<Item>  items_;
+    double             thickness_;
+    uint64_t           revision_;
+
+    const Item& GetItem(size_t i) const;
 
   public:
     PolylineSceneLayer() :
-      thickness_(1.0)
+      thickness_(1.0),
+      revision_(0)
     {
     }
 
+    void Copy(const PolylineSceneLayer& other);
+
+    virtual uint64_t GetRevision() const
+    {
+      return revision_;
+    }
+
     virtual ISceneLayer* Clone() const;
 
     void SetThickness(double thickness);
@@ -53,23 +71,45 @@
       return thickness_;
     }
 
-    void Copy(const PolylineSceneLayer& from);
-
-    void Reserve(size_t countChains);
+    void Reserve(size_t countChains)
+    {
+      items_.reserve(countChains);
+    }
 
     void AddChain(const Chain& chain,
-                  bool isClosed);
+                  bool isClosed,
+                  uint8_t red,
+                  uint8_t green,
+                  uint8_t blue);
+
+    void AddChain(const Chain& chain,
+                  bool isClosed,
+                  const Color& color)
+    {
+      AddChain(chain, isClosed, color.GetRed(), color.GetGreen(), color.GetBlue());
+    }
 
     void ClearAllChains();
 
     size_t GetChainsCount() const
     {
-      return chains_.size();
+      return items_.size();
+    }
+
+    const Chain& GetChain(size_t i) const
+    {
+      return GetItem(i).chain_;
     }
 
-    const Chain& GetChain(size_t i) const;
+    bool IsClosedChain(size_t i) const
+    {
+      return GetItem(i).closed_;
+    }
 
-    bool IsClosedChain(size_t i) const;
+    const Color& GetColor(size_t i) const
+    {
+      return GetItem(i).color_;
+    }
 
     virtual Type GetType() const
     {
--- a/Framework/Scene2D/TextSceneLayer.cpp	Tue May 28 14:18:46 2019 +0200
+++ b/Framework/Scene2D/TextSceneLayer.cpp	Tue May 28 15:58:21 2019 +0200
@@ -37,7 +37,7 @@
   ISceneLayer* TextSceneLayer::Clone() const
   {
     std::auto_ptr<TextSceneLayer> cloned(new TextSceneLayer);
-    cloned->SetColor(GetRed(), GetGreen(), GetBlue());
+    cloned->SetColor(GetColor());
     cloned->x_ = x_;
     cloned->y_ = y_;
     cloned->utf8_ = utf8_;
--- a/Framework/Scene2DViewport/AngleMeasureTool.cpp	Tue May 28 14:18:46 2019 +0200
+++ b/Framework/Scene2DViewport/AngleMeasureTool.cpp	Tue May 28 15:58:21 2019 +0200
@@ -100,8 +100,8 @@
           // Fill the polyline layer with the measurement lines
           PolylineSceneLayer* polylineLayer = layerHolder_->GetPolylineLayer(0);
           polylineLayer->ClearAllChains();
-          polylineLayer->SetColor(0, 183, 17);
 
+          const Color color(0, 183, 17);
 
           // sides
           {
@@ -109,13 +109,13 @@
               PolylineSceneLayer::Chain chain;
               chain.push_back(side1End_);
               chain.push_back(center_);
-              polylineLayer->AddChain(chain, false);
+              polylineLayer->AddChain(chain, false, color);
             }
             {
               PolylineSceneLayer::Chain chain;
               chain.push_back(side2End_);
               chain.push_back(center_);
-              polylineLayer->AddChain(chain, false);
+              polylineLayer->AddChain(chain, false, color);
             }
           }
 
@@ -126,14 +126,14 @@
               //TODO: take DPI into account
               AddSquare(chain, GetScene(), side1End_, 
                 GetController()->GetHandleSideLengthS());
-              polylineLayer->AddChain(chain, true);
+              polylineLayer->AddChain(chain, true, color);
             }
             {
               PolylineSceneLayer::Chain chain;
               //TODO: take DPI into account
               AddSquare(chain, GetScene(), side2End_, 
                 GetController()->GetHandleSideLengthS());
-              polylineLayer->AddChain(chain, true);
+              polylineLayer->AddChain(chain, true, color);
             }
           }
 
@@ -143,7 +143,7 @@
 
             AddShortestArc(chain, side1End_, center_, side2End_,
                            controller->GetAngleToolArcRadiusS());
-            polylineLayer->AddChain(chain, false);
+            polylineLayer->AddChain(chain, false, color);
           }
         }
         {
--- a/Framework/Scene2DViewport/LineMeasureTool.cpp	Tue May 28 14:18:46 2019 +0200
+++ b/Framework/Scene2DViewport/LineMeasureTool.cpp	Tue May 28 15:58:21 2019 +0200
@@ -107,16 +107,16 @@
 
           PolylineSceneLayer* polylineLayer = layerHolder_->GetPolylineLayer(0);
           polylineLayer->ClearAllChains();
-          polylineLayer->SetColor(
-            TOOL_LINES_COLOR_RED, 
-            TOOL_LINES_COLOR_GREEN, 
-            TOOL_LINES_COLOR_BLUE);
+
+          const Color color(TOOL_LINES_COLOR_RED, 
+                            TOOL_LINES_COLOR_GREEN, 
+                            TOOL_LINES_COLOR_BLUE);
 
           {
             PolylineSceneLayer::Chain chain;
             chain.push_back(start_);
             chain.push_back(end_);
-            polylineLayer->AddChain(chain, false);
+            polylineLayer->AddChain(chain, false, color);
           }
 
           // handles
@@ -128,7 +128,7 @@
               AddSquare(chain, GetScene(), start_, 
                 GetController()->GetHandleSideLengthS());
               
-              polylineLayer->AddChain(chain, true);
+              polylineLayer->AddChain(chain, true, color);
             }
 
             {
@@ -138,7 +138,7 @@
               AddSquare(chain, GetScene(), end_, 
                 GetController()->GetHandleSideLengthS());
               
-              polylineLayer->AddChain(chain, true);
+              polylineLayer->AddChain(chain, true, color);
             }
           }
 
--- a/Framework/StoneInitialization.cpp	Tue May 28 14:18:46 2019 +0200
+++ b/Framework/StoneInitialization.cpp	Tue May 28 15:58:21 2019 +0200
@@ -64,6 +64,10 @@
     OrthancStone::SdlWindow::GlobalFinalize();
 #endif
     
+#if ORTHANC_ENABLE_CURL == 1
+    Orthanc::HttpClient::GlobalFinalize();
+#endif
+
     Orthanc::Logging::Finalize();
   }
 }
--- a/Framework/Toolbox/DicomStructureSet.cpp	Tue May 28 14:18:46 2019 +0200
+++ b/Framework/Toolbox/DicomStructureSet.cpp	Tue May 28 15:58:21 2019 +0200
@@ -540,6 +540,13 @@
   }
 
 
+  Color DicomStructureSet::GetStructureColor(size_t index) const
+  {
+    const Structure& s = GetStructure(index);
+    return Color(s.red_, s.green_, s.blue_);
+  }
+  
+    
   void DicomStructureSet::GetStructureColor(uint8_t& red,
                                             uint8_t& green,
                                             uint8_t& blue,
--- a/Framework/Toolbox/DicomStructureSet.h	Tue May 28 14:18:46 2019 +0200
+++ b/Framework/Toolbox/DicomStructureSet.h	Tue May 28 15:58:21 2019 +0200
@@ -23,6 +23,7 @@
 
 #include "CoordinateSystem3D.h"
 #include "Extent2D.h"
+#include "../Scene2D/Color.h"
 
 #include <Plugins/Samples/Common/FullOrthancDataset.h>
 
@@ -152,6 +153,9 @@
 
     const std::string& GetStructureInterpretation(size_t index) const;
 
+    Color GetStructureColor(size_t index) const;
+
+    // TODO - remove
     void GetStructureColor(uint8_t& red,
                            uint8_t& green,
                            uint8_t& blue,
--- a/Framework/Toolbox/LinearAlgebra.cpp	Tue May 28 14:18:46 2019 +0200
+++ b/Framework/Toolbox/LinearAlgebra.cpp	Tue May 28 15:58:21 2019 +0200
@@ -26,6 +26,7 @@
 #include <Core/Toolbox.h>
 
 #include <stdio.h>
+#include <boost/lexical_cast.hpp>
 #include <boost/numeric/ublas/lu.hpp>
 
 namespace OrthancStone
@@ -68,15 +69,16 @@
 
       for (size_t i = 0; i < items.size(); i++)
       {
+        /**
+         * We try and avoid the use of "boost::lexical_cast<>" here,
+         * as it is very slow. As we are parsing many doubles, we
+         * prefer to use the standard "std::stod" function if
+         * available: http://www.cplusplus.com/reference/string/stod/
+         **/
+          
+#if __cplusplus >= 201103L  // Is C++11 enabled?
         try
         {
-          /**
-           * We don't use "boost::lexical_cast<>" here, as it is very
-           * slow. As we are parsing many doubles, we prefer to use
-           * the standard "std::stod" function:
-           * http://www.cplusplus.com/reference/string/stod/
-           **/
-          
           target[i] = std::stod(items[i]);
         }
         catch (std::exception&)
@@ -84,6 +86,17 @@
           target.clear();
           return false;
         }
+#else  // Fallback implementation using Boost
+        try
+        {
+          target[i] = boost::lexical_cast<double>(items[i]);
+        }
+        catch (boost::bad_lexical_cast&)
+        {
+          target.clear();
+          return false;
+        }
+#endif
       }
 
       return true;
--- a/Samples/Sdl/BasicScene.cpp	Tue May 28 14:18:46 2019 +0200
+++ b/Samples/Sdl/BasicScene.cpp	Tue May 28 15:58:21 2019 +0200
@@ -104,21 +104,21 @@
   {
     std::auto_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer);
 
-    layer->SetThickness(1);
+    layer->SetThickness(10);
 
     PolylineSceneLayer::Chain chain;
     chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5));
     chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5));
     chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5));
     chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5));
-    layer->AddChain(chain, true);
+    layer->AddChain(chain, true, 255, 0, 0);
 
     chain.clear();
     chain.push_back(ScenePoint2D(-5, -5));
     chain.push_back(ScenePoint2D(5, -5));
     chain.push_back(ScenePoint2D(5, 5));
     chain.push_back(ScenePoint2D(-5, 5));
-    layer->AddChain(chain, true);
+    layer->AddChain(chain, true, 0, 255, 0);
 
     double dy = 1.01;
     chain.clear();
@@ -126,9 +126,8 @@
     chain.push_back(ScenePoint2D(4, -4 + dy));
     chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy));
     chain.push_back(ScenePoint2D(4, 2));
-    layer->AddChain(chain, false);
+    layer->AddChain(chain, false, 0, 0, 255);
 
-    layer->SetColor(0,255, 255);
     scene.SetLayer(50, layer.release());
   }
 
--- a/Samples/Sdl/Loader.cpp	Tue May 28 14:18:46 2019 +0200
+++ b/Samples/Sdl/Loader.cpp	Tue May 28 15:58:21 2019 +0200
@@ -1794,8 +1794,6 @@
           command->SetBody(*it);
           command->SetPayload(new LookupInstance(loader, *it));
           Schedule(command.release());
-
-          printf("[%s]\n", it->c_str());
         }
       }
     };
@@ -1852,12 +1850,12 @@
 
         for (size_t i = 0; i < content_.GetStructuresCount(); i++)
         {
+          const Color& color = content_.GetStructureColor(i);
+
           std::vector< std::vector<DicomStructureSet::PolygonPoint> > polygons;
           
           if (content_.ProjectStructure(polygons, i, cuttingPlane))
           {
-            printf(">> %d\n", static_cast<int>(polygons.size()));
-            
             for (size_t j = 0; j < polygons.size(); j++)
             {
               PolylineSceneLayer::Chain chain;
@@ -1868,7 +1866,7 @@
                 chain[k] = ScenePoint2D(polygons[j][k].first, polygons[j][k].second);
               }
 
-              layer->AddChain(chain, true /* closed */);
+              layer->AddChain(chain, true /* closed */, color);
             }
           }
         }
@@ -2492,6 +2490,8 @@
   OrthancStone::StoneInitialize();
   //Orthanc::Logging::EnableInfoLevel(true);
 
+  printf("%d\n", __cplusplus);
+
   try
   {
     OrthancStone::NativeApplicationContext context;
--- a/Samples/Sdl/TrackerSampleApp.cpp	Tue May 28 14:18:46 2019 +0200
+++ b/Samples/Sdl/TrackerSampleApp.cpp	Tue May 28 15:58:21 2019 +0200
@@ -533,14 +533,14 @@
       chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5));
       chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5));
       chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5));
-      layer->AddChain(chain, true);
+      layer->AddChain(chain, true, 255, 0, 0);
 
       chain.clear();
       chain.push_back(ScenePoint2D(-5, -5));
       chain.push_back(ScenePoint2D(5, -5));
       chain.push_back(ScenePoint2D(5, 5));
       chain.push_back(ScenePoint2D(-5, 5));
-      layer->AddChain(chain, true);
+      layer->AddChain(chain, true, 0, 255, 0);
 
       double dy = 1.01;
       chain.clear();
@@ -548,9 +548,8 @@
       chain.push_back(ScenePoint2D(4, -4 + dy));
       chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy));
       chain.push_back(ScenePoint2D(4, 2));
-      layer->AddChain(chain, false);
+      layer->AddChain(chain, false, 0, 0, 255);
 
-      layer->SetColor(0, 255, 255);
       GetScene()->SetLayer(LINESET_1_ZINDEX, layer.release());
     }