comparison Framework/Toolbox/DicomStructure2.h @ 998:38b6bb0bdd72

added a new set of classes that correctly handle non-convex polygons (not used yet because of limitations in coordinates computing): DicomStructure2, DicomStructureSet2, DicomStructurePolygon2, DicomStructureSetSlicer2. Too many shortcuts have been taken when computing the actual position.
author Benjamin Golinvaux <bgo@osimis.io>
date Fri, 20 Sep 2019 11:58:00 +0200
parents
children 29f5f2031310
comparison
equal deleted inserted replaced
995:9893fa8cd7a6 998:38b6bb0bdd72
1 /**
2 * Stone of Orthanc
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2019 Osimis S.A., Belgium
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU Affero General Public License
9 * as published by the Free Software Foundation, either version 3 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 **/
20
21 #pragma once
22
23 #include "DicomStructurePolygon2.h"
24 #include "DicomStructureSetUtils.h"
25
26 namespace OrthancStone
27 {
28
29 /*
30 A structure has a color, a name, a set of slices..
31
32 Each slice is a polygon.
33 */
34 struct DicomStructure2
35 {
36 DicomStructure2() :
37 red_(0), green_(0), blue_(0), sliceThickness_(0), state_(Building) {}
38
39 void AddPolygon(const DicomStructurePolygon2& polygon);
40
41 /**
42 Once all polygons have been added, this method will determine:
43 - the slice orientation (through the normal vector)
44 - the spacing between slices (slice thickness)
45
46 it will also set up the info required to efficiently compute plane
47 intersections later on.
48 */
49 void ComputeDependentProperties();
50
51 /**
52 Being given a plane that is PARALLEL to the set of polygon contours, this
53 returns a pointer to the polygon located at that position (if it is closer
54 than thickness/2) or NULL if there is none.
55
56 TODO: use sorted vector to improve
57
58 DO NOT STORE THE RETURNED POINTER!
59 */
60 const DicomStructurePolygon2* GetPolygonClosestToSlice(const CoordinateSystem3D& plane) const;
61
62 Vector GetNormal() const;
63
64 Color GetColor() const
65 {
66 return Color(red_, green_, blue_);
67 }
68
69 bool IsValid() const
70 {
71 return state_ == Valid;
72 }
73
74 /**
75 This method is used to project the 3D structure on a 2D plane.
76
77 A structure is a stack of polygons, representing a volume.
78
79 We need to compute the intersection between this volume and the supplied
80 cutting plane (the "slice"). This is more than a cutting plane: it is also
81 a 2D-coordinate system (the plane has axes vectors)
82
83 The cutting plane is always parallel to the plane defined by two of the
84 world coordinate system axes.
85
86 The result is a set of closed polygons.
87
88 If the cut is parallel to the polygons, we pick the polygon closest to
89 the slice, project it on the slice and return it in slice coordinates.
90
91 If the cut is perpendicular to the polygons, for each polygon, we compute
92 the intersection between the cutting plane and the polygon slab (imaginary
93 volume created by extruding the polygon above and below its plane by
94 thickness/2) :
95 - each slab, intersected by the plane, gives a set of 0..* rectangles \
96 (only one if the polygon is convex)
97 - when doing this for the whole stack of slabs, we get a set of rectangles:
98 To compute these rectangles, for each polygon, we compute the intersection
99 between :
100 - the line defined by the intersection of the polygon plane and the cutting
101 plane
102 - the polygon itself
103 This yields 0 or 2*K points along the line C. These are turned into K
104 rectangles by taking two consecutive points along the line and extruding
105 this segment by sliceThickness/2 in the orientation of the polygon normal,
106 in both directions.
107
108 Then, once this list of rectangles is computed, we need to group the
109 connected rectangles together. Connected, here, means sharing at least part
110 of an edge --> union/find data structures and algorithm.
111 */
112 bool Project(std::vector< std::pair<Point2D, Point2D> >& polygons, const CoordinateSystem3D& plane) const;
113
114 std::string interpretation_;
115 std::string name_;
116 uint8_t red_;
117 uint8_t green_;
118 uint8_t blue_;
119
120 /** Internal */
121 const std::vector<DicomStructurePolygon2>& GetPolygons() const
122 {
123 return polygons_;
124 }
125
126 /** Internal */
127 double GetSliceThickness() const
128 {
129 return sliceThickness_;
130 }
131
132 private:
133 enum State
134 {
135 Building,
136 NormalComputed,
137 Valid, // When normal components AND slice thickness are computed
138 Invalid
139 };
140
141 void ComputeNormal();
142 void ComputeSliceThickness();
143
144 std::vector<DicomStructurePolygon2> polygons_;
145 Vector3D normal_;
146 double sliceThickness_;
147
148 /*
149 After creation (and while polygons are added), state is Building.
150 After ComputeDependentProperties() is called, state can either be
151 Valid or Invalid. In any case, the object becomes immutable.
152 */
153 State state_;
154 };
155 }