Mercurial > hg > orthanc-stone
changeset 645:1e9ed656318e
Merge + ongoing measure work
author | Benjamin Golinvaux <bgo@osimis.io> |
---|---|
date | Mon, 13 May 2019 15:12:56 +0200 |
parents | f939f449482c (diff) 7ca8dc7ec17b (current diff) |
children | b4fe9642e83b |
files | .hgignore Samples/Common/AngleMeasureTool.cpp Samples/Common/AngleMeasureTool.h Samples/Common/CreateAngleMeasureTracker.cpp Samples/Common/CreateAngleMeasureTracker.h Samples/Common/CreateLineMeasureTracker.cpp Samples/Common/CreateLineMeasureTracker.h Samples/Common/LineMeasureTool.cpp Samples/Common/LineMeasureTool.h Samples/Common/MeasureCommands.cpp Samples/Common/MeasureCommands.h Samples/Common/MeasureTools.cpp Samples/Common/MeasureTools.h Samples/Common/MeasureToolsToolbox.cpp Samples/Common/MeasureToolsToolbox.h Samples/Common/MeasureTrackers.cpp Samples/Common/MeasureTrackers.h Samples/Sdl/CMakeLists.txt Samples/Sdl/Loader.cpp Samples/Sdl/TrackerSample.cpp |
diffstat | 38 files changed, 2425 insertions(+), 1386 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Fri May 10 14:54:03 2019 +0200 +++ b/.hgignore Mon May 13 15:12:56 2019 +0200 @@ -30,3 +30,5 @@ Resources/CommandTool/protoc-tests/generated_ts/ Resources/CommandTool/protoc-tests/node_modules/ Samples/Sdl/ThirdPartyDownloads/ +Samples/Sdl/CMakeLists.txt.orig +
--- a/Framework/Scene2D/IPointerTracker.h Fri May 10 14:54:03 2019 +0200 +++ b/Framework/Scene2D/IPointerTracker.h Mon May 13 15:12:56 2019 +0200 @@ -38,8 +38,8 @@ virtual void Update(const PointerEvent& event) = 0; /** - This method will be called if the tracker is to be abandoned without - committing its result + This method will be called when the tracker should commit its result + before being destroyed. */ virtual void Release() = 0; };
--- a/Framework/Scene2D/Scene2D.cpp Fri May 10 14:54:03 2019 +0200 +++ b/Framework/Scene2D/Scene2D.cpp Mon May 13 15:12:56 2019 +0200 @@ -102,8 +102,8 @@ void Scene2D::SetLayer(int depth, ISceneLayer* layer) // Takes ownership { - LOG(INFO) << "SetLayer(" << depth << ", " << - reinterpret_cast<intptr_t>(layer) << ")"; + //LOG(INFO) << "SetLayer(" << depth << ", " << + // reinterpret_cast<intptr_t>(layer) << ")"; std::auto_ptr<Item> item(new Item(layer, layerCounter_++)); if (layer == NULL)
--- a/README.md Fri May 10 14:54:03 2019 +0200 +++ b/README.md Mon May 13 15:12:56 2019 +0200 @@ -244,3 +244,14 @@ ``` cmake -G "Visual Studio 15 2017 Win64" -DMSVC_MULTIPLE_PROCESSES=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Samples/Sdl ``` + +And under Ubuntu (note the /mnt/c/osi/dev/orthanc folder): +``` +cmake -G "Ninja" -DENABLE_OPENGL=ON -DSTATIC_BUILD=OFF -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="/mnt/c/osi/dev/orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Samples/Sdl +``` + +TODO trackers: +- text overlay 50% --> ColorTextureLayer 50% +- angle tracker: draw arcs + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/AngleMeasureTool.cpp Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,199 @@ +/** + * 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/>. + **/ + +#include "AngleMeasureTool.h" +#include "MeasureToolsToolbox.h" + +#include <Core/Logging.h> + +#include <boost/math/constants/constants.hpp> + +namespace OrthancStone +{ + AngleMeasureTool::~AngleMeasureTool() + { + // this measuring tool is a RABI for the corresponding visual layers + // stored in the 2D scene + Disable(); + RemoveFromScene(); + } + + void AngleMeasureTool::RemoveFromScene() + { + if (layersCreated) + { + assert(GetScene().HasLayer(polylineZIndex_)); + assert(GetScene().HasLayer(textZIndex_)); + GetScene().DeleteLayer(polylineZIndex_); + GetScene().DeleteLayer(textZIndex_); + } + } + + void AngleMeasureTool::SetSide1End(ScenePoint2D pt) + { + side1End_ = pt; + RefreshScene(); + } + + void AngleMeasureTool::SetSide2End(ScenePoint2D pt) + { + side2End_ = pt; + RefreshScene(); + } + + void AngleMeasureTool::SetCenter(ScenePoint2D pt) + { + center_ = pt; + RefreshScene(); + } + + PolylineSceneLayer* AngleMeasureTool::GetPolylineLayer() + { + assert(GetScene().HasLayer(polylineZIndex_)); + ISceneLayer* layer = &(GetScene().GetLayer(polylineZIndex_)); + PolylineSceneLayer* concreteLayer = dynamic_cast<PolylineSceneLayer*>(layer); + assert(concreteLayer != NULL); + return concreteLayer; + } + + TextSceneLayer* AngleMeasureTool::GetTextLayer() + { + assert(GetScene().HasLayer(textZIndex_)); + ISceneLayer* layer = &(GetScene().GetLayer(textZIndex_)); + TextSceneLayer* concreteLayer = dynamic_cast<TextSceneLayer*>(layer); + assert(concreteLayer != NULL); + return concreteLayer; + } + + + void AngleMeasureTool::RefreshScene() + { + if (IsEnabled()) + { + + // get the scaling factor + const double pixelToScene = + GetScene().GetCanvasToSceneTransform().ComputeZoom(); + + if (!layersCreated) + { + // Create the layers if need be + + assert(textZIndex_ == -1); + { + polylineZIndex_ = GetScene().GetMaxDepth() + 100; + //LOG(INFO) << "set polylineZIndex_ to: " << polylineZIndex_; + std::auto_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer()); + GetScene().SetLayer(polylineZIndex_, layer.release()); + } + { + textZIndex_ = GetScene().GetMaxDepth() + 100; + //LOG(INFO) << "set textZIndex_ to: " << textZIndex_; + std::auto_ptr<TextSceneLayer> layer(new TextSceneLayer()); + GetScene().SetLayer(textZIndex_, layer.release()); + } + layersCreated = true; + } + else + { + assert(GetScene().HasLayer(polylineZIndex_)); + assert(GetScene().HasLayer(textZIndex_)); + } + { + // Fill the polyline layer with the measurement line + + PolylineSceneLayer* polylineLayer = GetPolylineLayer(); + polylineLayer->ClearAllChains(); + polylineLayer->SetColor(0, 223, 21); + + // sides + { + { + PolylineSceneLayer::Chain chain; + chain.push_back(side1End_); + chain.push_back(center_); + polylineLayer->AddChain(chain, false); + } + { + PolylineSceneLayer::Chain chain; + chain.push_back(side2End_); + chain.push_back(center_); + polylineLayer->AddChain(chain, false); + } + } + + // handles + { + //void AddSquare(PolylineSceneLayer::Chain& chain,const Scene2D& scene,const ScenePoint2D& centerS,const double& sideLength) + + { + PolylineSceneLayer::Chain chain; + AddSquare(chain, GetScene(), side1End_, 10.0); //TODO: take DPI into account + polylineLayer->AddChain(chain, true); + } + + { + PolylineSceneLayer::Chain chain; + AddSquare(chain, GetScene(), side2End_, 10.0); //TODO: take DPI into account + polylineLayer->AddChain(chain, true); + } + } + + // arc + { + PolylineSceneLayer::Chain chain; + + AddArc(chain, GetScene(), side1End_, center_, side2End_, + 20.0*pixelToScene, true); //TODO: true means always clockwise + polylineLayer->AddChain(chain, true); + } + } + { + // Set the text layer proporeties + + // the angle is measured in a clockwise way between the points + double angleRad = MeasureAngle(side1End_, center_, side2End_); + double angleDeg = RadiansToDegrees(angleRad); + + TextSceneLayer* textLayer = GetTextLayer(); + + char buf[64]; + sprintf(buf, "%0.02f deg", angleDeg); + textLayer->SetText(buf); + textLayer->SetColor(0, 223, 21); + + ScenePoint2D textAnchor; + GetPositionOnBisectingLine( + textAnchor, side1End_, center_, side2End_, 40.0*pixelToScene); + textLayer->SetPosition(textAnchor.GetX(), textAnchor.GetY()); + } + } + else + { + if (layersCreated) + { + RemoveFromScene(); + layersCreated = false; + } + } + } + + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/AngleMeasureTool.h Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,74 @@ +/** + * 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 "MeasureTools.h" + +#include <Framework/Scene2D/Scene2D.h> +#include <Framework/Scene2D/ScenePoint2D.h> +#include <Framework/Scene2D/PolylineSceneLayer.h> +#include <Framework/Scene2D/TextSceneLayer.h> + +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> + +#include <vector> +#include <cmath> + +namespace OrthancStone +{ + class AngleMeasureTool : public MeasureTool + { + public: + AngleMeasureTool(Scene2D& scene) + : MeasureTool(scene) + , layersCreated(false) + , polylineZIndex_(-1) + , textZIndex_(-1) + { + + } + + ~AngleMeasureTool(); + + void SetSide1End(ScenePoint2D start); + void SetCenter(ScenePoint2D start); + void SetSide2End(ScenePoint2D start); + + private: + PolylineSceneLayer* GetPolylineLayer(); + TextSceneLayer* GetTextLayer(); + virtual void RefreshScene() ORTHANC_OVERRIDE; + void RemoveFromScene(); + + private: + ScenePoint2D side1End_; + ScenePoint2D side2End_; + ScenePoint2D center_; + bool layersCreated; + int polylineZIndex_; + int textZIndex_; + }; + + typedef boost::shared_ptr<AngleMeasureTool> AngleMeasureToolPtr; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/CreateAngleMeasureTracker.cpp Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,126 @@ +/** + * 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/>. + **/ + +#include "CreateAngleMeasureTracker.h" +#include <Core/OrthancException.h> + +using namespace Orthanc; + +namespace OrthancStone +{ + CreateAngleMeasureTracker::CreateAngleMeasureTracker( + Scene2D& scene, + std::vector<TrackerCommandPtr>& undoStack, + std::vector<MeasureToolPtr>& measureTools, + const PointerEvent& e) + : CreateMeasureTracker(scene, undoStack, measureTools) + , state_(CreatingSide1) + { + command_.reset( + new CreateAngleMeasureCommand( + scene, + measureTools, + e.GetMainPosition().Apply(scene.GetCanvasToSceneTransform()))); + } + + CreateAngleMeasureTracker::~CreateAngleMeasureTracker() + { + } + + void CreateAngleMeasureTracker::PointerMove(const PointerEvent& event) + { + if (!active_) + { + throw OrthancException(ErrorCode_InternalError, + "Internal error: wrong state in CreateAngleMeasureTracker::" + "PointerMove: active_ == false"); + } + + ScenePoint2D scenePos = event.GetMainPosition().Apply( + scene_.GetCanvasToSceneTransform()); + + switch (state_) + { + case CreatingSide1: + GetCommand()->SetCenter(scenePos); + break; + case CreatingSide2: + GetCommand()->SetSide2End(scenePos); + break; + default: + throw OrthancException(ErrorCode_InternalError, + "Wrong state in CreateAngleMeasureTracker::" + "PointerMove: state_ invalid"); + } + //LOG(TRACE) << "scenePos.GetX() = " << scenePos.GetX() << " " << + // "scenePos.GetY() = " << scenePos.GetY(); + } + + void CreateAngleMeasureTracker::PointerUp(const PointerEvent& e) + { + // TODO: the current app does not prevent multiple PointerDown AND + // PointerUp to be sent to the tracker. + // Unless we augment the PointerEvent structure with the button index, + // we cannot really tell if this pointer up event matches the initial + // pointer down event. Let's make it simple for now. + + switch (state_) + { + case CreatingSide1: + state_ = CreatingSide2; + break; + case CreatingSide2: + throw OrthancException(ErrorCode_InternalError, + "Wrong state in CreateAngleMeasureTracker::" + "PointerUp: state_ == CreatingSide2 ; this should not happen"); + break; + default: + throw OrthancException(ErrorCode_InternalError, + "Wrong state in CreateAngleMeasureTracker::" + "PointerMove: state_ invalid"); + } + } + + void CreateAngleMeasureTracker::PointerDown(const PointerEvent& e) + { + switch (state_) + { + case CreatingSide1: + throw OrthancException(ErrorCode_InternalError, + "Wrong state in CreateAngleMeasureTracker::" + "PointerDown: state_ == CreatingSide1 ; this should not happen"); + break; + case CreatingSide2: + // we are done + active_ = false; + break; + default: + throw OrthancException(ErrorCode_InternalError, + "Wrong state in CreateAngleMeasureTracker::" + "PointerMove: state_ invalid"); + } + } + + CreateAngleMeasureCommandPtr CreateAngleMeasureTracker::GetCommand() + { + return boost::dynamic_pointer_cast<CreateAngleMeasureCommand>(command_); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/CreateAngleMeasureTracker.h Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,64 @@ +/** + * 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 "MeasureTrackers.h" +#include "MeasureCommands.h" + +#include <vector> + +namespace OrthancStone +{ + class CreateAngleMeasureTracker : public CreateMeasureTracker + { + public: + /** + When you create this tracker, you need to supply it with the undo stack + where it will store the commands that perform the actual measure tool + creation and modification. + In turn, a container for these commands to store the actual measuring + must be supplied, too + */ + CreateAngleMeasureTracker( + Scene2D& scene, + std::vector<TrackerCommandPtr>& undoStack, + std::vector<MeasureToolPtr>& measureTools, + const PointerEvent& e); + + ~CreateAngleMeasureTracker(); + + virtual void PointerMove(const PointerEvent& e) ORTHANC_OVERRIDE; + virtual void PointerUp(const PointerEvent& e) ORTHANC_OVERRIDE; + virtual void PointerDown(const PointerEvent& e) ORTHANC_OVERRIDE; + + private: + CreateAngleMeasureCommandPtr GetCommand(); + + enum State + { + CreatingSide1, + CreatingSide2, + Finished // just for debug + }; + State state_; + + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/CreateCircleMeasureTracker.cpp Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,23 @@ +/** + * 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/>. + **/ + +namespace OrthancStone +{ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/CreateCircleMeasureTracker.h Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,25 @@ +/** + * 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 + +namespace OrthancStone +{ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/CreateLineMeasureTracker.cpp Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,89 @@ +/** + * 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/>. + **/ + +#include "CreateLineMeasureTracker.h" +#include <Core/OrthancException.h> + +using namespace Orthanc; + +namespace OrthancStone +{ + CreateLineMeasureTracker::CreateLineMeasureTracker( + Scene2D& scene, + std::vector<TrackerCommandPtr>& undoStack, + std::vector<MeasureToolPtr>& measureTools, + const PointerEvent& e) + : CreateMeasureTracker(scene, undoStack, measureTools) + { + command_.reset( + new CreateLineMeasureCommand( + scene, + measureTools, + e.GetMainPosition().Apply(scene.GetCanvasToSceneTransform()))); + } + + CreateLineMeasureTracker::~CreateLineMeasureTracker() + { + + } + + void CreateLineMeasureTracker::PointerMove(const PointerEvent& event) + { + if (!active_) + { + throw OrthancException(ErrorCode_InternalError, + "Internal error: wrong state in CreateLineMeasureTracker::" + "PointerMove: active_ == false"); + } + + ScenePoint2D scenePos = event.GetMainPosition().Apply( + scene_.GetCanvasToSceneTransform()); + + //LOG(TRACE) << "scenePos.GetX() = " << scenePos.GetX() << " " << + // "scenePos.GetY() = " << scenePos.GetY(); + + CreateLineMeasureTracker* concreteThis = + dynamic_cast<CreateLineMeasureTracker*>(this); + assert(concreteThis != NULL); + GetCommand()->SetEnd(scenePos); + } + + void CreateLineMeasureTracker::PointerUp(const PointerEvent& e) + { + // TODO: the current app does not prevent multiple PointerDown AND + // PointerUp to be sent to the tracker. + // Unless we augment the PointerEvent structure with the button index, + // we cannot really tell if this pointer up event matches the initial + // pointer down event. Let's make it simple for now. + active_ = false; + } + + void CreateLineMeasureTracker::PointerDown(const PointerEvent& e) + { + LOG(WARNING) << "Additional touches (fingers, pen, mouse buttons...) " + "are ignored when the line measure creation tracker is active"; + } + + CreateLineMeasureCommandPtr CreateLineMeasureTracker::GetCommand() + { + return boost::dynamic_pointer_cast<CreateLineMeasureCommand>(command_); + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/CreateLineMeasureTracker.h Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,52 @@ +/** + * 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 "MeasureTrackers.h" + +namespace OrthancStone +{ + class CreateLineMeasureTracker : public CreateMeasureTracker + { + public: + /** + When you create this tracker, you need to supply it with the undo stack + where it will store the commands that perform the actual measure tool + creation and modification. + In turn, a container for these commands to store the actual measuring + must be supplied, too + */ + CreateLineMeasureTracker( + Scene2D& scene, + std::vector<TrackerCommandPtr>& undoStack, + std::vector<MeasureToolPtr>& measureTools, + const PointerEvent& e); + + ~CreateLineMeasureTracker(); + + virtual void PointerMove(const PointerEvent& e) ORTHANC_OVERRIDE; + virtual void PointerUp(const PointerEvent& e) ORTHANC_OVERRIDE; + virtual void PointerDown(const PointerEvent& e) ORTHANC_OVERRIDE; + + private: + CreateLineMeasureCommandPtr GetCommand(); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/CreateMeasureTracker.cpp Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,20 @@ +/** + * 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/>. + **/ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/CreateMeasureTracker.h Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,22 @@ +/** + * 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 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/CreateSimpleTrackerAdapter.cpp Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,77 @@ +/** + * 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/>. + **/ + +#include "IFlexiblePointerTracker.h" +#include <Framework/Scene2D/IPointerTracker.h> + + +namespace OrthancStone +{ + namespace + { + class SimpleTrackerAdapter : public IFlexiblePointerTracker + { + public: + SimpleTrackerAdapter(PointerTrackerPtr wrappedTracker) + : wrappedTracker_(wrappedTracker) + , active_(true) + { + } + + virtual void PointerMove(const PointerEvent& event) ORTHANC_OVERRIDE + { + if(active_) + wrappedTracker_->Update(event); + }; + virtual void PointerUp(const PointerEvent& event) ORTHANC_OVERRIDE + { + if (wrappedTracker_) + { + wrappedTracker_->Release(); + wrappedTracker_ = NULL; + } + active_ = false; + } + virtual void PointerDown(const PointerEvent& event) ORTHANC_OVERRIDE + { + // nothing to do atm + } + virtual bool IsActive() const ORTHANC_OVERRIDE + { + return active_; + } + + virtual void Cancel() ORTHANC_OVERRIDE + { + wrappedTracker_ = NULL; + active_ = false; + } + + private: + PointerTrackerPtr wrappedTracker_; + bool active_; + }; + } + + FlexiblePointerTrackerPtr CreateSimpleTrackerAdapter(PointerTrackerPtr t) + { + return FlexiblePointerTrackerPtr(new SimpleTrackerAdapter(t)); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/EditAngleMeasureTracker.cpp Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,23 @@ +/** + * 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/>. + **/ + +namespace OrthancStone +{ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/EditAngleMeasureTracker.h Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,25 @@ +/** + * 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 + +namespace OrthancStone +{ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/EditCircleMeasureTracker.cpp Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,23 @@ +/** + * 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/>. + **/ + +namespace OrthancStone +{ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/EditCircleMeasureTracker.h Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,25 @@ +/** + * 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 + +namespace OrthancStone +{ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/EditLineMeasureTracker.cpp Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,23 @@ +/** + * 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/>. + **/ + +namespace OrthancStone +{ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/EditLineMeasureTracker.h Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,25 @@ +/** + * 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 + +namespace OrthancStone +{ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/IFlexiblePointerTracker.h Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,84 @@ +/** + * 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 <Framework/Scene2D/PointerEvent.h> +#include <boost/shared_ptr.hpp> + +namespace OrthancStone +{ + class IPointerTracker; + typedef boost::shared_ptr<IPointerTracker> PointerTrackerPtr; + + /** + This interface represents a flexible mouse tracker that can respond to + several events and is not automatically deleted upon mouse up or when touch + interaction is suspended : for instance, a stateful tracker with a two-step + interaction like: click & drag --> mouse up --> drag --> mouse click + (for instance, for an angle measuring tracker or an ellipse tracker) + */ + class IFlexiblePointerTracker : public boost::noncopyable + { + public: + virtual ~IFlexiblePointerTracker() {} + + /** + This method will be repeatedly called during user interaction + */ + virtual void PointerMove(const PointerEvent& event) = 0; + + /** + This method will be called when a touch/pointer is removed (mouse up, + pen lift, finger removed...) + */ + virtual void PointerUp(const PointerEvent& event) = 0; + + /** + This method will be called when a touch/pointer is added (mouse down, + pen or finger press) + */ + virtual void PointerDown(const PointerEvent& event) = 0; + + /** + This method will be repeatedly called by the tracker owner (for instance, + the application) to check whether the tracker must keep on receiving + interaction or if its job is done and it should be deleted. + */ + virtual bool IsActive() const = 0; + + /** + This will be called if the tracker needs to be dismissed without committing + its changes to the underlying model. If the model has been modified during + tracker lifetime, it must be restored to its initial value + */ + virtual void Cancel() = 0; + }; + + typedef boost::shared_ptr<IFlexiblePointerTracker> FlexiblePointerTrackerPtr; + + /** + This factory adopts the supplied simple tracker and creates a flexible + tracker wrapper around it. + */ + FlexiblePointerTrackerPtr CreateSimpleTrackerAdapter(PointerTrackerPtr); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/LineMeasureTool.cpp Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,200 @@ +/** + * 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/>. + **/ + +#include "LineMeasureTool.h" +#include "MeasureToolsToolbox.h" + +#include <Core/Logging.h> + + +namespace OrthancStone +{ + LineMeasureTool::~LineMeasureTool() + { + // this measuring tool is a RABI for the corresponding visual layers + // stored in the 2D scene + Disable(); + RemoveFromScene(); + } + + void LineMeasureTool::RemoveFromScene() + { + if (layersCreated) + { + assert(GetScene().HasLayer(polylineZIndex_)); + assert(GetScene().HasLayer(textZIndex_)); + GetScene().DeleteLayer(polylineZIndex_); + GetScene().DeleteLayer(textZIndex_); + } + } + + + void LineMeasureTool::SetStart(ScenePoint2D start) + { + start_ = start; + RefreshScene(); + } + + void LineMeasureTool::SetEnd(ScenePoint2D end) + { + end_ = end; + RefreshScene(); + } + + void LineMeasureTool::Set(ScenePoint2D start, ScenePoint2D end) + { + start_ = start; + end_ = end; + RefreshScene(); + } + + PolylineSceneLayer* LineMeasureTool::GetPolylineLayer() + { + assert(GetScene().HasLayer(polylineZIndex_)); + ISceneLayer* layer = &(GetScene().GetLayer(polylineZIndex_)); + PolylineSceneLayer* concreteLayer = dynamic_cast<PolylineSceneLayer*>(layer); + assert(concreteLayer != NULL); + return concreteLayer; + } + + TextSceneLayer* LineMeasureTool::GetTextLayer() + { + assert(GetScene().HasLayer(textZIndex_)); + ISceneLayer* layer = &(GetScene().GetLayer(textZIndex_)); + TextSceneLayer* concreteLayer = dynamic_cast<TextSceneLayer*>(layer); + assert(concreteLayer != NULL); + return concreteLayer; + } + + void LineMeasureTool::RefreshScene() + { + if (IsEnabled()) + { + if (!layersCreated) + { + // Create the layers if need be + + assert(textZIndex_ == -1); + { + polylineZIndex_ = GetScene().GetMaxDepth() + 100; + //LOG(INFO) << "set polylineZIndex_ to: " << polylineZIndex_; + std::auto_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer()); + GetScene().SetLayer(polylineZIndex_, layer.release()); + } + { + textZIndex_ = GetScene().GetMaxDepth() + 100; + //LOG(INFO) << "set textZIndex_ to: " << textZIndex_; + std::auto_ptr<TextSceneLayer> layer(new TextSceneLayer()); + GetScene().SetLayer(textZIndex_, layer.release()); + } + layersCreated = true; + } + else + { + assert(GetScene().HasLayer(polylineZIndex_)); + assert(GetScene().HasLayer(textZIndex_)); + } + { + // Fill the polyline layer with the measurement line + + PolylineSceneLayer* polylineLayer = GetPolylineLayer(); + polylineLayer->ClearAllChains(); + polylineLayer->SetColor(0, 223, 21); + + { + PolylineSceneLayer::Chain chain; + chain.push_back(start_); + chain.push_back(end_); + polylineLayer->AddChain(chain, false); + } + + // handles + { + //void AddSquare(PolylineSceneLayer::Chain& chain,const Scene2D& scene,const ScenePoint2D& centerS,const double& sideLength) + + { + PolylineSceneLayer::Chain chain; + AddSquare(chain, GetScene(), start_, 10.0); //TODO: take DPI into account + polylineLayer->AddChain(chain, true); + } + + { + PolylineSceneLayer::Chain chain; + AddSquare(chain, GetScene(), end_, 10.0); //TODO: take DPI into account + polylineLayer->AddChain(chain, true); + } + + //ScenePoint2D startC = start_.Apply(GetScene().GetSceneToCanvasTransform()); + //double squareSize = 10.0; + //double startHandleLX = startC.GetX() - squareSize/2; + //double startHandleTY = startC.GetY() - squareSize / 2; + //double startHandleRX = startC.GetX() + squareSize / 2; + //double startHandleBY = startC.GetY() + squareSize / 2; + //ScenePoint2D startLTC(startHandleLX, startHandleTY); + //ScenePoint2D startRTC(startHandleRX, startHandleTY); + //ScenePoint2D startRBC(startHandleRX, startHandleBY); + //ScenePoint2D startLBC(startHandleLX, startHandleBY); + + //ScenePoint2D startLT = startLTC.Apply(GetScene().GetCanvasToSceneTransform()); + //ScenePoint2D startRT = startRTC.Apply(GetScene().GetCanvasToSceneTransform()); + //ScenePoint2D startRB = startRBC.Apply(GetScene().GetCanvasToSceneTransform()); + //ScenePoint2D startLB = startLBC.Apply(GetScene().GetCanvasToSceneTransform()); + + //PolylineSceneLayer::Chain chain; + //chain.push_back(startLT); + //chain.push_back(startRT); + //chain.push_back(startRB); + //chain.push_back(startLB); + //polylineLayer->AddChain(chain, true); + } + + } + { + // Set the text layer proporeties + + TextSceneLayer* textLayer = GetTextLayer(); + double deltaX = end_.GetX() - start_.GetX(); + double deltaY = end_.GetY() - start_.GetY(); + double squareDist = deltaX * deltaX + deltaY * deltaY; + double dist = sqrt(squareDist); + char buf[64]; + sprintf(buf, "%0.02f units", dist); + textLayer->SetText(buf); + textLayer->SetColor(0, 223, 21); + + // TODO: for now we simply position the text overlay at the middle + // of the measuring segment + double midX = 0.5*(end_.GetX() + start_.GetX()); + double midY = 0.5*(end_.GetY() + start_.GetY()); + textLayer->SetPosition(midX, midY); + } + } + else + { + if (layersCreated) + { + RemoveFromScene(); + layersCreated = false; + } + } + } + + +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/LineMeasureTool.h Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,72 @@ +/** + * 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 "MeasureTools.h" + +#include <Framework/Scene2D/Scene2D.h> +#include <Framework/Scene2D/ScenePoint2D.h> +#include <Framework/Scene2D/PolylineSceneLayer.h> +#include <Framework/Scene2D/TextSceneLayer.h> + +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> + +#include <vector> +#include <cmath> + +namespace OrthancStone +{ + class LineMeasureTool : public MeasureTool + { + public: + LineMeasureTool(Scene2D& scene) + : MeasureTool(scene) + , layersCreated(false) + , polylineZIndex_(-1) + , textZIndex_(-1) + { + + } + + ~LineMeasureTool(); + + void SetStart(ScenePoint2D start); + void SetEnd(ScenePoint2D end); + void Set(ScenePoint2D start, ScenePoint2D end); + + private: + PolylineSceneLayer* GetPolylineLayer(); + TextSceneLayer* GetTextLayer(); + virtual void RefreshScene() ORTHANC_OVERRIDE; + void RemoveFromScene(); + + private: + ScenePoint2D start_; + ScenePoint2D end_; + bool layersCreated; + int polylineZIndex_; + int textZIndex_; + }; + + typedef boost::shared_ptr<LineMeasureTool> LineMeasureToolPtr; +} +
--- a/Samples/Common/MeasureCommands.cpp Fri May 10 14:54:03 2019 +0200 +++ b/Samples/Common/MeasureCommands.cpp Mon May 13 15:12:56 2019 +0200 @@ -52,14 +52,36 @@ : CreateMeasureCommand(scene, measureTools) , measureTool_(new LineMeasureTool(scene)) { - measureTool_ = LineMeasureToolPtr(new LineMeasureTool(scene)); measureTools_.push_back(measureTool_); measureTool_->Set(point, point); } - void CreateLineMeasureCommand::Update(ScenePoint2D scenePos) + void CreateLineMeasureCommand::SetEnd(ScenePoint2D scenePos) { measureTool_->SetEnd(scenePos); } + CreateAngleMeasureCommand::CreateAngleMeasureCommand( + Scene2D& scene, MeasureToolList& measureTools, ScenePoint2D point) + : CreateMeasureCommand(scene, measureTools) + , measureTool_(new AngleMeasureTool(scene)) + { + measureTools_.push_back(measureTool_); + measureTool_->SetSide1End(point); + measureTool_->SetCenter(point); + measureTool_->SetSide2End(point); + } + + /** This method sets center*/ + void CreateAngleMeasureCommand::SetCenter(ScenePoint2D scenePos) + { + measureTool_->SetCenter(scenePos); + } + + /** This method sets end of side 2*/ + void CreateAngleMeasureCommand::SetSide2End(ScenePoint2D scenePos) + { + measureTool_->SetSide2End(scenePos); + } + }
--- a/Samples/Common/MeasureCommands.h Fri May 10 14:54:03 2019 +0200 +++ b/Samples/Common/MeasureCommands.h Mon May 13 15:12:56 2019 +0200 @@ -24,9 +24,16 @@ // to be moved into Stone #include "MeasureTools.h" +#include "LineMeasureTool.h" +#include "AngleMeasureTool.h" namespace OrthancStone { + //class LineMeasureTool; + //typedef boost::shared_ptr<LineMeasureTool> LineMeasureToolPtr; + //class AngleMeasureTool; + //typedef boost::shared_ptr<AngleMeasureTool> AngleMeasureToolPtr; + class TrackerCommand { public: @@ -36,8 +43,6 @@ } virtual void Undo() = 0; virtual void Redo() = 0; - virtual void Update(ScenePoint2D scenePos) = 0; - Scene2D& GetScene() { return scene_; @@ -71,11 +76,11 @@ class CreateLineMeasureCommand : public CreateMeasureCommand { public: - CreateLineMeasureCommand(Scene2D& scene, - MeasureToolList& measureTools, - ScenePoint2D point); + CreateLineMeasureCommand( + Scene2D& scene, MeasureToolList& measureTools, ScenePoint2D point); - void Update(ScenePoint2D scenePos) ORTHANC_OVERRIDE; + // the starting position is set in the ctor + void SetEnd(ScenePoint2D scenePos); private: virtual MeasureToolPtr GetMeasureTool() ORTHANC_OVERRIDE @@ -83,10 +88,31 @@ return measureTool_; } LineMeasureToolPtr measureTool_; - ScenePoint2D start_; - ScenePoint2D end_; }; typedef boost::shared_ptr<CreateLineMeasureCommand> CreateLineMeasureCommandPtr; + + class CreateAngleMeasureCommand : public CreateMeasureCommand + { + public: + /** Ctor sets end of side 1*/ + CreateAngleMeasureCommand( + Scene2D& scene, MeasureToolList& measureTools, ScenePoint2D point); + + /** This method sets center*/ + void SetCenter(ScenePoint2D scenePos); + + /** This method sets end of side 2*/ + void SetSide2End(ScenePoint2D scenePos); + + private: + virtual MeasureToolPtr GetMeasureTool() ORTHANC_OVERRIDE + { + return measureTool_; + } + AngleMeasureToolPtr measureTool_; + }; + + typedef boost::shared_ptr<CreateAngleMeasureCommand> CreateAngleMeasureCommandPtr; }
--- a/Samples/Common/MeasureTools.cpp Fri May 10 14:54:03 2019 +0200 +++ b/Samples/Common/MeasureTools.cpp Mon May 13 15:12:56 2019 +0200 @@ -18,8 +18,11 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. **/ +#include "MeasureTools.h" + #include <Core/Logging.h> -#include "MeasureTools.h" + +#include <boost/math/constants/constants.hpp> namespace OrthancStone { @@ -34,163 +37,5 @@ enabled_ = false; RefreshScene(); } - - LineMeasureTool::~LineMeasureTool() - { - // this measuring tool is a RABI for the corresponding visual layers - // stored in the 2D scene - Disable(); - RemoveFromScene(); - } - - void LineMeasureTool::RemoveFromScene() - { - if (layersCreated) - { - assert(GetScene().HasLayer(polylineZIndex_)); - assert(GetScene().HasLayer(textZIndex_)); - GetScene().DeleteLayer(polylineZIndex_); - GetScene().DeleteLayer(textZIndex_); - } - } - - - void LineMeasureTool::SetStart(ScenePoint2D start) - { - start_ = start; - RefreshScene(); - } - - void LineMeasureTool::SetEnd(ScenePoint2D end) - { - end_ = end; - RefreshScene(); - } - - void LineMeasureTool::Set(ScenePoint2D start, ScenePoint2D end) - { - start_ = start; - end_ = end; - RefreshScene(); - } - - PolylineSceneLayer* LineMeasureTool::GetPolylineLayer() - { - assert(GetScene().HasLayer(polylineZIndex_)); - ISceneLayer* layer = &(GetScene().GetLayer(polylineZIndex_)); - PolylineSceneLayer* concreteLayer = dynamic_cast<PolylineSceneLayer*>(layer); - assert(concreteLayer != NULL); - return concreteLayer; - } - - TextSceneLayer* LineMeasureTool::GetTextLayer() - { - assert(GetScene().HasLayer(textZIndex_)); - ISceneLayer* layer = &(GetScene().GetLayer(textZIndex_)); - TextSceneLayer* concreteLayer = dynamic_cast<TextSceneLayer*>(layer); - assert(concreteLayer != NULL); - return concreteLayer; - } - - void LineMeasureTool::RefreshScene() - { - if (IsEnabled()) - { - if (!layersCreated) - { - // Create the layers if need be - - assert(textZIndex_ == -1); - { - polylineZIndex_ = GetScene().GetMaxDepth() + 100; - LOG(INFO) << "set polylineZIndex_ to: " << polylineZIndex_; - std::auto_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer()); - GetScene().SetLayer(polylineZIndex_, layer.release()); - } - { - textZIndex_ = GetScene().GetMaxDepth() + 100; - LOG(INFO) << "set textZIndex_ to: " << textZIndex_; - std::auto_ptr<TextSceneLayer> layer(new TextSceneLayer()); - GetScene().SetLayer(textZIndex_, layer.release()); - } - layersCreated = true; - } - else - { - assert(GetScene().HasLayer(polylineZIndex_)); - assert(GetScene().HasLayer(textZIndex_)); - } - { - // Fill the polyline layer with the measurement line - - PolylineSceneLayer* polylineLayer = GetPolylineLayer(); - polylineLayer->ClearAllChains(); - polylineLayer->SetColor(0, 223, 21); - - { - PolylineSceneLayer::Chain chain; - chain.push_back(start_); - chain.push_back(end_); - polylineLayer->AddChain(chain, false); - } - - // handles - { - ScenePoint2D startC = start_.Apply(GetScene().GetSceneToCanvasTransform()); - double squareSize = 10.0; //TODO: take DPI into account - double startHandleLX = startC.GetX() - squareSize/2; - double startHandleTY = startC.GetY() - squareSize / 2; - double startHandleRX = startC.GetX() + squareSize / 2; - double startHandleBY = startC.GetY() + squareSize / 2; - ScenePoint2D startLTC(startHandleLX, startHandleTY); - ScenePoint2D startRTC(startHandleRX, startHandleTY); - ScenePoint2D startRBC(startHandleRX, startHandleBY); - ScenePoint2D startLBC(startHandleLX, startHandleBY); - - ScenePoint2D startLT = startLTC.Apply(GetScene().GetCanvasToSceneTransform()); - ScenePoint2D startRT = startRTC.Apply(GetScene().GetCanvasToSceneTransform()); - ScenePoint2D startRB = startRBC.Apply(GetScene().GetCanvasToSceneTransform()); - ScenePoint2D startLB = startLBC.Apply(GetScene().GetCanvasToSceneTransform()); - - PolylineSceneLayer::Chain chain; - chain.push_back(startLT); - chain.push_back(startRT); - chain.push_back(startRB); - chain.push_back(startLB); - polylineLayer->AddChain(chain, true); - } - - - - } - { - // Set the text layer proporeties - - TextSceneLayer* textLayer = GetTextLayer(); - double deltaX = end_.GetX() - start_.GetX(); - double deltaY = end_.GetY() - start_.GetY(); - double squareDist = deltaX * deltaX + deltaY * deltaY; - double dist = sqrt(squareDist); - char buf[64]; - sprintf(buf, "%0.02f units", dist); - textLayer->SetText(buf); - textLayer->SetColor(0, 223, 21); - - // TODO: for now we simply position the text overlay at the middle - // of the measuring segment - double midX = 0.5*(end_.GetX() + start_.GetX()); - double midY = 0.5*(end_.GetY() + start_.GetY()); - textLayer->SetPosition(midX, midY); - } - } - else - { - if (layersCreated) - { - RemoveFromScene(); - layersCreated = false; - } - } - } }
--- a/Samples/Common/MeasureTools.h Fri May 10 14:54:03 2019 +0200 +++ b/Samples/Common/MeasureTools.h Mon May 13 15:12:56 2019 +0200 @@ -17,6 +17,7 @@ * 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 <Framework/Scene2D/Scene2D.h> @@ -25,6 +26,7 @@ #include <Framework/Scene2D/TextSceneLayer.h> #include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> #include <vector> #include <cmath> @@ -50,7 +52,7 @@ void Disable(); protected: - MeasureTool(Scene2D& scene) + MeasureTool(Scene2D& scene) : scene_(scene) , enabled_(true) { @@ -65,7 +67,6 @@ */ virtual void RefreshScene() = 0; - Scene2D& GetScene() { return scene_; @@ -86,40 +87,6 @@ }; typedef boost::shared_ptr<MeasureTool> MeasureToolPtr; - - class LineMeasureTool : public MeasureTool - { - public: - LineMeasureTool(Scene2D& scene) - : MeasureTool(scene) - , layersCreated(false) - , polylineZIndex_(-1) - , textZIndex_(-1) - { - - } - - ~LineMeasureTool(); - - void SetStart(ScenePoint2D start); - void SetEnd(ScenePoint2D end); - void Set(ScenePoint2D start, ScenePoint2D end); - - private: - PolylineSceneLayer* GetPolylineLayer(); - TextSceneLayer* GetTextLayer(); - virtual void RefreshScene() ORTHANC_OVERRIDE; - void RemoveFromScene(); - - private: - ScenePoint2D start_; - ScenePoint2D end_; - bool layersCreated; - int polylineZIndex_; - int textZIndex_; - }; - - typedef boost::shared_ptr<LineMeasureTool> LineMeasureToolPtr; typedef std::vector<MeasureToolPtr> MeasureToolList; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/MeasureToolsToolbox.cpp Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,229 @@ +/** + * 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/>. + **/ + +#include "MeasureToolsToolbox.h" + +#include <boost/math/constants/constants.hpp> + +namespace +{ + double g_pi = boost::math::constants::pi<double>(); +} + +namespace OrthancStone +{ + + void AddSquare(PolylineSceneLayer::Chain& chain, + const Scene2D& scene, + const ScenePoint2D& centerS, + const double& sideLength) + { + chain.clear(); + chain.reserve(4); + ScenePoint2D centerC = centerS.Apply(scene.GetSceneToCanvasTransform()); + //TODO: take DPI into account + double handleLX = centerC.GetX() - sideLength / 2; + double handleTY = centerC.GetY() - sideLength / 2; + double handleRX = centerC.GetX() + sideLength / 2; + double handleBY = centerC.GetY() + sideLength / 2; + ScenePoint2D LTC(handleLX, handleTY); + ScenePoint2D RTC(handleRX, handleTY); + ScenePoint2D RBC(handleRX, handleBY); + ScenePoint2D LBC(handleLX, handleBY); + + ScenePoint2D startLT = LTC.Apply(scene.GetCanvasToSceneTransform()); + ScenePoint2D startRT = RTC.Apply(scene.GetCanvasToSceneTransform()); + ScenePoint2D startRB = RBC.Apply(scene.GetCanvasToSceneTransform()); + ScenePoint2D startLB = LBC.Apply(scene.GetCanvasToSceneTransform()); + + chain.push_back(startLT); + chain.push_back(startRT); + chain.push_back(startRB); + chain.push_back(startLB); + } + + void AddArc( + PolylineSceneLayer::Chain& chain + , const Scene2D& scene + , const ScenePoint2D& p1 + , const ScenePoint2D& c + , const ScenePoint2D& p2 + , const double& radiusS + , const bool clockwise + , const int subdivisionsCount) + { + double p1cAngle = atan2(p1.GetY() - c.GetY(), p1.GetX() - c.GetX()); + double p2cAngle = atan2(p2.GetY() - c.GetY(), p2.GetX() - c.GetX()); + AddArc( + chain, scene, c, radiusS, p1cAngle, p2cAngle, + clockwise, subdivisionsCount); + } + + double RadiansToDegrees(double angleRad) + { + static const double factor = 180.0 / g_pi; + return angleRad * factor; + } + + void GetPositionOnBisectingLine( + ScenePoint2D& result + , const ScenePoint2D& p1 + , const ScenePoint2D& c + , const ScenePoint2D& p2 + , const double d) + { + // TODO: fix correct half-plane + double p1cAngle = atan2(p1.GetY() - c.GetY(), p1.GetX() - c.GetX()); + double p2cAngle = atan2(p2.GetY() - c.GetY(), p2.GetX() - c.GetX()); + double angle = 0.5*(p1cAngle + p2cAngle); + double unitVectorX = cos(angle); + double unitVectorY = sin(angle); + double posX = c.GetX() + d * unitVectorX; + double posY = c.GetX() + d * unitVectorY; + result = ScenePoint2D(posX, posY); + } + + void AddArc( + PolylineSceneLayer::Chain& chain + , const Scene2D& scene + , const ScenePoint2D& centerS + , const double& radiusS + , const double startAngleRad + , const double endAngleRad + , const bool clockwise + , const int subdivisionsCount) + { + double startAngleRadN = NormalizeAngle(startAngleRad); + double endAngleRadN = NormalizeAngle(endAngleRad); + + double angle1Rad = std::min(startAngleRadN, endAngleRadN); + double angle2Rad = std::max(startAngleRadN, endAngleRadN); + + // now we are sure angle1Rad < angle2Rad + // this means that if we draw from 1 to 2, it will be clockwise ( + // increasing angles). + // let's fix this: + if (!clockwise) + { + angle2Rad -= 2 * g_pi; + // now we are sure angle2Rad < angle1Rad (since they were normalized) + // and, thus, going from 1 to 2 means the angle values will DECREASE, + // which is the definition of anticlockwise + } + + chain.clear(); + chain.reserve(subdivisionsCount + 1); + + double angleIncr = (angle2Rad - angle1Rad) + / static_cast<double>(subdivisionsCount); + + double theta = angle1Rad; + for (int i = 0; i < subdivisionsCount + 1; ++i) + { + double offsetX = radiusS * cos(theta); + double offsetY = radiusS * sin(theta); + double pointX = centerS.GetX() + offsetX; + double pointY = centerS.GetY() + offsetY; + chain.push_back(ScenePoint2D(pointX, pointY)); + theta += angleIncr; + } + } + + void AddCircle(PolylineSceneLayer::Chain& chain, + const Scene2D& scene, + const ScenePoint2D& centerS, + const double& radiusS, + const int numSubdivisions) + { + //ScenePoint2D centerC = centerS.Apply(scene.GetSceneToCanvasTransform()); + //TODO: take DPI into account + + // TODO: automatically compute the number for segments for smooth + // display based on the radius in pixels. + + chain.clear(); + chain.reserve(numSubdivisions); + + double angleIncr = (2.0 * g_pi) + / static_cast<double>(numSubdivisions); + + double theta = 0; + for (int i = 0; i < numSubdivisions; ++i) + { + double offsetX = radiusS * cos(theta); + double offsetY = radiusS * sin(theta); + double pointX = centerS.GetX() + offsetX; + double pointY = centerS.GetY() + offsetY; + chain.push_back(ScenePoint2D(pointX, pointY)); + theta += angleIncr; + } + } + + double NormalizeAngle(double angle) + { + double retAngle = angle; + while (retAngle < 0) + retAngle += 2 * g_pi; + while (retAngle >= 2 * g_pi) + retAngle -= 2 * g_pi; + return retAngle; + } + + double MeasureAngle(const ScenePoint2D& p1, const ScenePoint2D& c, const ScenePoint2D& p2) + { + double p1cAngle = atan2(p1.GetY() - c.GetY(), p1.GetX() - c.GetX()); + double p2cAngle = atan2(p2.GetY() - c.GetY(), p2.GetX() - c.GetX()); + double delta = p2cAngle - p1cAngle; + return NormalizeAngle(delta); + } + + +#if 0 + void AddEllipse(PolylineSceneLayer::Chain& chain, + const Scene2D& scene, + const ScenePoint2D& centerS, + const double& halfHAxis, + const double& halfVAxis) + { + chain.clear(); + chain.reserve(4); + ScenePoint2D centerC = centerS.Apply(scene.GetSceneToCanvasTransform()); + //TODO: take DPI into account + double handleLX = centerC.GetX() - sideLength / 2; + double handleTY = centerC.GetY() - sideLength / 2; + double handleRX = centerC.GetX() + sideLength / 2; + double handleBY = centerC.GetY() + sideLength / 2; + ScenePoint2D LTC(handleLX, handleTY); + ScenePoint2D RTC(handleRX, handleTY); + ScenePoint2D RBC(handleRX, handleBY); + ScenePoint2D LBC(handleLX, handleBY); + + ScenePoint2D startLT = LTC.Apply(scene.GetCanvasToSceneTransform()); + ScenePoint2D startRT = RTC.Apply(scene.GetCanvasToSceneTransform()); + ScenePoint2D startRB = RBC.Apply(scene.GetCanvasToSceneTransform()); + ScenePoint2D startLB = LBC.Apply(scene.GetCanvasToSceneTransform()); + + chain.push_back(startLT); + chain.push_back(startRT); + chain.push_back(startRB); + chain.push_back(startLB); +} +#endif +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Common/MeasureToolsToolbox.h Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,135 @@ +/** + * 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/>. + **/ + +#include <Framework/Scene2D/PolylineSceneLayer.h> +#include <Framework/Scene2D/Scene2D.h> + +namespace OrthancStone +{ + + /** + This function will create a square around the center point supplied in + scene coordinates, with a side length given in canvas coordinates. The + square sides are parallel to the canvas boundaries. + */ + void AddSquare(PolylineSceneLayer::Chain& chain, + const Scene2D& scene, + const ScenePoint2D& centerS, + const double& sideLength); + + /** + Creates an arc centered pm c that goes + - from a point r1: + - so that r1 belongs to the p1,c line + - so that the distance from c to r1 equals radius + - to a point r2: + - so that r2 belongs to the p2,c line + - so that the distance from c to r2 equals radius + + if clockwise is true, the arc is drawn from r1 to r2 with increasing + angle values. Otherwise, the angle values decrease. + + Warning: the existing chain content will be wiped out. + */ + + void AddArc( + PolylineSceneLayer::Chain& chain + , const Scene2D& scene + , const ScenePoint2D& p1 + , const ScenePoint2D& c + , const ScenePoint2D& p2 + , const double& radiusS + , const bool clockwise + , const int subdivisionsCount = 63); + + /** + Creates an arc (open curve) with "numSubdivisions" + (N + 1 points) from start angle to end angle. + + if clockwise is true, the arc is drawn from start to end + by increasing the angle values. + + otherwise, the angle value decreases from start to end. + + Warning: the existing chain content will be wiped out. + */ + void AddArc( + PolylineSceneLayer::Chain& chain + , const Scene2D& scene + , const ScenePoint2D& centerS + , const double& radiusS + , const double startAngleRad + , const double endAngleRad + , const bool clockwise + , const int subdivisionsCount = 63); + + /** + Creates a circle (closed curve) with "numSubdivisions" + (N points) + + Warning: the existing chain content will be wiped out. + */ + void AddCircle(PolylineSceneLayer::Chain& chain, + const Scene2D& scene, + const ScenePoint2D& centerS, + const double& radiusS, + const int numSubdivisions = 63); + + /** + Adds or subtracts 2*pi as many times as need to shift the specified + angle to a value such as: 0 <= value < 2*pi + */ + double NormalizeAngle(double angle); + + /** + Returns the angle magnitude between the p1,c and p2,c lines. + The returned angle is between 0 and 2*pi + + If the angle is between 0 and pi, this means that the shortest arc + from p1 to p2 is clockwise. + + If the angle is between pi and 2*pi, this means that the shortest arc + from p1 to p2 is COUNTERclockwise. + + */ + double MeasureAngle( + const ScenePoint2D& p1 + , const ScenePoint2D& c + , const ScenePoint2D& p2); + + /** + RadiansToDegrees + */ + double RadiansToDegrees(double angleRad); + + /** + This function will return the coordinates of a point that: + - belongs to the two bisecting lines of the p1 c p2 angle. + - is a distance d from c. + Among the four possible points, the one returned will be the one belonging + to the *smallest* half-plane defined by the [c,p1[ and [c,p2[ half-lines. + */ + void GetPositionOnBisectingLine( + ScenePoint2D& result + , const ScenePoint2D& p1 + , const ScenePoint2D& c + , const ScenePoint2D& p2 + , const double d); +}
--- a/Samples/Common/MeasureTrackers.cpp Fri May 10 14:54:03 2019 +0200 +++ b/Samples/Common/MeasureTrackers.cpp Mon May 13 15:12:56 2019 +0200 @@ -31,12 +31,24 @@ std::vector<TrackerCommandPtr>& undoStack, std::vector<MeasureToolPtr>& measureTools) : scene_(scene) + , active_(true) , undoStack_(undoStack) , measureTools_(measureTools) , commitResult_(true) { } + void CreateMeasureTracker::Cancel() + { + commitResult_ = false; + active_ = false; + } + + bool CreateMeasureTracker::IsActive() const + { + return active_; + } + CreateMeasureTracker::~CreateMeasureTracker() { // if the tracker completes successfully, we add the command @@ -48,45 +60,6 @@ else command_->Undo(); } - - CreateLineMeasureTracker::CreateLineMeasureTracker( - Scene2D& scene, - std::vector<TrackerCommandPtr>& undoStack, - std::vector<MeasureToolPtr>& measureTools, - const PointerEvent& e) - : CreateMeasureTracker(scene, undoStack, measureTools) - { - command_.reset( - new CreateLineMeasureCommand( - scene, - measureTools, - e.GetMainPosition().Apply(scene.GetCanvasToSceneTransform()))); - } - - CreateLineMeasureTracker::~CreateLineMeasureTracker() - { - - } - - void CreateMeasureTracker::Update(const PointerEvent& event) - { - ScenePoint2D scenePos = event.GetMainPosition().Apply( - scene_.GetCanvasToSceneTransform()); - - LOG(TRACE) << "scenePos.GetX() = " << scenePos.GetX() << " " << - "scenePos.GetY() = " << scenePos.GetY(); - - CreateLineMeasureTracker* concreteThis = - dynamic_cast<CreateLineMeasureTracker*>(this); - assert(concreteThis != NULL); - command_->Update(scenePos); - } - - void CreateMeasureTracker::Release() - { - commitResult_ = false; - } - }
--- a/Samples/Common/MeasureTrackers.h Fri May 10 14:54:03 2019 +0200 +++ b/Samples/Common/MeasureTrackers.h Mon May 13 15:12:56 2019 +0200 @@ -20,8 +20,9 @@ #pragma once -#include "../../Framework/Scene2D/IPointerTracker.h" +#include "IFlexiblePointerTracker.h" #include "../../Framework/Scene2D/Scene2D.h" +#include "../../Framework/Scene2D/PointerEvent.h" #include "MeasureTools.h" #include "MeasureCommands.h" @@ -30,11 +31,11 @@ namespace OrthancStone { - class CreateMeasureTracker : public IPointerTracker + class CreateMeasureTracker : public IFlexiblePointerTracker { public: - virtual void Update(const PointerEvent& e) ORTHANC_OVERRIDE; - virtual void Release() ORTHANC_OVERRIDE; + virtual void Cancel() ORTHANC_OVERRIDE; + virtual bool IsActive() const ORTHANC_OVERRIDE; protected: CreateMeasureTracker( Scene2D& scene, @@ -43,32 +44,14 @@ ~CreateMeasureTracker(); + protected: + CreateMeasureCommandPtr command_; + Scene2D& scene_; + bool active_; private: - Scene2D& scene_; std::vector<TrackerCommandPtr>& undoStack_; std::vector<MeasureToolPtr>& measureTools_; bool commitResult_; - - protected: - CreateMeasureCommandPtr command_; - }; - - class CreateLineMeasureTracker : public CreateMeasureTracker - { - public: - /** - When you create this tracker, you need to supply it with the undo stack - where it will store the commands that perform the actual measure tool - creation and modification. - In turn, a container for these commands to store the actual measuring - must be supplied, too - */ - CreateLineMeasureTracker( - Scene2D& scene, - std::vector<TrackerCommandPtr>& undoStack, - std::vector<MeasureToolPtr>& measureTools, - const PointerEvent& e); - - ~CreateLineMeasureTracker(); }; } +
--- a/Samples/Sdl/CMakeLists.txt Fri May 10 14:54:03 2019 +0200 +++ b/Samples/Sdl/CMakeLists.txt Mon May 13 15:12:56 2019 +0200 @@ -69,13 +69,51 @@ LIST(APPEND TRACKERSAMPLE_SOURCE "../../../SDL-Console/SDL_Console.h") endif() +LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/AngleMeasureTool.cpp") +LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/AngleMeasureTool.h") + +LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/CreateAngleMeasureTracker.cpp") +LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/CreateAngleMeasureTracker.h") +LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/CreateCircleMeasureTracker.cpp") +LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/CreateCircleMeasureTracker.h") +LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/CreateLineMeasureTracker.cpp") +LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/CreateLineMeasureTracker.h") + +LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/CreateMeasureTracker.cpp") +LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/CreateMeasureTracker.h") + +LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/CreateSimpleTrackerAdapter.cpp") + +LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/EditAngleMeasureTracker.cpp") +LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/EditAngleMeasureTracker.h") +LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/EditCircleMeasureTracker.cpp") +LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/EditCircleMeasureTracker.h") +LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/EditLineMeasureTracker.cpp") +LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/EditLineMeasureTracker.h") + +LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/IFlexiblePointerTracker.h") + +LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/LineMeasureTool.cpp") +LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/LineMeasureTool.h") + LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/MeasureCommands.cpp") LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/MeasureCommands.h") LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/MeasureTools.cpp") LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/MeasureTools.h") + +LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/MeasureToolsToolbox.cpp") +LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/MeasureToolsToolbox.h") + LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/MeasureTrackers.cpp") LIST(APPEND TRACKERSAMPLE_SOURCE "../Common/MeasureTrackers.h") + LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSample.cpp") +LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSampleApp.cpp") +LIST(APPEND TRACKERSAMPLE_SOURCE "TrackerSampleApp.h") + +if (MSVC AND MSVC_VERSION GREATER 1700) + LIST(APPEND TRACKERSAMPLE_SOURCE "cpp.hint") +endif() add_executable(TrackerSample ${TRACKERSAMPLE_SOURCE}
--- a/Samples/Sdl/Loader.cpp Fri May 10 14:54:03 2019 +0200 +++ b/Samples/Sdl/Loader.cpp Mon May 13 15:12:56 2019 +0200 @@ -28,23 +28,17 @@ #include "../../Framework/Volumes/ImageBuffer3D.h" // From Orthanc framework -#include <Core/Compression/GzipCompressor.h> -#include <Core/Compression/ZlibCompressor.h> #include <Core/DicomFormat/DicomArray.h> #include <Core/DicomFormat/DicomImageInformation.h> #include <Core/HttpClient.h> #include <Core/IDynamicObject.h> #include <Core/Images/Image.h> #include <Core/Images/ImageProcessing.h> -#include <Core/Images/JpegReader.h> -#include <Core/Images/PamReader.h> -#include <Core/Images/PngReader.h> #include <Core/Images/PngWriter.h> #include <Core/Logging.h> #include <Core/MultiThreading/SharedMessageQueue.h> #include <Core/OrthancException.h> #include <Core/Toolbox.h> -#include <Core/SystemToolbox.h> #include <json/reader.h> #include <json/value.h> @@ -62,9 +56,7 @@ public: enum Type { - Type_OrthancRestApi, - Type_GetOrthancImage, - Type_GetOrthancWebViewerJpeg + Type_OrthancApi }; virtual ~IOracleCommand() @@ -139,54 +131,20 @@ - class OracleCommandExceptionMessage : - public OrthancStone::BaseMessage<OrthancStone::MessageType_OracleCommandExceptionMessage> - { - private: - const IOracleCommand& command_; - Orthanc::OrthancException exception_; - - public: - OracleCommandExceptionMessage(const IOracleCommand& command, - const Orthanc::OrthancException& exception) : - command_(command), - exception_(exception) - { - } - - OracleCommandExceptionMessage(const IOracleCommand& command, - const Orthanc::ErrorCode& error) : - command_(command), - exception_(error) - { - } - - const IOracleCommand& GetCommand() const - { - return command_; - } - - const Orthanc::OrthancException& GetException() const - { - return exception_; - } - }; - - typedef std::map<std::string, std::string> HttpHeaders; - class OrthancRestApiCommand : public OracleCommandWithPayload + class OrthancApiOracleCommand : public OracleCommandWithPayload { public: - class SuccessMessage : public OrthancStone::OriginMessage<OrthancStone::MessageType_OrthancRestApiCommand, - OrthancRestApiCommand> + class SuccessMessage : public OrthancStone::OriginMessage<OrthancStone::MessageType_HttpRequestSuccess, // TODO + OrthancApiOracleCommand> { private: HttpHeaders headers_; std::string answer_; public: - SuccessMessage(const OrthancRestApiCommand& command, + SuccessMessage(const OrthancApiOracleCommand& command, const HttpHeaders& answerHeaders, std::string& answer /* will be swapped to avoid a memcpy() */) : OriginMessage(command), @@ -216,6 +174,27 @@ }; + class FailureMessage : public OrthancStone::OriginMessage<OrthancStone::MessageType_HttpRequestError, // TODO + OrthancApiOracleCommand> + { + private: + Orthanc::HttpStatus status_; + + public: + FailureMessage(const OrthancApiOracleCommand& command, + Orthanc::HttpStatus status) : + OriginMessage(command), + status_(status) + { + } + + Orthanc::HttpStatus GetHttpStatus() const + { + return status_; + } + }; + + private: Orthanc::HttpMethod method_; std::string uri_; @@ -224,10 +203,10 @@ unsigned int timeout_; std::auto_ptr< OrthancStone::MessageHandler<SuccessMessage> > successCallback_; - std::auto_ptr< OrthancStone::MessageHandler<OracleCommandExceptionMessage> > failureCallback_; + std::auto_ptr< OrthancStone::MessageHandler<FailureMessage> > failureCallback_; public: - OrthancRestApiCommand() : + OrthancApiOracleCommand() : method_(Orthanc::HttpMethod_Get), uri_("/"), timeout_(10) @@ -236,7 +215,7 @@ virtual Type GetType() const { - return Type_OrthancRestApi; + return Type_OrthancApi; } void SetMethod(Orthanc::HttpMethod method) @@ -260,11 +239,6 @@ body_ = writer.write(json); } - void SetHttpHeaders(const HttpHeaders& headers) - { - headers_ = headers; - } - void SetHttpHeader(const std::string& key, const std::string& value) { @@ -312,423 +286,6 @@ - - class GetOrthancImageCommand : public OracleCommandWithPayload - { - public: - class SuccessMessage : public OrthancStone::OriginMessage<OrthancStone::MessageType_GetOrthancImageCommand, - GetOrthancImageCommand> - { - private: - std::auto_ptr<Orthanc::ImageAccessor> image_; - Orthanc::MimeType mime_; - - public: - SuccessMessage(const GetOrthancImageCommand& command, - Orthanc::ImageAccessor* image, // Takes ownership - Orthanc::MimeType mime) : - OriginMessage(command), - image_(image), - mime_(mime) - { - if (image == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - } - - const Orthanc::ImageAccessor& GetImage() const - { - return *image_; - } - - Orthanc::MimeType GetMimeType() const - { - return mime_; - } - }; - - - private: - std::string uri_; - HttpHeaders headers_; - unsigned int timeout_; - - std::auto_ptr< OrthancStone::MessageHandler<SuccessMessage> > successCallback_; - std::auto_ptr< OrthancStone::MessageHandler<OracleCommandExceptionMessage> > failureCallback_; - - public: - GetOrthancImageCommand() : - uri_("/"), - timeout_(10) - { - } - - virtual Type GetType() const - { - return Type_GetOrthancImage; - } - - void SetUri(const std::string& uri) - { - uri_ = uri; - } - - void SetHttpHeader(const std::string& key, - const std::string& value) - { - headers_[key] = value; - } - - const std::string& GetUri() const - { - return uri_; - } - - const HttpHeaders& GetHttpHeaders() const - { - return headers_; - } - - void SetTimeout(unsigned int seconds) - { - timeout_ = seconds; - } - - unsigned int GetTimeout() const - { - return timeout_; - } - - void ProcessHttpAnswer(IMessageEmitter& emitter, - const OrthancStone::IObserver& receiver, - const std::string& answer, - const HttpHeaders& answerHeaders) const - { - Orthanc::MimeType contentType = Orthanc::MimeType_Binary; - - for (HttpHeaders::const_iterator it = answerHeaders.begin(); - it != answerHeaders.end(); ++it) - { - std::string s; - Orthanc::Toolbox::ToLowerCase(s, it->first); - - if (s == "content-type") - { - contentType = Orthanc::StringToMimeType(it->second); - break; - } - } - - std::auto_ptr<Orthanc::ImageAccessor> image; - - switch (contentType) - { - case Orthanc::MimeType_Png: - { - image.reset(new Orthanc::PngReader); - dynamic_cast<Orthanc::PngReader&>(*image).ReadFromMemory(answer); - break; - } - - case Orthanc::MimeType_Pam: - { - image.reset(new Orthanc::PamReader); - dynamic_cast<Orthanc::PamReader&>(*image).ReadFromMemory(answer); - break; - } - - case Orthanc::MimeType_Jpeg: - { - image.reset(new Orthanc::JpegReader); - dynamic_cast<Orthanc::JpegReader&>(*image).ReadFromMemory(answer); - break; - } - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol, - "Unsupported HTTP Content-Type for an image: " + - std::string(Orthanc::EnumerationToString(contentType))); - } - - SuccessMessage message(*this, image.release(), contentType); - emitter.EmitMessage(receiver, message); - } - }; - - - - class GetOrthancWebViewerJpegCommand : public OracleCommandWithPayload - { - public: - class SuccessMessage : public OrthancStone::OriginMessage<OrthancStone::MessageType_GetOrthancWebViewerJpegCommand, - GetOrthancWebViewerJpegCommand> - { - private: - std::auto_ptr<Orthanc::ImageAccessor> image_; - - public: - SuccessMessage(const GetOrthancWebViewerJpegCommand& command, - Orthanc::ImageAccessor* image) : // Takes ownership - OriginMessage(command), - image_(image) - { - if (image == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - } - - const Orthanc::ImageAccessor& GetImage() const - { - return *image_; - } - }; - - private: - std::string instanceId_; - unsigned int frame_; - unsigned int quality_; - HttpHeaders headers_; - unsigned int timeout_; - Orthanc::PixelFormat expectedFormat_; - - std::auto_ptr< OrthancStone::MessageHandler<SuccessMessage> > successCallback_; - std::auto_ptr< OrthancStone::MessageHandler<OracleCommandExceptionMessage> > failureCallback_; - - public: - GetOrthancWebViewerJpegCommand() : - frame_(0), - quality_(95), - timeout_(10), - expectedFormat_(Orthanc::PixelFormat_Grayscale8) - { - } - - virtual Type GetType() const - { - return Type_GetOrthancWebViewerJpeg; - } - - void SetExpectedFormat(Orthanc::PixelFormat format) - { - expectedFormat_ = format; - } - - void SetInstance(const std::string& instanceId) - { - instanceId_ = instanceId; - } - - void SetFrame(unsigned int frame) - { - frame_ = frame; - } - - void SetQuality(unsigned int quality) - { - if (quality <= 0 || - quality > 100) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - else - { - quality_ = quality; - } - } - - void SetHttpHeader(const std::string& key, - const std::string& value) - { - headers_[key] = value; - } - - Orthanc::PixelFormat GetExpectedFormat() const - { - return expectedFormat_; - } - - const std::string& GetInstanceId() const - { - return instanceId_; - } - - unsigned int GetFrame() const - { - return frame_; - } - - unsigned int GetQuality() const - { - return quality_; - } - - const HttpHeaders& GetHttpHeaders() const - { - return headers_; - } - - void SetTimeout(unsigned int seconds) - { - timeout_ = seconds; - } - - unsigned int GetTimeout() const - { - return timeout_; - } - - std::string GetUri() const - { - return ("/web-viewer/instances/jpeg" + boost::lexical_cast<std::string>(quality_) + - "-" + instanceId_ + "_" + boost::lexical_cast<std::string>(frame_)); - } - - void ProcessHttpAnswer(IMessageEmitter& emitter, - const OrthancStone::IObserver& receiver, - const std::string& answer) const - { - // This code comes from older "OrthancSlicesLoader::ParseSliceImageJpeg()" - - Json::Value encoded; - - { - Json::Reader reader; - if (!reader.parse(answer, encoded)) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - } - - if (encoded.type() != Json::objectValue || - !encoded.isMember("Orthanc") || - encoded["Orthanc"].type() != Json::objectValue) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - - const Json::Value& info = encoded["Orthanc"]; - if (!info.isMember("PixelData") || - !info.isMember("Stretched") || - !info.isMember("Compression") || - info["Compression"].type() != Json::stringValue || - info["PixelData"].type() != Json::stringValue || - info["Stretched"].type() != Json::booleanValue || - info["Compression"].asString() != "Jpeg") - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - - bool isSigned = false; - bool isStretched = info["Stretched"].asBool(); - - if (info.isMember("IsSigned")) - { - if (info["IsSigned"].type() != Json::booleanValue) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - else - { - isSigned = info["IsSigned"].asBool(); - } - } - - std::auto_ptr<Orthanc::ImageAccessor> reader; - - { - std::string jpeg; - Orthanc::Toolbox::DecodeBase64(jpeg, info["PixelData"].asString()); - - reader.reset(new Orthanc::JpegReader); - dynamic_cast<Orthanc::JpegReader&>(*reader).ReadFromMemory(jpeg); - } - - if (reader->GetFormat() == Orthanc::PixelFormat_RGB24) // This is a color image - { - if (expectedFormat_ != Orthanc::PixelFormat_RGB24) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - - if (isSigned || isStretched) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - else - { - SuccessMessage message(*this, reader.release()); - emitter.EmitMessage(receiver, message); - return; - } - } - - if (reader->GetFormat() != Orthanc::PixelFormat_Grayscale8) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - - if (!isStretched) - { - if (expectedFormat_ != reader->GetFormat()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - else - { - SuccessMessage message(*this, reader.release()); - emitter.EmitMessage(receiver, message); - return; - } - } - - int32_t stretchLow = 0; - int32_t stretchHigh = 0; - - if (!info.isMember("StretchLow") || - !info.isMember("StretchHigh") || - info["StretchLow"].type() != Json::intValue || - info["StretchHigh"].type() != Json::intValue) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - - stretchLow = info["StretchLow"].asInt(); - stretchHigh = info["StretchHigh"].asInt(); - - if (stretchLow < -32768 || - stretchHigh > 65535 || - (stretchLow < 0 && stretchHigh > 32767)) - { - // This range cannot be represented with a uint16_t or an int16_t - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - - // Decode a grayscale JPEG 8bpp image coming from the Web viewer - std::auto_ptr<Orthanc::ImageAccessor> image - (new Orthanc::Image(expectedFormat_, reader->GetWidth(), reader->GetHeight(), false)); - - Orthanc::ImageProcessing::Convert(*image, *reader); - reader.reset(); - - float scaling = static_cast<float>(stretchHigh - stretchLow) / 255.0f; - - if (!OrthancStone::LinearAlgebra::IsCloseToZero(scaling)) - { - float offset = static_cast<float>(stretchLow) / scaling; - Orthanc::ImageProcessing::ShiftScale(*image, offset, scaling, true); - } - - SuccessMessage message(*this, image.release()); - emitter.EmitMessage(receiver, message); - } - }; - - - - - class NativeOracle : public IOracle { private: @@ -779,115 +336,53 @@ std::vector<boost::thread*> workers_; - void CopyHttpHeaders(Orthanc::HttpClient& client, - const HttpHeaders& headers) - { - for (HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); it++ ) - { - client.AddHeader(it->first, it->second); - } - } - - - void DecodeAnswer(std::string& answer, - const HttpHeaders& headers) + void Execute(const OrthancStone::IObserver& receiver, + const OrthancApiOracleCommand& command) { - Orthanc::HttpCompression contentEncoding = Orthanc::HttpCompression_None; - - for (HttpHeaders::const_iterator it = headers.begin(); - it != headers.end(); ++it) - { - std::string s; - Orthanc::Toolbox::ToLowerCase(s, it->first); - - if (s == "content-encoding") - { - if (it->second == "gzip") - { - contentEncoding = Orthanc::HttpCompression_Gzip; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol, - "Unsupported HTTP Content-Encoding: " + it->second); - } - - break; - } - } - - if (contentEncoding == Orthanc::HttpCompression_Gzip) - { - std::string compressed; - answer.swap(compressed); - - Orthanc::GzipCompressor compressor; - compressor.Uncompress(answer, compressed.c_str(), compressed.size()); - } - } - - - void Execute(const OrthancStone::IObserver& receiver, - const OrthancRestApiCommand& command) - { - Orthanc::HttpClient client(orthanc_, command.GetUri()); + Orthanc::HttpClient client(orthanc_, command.GetUri()); client.SetMethod(command.GetMethod()); client.SetTimeout(command.GetTimeout()); - CopyHttpHeaders(client, command.GetHttpHeaders()); - if (command.GetMethod() == Orthanc::HttpMethod_Post || command.GetMethod() == Orthanc::HttpMethod_Put) { client.SetBody(command.GetBody()); } - - std::string answer; - HttpHeaders answerHeaders; - client.ApplyAndThrowException(answer, answerHeaders); - - DecodeAnswer(answer, answerHeaders); - - OrthancRestApiCommand::SuccessMessage message(command, answerHeaders, answer); - emitter_.EmitMessage(receiver, message); - } - - - void Execute(const OrthancStone::IObserver& receiver, - const GetOrthancImageCommand& command) - { - Orthanc::HttpClient client(orthanc_, command.GetUri()); - client.SetTimeout(command.GetTimeout()); - - CopyHttpHeaders(client, command.GetHttpHeaders()); + + { + const HttpHeaders& headers = command.GetHttpHeaders(); + for (HttpHeaders::const_iterator it = headers.begin(); it != headers.end(); it++ ) + { + client.AddHeader(it->first, it->second); + } + } std::string answer; HttpHeaders answerHeaders; - client.ApplyAndThrowException(answer, answerHeaders); + + bool success; + try + { + success = client.Apply(answer, answerHeaders); + } + catch (Orthanc::OrthancException& e) + { + success = false; + } - DecodeAnswer(answer, answerHeaders); - - command.ProcessHttpAnswer(emitter_, receiver, answer, answerHeaders); + if (success) + { + OrthancApiOracleCommand::SuccessMessage message(command, answerHeaders, answer); + emitter_.EmitMessage(receiver, message); + } + else + { + OrthancApiOracleCommand::FailureMessage message(command, client.GetLastStatus()); + emitter_.EmitMessage(receiver, message); + } } - void Execute(const OrthancStone::IObserver& receiver, - const GetOrthancWebViewerJpegCommand& command) - { - Orthanc::HttpClient client(orthanc_, command.GetUri()); - client.SetTimeout(command.GetTimeout()); - - CopyHttpHeaders(client, command.GetHttpHeaders()); - - std::string answer; - HttpHeaders answerHeaders; - client.ApplyAndThrowException(answer, answerHeaders); - - DecodeAnswer(answer, answerHeaders); - - command.ProcessHttpAnswer(emitter_, receiver, answer); - } - void Step() { @@ -895,27 +390,15 @@ if (object.get() != NULL) { - printf("===========================> REQUEST\n"); - const Item& item = dynamic_cast<Item&>(*object); try { switch (item.GetCommand().GetType()) { - case IOracleCommand::Type_OrthancRestApi: - Execute(item.GetReceiver(), - dynamic_cast<const OrthancRestApiCommand&>(item.GetCommand())); - break; - - case IOracleCommand::Type_GetOrthancImage: + case IOracleCommand::Type_OrthancApi: Execute(item.GetReceiver(), - dynamic_cast<const GetOrthancImageCommand&>(item.GetCommand())); - break; - - case IOracleCommand::Type_GetOrthancWebViewerJpeg: - Execute(item.GetReceiver(), - dynamic_cast<const GetOrthancWebViewerJpegCommand&>(item.GetCommand())); + dynamic_cast<const OrthancApiOracleCommand&>(item.GetCommand())); break; default: @@ -925,13 +408,10 @@ catch (Orthanc::OrthancException& e) { LOG(ERROR) << "Exception within the oracle: " << e.What(); - emitter_.EmitMessage(item.GetReceiver(), OracleCommandExceptionMessage(item.GetCommand(), e)); } catch (...) { LOG(ERROR) << "Native exception within the oracle"; - emitter_.EmitMessage(item.GetReceiver(), OracleCommandExceptionMessage - (item.GetCommand(), Orthanc::ErrorCode_InternalError)); } } } @@ -1082,15 +562,8 @@ virtual void EmitMessage(const OrthancStone::IObserver& observer, const OrthancStone::IMessage& message) { - try - { - boost::unique_lock<boost::shared_mutex> lock(mutex_); - oracleObservable_.EmitMessage(observer, message); - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "Exception while emitting a message: " << e.What(); - } + boost::unique_lock<boost::shared_mutex> lock(mutex_); + oracleObservable_.EmitMessage(observer, message); } @@ -1329,15 +802,12 @@ OrthancStone::CoordinateSystem3D GetFrameGeometry(unsigned int frame) const { - if (frame == 0) - { - return geometry_; - } - else if (frame >= imageInformation_.GetNumberOfFrames()) + if (frame >= imageInformation_.GetNumberOfFrames()) { throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } - else if (sopClassUid_ == OrthancStone::SopClassUid_RTDose) + + if (sopClassUid_ == OrthancStone::SopClassUid_RTDose) { if (frame >= frameOffsets_.size()) { @@ -1349,10 +819,6 @@ geometry_.GetAxisX(), geometry_.GetAxisY()); } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } } bool FrameContainsPlane(unsigned int frame, @@ -1455,10 +921,10 @@ class MessageHandler : public Orthanc::IDynamicObject { public: - virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const = 0; + virtual void Handle(const OrthancApiOracleCommand::SuccessMessage& message) const = 0; }; - void Handle(const OrthancRestApiCommand::SuccessMessage& message) + void Handle(const OrthancApiOracleCommand::SuccessMessage& message) { dynamic_cast<const MessageHandler&>(message.GetOrigin().GetPayload()).Handle(message); } @@ -1475,7 +941,7 @@ { } - virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const + virtual void Handle(const OrthancApiOracleCommand::SuccessMessage& message) const { Json::Value value; message.ParseJsonBody(value); @@ -1509,7 +975,7 @@ { } - virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const + virtual void Handle(const OrthancApiOracleCommand::SuccessMessage& message) const { Json::Value value; message.ParseJsonBody(value); @@ -1537,7 +1003,7 @@ active_(false) { oracle.RegisterObserverCallback( - new OrthancStone::Callable<AxialVolumeOrthancLoader, OrthancRestApiCommand::SuccessMessage> + new OrthancStone::Callable<AxialVolumeOrthancLoader, OrthancApiOracleCommand::SuccessMessage> (*this, &AxialVolumeOrthancLoader::Handle)); } @@ -1551,7 +1017,7 @@ active_ = true; - std::auto_ptr<Refactoring::OrthancRestApiCommand> command(new Refactoring::OrthancRestApiCommand); + std::auto_ptr<Refactoring::OrthancApiOracleCommand> command(new Refactoring::OrthancApiOracleCommand); command->SetUri("/series/" + seriesId + "/instances-tags"); command->SetPayload(new LoadSeriesGeometryHandler(*this)); @@ -1573,7 +1039,7 @@ // TODO => Should be part of a second call if needed - std::auto_ptr<Refactoring::OrthancRestApiCommand> command(new Refactoring::OrthancRestApiCommand); + std::auto_ptr<Refactoring::OrthancApiOracleCommand> command(new Refactoring::OrthancApiOracleCommand); command->SetUri("/instances/" + instanceId + "/tags?ignore-length=3004-000c"); command->SetPayload(new LoadInstanceGeometryHandler(*this)); @@ -1588,7 +1054,7 @@ class Toto : public OrthancStone::IObserver { private: - void Handle(const Refactoring::OrthancRestApiCommand::SuccessMessage& message) + void Handle(const Refactoring::OrthancApiOracleCommand::SuccessMessage& message) { Json::Value v; message.ParseJsonBody(v); @@ -1596,30 +1062,9 @@ printf("ICI [%s]\n", v.toStyledString().c_str()); } - void Handle(const Refactoring::GetOrthancImageCommand::SuccessMessage& message) - { - printf("IMAGE %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight()); - } - - void Handle(const Refactoring::GetOrthancWebViewerJpegCommand::SuccessMessage& message) - { - printf("WebViewer %dx%d\n", message.GetImage().GetWidth(), message.GetImage().GetHeight()); - } - - void Handle(const Refactoring::OracleCommandExceptionMessage& message) + void Handle(const Refactoring::OrthancApiOracleCommand::FailureMessage& message) { - printf("EXCEPTION: [%s] on command type %d\n", message.GetException().What(), message.GetCommand().GetType()); - - switch (message.GetCommand().GetType()) - { - case Refactoring::IOracleCommand::Type_GetOrthancWebViewerJpeg: - printf("URI: [%s]\n", dynamic_cast<const Refactoring::GetOrthancWebViewerJpegCommand&> - (message.GetCommand()).GetUri().c_str()); - break; - - default: - break; - } + printf("ERROR %d\n", message.GetHttpStatus()); } public: @@ -1628,19 +1073,7 @@ { oracle.RegisterObserverCallback (new OrthancStone::Callable - <Toto, Refactoring::OrthancRestApiCommand::SuccessMessage>(*this, &Toto::Handle)); - - oracle.RegisterObserverCallback - (new OrthancStone::Callable - <Toto, Refactoring::GetOrthancImageCommand::SuccessMessage>(*this, &Toto::Handle)); - - oracle.RegisterObserverCallback - (new OrthancStone::Callable - <Toto, Refactoring::GetOrthancWebViewerJpegCommand::SuccessMessage>(*this, &Toto::Handle)); - - oracle.RegisterObserverCallback - (new OrthancStone::Callable - <Toto, Refactoring::OracleCommandExceptionMessage>(*this, &Toto::Handle)); + <Toto, Refactoring::OrthancApiOracleCommand::SuccessMessage>(*this, &Toto::Handle)); } }; @@ -1668,13 +1101,12 @@ oracle.Start(); - if (1) { Json::Value v = Json::objectValue; v["Level"] = "Series"; v["Query"] = Json::objectValue; - std::auto_ptr<Refactoring::OrthancRestApiCommand> command(new Refactoring::OrthancRestApiCommand); + std::auto_ptr<Refactoring::OrthancApiOracleCommand> command(new Refactoring::OrthancApiOracleCommand); command->SetMethod(Orthanc::HttpMethod_Post); command->SetUri("/tools/find"); command->SetBody(v); @@ -1682,64 +1114,11 @@ oracle.Schedule(*toto, command.release()); } - if (1) - { - std::auto_ptr<Refactoring::GetOrthancImageCommand> command(new Refactoring::GetOrthancImageCommand); - command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Jpeg))); - command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview"); - oracle.Schedule(*toto, command.release()); - } - - if (1) - { - std::auto_ptr<Refactoring::GetOrthancImageCommand> command(new Refactoring::GetOrthancImageCommand); - command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png))); - command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/preview"); - oracle.Schedule(*toto, command.release()); - } - - if (1) - { - std::auto_ptr<Refactoring::GetOrthancImageCommand> command(new Refactoring::GetOrthancImageCommand); - command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Png))); - command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16"); - oracle.Schedule(*toto, command.release()); - } - - if (1) - { - std::auto_ptr<Refactoring::GetOrthancImageCommand> command(new Refactoring::GetOrthancImageCommand); - command->SetHttpHeader("Accept-Encoding", "gzip"); - command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam))); - command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16"); - oracle.Schedule(*toto, command.release()); - } - - if (1) - { - std::auto_ptr<Refactoring::GetOrthancImageCommand> command(new Refactoring::GetOrthancImageCommand); - command->SetHttpHeader("Accept", std::string(Orthanc::EnumerationToString(Orthanc::MimeType_Pam))); - command->SetUri("/instances/6687cc73-07cae193-52ff29c8-f646cb16-0753ed92/image-uint16"); - oracle.Schedule(*toto, command.release()); - } - - if (1) - { - std::auto_ptr<Refactoring::GetOrthancWebViewerJpegCommand> command(new Refactoring::GetOrthancWebViewerJpegCommand); - command->SetHttpHeader("Accept-Encoding", "gzip"); - command->SetInstance("e6c7c20b-c9f65d7e-0d76f2e2-830186f2-3e3c600e"); - command->SetQuality(90); - oracle.Schedule(*toto, command.release()); - } - - // 2017-11-17-Anonymized loader1->LoadSeries(oracle, "cb3ea4d1-d08f3856-ad7b6314-74d88d77-60b05618"); // CT loader2->LoadInstance(oracle, "41029085-71718346-811efac4-420e2c15-d39f99b6"); // RT-DOSE - LOG(WARNING) << "...Waiting for Ctrl-C..."; - Orthanc::SystemToolbox::ServerBarrier(); - //boost::this_thread::sleep(boost::posix_time::seconds(1)); + boost::this_thread::sleep(boost::posix_time::seconds(1)); oracle.Stop(); }
--- a/Samples/Sdl/TrackerSample.cpp Fri May 10 14:54:03 2019 +0200 +++ b/Samples/Sdl/TrackerSample.cpp Mon May 13 15:12:56 2019 +0200 @@ -18,24 +18,18 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. **/ +#include "TrackerSampleApp.h" // From Stone #include "../../Applications/Sdl/SdlOpenGLWindow.h" #include "../../Framework/Scene2D/CairoCompositor.h" #include "../../Framework/Scene2D/ColorTextureSceneLayer.h" #include "../../Framework/Scene2D/OpenGLCompositor.h" -#include "../../Framework/Scene2D/PanSceneTracker.h" -#include "../../Framework/Scene2D/RotateSceneTracker.h" -#include "../../Framework/Scene2D/Scene2D.h" -#include "../../Framework/Scene2D/ZoomSceneTracker.h" #include "../../Framework/StoneInitialization.h" -// From Orthanc framework #include <Core/Logging.h> #include <Core/OrthancException.h> -#include <Core/Images/Image.h> -#include <Core/Images/ImageProcessing.h> -#include <Core/Images/PngWriter.h> + #include <boost/shared_ptr.hpp> #include <boost/weak_ptr.hpp> @@ -43,11 +37,6 @@ #include <SDL.h> #include <stdio.h> - -// to be moved into Stone -#include "../Common/MeasureTrackers.h" -#include "../Common/MeasureCommands.h" - /* TODO: @@ -63,369 +52,6 @@ using namespace Orthanc; using namespace OrthancStone; -namespace OrthancStone -{ - enum GuiTool - { - GuiTool_Rotate = 0, - GuiTool_Pan, - GuiTool_Zoom, - GuiTool_LineMeasure, - GuiTool_CircleMeasure, - GuiTool_AngleMeasure, - GuiTool_EllipseMeasure, - GuiTool_LAST - }; - - const char* MeasureToolToString(size_t i) - { - static const char* descs[] = { - "GuiTool_Rotate", - "GuiTool_Pan", - "GuiTool_Zoom", - "GuiTool_LineMeasure", - "GuiTool_CircleMeasure", - "GuiTool_AngleMeasure", - "GuiTool_EllipseMeasure", - "GuiTool_LAST" - }; - if (i >= GuiTool_LAST) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong tool index"); - } - return descs[i]; - } -} - -class TrackerSampleApp -{ -public: - // 12 because. - TrackerSampleApp() : currentTool_(GuiTool_Rotate) - { - TEXTURE_2x2_1_ZINDEX = 1; - TEXTURE_1x1_ZINDEX = 2; - TEXTURE_2x2_2_ZINDEX = 3; - LINESET_1_ZINDEX = 4; - LINESET_2_ZINDEX = 5; - INFOTEXT_LAYER_ZINDEX = 6; - } - void PrepareScene(); - void Run(); - -private: - Scene2D& GetScene() - { - return scene_; - } - - void SelectNextTool() - { - currentTool_ = static_cast<GuiTool>(currentTool_ + 1); - if (currentTool_ == GuiTool_LAST) - currentTool_ = static_cast<GuiTool>(0);; - printf("Current tool is now: %s\n", MeasureToolToString(currentTool_)); - } - - void HandleApplicationEvent( - const OpenGLCompositor& compositor, - const SDL_Event& event, - std::auto_ptr<IPointerTracker>& activeTracker); - - IPointerTracker* TrackerHitTest(const PointerEvent& e); - - IPointerTracker* CreateSuitableTracker( - const SDL_Event& event, - const PointerEvent& e, - const OpenGLCompositor& compositor); - - void TakeScreenshot( - const std::string& target, - unsigned int canvasWidth, - unsigned int canvasHeight); - - /** - This adds the command at the top of the undo stack - */ - void Commit(TrackerCommandPtr cmd); - void Undo(); - void Redo(); - -private: - static const unsigned int FONT_SIZE = 32; - - std::vector<TrackerCommandPtr> undoStack_; - - // we store the measure tools here so that they don't get deleted - std::vector<MeasureToolPtr> measureTools_; - - //static const int LAYER_POSITION = 150; -#if 0 - int TEXTURE_2x2_1_ZINDEX = 12; - int TEXTURE_1x1_ZINDEX = 13; - int TEXTURE_2x2_2_ZINDEX = 14; - int LINESET_1_ZINDEX = 50; - int LINESET_2_ZINDEX = 100; - int INFOTEXT_LAYER_ZINDEX = 150; -#else - int TEXTURE_2x2_1_ZINDEX; - int TEXTURE_1x1_ZINDEX; - int TEXTURE_2x2_2_ZINDEX; - int LINESET_1_ZINDEX; - int LINESET_2_ZINDEX; - int INFOTEXT_LAYER_ZINDEX; -#endif - Scene2D scene_; - GuiTool currentTool_; -}; - - -void TrackerSampleApp::PrepareScene() -{ - // Texture of 2x2 size - { - Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); - - uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); - p[0] = 255; - p[1] = 0; - p[2] = 0; - - p[3] = 0; - p[4] = 255; - p[5] = 0; - - p = reinterpret_cast<uint8_t*>(i.GetRow(1)); - p[0] = 0; - p[1] = 0; - p[2] = 255; - - p[3] = 255; - p[4] = 0; - p[5] = 0; - - scene_.SetLayer(TEXTURE_2x2_1_ZINDEX, new ColorTextureSceneLayer(i)); - - std::auto_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); - l->SetOrigin(-3, 2); - l->SetPixelSpacing(1.5, 1); - l->SetAngle(20.0 / 180.0 * M_PI); - scene_.SetLayer(TEXTURE_2x2_2_ZINDEX, l.release()); - } - - // Texture of 1x1 size - { - Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false); - - uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); - p[0] = 255; - p[1] = 0; - p[2] = 0; - - std::auto_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); - l->SetOrigin(-2, 1); - l->SetAngle(20.0 / 180.0 * M_PI); - scene_.SetLayer(TEXTURE_1x1_ZINDEX, l.release()); - } - - // Some lines - { - std::auto_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer); - - layer->SetThickness(1); - - 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); - - 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); - - double dy = 1.01; - chain.clear(); - chain.push_back(ScenePoint2D(-4, -4)); - 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->SetColor(0, 255, 255); - scene_.SetLayer(LINESET_1_ZINDEX, layer.release()); - } - - // Some text - { - std::auto_ptr<TextSceneLayer> layer(new TextSceneLayer); - layer->SetText("Hello"); - scene_.SetLayer(LINESET_2_ZINDEX, layer.release()); - } -} - - -void TrackerSampleApp::TakeScreenshot(const std::string& target, - unsigned int canvasWidth, - unsigned int canvasHeight) -{ - // Take a screenshot, then save it as PNG file - CairoCompositor compositor(scene_, canvasWidth, canvasHeight); - compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE, Orthanc::Encoding_Latin1); - compositor.Refresh(); - - Orthanc::ImageAccessor canvas; - compositor.GetCanvas().GetReadOnlyAccessor(canvas); - - Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false); - Orthanc::ImageProcessing::Convert(png, canvas); - - Orthanc::PngWriter writer; - writer.WriteToFile(target, png); -} - - -IPointerTracker* TrackerSampleApp::TrackerHitTest(const PointerEvent& e) -{ - // std::vector<MeasureToolPtr> measureTools_; - return NULL; -} - -IPointerTracker* TrackerSampleApp::CreateSuitableTracker( - const SDL_Event& event, - const PointerEvent& e, - const OpenGLCompositor& compositor) -{ - switch (event.button.button) - { - case SDL_BUTTON_MIDDLE: - return new PanSceneTracker(scene_, e); - - case SDL_BUTTON_RIGHT: - return new ZoomSceneTracker( - scene_, e, compositor.GetCanvasHeight()); - - case SDL_BUTTON_LEFT: - { - // TODO: we need to iterate on the set of measuring tool and perform - // a hit test to check if a tracker needs to be created for edition. - // Otherwise, depending upon the active tool, we might want to create - // a "measuring tool creation" tracker - - // TODO: if there are conflicts, we should prefer a tracker that - // pertains to the type of measuring tool currently selected (TBD?) - IPointerTracker* hitTestTracker = TrackerHitTest(e); - - if (hitTestTracker != NULL) - { - return hitTestTracker; - } - else - { - switch (currentTool_) - { - case GuiTool_Rotate: - return new RotateSceneTracker(scene_, e); - case GuiTool_LineMeasure: - return new CreateLineMeasureTracker( - scene_, undoStack_, measureTools_, e); - //case GuiTool_AngleMeasure: - // return new AngleMeasureTracker(scene_, measureTools_, undoStack_, e); - //case GuiTool_CircleMeasure: - // return new CircleMeasureTracker(scene_, measureTools_, undoStack_, e); - //case GuiTool_EllipseMeasure: - // return new EllipseMeasureTracker(scene_, measureTools_, undoStack_, e); - default: - throw OrthancException(ErrorCode_InternalError, "Wrong tool!"); - } - } - } - default: - return NULL; - } -} - -void TrackerSampleApp::HandleApplicationEvent( - const OpenGLCompositor& compositor, - const SDL_Event& event, - std::auto_ptr<IPointerTracker>& activeTracker) -{ - if (event.type == SDL_MOUSEMOTION) - { - int scancodeCount = 0; - const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); - - if (activeTracker.get() == NULL && - SDL_SCANCODE_LCTRL < scancodeCount && - keyboardState[SDL_SCANCODE_LCTRL]) - { - // The "left-ctrl" key is down, while no tracker is present - - PointerEvent e; - e.AddPosition(compositor.GetPixelCenterCoordinates(event.button.x, event.button.y)); - - ScenePoint2D p = e.GetMainPosition().Apply(scene_.GetCanvasToSceneTransform()); - - char buf[64]; - sprintf(buf, "(%0.02f,%0.02f)", p.GetX(), p.GetY()); - - if (scene_.HasLayer(INFOTEXT_LAYER_ZINDEX)) - { - TextSceneLayer& layer = - dynamic_cast<TextSceneLayer&>(scene_.GetLayer(INFOTEXT_LAYER_ZINDEX)); - layer.SetText(buf); - layer.SetPosition(p.GetX(), p.GetY()); - } - else - { - std::auto_ptr<TextSceneLayer> layer(new TextSceneLayer); - layer->SetColor(0, 255, 0); - layer->SetText(buf); - layer->SetBorder(20); - layer->SetAnchor(BitmapAnchor_BottomCenter); - layer->SetPosition(p.GetX(), p.GetY()); - scene_.SetLayer(INFOTEXT_LAYER_ZINDEX, layer.release()); - } - } - else - { - scene_.DeleteLayer(INFOTEXT_LAYER_ZINDEX); - } - } - else if (event.type == SDL_MOUSEBUTTONDOWN) - { - PointerEvent e; - e.AddPosition(compositor.GetPixelCenterCoordinates(event.button.x, event.button.y)); - - activeTracker.reset(CreateSuitableTracker(event, e, compositor)); - } - else if (event.type == SDL_KEYDOWN && - event.key.repeat == 0 /* Ignore key bounce */) - { - switch (event.key.keysym.sym) - { - case SDLK_s: - scene_.FitContent(compositor.GetCanvasWidth(), - compositor.GetCanvasHeight()); - break; - - case SDLK_c: - TakeScreenshot( - "screenshot.png", - compositor.GetCanvasWidth(), - compositor.GetCanvasHeight()); - break; - - default: - break; - } - } -} - static void GLAPIENTRY OpenGLMessageCallback(GLenum source, @@ -446,23 +72,19 @@ bool g_stopApplication = false; -void TrackerSampleApp::Run() +void Run(TrackerSampleApp* app) { SdlOpenGLWindow window("Hello", 1024, 768); - scene_.FitContent(window.GetCanvasWidth(), window.GetCanvasHeight()); + app->GetScene().FitContent(window.GetCanvasWidth(), window.GetCanvasHeight()); glEnable(GL_DEBUG_OUTPUT); glDebugMessageCallback(OpenGLMessageCallback, 0); - OpenGLCompositor compositor(window, scene_); + OpenGLCompositor compositor(window, app->GetScene()); compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE, Orthanc::Encoding_Latin1); - // this will either be empty or contain the current tracker, if any - std::auto_ptr<IPointerTracker> tracker; - - while (!g_stopApplication) { compositor.Refresh(); @@ -475,29 +97,10 @@ g_stopApplication = true; break; } - else if (event.type == SDL_MOUSEMOTION) - { - if (tracker.get() != NULL) - { - PointerEvent e; - e.AddPosition(compositor.GetPixelCenterCoordinates(event.button.x, event.button.y)); - LOG(TRACE) << "event.button.x = " << event.button.x << " " << - "event.button.y = " << event.button.y; - tracker->Update(e); - } - } - else if (event.type == SDL_MOUSEBUTTONUP) - { - if (tracker.get() != NULL) - { - tracker->Release(); - tracker.reset(NULL); - } - } else if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { - tracker.reset(NULL); + app->DisableTracker(); // was: tracker.reset(NULL); compositor.UpdateSize(); } else if (event.type == SDL_KEYDOWN && @@ -512,16 +115,11 @@ case SDLK_q: g_stopApplication = true; break; - - case SDLK_t: - SelectNextTool(); - break; - default: break; } } - HandleApplicationEvent(compositor, event, tracker); + app->HandleApplicationEvent(compositor, event); } SDL_Delay(1); } @@ -537,12 +135,14 @@ { StoneInitialize(); Orthanc::Logging::EnableInfoLevel(true); + Orthanc::Logging::EnableTraceLevel(true); + try { TrackerSampleApp app; app.PrepareScene(); - app.Run(); + Run(&app); } catch (Orthanc::OrthancException& e) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Sdl/TrackerSampleApp.cpp Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,421 @@ +/** + * 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/>. + **/ + +#include "TrackerSampleApp.h" + +#include "../Common/CreateLineMeasureTracker.h" + +#include "../../Applications/Sdl/SdlOpenGLWindow.h" + +#include "../../Framework/Scene2D/PanSceneTracker.h" +#include "../../Framework/Scene2D/RotateSceneTracker.h" +#include "../../Framework/Scene2D/Scene2D.h" +#include "../../Framework/Scene2D/ZoomSceneTracker.h" +#include "../../Framework/Scene2D/CairoCompositor.h" +#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/StoneInitialization.h" + + // From Orthanc framework +#include <Core/Logging.h> +#include <Core/OrthancException.h> +#include <Core/Images/Image.h> +#include <Core/Images/ImageProcessing.h> +#include <Core/Images/PngWriter.h> + +#include <SDL.h> +#include <stdio.h> + +using namespace Orthanc; + +namespace OrthancStone +{ + const char* MeasureToolToString(size_t i) + { + static const char* descs[] = { + "GuiTool_Rotate", + "GuiTool_Pan", + "GuiTool_Zoom", + "GuiTool_LineMeasure", + "GuiTool_CircleMeasure", + "GuiTool_AngleMeasure", + "GuiTool_EllipseMeasure", + "GuiTool_LAST" + }; + if (i >= GuiTool_LAST) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Wrong tool index"); + } + return descs[i]; + } + + Scene2D& TrackerSampleApp::GetScene() + { + return scene_; + } + + void TrackerSampleApp::SelectNextTool() + { + currentTool_ = static_cast<GuiTool>(currentTool_ + 1); + if (currentTool_ == GuiTool_LAST) + currentTool_ = static_cast<GuiTool>(0);; + printf("Current tool is now: %s\n", MeasureToolToString(currentTool_)); + } + + void TrackerSampleApp::DisplayInfoText(const PointerEvent& e) + { + ScenePoint2D p = e.GetMainPosition().Apply(scene_.GetCanvasToSceneTransform()); + + char buf[64]; + sprintf(buf, "(%0.02f,%0.02f)", p.GetX(), p.GetY()); + + if (scene_.HasLayer(INFOTEXT_LAYER_ZINDEX)) + { + TextSceneLayer& layer = + dynamic_cast<TextSceneLayer&>(scene_.GetLayer(INFOTEXT_LAYER_ZINDEX)); + layer.SetText(buf); + layer.SetPosition(p.GetX(), p.GetY()); + } + else + { + std::auto_ptr<TextSceneLayer> layer(new TextSceneLayer); + layer->SetColor(0, 255, 0); + layer->SetText(buf); + layer->SetBorder(20); + layer->SetAnchor(BitmapAnchor_BottomCenter); + layer->SetPosition(p.GetX(), p.GetY()); + scene_.SetLayer(INFOTEXT_LAYER_ZINDEX, layer.release()); + } + } + + void TrackerSampleApp::HideInfoText() + { + scene_.DeleteLayer(INFOTEXT_LAYER_ZINDEX); + } + + void TrackerSampleApp::HandleApplicationEvent( + const OpenGLCompositor & compositor, + const SDL_Event & event) + { + if (event.type == SDL_MOUSEMOTION) + { + int scancodeCount = 0; + const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); + + if (activeTracker_.get() == NULL && + SDL_SCANCODE_LCTRL < scancodeCount && + keyboardState[SDL_SCANCODE_LCTRL]) + { + // The "left-ctrl" key is down, while no tracker is present + // Let's display the info text + PointerEvent e; + e.AddPosition(compositor.GetPixelCenterCoordinates(event.button.x, event.button.y)); + DisplayInfoText(e); + } + else + { + HideInfoText(); + //LOG(TRACE) << "(event.type == SDL_MOUSEMOTION)"; + if (activeTracker_.get() != NULL) + { + //LOG(TRACE) << "(activeTracker_.get() != NULL)"; + PointerEvent e; + e.AddPosition(compositor.GetPixelCenterCoordinates(event.button.x, event.button.y)); + //LOG(TRACE) << "event.button.x = " << event.button.x << " " << + // "event.button.y = " << event.button.y; + //LOG(TRACE) << "activeTracker_->PointerMove(e); " << + // e.GetMainPosition().GetX() << " " << e.GetMainPosition().GetY(); + activeTracker_->PointerMove(e); + if (!activeTracker_->IsActive()) + activeTracker_ = NULL; + } + } + } + else if (event.type == SDL_MOUSEBUTTONUP) + { + if (activeTracker_) + { + PointerEvent e; + e.AddPosition(compositor.GetPixelCenterCoordinates(event.button.x, event.button.y)); + activeTracker_->PointerUp(e); + if (!activeTracker_->IsActive()) + activeTracker_ = NULL; + } + } + else if (event.type == SDL_MOUSEBUTTONDOWN) + { + PointerEvent e; + e.AddPosition(compositor.GetPixelCenterCoordinates(event.button.x, event.button.y)); + if (activeTracker_) + { + activeTracker_->PointerDown(e); + if (!activeTracker_->IsActive()) + activeTracker_ = NULL; + } + else + { + // we ATTEMPT to create a tracker if need be + activeTracker_ = CreateSuitableTracker(event, e, compositor); + } + } + else if (event.type == SDL_KEYDOWN && + event.key.repeat == 0 /* Ignore key bounce */) + { + switch (event.key.keysym.sym) + { + case SDLK_ESCAPE: + if (activeTracker_) + { + activeTracker_->Cancel(); + if (!activeTracker_->IsActive()) + activeTracker_ = NULL; + } + break; + + case SDLK_t: + if (!activeTracker_) + SelectNextTool(); + else + { + LOG(WARNING) << "You cannot change the active tool when an interaction" + " is taking place"; + } + break; + + case SDLK_s: + scene_.FitContent(compositor.GetCanvasWidth(), + compositor.GetCanvasHeight()); + break; + + case SDLK_c: + TakeScreenshot( + "screenshot.png", + compositor.GetCanvasWidth(), + compositor.GetCanvasHeight()); + break; + + default: + break; + } + } + } + + FlexiblePointerTrackerPtr TrackerSampleApp::CreateSuitableTracker( + const SDL_Event & event, + const PointerEvent & e, + const OpenGLCompositor & compositor) + { + switch (event.button.button) + { + case SDL_BUTTON_MIDDLE: + return CreateSimpleTrackerAdapter(PointerTrackerPtr( + new PanSceneTracker(scene_, e))); + + case SDL_BUTTON_RIGHT: + return CreateSimpleTrackerAdapter(PointerTrackerPtr( + new ZoomSceneTracker(scene_, e, compositor.GetCanvasHeight()))); + + case SDL_BUTTON_LEFT: + { + //LOG(TRACE) << "CreateSuitableTracker: case SDL_BUTTON_LEFT:"; + // TODO: we need to iterate on the set of measuring tool and perform + // a hit test to check if a tracker needs to be created for edition. + // Otherwise, depending upon the active tool, we might want to create + // a "measuring tool creation" tracker + + // TODO: if there are conflicts, we should prefer a tracker that + // pertains to the type of measuring tool currently selected (TBD?) + FlexiblePointerTrackerPtr hitTestTracker = TrackerHitTest(e); + + if (hitTestTracker != NULL) + { + //LOG(TRACE) << "hitTestTracker != NULL"; + return hitTestTracker; + } + else + { + switch (currentTool_) + { + case GuiTool_Rotate: + //LOG(TRACE) << "Creating RotateSceneTracker"; + return CreateSimpleTrackerAdapter(PointerTrackerPtr( + new RotateSceneTracker(scene_, e))); + case GuiTool_LineMeasure: + return FlexiblePointerTrackerPtr(new CreateLineMeasureTracker( + scene_, undoStack_, measureTools_, e)); + case GuiTool_Pan: + return CreateSimpleTrackerAdapter(PointerTrackerPtr( + new PanSceneTracker(scene_, e))); + case GuiTool_Zoom: + return CreateSimpleTrackerAdapter(PointerTrackerPtr( + new ZoomSceneTracker(scene_, e, compositor.GetCanvasHeight()))); + //case GuiTool_AngleMeasure: + // return new AngleMeasureTracker(scene_, measureTools_, undoStack_, e); + //case GuiTool_CircleMeasure: + // return new CircleMeasureTracker(scene_, measureTools_, undoStack_, e); + //case GuiTool_EllipseMeasure: + // return new EllipseMeasureTracker(scene_, measureTools_, undoStack_, e); + case GuiTool_AngleMeasure: + LOG(ERROR) << "Not implemented yet!"; + return NULL; + case GuiTool_CircleMeasure: + LOG(ERROR) << "Not implemented yet!"; + return NULL; + case GuiTool_EllipseMeasure: + LOG(ERROR) << "Not implemented yet!"; + return NULL; + default: + throw OrthancException(ErrorCode_InternalError, "Wrong tool!"); + } + } + } + default: + return NULL; + } + } + + + void TrackerSampleApp::PrepareScene() + { + // Texture of 2x2 size + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); + + uint8_t* p = reinterpret_cast<uint8_t*>(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + p[3] = 0; + p[4] = 255; + p[5] = 0; + + p = reinterpret_cast<uint8_t*>(i.GetRow(1)); + p[0] = 0; + p[1] = 0; + p[2] = 255; + + p[3] = 255; + p[4] = 0; + p[5] = 0; + + scene_.SetLayer(TEXTURE_2x2_1_ZINDEX, new ColorTextureSceneLayer(i)); + + std::auto_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-3, 2); + l->SetPixelSpacing(1.5, 1); + l->SetAngle(20.0 / 180.0 * M_PI); + scene_.SetLayer(TEXTURE_2x2_2_ZINDEX, l.release()); + } + + // Texture of 1x1 size + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false); + + uint8_t* p = reinterpret_cast<uint8_t*>(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + std::auto_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-2, 1); + l->SetAngle(20.0 / 180.0 * M_PI); + scene_.SetLayer(TEXTURE_1x1_ZINDEX, l.release()); + } + + // Some lines + { + std::auto_ptr<PolylineSceneLayer> layer(new PolylineSceneLayer); + + layer->SetThickness(1); + + 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); + + 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); + + double dy = 1.01; + chain.clear(); + chain.push_back(ScenePoint2D(-4, -4)); + 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->SetColor(0, 255, 255); + scene_.SetLayer(LINESET_1_ZINDEX, layer.release()); + } + + // Some text + { + std::auto_ptr<TextSceneLayer> layer(new TextSceneLayer); + layer->SetText("Hello"); + scene_.SetLayer(LINESET_2_ZINDEX, layer.release()); + } + } + + + void TrackerSampleApp::DisableTracker() + { + if (activeTracker_) + { + activeTracker_->Cancel(); + activeTracker_ = NULL; + } + } + + void TrackerSampleApp::TakeScreenshot(const std::string& target, + unsigned int canvasWidth, + unsigned int canvasHeight) + { + // Take a screenshot, then save it as PNG file + CairoCompositor compositor(scene_, canvasWidth, canvasHeight); + compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE, Orthanc::Encoding_Latin1); + compositor.Refresh(); + + Orthanc::ImageAccessor canvas; + compositor.GetCanvas().GetReadOnlyAccessor(canvas); + + Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false); + Orthanc::ImageProcessing::Convert(png, canvas); + + Orthanc::PngWriter writer; + writer.WriteToFile(target, png); + } + + + FlexiblePointerTrackerPtr TrackerSampleApp::TrackerHitTest(const PointerEvent & e) + { + // std::vector<MeasureToolPtr> measureTools_; + return nullptr; + } + + + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Sdl/TrackerSampleApp.h Mon May 13 15:12:56 2019 +0200 @@ -0,0 +1,136 @@ +/** + * 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/>. + **/ + +#include <Framework/Scene2D/OpenGLCompositor.h> + +#include "../Common/IFlexiblePointerTracker.h" +#include "../Common/MeasureTools.h" + +#include <SDL.h> + +#include <boost/make_shared.hpp> +#include <boost/shared_ptr.hpp> + +namespace OrthancStone +{ + class TrackerCommand; + typedef boost::shared_ptr<TrackerCommand> TrackerCommandPtr; + + enum GuiTool + { + GuiTool_Rotate = 0, + GuiTool_Pan, + GuiTool_Zoom, + GuiTool_LineMeasure, + GuiTool_CircleMeasure, + GuiTool_AngleMeasure, + GuiTool_EllipseMeasure, + GuiTool_LAST + }; + + const char* MeasureToolToString(size_t i); + + static const unsigned int FONT_SIZE = 32; + + class Scene2D; + + class TrackerSampleApp + { + public: + // 12 because. + TrackerSampleApp() : currentTool_(GuiTool_Rotate) + { + TEXTURE_2x2_1_ZINDEX = 1; + TEXTURE_1x1_ZINDEX = 2; + TEXTURE_2x2_2_ZINDEX = 3; + LINESET_1_ZINDEX = 4; + LINESET_2_ZINDEX = 5; + INFOTEXT_LAYER_ZINDEX = 6; + } + void PrepareScene(); + + void DisableTracker(); + + Scene2D& GetScene(); + + void HandleApplicationEvent( + const OpenGLCompositor& compositor, + const SDL_Event& event); + + private: + void SelectNextTool(); + + + FlexiblePointerTrackerPtr TrackerHitTest(const PointerEvent& e); + + FlexiblePointerTrackerPtr CreateSuitableTracker( + const SDL_Event& event, + const PointerEvent& e, + const OpenGLCompositor& compositor); + + void TakeScreenshot( + const std::string& target, + unsigned int canvasWidth, + unsigned int canvasHeight); + + /** + This adds the command at the top of the undo stack + */ + void Commit(TrackerCommandPtr cmd); + void Undo(); + void Redo(); + + private: + void DisplayInfoText(const PointerEvent& e); + void HideInfoText(); + + private: + /** + WARNING: the measuring tools do store a reference to the scene, and it + paramount that the scene gets destroyed AFTER the measurement tools. + */ + Scene2D scene_; + + FlexiblePointerTrackerPtr activeTracker_; + std::vector<TrackerCommandPtr> undoStack_; + + // we store the measure tools here so that they don't get deleted + std::vector<MeasureToolPtr> measureTools_; + + //static const int LAYER_POSITION = 150; +#if 0 + int TEXTURE_2x2_1_ZINDEX = 12; + int TEXTURE_1x1_ZINDEX = 13; + int TEXTURE_2x2_2_ZINDEX = 14; + int LINESET_1_ZINDEX = 50; + int LINESET_2_ZINDEX = 100; + int INFOTEXT_LAYER_ZINDEX = 150; +#else + int TEXTURE_2x2_1_ZINDEX; + int TEXTURE_1x1_ZINDEX; + int TEXTURE_2x2_2_ZINDEX; + int LINESET_1_ZINDEX; + int LINESET_2_ZINDEX; + int INFOTEXT_LAYER_ZINDEX; +#endif + GuiTool currentTool_; + }; + +}