Mercurial > hg > orthanc-stone
changeset 110:53025eecbc95 wasm
renamed SliceGeometry as CoordinateSystem3D
line wrap: on
line diff
--- a/Framework/Layers/CircleMeasureTracker.cpp Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Layers/CircleMeasureTracker.cpp Wed Jun 14 15:50:38 2017 +0200 @@ -31,7 +31,7 @@ namespace OrthancStone { CircleMeasureTracker::CircleMeasureTracker(IStatusBar* statusBar, - const SliceGeometry& slice, + const CoordinateSystem3D& slice, double x, double y, uint8_t red,
--- a/Framework/Layers/CircleMeasureTracker.h Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Layers/CircleMeasureTracker.h Wed Jun 14 15:50:38 2017 +0200 @@ -24,25 +24,25 @@ #include "../Widgets/IWorldSceneMouseTracker.h" #include "../Viewport/IStatusBar.h" -#include "../Toolbox/SliceGeometry.h" +#include "../Toolbox/CoordinateSystem3D.h" namespace OrthancStone { class CircleMeasureTracker : public IWorldSceneMouseTracker { private: - IStatusBar* statusBar_; - SliceGeometry slice_; - double x1_; - double y1_; - double x2_; - double y2_; - uint8_t color_[3]; - unsigned int fontSize_; + IStatusBar* statusBar_; + CoordinateSystem3D slice_; + double x1_; + double y1_; + double x2_; + double y2_; + uint8_t color_[3]; + unsigned int fontSize_; public: CircleMeasureTracker(IStatusBar* statusBar, - const SliceGeometry& slice, + const CoordinateSystem3D& slice, double x, double y, uint8_t red,
--- a/Framework/Layers/ColorFrameRenderer.cpp Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Layers/ColorFrameRenderer.cpp Wed Jun 14 15:50:38 2017 +0200 @@ -38,7 +38,7 @@ ColorFrameRenderer::ColorFrameRenderer(Orthanc::ImageAccessor* frame, - const SliceGeometry& frameSlice, + const CoordinateSystem3D& frameSlice, double pixelSpacingX, double pixelSpacingY, bool isFullQuality) :
--- a/Framework/Layers/ColorFrameRenderer.h Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Layers/ColorFrameRenderer.h Wed Jun 14 15:50:38 2017 +0200 @@ -35,7 +35,7 @@ public: ColorFrameRenderer(Orthanc::ImageAccessor* frame, // Takes ownership - const SliceGeometry& frameSlice, + const CoordinateSystem3D& frameSlice, double pixelSpacingX, double pixelSpacingY, bool isFullQuality);
--- a/Framework/Layers/FrameRenderer.cpp Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Layers/FrameRenderer.cpp Wed Jun 14 15:50:38 2017 +0200 @@ -28,7 +28,7 @@ namespace OrthancStone { - FrameRenderer::FrameRenderer(const SliceGeometry& frameSlice, + FrameRenderer::FrameRenderer(const CoordinateSystem3D& frameSlice, double pixelSpacingX, double pixelSpacingY, bool isFullQuality) :
--- a/Framework/Layers/FrameRenderer.h Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Layers/FrameRenderer.h Wed Jun 14 15:50:38 2017 +0200 @@ -30,7 +30,7 @@ class FrameRenderer : public ILayerRenderer { private: - SliceGeometry frameSlice_; + CoordinateSystem3D frameSlice_; double pixelSpacingX_; double pixelSpacingY_; RenderStyle style_; @@ -41,7 +41,7 @@ virtual CairoSurface* GenerateDisplay(const RenderStyle& style) = 0; public: - FrameRenderer(const SliceGeometry& frameSlice, + FrameRenderer(const CoordinateSystem3D& frameSlice, double pixelSpacingX, double pixelSpacingY, bool isFullQuality); @@ -49,7 +49,7 @@ virtual bool RenderLayer(CairoContext& context, const ViewportGeometry& view); - virtual const SliceGeometry& GetLayerSlice() + virtual const CoordinateSystem3D& GetLayerSlice() { return frameSlice_; }
--- a/Framework/Layers/GrayscaleFrameRenderer.cpp Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Layers/GrayscaleFrameRenderer.cpp Wed Jun 14 15:50:38 2017 +0200 @@ -110,7 +110,7 @@ GrayscaleFrameRenderer::GrayscaleFrameRenderer(Orthanc::ImageAccessor* frame, const DicomFrameConverter& converter, - const SliceGeometry& frameSlice, + const CoordinateSystem3D& frameSlice, double pixelSpacingX, double pixelSpacingY, bool isFullQuality) :
--- a/Framework/Layers/GrayscaleFrameRenderer.h Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Layers/GrayscaleFrameRenderer.h Wed Jun 14 15:50:38 2017 +0200 @@ -39,7 +39,7 @@ public: GrayscaleFrameRenderer(Orthanc::ImageAccessor* frame, // Takes ownership const DicomFrameConverter& converter, - const SliceGeometry& frameSlice, + const CoordinateSystem3D& frameSlice, double pixelSpacingX, double pixelSpacingY, bool isFullQuality);
--- a/Framework/Layers/ILayerRenderer.h Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Layers/ILayerRenderer.h Wed Jun 14 15:50:38 2017 +0200 @@ -22,7 +22,7 @@ #pragma once #include "../Viewport/CairoContext.h" -#include "../Toolbox/SliceGeometry.h" +#include "../Toolbox/CoordinateSystem3D.h" #include "../Toolbox/ViewportGeometry.h" #include "RenderStyle.h" @@ -40,7 +40,7 @@ virtual void SetLayerStyle(const RenderStyle& style) = 0; - virtual const SliceGeometry& GetLayerSlice() = 0; + virtual const CoordinateSystem3D& GetLayerSlice() = 0; virtual bool IsFullQuality() = 0; };
--- a/Framework/Layers/ILayerSource.h Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Layers/ILayerSource.h Wed Jun 14 15:50:38 2017 +0200 @@ -66,8 +66,8 @@ virtual void Register(IObserver& observer) = 0; virtual bool GetExtent(std::vector<Vector>& points, - const SliceGeometry& viewportSlice) = 0; + const CoordinateSystem3D& viewportSlice) = 0; - virtual void ScheduleLayerCreation(const SliceGeometry& viewportSlice) = 0; + virtual void ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice) = 0; }; }
--- a/Framework/Layers/LineLayerRenderer.cpp Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Layers/LineLayerRenderer.cpp Wed Jun 14 15:50:38 2017 +0200 @@ -39,7 +39,7 @@ bool LineLayerRenderer::RenderLayer(CairoContext& context, const ViewportGeometry& view, - const SliceGeometry& slice) + const CoordinateSystem3D& slice) { if (visible_) {
--- a/Framework/Layers/LineLayerRenderer.h Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Layers/LineLayerRenderer.h Wed Jun 14 15:50:38 2017 +0200 @@ -43,7 +43,7 @@ virtual bool RenderLayer(CairoContext& context, const ViewportGeometry& view, - const SliceGeometry& slice); + const CoordinateSystem3D& slice); virtual void SetLayerStyle(const RenderStyle& style);
--- a/Framework/Layers/LineMeasureTracker.cpp Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Layers/LineMeasureTracker.cpp Wed Jun 14 15:50:38 2017 +0200 @@ -28,7 +28,7 @@ namespace OrthancStone { LineMeasureTracker::LineMeasureTracker(IStatusBar* statusBar, - const SliceGeometry& slice, + const CoordinateSystem3D& slice, double x, double y, uint8_t red,
--- a/Framework/Layers/LineMeasureTracker.h Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Layers/LineMeasureTracker.h Wed Jun 14 15:50:38 2017 +0200 @@ -24,25 +24,25 @@ #include "../Widgets/IWorldSceneMouseTracker.h" #include "../Viewport/IStatusBar.h" -#include "../Toolbox/SliceGeometry.h" +#include "../Toolbox/CoordinateSystem3D.h" namespace OrthancStone { class LineMeasureTracker : public IWorldSceneMouseTracker { private: - IStatusBar* statusBar_; - SliceGeometry slice_; - double x1_; - double y1_; - double x2_; - double y2_; - uint8_t color_[3]; - unsigned int fontSize_; + IStatusBar* statusBar_; + CoordinateSystem3D slice_; + double x1_; + double y1_; + double x2_; + double y2_; + uint8_t color_[3]; + unsigned int fontSize_; public: LineMeasureTracker(IStatusBar* statusBar, - const SliceGeometry& slice, + const CoordinateSystem3D& slice, double x, double y, uint8_t red,
--- a/Framework/Layers/OrthancFrameLayerSource.cpp Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Layers/OrthancFrameLayerSource.cpp Wed Jun 14 15:50:38 2017 +0200 @@ -88,7 +88,7 @@ bool OrthancFrameLayerSource::GetExtent(std::vector<Vector>& points, - const SliceGeometry& viewportSlice) + const CoordinateSystem3D& viewportSlice) { size_t index; if (loader_.IsGeometryReady() && @@ -104,7 +104,7 @@ } - void OrthancFrameLayerSource::ScheduleLayerCreation(const SliceGeometry& viewportSlice) + void OrthancFrameLayerSource::ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice) { size_t index;
--- a/Framework/Layers/OrthancFrameLayerSource.h Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Layers/OrthancFrameLayerSource.h Wed Jun 14 15:50:38 2017 +0200 @@ -74,8 +74,8 @@ } virtual bool GetExtent(std::vector<Vector>& points, - const SliceGeometry& viewportSlice); + const CoordinateSystem3D& viewportSlice); - virtual void ScheduleLayerCreation(const SliceGeometry& viewportSlice); + virtual void ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice); }; }
--- a/Framework/Layers/SliceOutlineRenderer.h Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Layers/SliceOutlineRenderer.h Wed Jun 14 15:50:38 2017 +0200 @@ -46,7 +46,7 @@ style_ = style; } - virtual const SliceGeometry& GetLayerSlice() + virtual const CoordinateSystem3D& GetLayerSlice() { return slice_.GetGeometry(); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/CoordinateSystem3D.cpp Wed Jun 14 15:50:38 2017 +0200 @@ -0,0 +1,155 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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/>. + **/ + + +#include "CoordinateSystem3D.h" + +#include "GeometryToolbox.h" + +#include "../../Resources/Orthanc/Core/Logging.h" +#include "../../Resources/Orthanc/Core/Toolbox.h" +#include "../../Resources/Orthanc/Core/OrthancException.h" + +namespace OrthancStone +{ + void CoordinateSystem3D::CheckAndComputeNormal() + { + // DICOM expects normal vectors to define the axes: "The row and + // column direction cosine vectors shall be normal, i.e., the dot + // product of each direction cosine vector with itself shall be + // unity." + // http://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.7.6.2.html + if (!GeometryToolbox::IsNear(boost::numeric::ublas::norm_2(axisX_), 1.0) || + !GeometryToolbox::IsNear(boost::numeric::ublas::norm_2(axisY_), 1.0)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + // The vectors within "Image Orientation Patient" must be + // orthogonal, according to the DICOM specification: "The row and + // column direction cosine vectors shall be orthogonal, i.e., + // their dot product shall be zero." + // http://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.7.6.2.html + if (!GeometryToolbox::IsCloseToZero(boost::numeric::ublas::inner_prod(axisX_, axisY_))) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + GeometryToolbox::CrossProduct(normal_, axisX_, axisY_); + + // Just a sanity check, it should be useless by construction + assert(GeometryToolbox::IsNear(boost::numeric::ublas::norm_2(normal_), 1.0)); + } + + + void CoordinateSystem3D::SetupCanonical() + { + GeometryToolbox::AssignVector(origin_, 0, 0, 0); + GeometryToolbox::AssignVector(axisX_, 1, 0, 0); + GeometryToolbox::AssignVector(axisY_, 0, 1, 0); + CheckAndComputeNormal(); + } + + + CoordinateSystem3D::CoordinateSystem3D(const Vector& origin, + const Vector& axisX, + const Vector& axisY) : + origin_(origin), + axisX_(axisX), + axisY_(axisY) + { + CheckAndComputeNormal(); + } + + + void CoordinateSystem3D::Setup(const std::string& imagePositionPatient, + const std::string& imageOrientationPatient) + { + std::string tmpPosition = Orthanc::Toolbox::StripSpaces(imagePositionPatient); + std::string tmpOrientation = Orthanc::Toolbox::StripSpaces(imageOrientationPatient); + + Vector orientation; + if (!GeometryToolbox::ParseVector(origin_, tmpPosition) || + !GeometryToolbox::ParseVector(orientation, tmpOrientation) || + origin_.size() != 3 || + orientation.size() != 6) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + axisX_.resize(3); + axisX_[0] = orientation[0]; + axisX_[1] = orientation[1]; + axisX_[2] = orientation[2]; + + axisY_.resize(3); + axisY_[0] = orientation[3]; + axisY_[1] = orientation[4]; + axisY_[2] = orientation[5]; + + CheckAndComputeNormal(); + } + + + CoordinateSystem3D::CoordinateSystem3D(const OrthancPlugins::IDicomDataset& dicom) + { + std::string a, b; + + if (dicom.GetStringValue(a, OrthancPlugins::DICOM_TAG_IMAGE_POSITION_PATIENT) && + dicom.GetStringValue(b, OrthancPlugins::DICOM_TAG_IMAGE_ORIENTATION_PATIENT)) + { + Setup(a, b); + } + else + { + SetupCanonical(); + } + } + + + Vector CoordinateSystem3D::MapSliceToWorldCoordinates(double x, + double y) const + { + return origin_ + x * axisX_ + y * axisY_; + } + + + double CoordinateSystem3D::ProjectAlongNormal(const Vector& point) const + { + return boost::numeric::ublas::inner_prod(point, normal_); + } + + + void CoordinateSystem3D::ProjectPoint(double& offsetX, + double& offsetY, + const Vector& point) const + { + // Project the point onto the slice + Vector projection; + GeometryToolbox::ProjectPointOntoPlane(projection, point, normal_, origin_); + + // As the axes are orthonormal vectors thanks to + // CheckAndComputeNormal(), the following dot products give the + // offset of the origin of the slice wrt. the origin of the + // reference plane https://en.wikipedia.org/wiki/Vector_projection + offsetX = boost::numeric::ublas::inner_prod(axisX_, projection - origin_); + offsetY = boost::numeric::ublas::inner_prod(axisY_, projection - origin_); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/CoordinateSystem3D.h Wed Jun 14 15:50:38 2017 +0200 @@ -0,0 +1,92 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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 "GeometryToolbox.h" +#include "../../Resources/Orthanc/Plugins/Samples/Common/IDicomDataset.h" + +namespace OrthancStone +{ + // Geometry of a 3D plane + class CoordinateSystem3D + { + private: + Vector origin_; + Vector normal_; + Vector axisX_; + Vector axisY_; + + void CheckAndComputeNormal(); + + void Setup(const std::string& imagePositionPatient, + const std::string& imageOrientationPatient); + + void SetupCanonical(); + + public: + CoordinateSystem3D() + { + SetupCanonical(); + } + + CoordinateSystem3D(const Vector& origin, + const Vector& axisX, + const Vector& axisY); + + CoordinateSystem3D(const OrthancPlugins::IDicomDataset& dicom); + + CoordinateSystem3D(const std::string& imagePositionPatient, + const std::string& imageOrientationPatient) + { + Setup(imagePositionPatient, imageOrientationPatient); + } + + const Vector& GetNormal() const + { + return normal_; + } + + const Vector& GetOrigin() const + { + return origin_; + } + + const Vector& GetAxisX() const + { + return axisX_; + } + + const Vector& GetAxisY() const + { + return axisY_; + } + + Vector MapSliceToWorldCoordinates(double x, + double y) const; + + double ProjectAlongNormal(const Vector& point) const; + + void ProjectPoint(double& offsetX, + double& offsetY, + const Vector& point) const; + }; +}
--- a/Framework/Toolbox/DicomStructureSet.cpp Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Toolbox/DicomStructureSet.cpp Wed Jun 14 15:50:38 2017 +0200 @@ -61,7 +61,7 @@ } - SliceGeometry DicomStructureSet::ExtractSliceGeometry(double& sliceThickness, + CoordinateSystem3D DicomStructureSet::ExtractSliceGeometry(double& sliceThickness, OrthancPlugins::IOrthancConnection& orthanc, const OrthancPlugins::IDicomDataset& tags, size_t contourIndex, @@ -122,7 +122,7 @@ } FullOrthancDataset parentTags(orthanc, "/instances/" + parentLookup[0]["ID"].asString() + "/tags"); - SliceGeometry slice(parentTags); + CoordinateSystem3D slice(parentTags); Vector v; if (GeometryToolbox::ParseVector(v, parentTags, DICOM_TAG_SLICE_THICKNESS) && @@ -161,7 +161,7 @@ bool DicomStructureSet::IsPolygonOnSlice(const Polygon& polygon, - const SliceGeometry& geometry) const + const CoordinateSystem3D& geometry) const { double d = boost::numeric::ublas::inner_prod(geometry.GetOrigin(), normal_); @@ -267,7 +267,7 @@ } Polygon polygon; - SliceGeometry geometry = ExtractSliceGeometry(polygon.sliceThickness_, orthanc, tags, i, j); + CoordinateSystem3D geometry = ExtractSliceGeometry(polygon.sliceThickness_, orthanc, tags, i, j); polygon.projectionAlongNormal_ = geometry.ProjectAlongNormal(geometry.GetOrigin()); for (size_t k = 0; k < countPoints; k++) @@ -351,7 +351,7 @@ void DicomStructureSet::Render(CairoContext& context, - const SliceGeometry& slice) const + const CoordinateSystem3D& slice) const { cairo_t* cr = context.GetObject();
--- a/Framework/Toolbox/DicomStructureSet.h Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Toolbox/DicomStructureSet.h Wed Jun 14 15:50:38 2017 +0200 @@ -21,7 +21,7 @@ #pragma once -#include "SliceGeometry.h" +#include "CoordinateSystem3D.h" #include "../Viewport/CairoContext.h" #include "../../Resources/Orthanc/Plugins/Samples/Common/IOrthancConnection.h" @@ -59,16 +59,16 @@ std::string parentSeriesId_; Vector normal_; - SliceGeometry ExtractSliceGeometry(double& sliceThickness, - OrthancPlugins::IOrthancConnection& orthanc, - const OrthancPlugins::IDicomDataset& tags, - size_t contourIndex, - size_t sliceIndex); + CoordinateSystem3D ExtractSliceGeometry(double& sliceThickness, + OrthancPlugins::IOrthancConnection& orthanc, + const OrthancPlugins::IDicomDataset& tags, + size_t contourIndex, + size_t sliceIndex); const Structure& GetStructure(size_t index) const; bool IsPolygonOnSlice(const Polygon& polygon, - const SliceGeometry& geometry) const; + const CoordinateSystem3D& geometry) const; public: @@ -97,6 +97,6 @@ } void Render(CairoContext& context, - const SliceGeometry& slice) const; + const CoordinateSystem3D& slice) const; }; }
--- a/Framework/Toolbox/OrthancSeriesLoader.cpp Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Toolbox/OrthancSeriesLoader.cpp Wed Jun 14 15:50:38 2017 +0200 @@ -34,9 +34,9 @@ class OrthancSeriesLoader::Slice : public boost::noncopyable { private: - std::string instanceId_; - SliceGeometry geometry_; - double projectionAlongNormal_; + std::string instanceId_; + CoordinateSystem3D geometry_; + double projectionAlongNormal_; public: Slice(const std::string& instanceId, @@ -52,7 +52,7 @@ return instanceId_; } - const SliceGeometry& GetGeometry() const + const CoordinateSystem3D& GetGeometry() const { return geometry_; }
--- a/Framework/Toolbox/OrthancSlicesLoader.cpp Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Toolbox/OrthancSlicesLoader.cpp Wed Jun 14 15:50:38 2017 +0200 @@ -597,7 +597,7 @@ bool OrthancSlicesLoader::LookupSlice(size_t& index, - const SliceGeometry& plane) const + const CoordinateSystem3D& plane) const { if (state_ != State_GeometryReady) {
--- a/Framework/Toolbox/OrthancSlicesLoader.h Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Toolbox/OrthancSlicesLoader.h Wed Jun 14 15:50:38 2017 +0200 @@ -123,7 +123,7 @@ const Slice& GetSlice(size_t index) const; bool LookupSlice(size_t& index, - const SliceGeometry& plane) const; + const CoordinateSystem3D& plane) const; void ScheduleLoadSliceImage(size_t index, SliceImageQuality quality);
--- a/Framework/Toolbox/ParallelSlices.cpp Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Toolbox/ParallelSlices.cpp Wed Jun 14 15:50:38 2017 +0200 @@ -41,7 +41,7 @@ for (size_t i = 0; i < slices_.size(); i++) { assert(other.slices_[i] != NULL); - slices_[i] = new SliceGeometry(*other.slices_[i]); + slices_[i] = new CoordinateSystem3D(*other.slices_[i]); } } @@ -59,16 +59,16 @@ } - void ParallelSlices::AddSlice(const SliceGeometry& slice) + void ParallelSlices::AddSlice(const CoordinateSystem3D& slice) { if (slices_.empty()) { normal_ = slice.GetNormal(); - slices_.push_back(new SliceGeometry(slice)); + slices_.push_back(new CoordinateSystem3D(slice)); } else if (GeometryToolbox::IsParallel(slice.GetNormal(), normal_)) { - slices_.push_back(new SliceGeometry(slice)); + slices_.push_back(new CoordinateSystem3D(slice)); } else { @@ -82,12 +82,12 @@ const Vector& axisX, const Vector& axisY) { - SliceGeometry slice(origin, axisX, axisY); + CoordinateSystem3D slice(origin, axisX, axisY); AddSlice(slice); } - const SliceGeometry& ParallelSlices::GetSlice(size_t index) const + const CoordinateSystem3D& ParallelSlices::GetSlice(size_t index) const { if (index >= slices_.size()) { @@ -135,7 +135,7 @@ for (size_t i = slices_.size(); i > 0; i--) { - const SliceGeometry& slice = *slices_[i - 1]; + const CoordinateSystem3D& slice = *slices_[i - 1]; reversed->AddSlice(slice.GetOrigin(), -slice.GetAxisX(),
--- a/Framework/Toolbox/ParallelSlices.h Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Toolbox/ParallelSlices.h Wed Jun 14 15:50:38 2017 +0200 @@ -21,15 +21,15 @@ #pragma once -#include "SliceGeometry.h" +#include "CoordinateSystem3D.h" namespace OrthancStone { class ParallelSlices : public boost::noncopyable { private: - Vector normal_; - std::vector<SliceGeometry*> slices_; + Vector normal_; + std::vector<CoordinateSystem3D*> slices_; ParallelSlices& operator= (const ParallelSlices& other); // Forbidden @@ -45,7 +45,7 @@ return normal_; } - void AddSlice(const SliceGeometry& slice); + void AddSlice(const CoordinateSystem3D& slice); void AddSlice(const Vector& origin, const Vector& axisX, @@ -56,7 +56,7 @@ return slices_.size(); } - const SliceGeometry& GetSlice(size_t index) const; + const CoordinateSystem3D& GetSlice(size_t index) const; bool ComputeClosestSlice(size_t& closestSlice, double& closestDistance,
--- a/Framework/Toolbox/ParallelSlicesCursor.cpp Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Toolbox/ParallelSlicesCursor.cpp Wed Jun 14 15:50:38 2017 +0200 @@ -51,11 +51,11 @@ } - SliceGeometry ParallelSlicesCursor::GetSlice(size_t slice) + CoordinateSystem3D ParallelSlicesCursor::GetSlice(size_t slice) { if (slices_.get() == NULL) { - return SliceGeometry(); + return CoordinateSystem3D(); } else { @@ -72,7 +72,7 @@ } - SliceGeometry ParallelSlicesCursor::GetCurrentSlice() + CoordinateSystem3D ParallelSlicesCursor::GetCurrentSlice() { if (slices_.get() != NULL && currentSlice_ < slices_->GetSliceCount()) @@ -81,7 +81,7 @@ } else { - return SliceGeometry(); // No slice is available, return the canonical geometry + return CoordinateSystem3D(); // No slice is available, return the canonical geometry } }
--- a/Framework/Toolbox/ParallelSlicesCursor.h Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Toolbox/ParallelSlicesCursor.h Wed Jun 14 15:50:38 2017 +0200 @@ -44,9 +44,9 @@ size_t GetSliceCount(); - SliceGeometry GetSlice(size_t slice); + CoordinateSystem3D GetSlice(size_t slice); - SliceGeometry GetCurrentSlice(); + CoordinateSystem3D GetCurrentSlice(); // Returns "true" iff. the slice has actually changed bool SetDefaultSlice();
--- a/Framework/Toolbox/Slice.cpp Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Toolbox/Slice.cpp Wed Jun 14 15:50:38 2017 +0200 @@ -53,7 +53,7 @@ if (dataset.GetStringValue(position, OrthancPlugins::DICOM_TAG_IMAGE_POSITION_PATIENT) && dataset.GetStringValue(orientation, OrthancPlugins::DICOM_TAG_IMAGE_ORIENTATION_PATIENT)) { - geometry_ = SliceGeometry(position, orientation); + geometry_ = CoordinateSystem3D(position, orientation); } if (reader.GetUnsignedIntegerValue(width_, OrthancPlugins::DICOM_TAG_COLUMNS) && @@ -95,7 +95,7 @@ } - const SliceGeometry& Slice::GetGeometry() const + const CoordinateSystem3D& Slice::GetGeometry() const { if (type_ == Type_Invalid) { @@ -172,7 +172,7 @@ } - bool Slice::ContainsPlane(const SliceGeometry& plane) const + bool Slice::ContainsPlane(const CoordinateSystem3D& plane) const { if (type_ == Type_Invalid) {
--- a/Framework/Toolbox/Slice.h Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Toolbox/Slice.h Wed Jun 14 15:50:38 2017 +0200 @@ -21,7 +21,7 @@ #pragma once -#include "SliceGeometry.h" +#include "CoordinateSystem3D.h" #include "DicomFrameConverter.h" namespace OrthancStone @@ -40,7 +40,7 @@ Type type_; std::string orthancInstanceId_; unsigned int frame_; - SliceGeometry geometry_; + CoordinateSystem3D geometry_; double pixelSpacingX_; double pixelSpacingY_; double thickness_; @@ -55,7 +55,7 @@ // TODO Is this constructor the best way to go to tackle missing // layers within LayerWidget? - Slice(const SliceGeometry& plane, + Slice(const CoordinateSystem3D& plane, double thickness) : type_(Type_Standalone), frame_(0), @@ -68,7 +68,7 @@ { } - Slice(const SliceGeometry& plane, + Slice(const CoordinateSystem3D& plane, double pixelSpacingX, double pixelSpacingY, double thickness, @@ -104,7 +104,7 @@ unsigned int GetFrame() const; - const SliceGeometry& GetGeometry() const; + const CoordinateSystem3D& GetGeometry() const; double GetThickness() const; @@ -118,7 +118,7 @@ const DicomFrameConverter& GetConverter() const; - bool ContainsPlane(const SliceGeometry& plane) const; + bool ContainsPlane(const CoordinateSystem3D& plane) const; void GetExtent(std::vector<Vector>& points) const; };
--- a/Framework/Toolbox/SliceGeometry.cpp Wed Jun 14 15:34:08 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,155 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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/>. - **/ - - -#include "SliceGeometry.h" - -#include "GeometryToolbox.h" - -#include "../../Resources/Orthanc/Core/Logging.h" -#include "../../Resources/Orthanc/Core/Toolbox.h" -#include "../../Resources/Orthanc/Core/OrthancException.h" - -namespace OrthancStone -{ - void SliceGeometry::CheckAndComputeNormal() - { - // DICOM expects normal vectors to define the axes: "The row and - // column direction cosine vectors shall be normal, i.e., the dot - // product of each direction cosine vector with itself shall be - // unity." - // http://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.7.6.2.html - if (!GeometryToolbox::IsNear(boost::numeric::ublas::norm_2(axisX_), 1.0) || - !GeometryToolbox::IsNear(boost::numeric::ublas::norm_2(axisY_), 1.0)) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - - // The vectors within "Image Orientation Patient" must be - // orthogonal, according to the DICOM specification: "The row and - // column direction cosine vectors shall be orthogonal, i.e., - // their dot product shall be zero." - // http://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.7.6.2.html - if (!GeometryToolbox::IsCloseToZero(boost::numeric::ublas::inner_prod(axisX_, axisY_))) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - - GeometryToolbox::CrossProduct(normal_, axisX_, axisY_); - - // Just a sanity check, it should be useless by construction - assert(GeometryToolbox::IsNear(boost::numeric::ublas::norm_2(normal_), 1.0)); - } - - - void SliceGeometry::SetupCanonical() - { - GeometryToolbox::AssignVector(origin_, 0, 0, 0); - GeometryToolbox::AssignVector(axisX_, 1, 0, 0); - GeometryToolbox::AssignVector(axisY_, 0, 1, 0); - CheckAndComputeNormal(); - } - - - SliceGeometry::SliceGeometry(const Vector& origin, - const Vector& axisX, - const Vector& axisY) : - origin_(origin), - axisX_(axisX), - axisY_(axisY) - { - CheckAndComputeNormal(); - } - - - void SliceGeometry::Setup(const std::string& imagePositionPatient, - const std::string& imageOrientationPatient) - { - std::string tmpPosition = Orthanc::Toolbox::StripSpaces(imagePositionPatient); - std::string tmpOrientation = Orthanc::Toolbox::StripSpaces(imageOrientationPatient); - - Vector orientation; - if (!GeometryToolbox::ParseVector(origin_, tmpPosition) || - !GeometryToolbox::ParseVector(orientation, tmpOrientation) || - origin_.size() != 3 || - orientation.size() != 6) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - - axisX_.resize(3); - axisX_[0] = orientation[0]; - axisX_[1] = orientation[1]; - axisX_[2] = orientation[2]; - - axisY_.resize(3); - axisY_[0] = orientation[3]; - axisY_[1] = orientation[4]; - axisY_[2] = orientation[5]; - - CheckAndComputeNormal(); - } - - - SliceGeometry::SliceGeometry(const OrthancPlugins::IDicomDataset& dicom) - { - std::string a, b; - - if (dicom.GetStringValue(a, OrthancPlugins::DICOM_TAG_IMAGE_POSITION_PATIENT) && - dicom.GetStringValue(b, OrthancPlugins::DICOM_TAG_IMAGE_ORIENTATION_PATIENT)) - { - Setup(a, b); - } - else - { - SetupCanonical(); - } - } - - - Vector SliceGeometry::MapSliceToWorldCoordinates(double x, - double y) const - { - return origin_ + x * axisX_ + y * axisY_; - } - - - double SliceGeometry::ProjectAlongNormal(const Vector& point) const - { - return boost::numeric::ublas::inner_prod(point, normal_); - } - - - void SliceGeometry::ProjectPoint(double& offsetX, - double& offsetY, - const Vector& point) const - { - // Project the point onto the slice - Vector projection; - GeometryToolbox::ProjectPointOntoPlane(projection, point, normal_, origin_); - - // As the axes are orthonormal vectors thanks to - // CheckAndComputeNormal(), the following dot products give the - // offset of the origin of the slice wrt. the origin of the - // reference plane https://en.wikipedia.org/wiki/Vector_projection - offsetX = boost::numeric::ublas::inner_prod(axisX_, projection - origin_); - offsetY = boost::numeric::ublas::inner_prod(axisY_, projection - origin_); - } -}
--- a/Framework/Toolbox/SliceGeometry.h Wed Jun 14 15:34:08 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,92 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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 "GeometryToolbox.h" -#include "../../Resources/Orthanc/Plugins/Samples/Common/IDicomDataset.h" - -namespace OrthancStone -{ - // Geometry of a 3D plane - class SliceGeometry - { - private: - Vector origin_; - Vector normal_; - Vector axisX_; - Vector axisY_; - - void CheckAndComputeNormal(); - - void Setup(const std::string& imagePositionPatient, - const std::string& imageOrientationPatient); - - void SetupCanonical(); - - public: - SliceGeometry() - { - SetupCanonical(); - } - - SliceGeometry(const Vector& origin, - const Vector& axisX, - const Vector& axisY); - - SliceGeometry(const OrthancPlugins::IDicomDataset& dicom); - - SliceGeometry(const std::string& imagePositionPatient, - const std::string& imageOrientationPatient) - { - Setup(imagePositionPatient, imageOrientationPatient); - } - - const Vector& GetNormal() const - { - return normal_; - } - - const Vector& GetOrigin() const - { - return origin_; - } - - const Vector& GetAxisX() const - { - return axisX_; - } - - const Vector& GetAxisY() const - { - return axisY_; - } - - Vector MapSliceToWorldCoordinates(double x, - double y) const; - - double ProjectAlongNormal(const Vector& point) const; - - void ProjectPoint(double& offsetX, - double& offsetY, - const Vector& point) const; - }; -}
--- a/Framework/Toolbox/SlicesSorter.cpp Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Toolbox/SlicesSorter.cpp Wed Jun 14 15:50:38 2017 +0200 @@ -195,7 +195,7 @@ bool SlicesSorter::LookupSlice(size_t& index, - const SliceGeometry& slice) const + const CoordinateSystem3D& slice) const { // TODO Turn this linear-time lookup into a log-time lookup, // keeping track of whether the slices are sorted along the normal
--- a/Framework/Toolbox/SlicesSorter.h Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Toolbox/SlicesSorter.h Wed Jun 14 15:50:38 2017 +0200 @@ -66,6 +66,6 @@ bool SelectNormal(Vector& normal) const; bool LookupSlice(size_t& index, - const SliceGeometry& slice) const; + const CoordinateSystem3D& slice) const; }; }
--- a/Framework/Volumes/ImageBuffer3D.cpp Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Volumes/ImageBuffer3D.cpp Wed Jun 14 15:50:38 2017 +0200 @@ -132,7 +132,7 @@ } - void ImageBuffer3D::SetAxialGeometry(const SliceGeometry& geometry) + void ImageBuffer3D::SetAxialGeometry(const CoordinateSystem3D& geometry) { axialGeometry_ = geometry; }
--- a/Framework/Volumes/ImageBuffer3D.h Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Volumes/ImageBuffer3D.h Wed Jun 14 15:50:38 2017 +0200 @@ -22,7 +22,7 @@ #pragma once #include "../Enumerations.h" -#include "../Toolbox/SliceGeometry.h" +#include "../Toolbox/CoordinateSystem3D.h" #include "../Toolbox/ParallelSlices.h" #include "../../Resources/Orthanc/Core/Images/Image.h" @@ -32,7 +32,7 @@ class ImageBuffer3D : public boost::noncopyable { private: - SliceGeometry axialGeometry_; + CoordinateSystem3D axialGeometry_; Vector voxelDimensions_; Orthanc::Image image_; Orthanc::PixelFormat format_; @@ -58,7 +58,7 @@ // Set the geometry of the first axial slice (i.e. the one whose // depth == 0) - void SetAxialGeometry(const SliceGeometry& geometry); + void SetAxialGeometry(const CoordinateSystem3D& geometry); void SetVoxelDimensions(double x, double y,
--- a/Framework/Widgets/IWorldSceneInteractor.h Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Widgets/IWorldSceneInteractor.h Wed Jun 14 15:50:38 2017 +0200 @@ -23,7 +23,6 @@ #include "IWorldSceneMouseTracker.h" -#include "../Toolbox/SliceGeometry.h" #include "../Toolbox/ViewportGeometry.h" #include "../Enumerations.h" #include "../Viewport/IStatusBar.h"
--- a/Framework/Widgets/LayerWidget.cpp Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Widgets/LayerWidget.cpp Wed Jun 14 15:50:38 2017 +0200 @@ -31,7 +31,7 @@ class LayerWidget::Scene : public boost::noncopyable { private: - SliceGeometry slice_; + CoordinateSystem3D slice_; size_t countMissing_; std::vector<ILayerRenderer*> renderers_; @@ -54,7 +54,7 @@ } public: - Scene(const SliceGeometry& slice, + Scene(const CoordinateSystem3D& slice, size_t countLayers) : slice_(slice), countMissing_(countLayers), @@ -84,7 +84,7 @@ countMissing_--; } - const SliceGeometry& GetSlice() const + const CoordinateSystem3D& GetSlice() const { return slice_; } @@ -106,7 +106,7 @@ bool RenderScene(CairoContext& context, const ViewportGeometry& view, - const SliceGeometry& viewportSlice) + const CoordinateSystem3D& viewportSlice) { bool fullQuality = true; cairo_t *cr = context.GetObject(); @@ -115,7 +115,7 @@ { if (renderers_[i] != NULL) { - const SliceGeometry& frameSlice = renderers_[i]->GetLayerSlice(); + const CoordinateSystem3D& frameSlice = renderers_[i]->GetLayerSlice(); double x0, y0, x1, y1, x2, y2; viewportSlice.ProjectPoint(x0, y0, frameSlice.GetOrigin()); @@ -374,7 +374,7 @@ } - void LayerWidget::SetSlice(const SliceGeometry& slice) + void LayerWidget::SetSlice(const CoordinateSystem3D& slice) { LOG(INFO) << "Setting slice origin: (" << slice.GetOrigin()[0] << "," << slice.GetOrigin()[1]
--- a/Framework/Widgets/LayerWidget.h Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Widgets/LayerWidget.h Wed Jun 14 15:50:38 2017 +0200 @@ -42,7 +42,7 @@ LayersIndex layersIndex_; std::vector<ILayerSource*> layers_; std::vector<RenderStyle> styles_; - SliceGeometry slice_; + CoordinateSystem3D slice_; std::auto_ptr<Scene> currentScene_; std::auto_ptr<Scene> pendingScene_; std::vector<bool> changedLayers_; @@ -100,9 +100,9 @@ void SetLayerStyle(size_t layer, const RenderStyle& style); - void SetSlice(const SliceGeometry& slice); + void SetSlice(const CoordinateSystem3D& slice); - const SliceGeometry& GetSlice() const + const CoordinateSystem3D& GetSlice() const { return slice_; }
--- a/Framework/Widgets/TestWorldSceneWidget.cpp Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Widgets/TestWorldSceneWidget.cpp Wed Jun 14 15:50:38 2017 +0200 @@ -21,6 +21,7 @@ #include "TestWorldSceneWidget.h" +#include <math.h> #include <stdio.h> namespace OrthancStone
--- a/Framework/Widgets/TestWorldSceneWidget.h Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Widgets/TestWorldSceneWidget.h Wed Jun 14 15:50:38 2017 +0200 @@ -23,6 +23,8 @@ #include "WorldSceneWidget.h" +#include <memory> + namespace OrthancStone { namespace Samples
--- a/Framework/Widgets/WorldSceneWidget.cpp Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/Widgets/WorldSceneWidget.cpp Wed Jun 14 15:50:38 2017 +0200 @@ -21,6 +21,10 @@ #include "WorldSceneWidget.h" +#include <math.h> +#include <memory> +#include <cassert> + namespace OrthancStone { static void MapMouseToScene(double& sceneX,
--- a/Framework/dev.h Wed Jun 14 15:34:08 2017 +0200 +++ b/Framework/dev.h Wed Jun 14 15:50:38 2017 +0200 @@ -250,7 +250,7 @@ double pixelSpacingX_; double pixelSpacingY_; double sliceThickness_; - SliceGeometry reference_; + CoordinateSystem3D reference_; DicomFrameConverter converter_; double ComputeAxialThickness(const OrthancVolumeImage& volume) const @@ -315,7 +315,7 @@ origin += (static_cast<double>(volume.GetSliceCount() - 1) * axialThickness * axial.GetGeometry().GetNormal()); - reference_ = SliceGeometry(origin, + reference_ = CoordinateSystem3D(origin, axial.GetGeometry().GetAxisX(), -axial.GetGeometry().GetNormal()); } @@ -337,7 +337,7 @@ origin += (static_cast<double>(volume.GetSliceCount() - 1) * axialThickness * axial.GetGeometry().GetNormal()); - reference_ = SliceGeometry(origin, + reference_ = CoordinateSystem3D(origin, axial.GetGeometry().GetAxisY(), axial.GetGeometry().GetNormal()); } @@ -383,7 +383,7 @@ } bool LookupSlice(size_t& index, - const SliceGeometry& slice) const + const CoordinateSystem3D& slice) const { bool opposite; if (!GeometryToolbox::IsParallelOrOpposite(opposite, @@ -419,7 +419,7 @@ } else { - SliceGeometry origin(reference_.GetOrigin() + + CoordinateSystem3D origin(reference_.GetOrigin() + static_cast<double>(slice) * sliceThickness_ * reference_.GetNormal(), reference_.GetAxisX(), reference_.GetAxisY()); @@ -505,7 +505,7 @@ bool DetectProjection(VolumeProjection& projection, - const SliceGeometry& viewportSlice) + const CoordinateSystem3D& viewportSlice) { bool isOpposite; // Ignored @@ -545,7 +545,7 @@ } virtual bool GetExtent(std::vector<Vector>& points, - const SliceGeometry& viewportSlice) + const CoordinateSystem3D& viewportSlice) { VolumeProjection projection; @@ -565,7 +565,7 @@ } - virtual void ScheduleLayerCreation(const SliceGeometry& viewportSlice) + virtual void ScheduleLayerCreation(const CoordinateSystem3D& viewportSlice) { VolumeProjection projection;
--- a/Resources/CMake/OrthancStone.cmake Wed Jun 14 15:34:08 2017 +0200 +++ b/Resources/CMake/OrthancStone.cmake Wed Jun 14 15:50:38 2017 +0200 @@ -214,7 +214,7 @@ ${ORTHANC_STONE_DIR}/Framework/Toolbox/ParallelSlices.cpp ${ORTHANC_STONE_DIR}/Framework/Toolbox/ParallelSlicesCursor.cpp ${ORTHANC_STONE_DIR}/Framework/Toolbox/Slice.cpp - ${ORTHANC_STONE_DIR}/Framework/Toolbox/SliceGeometry.cpp + ${ORTHANC_STONE_DIR}/Framework/Toolbox/CoordinateSystem3D.cpp ${ORTHANC_STONE_DIR}/Framework/Toolbox/SlicesSorter.cpp ${ORTHANC_STONE_DIR}/Framework/Toolbox/ViewportGeometry.cpp ${ORTHANC_STONE_DIR}/Framework/Viewport/CairoContext.cpp