Mercurial > hg > orthanc-stone
annotate Framework/Toolbox/DicomStructureSet.cpp @ 118:a4d0b6c82b29 wasm
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 02 Oct 2017 14:31:26 +0200 |
parents | 2eca030792aa |
children | e66b2c757790 |
rev | line source |
---|---|
0 | 1 /** |
2 * Stone of Orthanc | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
40
7207a407bcd8
shared copyright with osimis
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
35
diff
changeset
|
5 * Copyright (C) 2017 Osimis, Belgium |
0 | 6 * |
7 * This program is free software: you can redistribute it and/or | |
47 | 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. | |
0 | 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 | |
47 | 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 | |
0 | 18 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 **/ | |
20 | |
21 | |
22 #include "DicomStructureSet.h" | |
23 | |
35 | 24 #include "../Toolbox/MessagingToolbox.h" |
0 | 25 |
113
2eca030792aa
using the Orthanc Framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
110
diff
changeset
|
26 #include <Core/Logging.h> |
2eca030792aa
using the Orthanc Framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
110
diff
changeset
|
27 #include <Core/OrthancException.h> |
2eca030792aa
using the Orthanc Framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
110
diff
changeset
|
28 #include <Plugins/Samples/Common/FullOrthancDataset.h> |
118
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
29 #include <Plugins/Samples/Common/DicomDatasetReader.h> |
113
2eca030792aa
using the Orthanc Framework
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
110
diff
changeset
|
30 |
0 | 31 #include <stdio.h> |
32 #include <boost/lexical_cast.hpp> | |
33 | |
34 namespace OrthancStone | |
35 { | |
32 | 36 static const OrthancPlugins::DicomTag DICOM_TAG_CONTOUR_GEOMETRIC_TYPE(0x3006, 0x0042); |
37 static const OrthancPlugins::DicomTag DICOM_TAG_CONTOUR_IMAGE_SEQUENCE(0x3006, 0x0016); | |
38 static const OrthancPlugins::DicomTag DICOM_TAG_CONTOUR_SEQUENCE(0x3006, 0x0040); | |
39 static const OrthancPlugins::DicomTag DICOM_TAG_NUMBER_OF_CONTOUR_POINTS(0x3006, 0x0046); | |
40 static const OrthancPlugins::DicomTag DICOM_TAG_REFERENCED_SOP_INSTANCE_UID(0x0008, 0x1155); | |
41 static const OrthancPlugins::DicomTag DICOM_TAG_ROI_CONTOUR_SEQUENCE(0x3006, 0x0039); | |
42 static const OrthancPlugins::DicomTag DICOM_TAG_ROI_DISPLAY_COLOR(0x3006, 0x002a); | |
43 static const OrthancPlugins::DicomTag DICOM_TAG_ROI_NAME(0x3006, 0x0026); | |
44 static const OrthancPlugins::DicomTag DICOM_TAG_RT_ROI_INTERPRETED_TYPE(0x3006, 0x00a4); | |
45 static const OrthancPlugins::DicomTag DICOM_TAG_RT_ROI_OBSERVATIONS_SEQUENCE(0x3006, 0x0080); | |
46 static const OrthancPlugins::DicomTag DICOM_TAG_STRUCTURE_SET_ROI_SEQUENCE(0x3006, 0x0020); | |
0 | 47 |
48 | |
49 static uint8_t ConvertColor(double v) | |
50 { | |
51 if (v < 0) | |
52 { | |
53 return 0; | |
54 } | |
55 else if (v >= 255) | |
56 { | |
57 return 255; | |
58 } | |
59 else | |
60 { | |
61 return static_cast<uint8_t>(v); | |
62 } | |
63 } | |
64 | |
65 | |
118
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
66 static bool ParseVector(Vector& target, |
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
67 const OrthancPlugins::IDicomDataset& dataset, |
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
68 const OrthancPlugins::DicomPath& tag) |
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
69 { |
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
70 std::string value; |
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
71 return (dataset.GetStringValue(value, tag) && |
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
72 GeometryToolbox::ParseVector(target, value)); |
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
73 } |
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
74 |
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
75 CoordinateSystem3D DicomStructureSet:: |
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
76 ExtractSliceGeometry(double& sliceThickness, |
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
77 OrthancPlugins::IOrthancConnection& orthanc, |
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
78 const OrthancPlugins::IDicomDataset& tags, |
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
79 size_t contourIndex, |
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
80 size_t sliceIndex) |
0 | 81 { |
32 | 82 using namespace OrthancPlugins; |
0 | 83 |
32 | 84 size_t size; |
85 if (!tags.GetSequenceSize(size, DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, contourIndex, | |
86 DICOM_TAG_CONTOUR_SEQUENCE, sliceIndex, | |
87 DICOM_TAG_CONTOUR_IMAGE_SEQUENCE)) || | |
88 size != 1) | |
0 | 89 { |
90 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
91 } | |
92 | |
32 | 93 DicomDatasetReader reader(tags); |
118
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
94 std::string parentUid = reader.GetMandatoryStringValue |
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
95 (DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, contourIndex, |
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
96 DICOM_TAG_CONTOUR_SEQUENCE, sliceIndex, |
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
97 DICOM_TAG_CONTOUR_IMAGE_SEQUENCE, 0, |
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
98 DICOM_TAG_REFERENCED_SOP_INSTANCE_UID)); |
0 | 99 |
34 | 100 Json::Value parentLookup; |
101 MessagingToolbox::RestApiPost(parentLookup, orthanc, "/tools/lookup", parentUid); | |
0 | 102 |
34 | 103 if (parentLookup.type() != Json::arrayValue || |
104 parentLookup.size() != 1 || | |
105 !parentLookup[0].isMember("Type") || | |
106 !parentLookup[0].isMember("Path") || | |
107 parentLookup[0]["Type"].type() != Json::stringValue || | |
108 parentLookup[0]["ID"].type() != Json::stringValue || | |
109 parentLookup[0]["Type"].asString() != "Instance") | |
0 | 110 { |
111 throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); | |
112 } | |
113 | |
114 Json::Value parentInstance; | |
34 | 115 MessagingToolbox::RestApiGet(parentInstance, orthanc, "/instances/" + parentLookup[0]["ID"].asString()); |
0 | 116 |
117 if (parentInstance.type() != Json::objectValue || | |
118 !parentInstance.isMember("ParentSeries") || | |
119 parentInstance["ParentSeries"].type() != Json::stringValue) | |
120 { | |
121 throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); | |
122 } | |
123 | |
124 std::string parentSeriesId = parentInstance["ParentSeries"].asString(); | |
125 bool isFirst = parentSeriesId_.empty(); | |
126 | |
127 if (isFirst) | |
128 { | |
129 parentSeriesId_ = parentSeriesId; | |
130 } | |
131 else if (parentSeriesId_ != parentSeriesId) | |
132 { | |
133 LOG(ERROR) << "This RT-STRUCT refers to several different series"; | |
134 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
135 } | |
136 | |
34 | 137 FullOrthancDataset parentTags(orthanc, "/instances/" + parentLookup[0]["ID"].asString() + "/tags"); |
110
53025eecbc95
renamed SliceGeometry as CoordinateSystem3D
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
47
diff
changeset
|
138 CoordinateSystem3D slice(parentTags); |
0 | 139 |
32 | 140 Vector v; |
118
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
141 if (ParseVector(v, parentTags, DICOM_TAG_SLICE_THICKNESS) && |
32 | 142 v.size() > 0) |
0 | 143 { |
32 | 144 sliceThickness = v[0]; |
145 } | |
146 else | |
147 { | |
148 sliceThickness = 1; // 1 mm by default | |
0 | 149 } |
150 | |
151 if (isFirst) | |
152 { | |
153 normal_ = slice.GetNormal(); | |
154 } | |
155 else if (!GeometryToolbox::IsParallel(normal_, slice.GetNormal())) | |
156 { | |
157 LOG(ERROR) << "Incompatible orientation of slices in this RT-STRUCT"; | |
158 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
159 } | |
160 | |
161 return slice; | |
162 } | |
163 | |
164 | |
165 const DicomStructureSet::Structure& DicomStructureSet::GetStructure(size_t index) const | |
166 { | |
167 if (index >= structures_.size()) | |
168 { | |
169 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); | |
170 } | |
171 | |
172 return structures_[index]; | |
173 } | |
174 | |
175 | |
176 bool DicomStructureSet::IsPolygonOnSlice(const Polygon& polygon, | |
110
53025eecbc95
renamed SliceGeometry as CoordinateSystem3D
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
47
diff
changeset
|
177 const CoordinateSystem3D& geometry) const |
0 | 178 { |
179 double d = boost::numeric::ublas::inner_prod(geometry.GetOrigin(), normal_); | |
180 | |
181 return (GeometryToolbox::IsNear(d, polygon.projectionAlongNormal_, polygon.sliceThickness_ / 2.0) && | |
182 !polygon.points_.empty()); | |
183 } | |
184 | |
185 | |
31
9aace933cb64
sharing code with the Orthanc core
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
16
diff
changeset
|
186 DicomStructureSet::DicomStructureSet(OrthancPlugins::IOrthancConnection& orthanc, |
0 | 187 const std::string& instanceId) |
188 { | |
32 | 189 using namespace OrthancPlugins; |
0 | 190 |
32 | 191 FullOrthancDataset tags(orthanc, "/instances/" + instanceId + "/tags"); |
192 DicomDatasetReader reader(tags); | |
193 | |
194 size_t count, tmp; | |
195 if (!tags.GetSequenceSize(count, DICOM_TAG_RT_ROI_OBSERVATIONS_SEQUENCE) || | |
196 !tags.GetSequenceSize(tmp, DICOM_TAG_ROI_CONTOUR_SEQUENCE) || | |
197 tmp != count || | |
198 !tags.GetSequenceSize(tmp, DICOM_TAG_STRUCTURE_SET_ROI_SEQUENCE) || | |
199 tmp != count) | |
0 | 200 { |
201 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
202 } | |
203 | |
32 | 204 structures_.resize(count); |
205 for (size_t i = 0; i < count; i++) | |
206 { | |
207 structures_[i].interpretation_ = reader.GetStringValue(DicomPath(DICOM_TAG_RT_ROI_OBSERVATIONS_SEQUENCE, i, | |
208 DICOM_TAG_RT_ROI_INTERPRETED_TYPE), | |
209 "No interpretation"); | |
0 | 210 |
32 | 211 structures_[i].name_ = reader.GetStringValue(DicomPath(DICOM_TAG_STRUCTURE_SET_ROI_SEQUENCE, i, |
212 DICOM_TAG_ROI_NAME), | |
213 "No interpretation"); | |
0 | 214 |
215 Vector color; | |
118
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
216 if (ParseVector(color, tags, DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, |
a4d0b6c82b29
using Orthanc::DicomMap instead of OrthancPlugins::DicomDatasetReader
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
113
diff
changeset
|
217 DICOM_TAG_ROI_DISPLAY_COLOR)) && |
32 | 218 color.size() == 3) |
0 | 219 { |
32 | 220 structures_[i].red_ = ConvertColor(color[0]); |
221 structures_[i].green_ = ConvertColor(color[1]); | |
222 structures_[i].blue_ = ConvertColor(color[2]); | |
223 } | |
224 else | |
225 { | |
226 structures_[i].red_ = 255; | |
227 structures_[i].green_ = 0; | |
228 structures_[i].blue_ = 0; | |
0 | 229 } |
230 | |
32 | 231 size_t countSlices; |
232 if (!tags.GetSequenceSize(countSlices, DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, | |
233 DICOM_TAG_CONTOUR_SEQUENCE))) | |
234 { | |
235 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
236 } | |
0 | 237 |
238 LOG(WARNING) << "New RT structure: \"" << structures_[i].name_ | |
239 << "\" with interpretation \"" << structures_[i].interpretation_ | |
32 | 240 << "\" containing " << countSlices << " slices (color: " |
0 | 241 << static_cast<int>(structures_[i].red_) << "," |
242 << static_cast<int>(structures_[i].green_) << "," | |
243 << static_cast<int>(structures_[i].blue_) << ")"; | |
244 | |
32 | 245 for (size_t j = 0; j < countSlices; j++) |
0 | 246 { |
32 | 247 unsigned int countPoints; |
0 | 248 |
32 | 249 if (!reader.GetUnsignedIntegerValue(countPoints, DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, |
250 DICOM_TAG_CONTOUR_SEQUENCE, j, | |
251 DICOM_TAG_NUMBER_OF_CONTOUR_POINTS))) | |
252 { | |
253 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
254 } | |
255 | |
256 LOG(INFO) << "Parsing slice containing " << countPoints << " vertices"; | |
0 | 257 |
32 | 258 std::string type = reader.GetMandatoryStringValue(DicomPath(DICOM_TAG_ROI_CONTOUR_SEQUENCE, i, |
259 DICOM_TAG_CONTOUR_SEQUENCE, j, | |
260 DICOM_TAG_CONTOUR_GEOMETRIC_TYPE)); | |
261 if (type != "CLOSED_PLANAR") | |
0 | 262 { |
32 | 263 LOG(ERROR) << "Cannot handle contour with geometry type: " << type; |
0 | 264 throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); |
265 } | |
266 | |
32 | 267 // The "CountourData" tag (3006,0050) is too large to be |
268 // returned by the "/instances/{id}/tags" URI: Access it using | |
269 // the raw "/instances/{id}/content/{...}" endpoint | |
0 | 270 std::string slicesData; |
271 orthanc.RestApiGet(slicesData, "/instances/" + instanceId + "/content/3006-0039/" + | |
272 boost::lexical_cast<std::string>(i) + "/3006-0040/" + | |
273 boost::lexical_cast<std::string>(j) + "/3006-0050"); | |
274 | |
275 Vector points; | |
276 if (!GeometryToolbox::ParseVector(points, slicesData) || | |
32 | 277 points.size() != 3 * countPoints) |
0 | 278 { |
279 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
280 } | |
281 | |
282 Polygon polygon; | |
110
53025eecbc95
renamed SliceGeometry as CoordinateSystem3D
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
47
diff
changeset
|
283 CoordinateSystem3D geometry = ExtractSliceGeometry(polygon.sliceThickness_, orthanc, tags, i, j); |
0 | 284 polygon.projectionAlongNormal_ = geometry.ProjectAlongNormal(geometry.GetOrigin()); |
285 | |
32 | 286 for (size_t k = 0; k < countPoints; k++) |
0 | 287 { |
288 Vector v(3); | |
289 v[0] = points[3 * k]; | |
290 v[1] = points[3 * k + 1]; | |
291 v[2] = points[3 * k + 2]; | |
292 | |
293 if (!GeometryToolbox::IsNear(geometry.ProjectAlongNormal(v), | |
294 polygon.projectionAlongNormal_, | |
295 polygon.sliceThickness_ / 2.0 /* in mm */)) | |
296 { | |
297 LOG(ERROR) << "This RT-STRUCT contains a point that is off the slice of its instance"; | |
298 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
299 } | |
300 | |
301 polygon.points_.push_back(v); | |
302 } | |
303 | |
304 structures_[i].polygons_.push_back(polygon); | |
305 } | |
306 } | |
307 | |
308 if (parentSeriesId_.empty() || | |
309 normal_.size() != 3) | |
310 { | |
311 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); | |
312 } | |
313 } | |
314 | |
315 | |
316 Vector DicomStructureSet::GetStructureCenter(size_t index) const | |
317 { | |
318 const Structure& structure = GetStructure(index); | |
319 | |
320 Vector center; | |
321 GeometryToolbox::AssignVector(center, 0, 0, 0); | |
322 if (structure.polygons_.empty()) | |
323 { | |
324 return center; | |
325 } | |
326 | |
327 double n = static_cast<double>(structure.polygons_.size()); | |
328 | |
329 for (Polygons::const_iterator polygon = structure.polygons_.begin(); | |
330 polygon != structure.polygons_.end(); ++polygon) | |
331 { | |
332 if (!polygon->points_.empty()) | |
333 { | |
334 center += polygon->points_.front() / n; | |
335 } | |
336 } | |
337 | |
338 return center; | |
339 } | |
340 | |
341 | |
342 const std::string& DicomStructureSet::GetStructureName(size_t index) const | |
343 { | |
344 return GetStructure(index).name_; | |
345 } | |
346 | |
347 | |
348 const std::string& DicomStructureSet::GetStructureInterpretation(size_t index) const | |
349 { | |
350 return GetStructure(index).interpretation_; | |
351 } | |
352 | |
353 | |
354 void DicomStructureSet::GetStructureColor(uint8_t& red, | |
355 uint8_t& green, | |
356 uint8_t& blue, | |
357 size_t index) const | |
358 { | |
359 const Structure& s = GetStructure(index); | |
360 red = s.red_; | |
361 green = s.green_; | |
362 blue = s.blue_; | |
363 } | |
364 | |
365 | |
366 void DicomStructureSet::Render(CairoContext& context, | |
110
53025eecbc95
renamed SliceGeometry as CoordinateSystem3D
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
47
diff
changeset
|
367 const CoordinateSystem3D& slice) const |
0 | 368 { |
369 cairo_t* cr = context.GetObject(); | |
370 | |
371 for (Structures::const_iterator structure = structures_.begin(); | |
372 structure != structures_.end(); ++structure) | |
373 { | |
374 for (Polygons::const_iterator polygon = structure->polygons_.begin(); | |
375 polygon != structure->polygons_.end(); ++polygon) | |
376 { | |
377 if (IsPolygonOnSlice(*polygon, slice)) | |
378 { | |
379 context.SetSourceColor(structure->red_, structure->green_, structure->blue_); | |
380 | |
381 Points::const_iterator p = polygon->points_.begin(); | |
382 | |
383 double x, y; | |
384 slice.ProjectPoint(x, y, *p); | |
385 cairo_move_to(cr, x, y); | |
386 ++p; | |
387 | |
388 while (p != polygon->points_.end()) | |
389 { | |
390 slice.ProjectPoint(x, y, *p); | |
391 cairo_line_to(cr, x, y); | |
392 ++p; | |
393 } | |
394 | |
395 slice.ProjectPoint(x, y, *polygon->points_.begin()); | |
396 cairo_line_to(cr, x, y); | |
397 } | |
398 } | |
399 } | |
400 | |
401 cairo_stroke(cr); | |
402 } | |
403 } |