Mercurial > hg > orthanc-stl
diff Sources/StructurePolygon.cpp @ 33:2460b376d3f7
reorganization
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 04 Apr 2024 18:50:11 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sources/StructurePolygon.cpp Thu Apr 04 18:50:11 2024 +0200 @@ -0,0 +1,187 @@ +/** + * SPDX-FileCopyrightText: 2023-2024 Sebastien Jodogne, UCLouvain, Belgium + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +/** + * STL plugin for Orthanc + * Copyright (C) 2023-2024 Sebastien Jodogne, UCLouvain, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "StructurePolygon.h" + +#include "STLToolbox.h" + +#include <OrthancException.h> +#include <SerializationToolbox.h> + +#include <dcmtk/dcmdata/dcfilefo.h> +#include <dcmtk/dcmdata/dcdeftag.h> + + +StructurePolygon::StructurePolygon(Orthanc::ParsedDicomFile& dicom, + unsigned long roiIndex, + unsigned long contourIndex) +{ + DcmDataset& dataset = *dicom.GetDcmtkObject().getDataset(); + + DcmItem* structure = NULL; + DcmItem* roi = NULL; + DcmItem* contour = NULL; + DcmSequenceOfItems* referenced = NULL; + + if (!dataset.findAndGetSequenceItem(DCM_StructureSetROISequence, structure, roiIndex).good() || + structure == NULL || + !dataset.findAndGetSequenceItem(DCM_ROIContourSequence, roi, roiIndex).good() || + roi == NULL || + !roi->findAndGetSequenceItem(DCM_ContourSequence, contour, contourIndex).good() || + contour == NULL || + !contour->findAndGetSequence(DCM_ContourImageSequence, referenced).good() || + referenced == NULL || + referenced->card() != 1) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + roiName_ = STLToolbox::GetStringValue(*structure, DCM_ROIName); + referencedSopInstanceUid_ = STLToolbox::GetStringValue(*referenced->getItem(0), DCM_ReferencedSOPInstanceUID); + + if (STLToolbox::GetStringValue(*contour, DCM_ContourGeometricType) != "CLOSED_PLANAR") + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + { + std::vector<std::string> tokens; + Orthanc::Toolbox::TokenizeString(tokens, STLToolbox::GetStringValue(*roi, DCM_ROIDisplayColor), '\\'); + + uint32_t r, g, b; + if (tokens.size() != 3 || + !Orthanc::SerializationToolbox::ParseFirstUnsignedInteger32(r, tokens[0]) || + !Orthanc::SerializationToolbox::ParseFirstUnsignedInteger32(g, tokens[1]) || + !Orthanc::SerializationToolbox::ParseFirstUnsignedInteger32(b, tokens[2]) || + r > 255 || + g > 255 || + b > 255) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + red_ = r; + green_ = g; + blue_ = b; + } + + { + std::vector<std::string> tokens; + Orthanc::Toolbox::TokenizeString(tokens, STLToolbox::GetStringValue(*contour, DCM_ContourData), '\\'); + + const std::string s = STLToolbox::GetStringValue(*contour, DCM_NumberOfContourPoints); + + uint32_t countPoints; + if (!Orthanc::SerializationToolbox::ParseUnsignedInteger32(countPoints, s) || + tokens.size() != 3 * countPoints) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + points_.reserve(countPoints); + + for (size_t i = 0; i < tokens.size(); i += 3) + { + double x, y, z; + if (!STLToolbox::MyParseDouble(x, tokens[i]) || + !STLToolbox::MyParseDouble(y, tokens[i + 1]) || + !STLToolbox::MyParseDouble(z, tokens[i + 2])) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + points_.push_back(Vector3D(x, y, z)); + } + + assert(points_.size() == countPoints); + } +} + + +const Vector3D& StructurePolygon::GetPoint(size_t i) const +{ + if (i >= points_.size()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + return points_[i]; + } +} + + +bool StructurePolygon::IsCoplanar(Vector3D& normal) const +{ + if (points_.size() < 3) + { + return false; + } + + bool hasNormal = false; + + for (size_t i = 0; i < points_.size(); i++) + { + normal = Vector3D::CrossProduct(Vector3D(points_[1], points_[0]), + Vector3D(points_[2], points_[0])); + if (!STLToolbox::IsNear(normal.ComputeNorm(), 0)) + { + normal.Normalize(); + hasNormal = true; + } + } + + if (!hasNormal) + { + return false; + } + + double a = Vector3D::DotProduct(points_[0], normal); + + for (size_t i = 1; i < points_.size(); i++) + { + double b = Vector3D::DotProduct(points_[i], normal); + if (!STLToolbox::IsNear(a, b)) + { + return false; + } + } + + return true; +} + + +void StructurePolygon::Add(Extent2D& extent, + const Vector3D& axisX, + const Vector3D& axisY) const +{ + assert(STLToolbox::IsNear(1, axisX.ComputeNorm())); + assert(STLToolbox::IsNear(1, axisY.ComputeNorm())); + + for (size_t i = 0; i < points_.size(); i++) + { + extent.Add(Vector3D::DotProduct(axisX, points_[i]), + Vector3D::DotProduct(axisY, points_[i])); + } +}