1 /*=========================================================================
2
3 Program: GDCM (Grassroots DICOM). A DICOM library
4
5 Copyright (c) 2006-2011 Mathieu Malaterre
6 All rights reserved.
7 See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
8
9 This software is distributed WITHOUT ANY WARRANTY; without even
10 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11 PURPOSE. See the above copyright notice for more information.
12
13 =========================================================================*/
14 #include "gdcmSurfaceWriter.h"
15 #include "gdcmAttribute.h"
16 #include "gdcmUIDGenerator.h"
17
18 namespace gdcm
19 {
20
SurfaceWriter()21 SurfaceWriter::SurfaceWriter():
22 NumberOfSurfaces(0)
23 {
24 }
25
26 SurfaceWriter::~SurfaceWriter()
27 = default;
28
ComputeNumberOfSurfaces()29 void SurfaceWriter::ComputeNumberOfSurfaces()
30 {
31 std::vector< SmartPointer< Segment > >::const_iterator it = Segments.begin();
32 std::vector< SmartPointer< Segment > >::const_iterator itEnd = Segments.end();
33 for (; it != itEnd; it++)
34 {
35 NumberOfSurfaces += (*it)->GetSurfaceCount();
36 }
37 }
38
GetNumberOfSurfaces()39 unsigned long SurfaceWriter::GetNumberOfSurfaces()
40 {
41 if (NumberOfSurfaces == 0)
42 {
43 ComputeNumberOfSurfaces();
44 }
45
46 return NumberOfSurfaces;
47 }
48
SetNumberOfSurfaces(const unsigned long nb)49 void SurfaceWriter::SetNumberOfSurfaces(const unsigned long nb)
50 {
51 NumberOfSurfaces = nb;
52 }
53
PrepareWrite()54 bool SurfaceWriter::PrepareWrite()
55 {
56 if( !SegmentWriter::PrepareWrite() )
57 {
58 return false;
59 }
60
61 File & file = GetFile();
62 DataSet & ds = file.GetDataSet();
63
64 FileMetaInformation & fmi = file.GetHeader();
65 const TransferSyntax & ts = fmi.GetDataSetTransferSyntax();
66 assert( ts.IsExplicit() || ts.IsImplicit() );
67
68 // Number Of Surface
69 const unsigned long nbSurfaces = this->GetNumberOfSurfaces();
70 if (nbSurfaces == 0)
71 {
72 gdcmErrorMacro( "No surface to write" );
73 return false;
74 }
75 Attribute<0x0066, 0x0001> numberOfSurfaces;
76 numberOfSurfaces.SetValue( (unsigned int)nbSurfaces );
77 ds.Replace( numberOfSurfaces.GetAsDataElement() );
78
79 // Surface Sequence
80 SmartPointer<SequenceOfItems> surfacesSQ;
81 if( !ds.FindDataElement( Tag(0x0066, 0x0002) ) )
82 {
83 surfacesSQ = new SequenceOfItems;
84 DataElement detmp( Tag(0x0066, 0x0002) );
85 detmp.SetVR( VR::SQ );
86 detmp.SetValue( *surfacesSQ );
87 detmp.SetVLToUndefined();
88 ds.Insert( detmp );
89 }
90 surfacesSQ = ds.GetDataElement( Tag(0x0066, 0x0002) ).GetValueAsSQ();
91 surfacesSQ->SetLengthToUndefined();
92
93 // Fill the Surface Sequence
94 {
95 const size_t nbItems = surfacesSQ->GetNumberOfItems();
96 if (nbItems < nbSurfaces)
97 {
98 const size_t diff = nbSurfaces - nbItems;
99 const size_t nbOfItemToMake = (diff > 0?diff:0);
100 for(unsigned int i = 1; i <= nbOfItemToMake; ++i)
101 {
102 Item item;
103 item.SetVLToUndefined();
104 surfacesSQ->AddItem(item);
105 }
106 }
107 }
108 // else Should I remove items?
109
110 std::vector< SmartPointer< Segment > > segments = this->GetSegments();
111 std::vector< SmartPointer< Segment > >::const_iterator it0 = segments.begin();
112 std::vector< SmartPointer< Segment > >::const_iterator it0End = segments.end();
113 unsigned int numSegment= 1;
114 unsigned int numSurface= 1;
115 for (; it0 != it0End; it0++)
116 {
117 SmartPointer< Segment > segment = *it0;
118 assert( segment );
119
120 std::vector< SmartPointer< Surface > > surfaces = segment->GetSurfaces();
121 std::vector< SmartPointer< Surface > >::const_iterator it1 = surfaces.begin();
122 std::vector< SmartPointer< Surface > >::const_iterator it1End = surfaces.end();
123 for (; it1 != it1End; it1++)
124 {
125 SmartPointer< Surface > surface = *it1;
126 assert( surface );
127
128 Item & surfaceItem = surfacesSQ->GetItem( numSurface );
129 DataSet & surfaceDS = surfaceItem.GetNestedDataSet();
130
131 // Recommended Display Grayscale Value
132 Attribute<0x0062, 0x000C> recommendedDisplayGrayscaleValue;
133 recommendedDisplayGrayscaleValue.SetValue( surface->GetRecommendedDisplayGrayscaleValue() );
134 surfaceDS.Replace( recommendedDisplayGrayscaleValue.GetAsDataElement() );
135
136 // Recommended Display CIELab Value
137 Attribute<0x0062, 0x000D> recommendedDisplayCIELabValue;
138 recommendedDisplayCIELabValue.SetValues( surface->GetRecommendedDisplayCIELabValue(), 3 );
139 surfaceDS.Replace( recommendedDisplayCIELabValue.GetAsDataElement() );
140
141 // Surface Number (Type 1)
142 Attribute<0x0066, 0x0003> surfaceNumberAt;
143 unsigned long surfaceNumber = surface->GetSurfaceNumber();
144 if (surfaceNumber == 0)
145 surfaceNumber = numSurface;
146 surfaceNumberAt.SetValue( (unsigned int)surfaceNumber );
147 surfaceDS.Replace( surfaceNumberAt.GetAsDataElement() );
148
149 // Surface Comments (Type 3)
150 const char * surfaceComments = surface->GetSurfaceComments();
151 if (strcmp(surfaceComments, "") != 0)
152 {
153 Attribute<0x0066, 0x0004> surfaceCommentsAt;
154 surfaceCommentsAt.SetValue( surfaceComments );
155 surfaceDS.Replace( surfaceCommentsAt.GetAsDataElement() );
156 }
157
158 // Surface Processing
159 const bool surfaceProcessing = surface->GetSurfaceProcessing();
160 Attribute<0x0066, 0x0009> surfaceProcessingAt;
161 surfaceProcessingAt.SetValue( (surfaceProcessing ? "YES" : "NO") );
162 surfaceDS.Replace( surfaceProcessingAt.GetAsDataElement() );
163
164 if (surfaceProcessing)
165 {
166 Attribute<0x0066, 0x000A> surfaceProcessingRatioAt;
167 surfaceProcessingRatioAt.SetValue( surface->GetSurfaceProcessingRatio() );
168 surfaceDS.Replace( surfaceProcessingRatioAt.GetAsDataElement() );
169
170 const char * surfaceProcessingDescription = surface->GetSurfaceProcessingDescription();
171 if (strcmp(surfaceProcessingDescription, "") != 0)
172 {
173 Attribute<0x0066, 0x000B> surfaceProcessingDescriptionAt;
174 surfaceProcessingDescriptionAt.SetValue( surfaceProcessingDescription );
175 surfaceDS.Replace( surfaceProcessingDescriptionAt.GetAsDataElement() );
176 }
177
178 //***** Surface Processing Algorithm Identification Sequence *****//
179 {
180 SmartPointer<SequenceOfItems> processingAlgoIdSQ;
181 const Tag processingAlgoIdTag(0x0066, 0x0035);
182 if( !surfaceDS.FindDataElement( processingAlgoIdTag ) )
183 {
184 processingAlgoIdSQ = new SequenceOfItems;
185 DataElement detmp( processingAlgoIdTag );
186 detmp.SetVR( VR::SQ );
187 detmp.SetValue( *processingAlgoIdSQ );
188 detmp.SetVLToUndefined();
189 surfaceDS.Insert( detmp );
190 }
191 processingAlgoIdSQ = surfaceDS.GetDataElement( processingAlgoIdTag ).GetValueAsSQ();
192 processingAlgoIdSQ->SetLengthToUndefined();
193
194 if (processingAlgoIdSQ->GetNumberOfItems() < 1) // One item shall be permitted
195 {
196 Item item;
197 item.SetVLToUndefined();
198 processingAlgoIdSQ->AddItem(item);
199 }
200
201 Item & processingAlgoIdItem = processingAlgoIdSQ->GetItem(1);
202 DataSet & processingAlgoIdDS = processingAlgoIdItem.GetNestedDataSet();
203
204 //***** Algorithm Family Code Sequence *****//
205 //See: PS.3.3 Table 8.8-1 and PS 3.16 Context ID 7162
206 {
207 const SegmentHelper::BasicCodedEntry & processingAlgo = surface->GetProcessingAlgorithm();
208 if (processingAlgo.IsEmpty())
209 {
210 gdcmWarningMacro("Surface processing algorithm family not specified or incomplete");
211 }
212
213 SmartPointer<SequenceOfItems> algoFamilyCodeSQ;
214 const Tag algoFamilyCodeTag(0x0066, 0x002F);
215 if( !processingAlgoIdDS.FindDataElement( algoFamilyCodeTag ) )
216 {
217 algoFamilyCodeSQ = new SequenceOfItems;
218 DataElement detmp( algoFamilyCodeTag );
219 detmp.SetVR( VR::SQ );
220 detmp.SetValue( *algoFamilyCodeSQ );
221 detmp.SetVLToUndefined();
222 processingAlgoIdDS.Insert( detmp );
223 }
224 algoFamilyCodeSQ = processingAlgoIdDS.GetDataElement( algoFamilyCodeTag ).GetValueAsSQ();
225 algoFamilyCodeSQ->SetLengthToUndefined();
226
227 // Fill the Algorithm Family Code Sequence
228 if (algoFamilyCodeSQ->GetNumberOfItems() < 1)
229 {
230 Item item;
231 item.SetVLToUndefined();
232 algoFamilyCodeSQ->AddItem(item);
233 }
234
235 Item & algoFamilyCodeItem = algoFamilyCodeSQ->GetItem(1);
236 DataSet & algoFamilyCodeDS = algoFamilyCodeItem.GetNestedDataSet();
237
238 //***** CODE SEQUENCE MACRO ATTRIBUTES *****//
239 {
240 // Code Value (Type 1)
241 Attribute<0x0008, 0x0100> codeValueAt;
242 codeValueAt.SetValue( processingAlgo.CV );
243 algoFamilyCodeDS.Replace( codeValueAt.GetAsDataElement() );
244
245 // Coding Scheme (Type 1)
246 Attribute<0x0008, 0x0102> codingSchemeAt;
247 codingSchemeAt.SetValue( processingAlgo.CSD );
248 algoFamilyCodeDS.Replace( codingSchemeAt.GetAsDataElement() );
249
250 // Code Meaning (Type 1)
251 Attribute<0x0008, 0x0104> codeMeaningAt;
252 codeMeaningAt.SetValue( processingAlgo.CM );
253 algoFamilyCodeDS.Replace( codeMeaningAt.GetAsDataElement() );
254 }
255 }
256 }
257 }
258
259 // Presentation Opacity
260 Attribute<0x0066, 0x000C> presentationOpacity;
261 presentationOpacity.SetValue( surface->GetRecommendedPresentationOpacity() );
262 surfaceDS.Replace( presentationOpacity.GetAsDataElement() );
263
264 // Presentation Type
265 Attribute<0x0066, 0x000D> presentationType;
266 const char * reconmmendedPresentationType = Surface::GetVIEWTypeString( surface->GetRecommendedPresentationType() );
267 if (reconmmendedPresentationType != nullptr)
268 presentationType.SetValue( reconmmendedPresentationType );
269 else
270 presentationType.SetValue( Surface::GetVIEWTypeString(Surface::SURFACE) ); // Is it the right thing to do?
271 surfaceDS.Replace( presentationType.GetAsDataElement() );
272
273 // Finite Volume
274 Attribute<0x0066, 0x000E> finiteVolumeAt;
275 Surface::STATES finiteVolume = surface->GetFiniteVolume();
276 if (finiteVolume == Surface::STATES_END)
277 finiteVolume = Surface::UNKNOWN;
278 finiteVolumeAt.SetValue( Surface::GetSTATESString( finiteVolume ) );
279 surfaceDS.Replace( finiteVolumeAt.GetAsDataElement() );
280
281 // Manifold
282 Attribute<0x0066, 0x0010> manifoldAt;
283 Surface::STATES manifold = surface->GetManifold();
284 if (manifold == Surface::STATES_END)
285 manifold = Surface::UNKNOWN;
286 manifoldAt.SetValue( Surface::GetSTATESString( manifold ) );
287 surfaceDS.Replace( manifoldAt.GetAsDataElement() );
288
289 //****** Surface Points *****//
290 // (0066,0011) SQ (Sequence with undefined length #=1) # u/l, 1 Surface Points Sequence
291 // (fffe,e000) na (Item with undefined length #=1) # u/l, 1 Item
292 // (0066,0015) UL # 0, 1 Number Of Surface Points
293 // (0066,0016) OW # 0, 1 Point Coordinates Data
294 // (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem
295 // (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem
296 if ( !PrepareWritePointMacro(surface, surfaceDS, ts) )
297 return false;
298
299 //****** Surface Points Normals *****//
300 // (0066,0012) SQ (Sequence with undefined length #=1) # u/l, 1 Surface Points Sequence
301 // (fffe,e000) na (Item with undefined length #=1) # u/l, 1 Item
302 // (0066,001e) UL # 0, 1 Number of Vectors
303 // (0066,001f) US # 0, 1 Vector Dimensionality
304 // (0066,0021) OF # 0, 1_n Vector Coordinate Data
305 // (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem
306 // (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem
307 const unsigned long numberofvectors = surface->GetNumberOfVectors();
308 SmartPointer< MeshPrimitive > meshPrimitive = surface->GetMeshPrimitive();
309 const MeshPrimitive::MPType primitiveType = meshPrimitive->GetPrimitiveType();
310 if (numberofvectors > 0
311 && primitiveType != MeshPrimitive::TRIANGLE_STRIP
312 && primitiveType != MeshPrimitive::TRIANGLE_FAN)
313 {
314 SmartPointer<SequenceOfItems> surfacePointsNormalsSQ;
315 if( !surfaceDS.FindDataElement( Tag(0x0066, 0x0012) ) )
316 {
317 surfacePointsNormalsSQ = new SequenceOfItems;
318 DataElement detmp( Tag(0x0066, 0x0012) );
319 detmp.SetVR( VR::SQ );
320 detmp.SetValue( *surfacePointsNormalsSQ );
321 detmp.SetVLToUndefined();
322 surfaceDS.Insert( detmp );
323 }
324 surfacePointsNormalsSQ = surfaceDS.GetDataElement( Tag(0x0066, 0x0012) ).GetValueAsSQ();
325 surfacePointsNormalsSQ->SetLengthToUndefined();
326
327 if (surfacePointsNormalsSQ->GetNumberOfItems() < 1) // One item shall be permitted
328 {
329 Item item;
330 item.SetVLToUndefined();
331 surfacePointsNormalsSQ->AddItem(item);
332 }
333
334 Item & surfacePointsNormalsItem = surfacePointsNormalsSQ->GetItem(1);
335 DataSet & surfacePointsNormalsDS = surfacePointsNormalsItem.GetNestedDataSet();
336
337 // Number of Vectors
338 Attribute<0x0066, 0x001E> numberOfVectors;
339 numberOfVectors.SetValue( (unsigned int)surface->GetNumberOfVectors() );
340 surfacePointsNormalsDS.Replace( numberOfVectors.GetAsDataElement() );
341
342 // Vector Dimensionality
343 Attribute<0x0066, 0x001F> vectorDimensionalityAt;
344 unsigned short vectorDimensionality = surface->GetVectorDimensionality();
345 assert( vectorDimensionality );
346 vectorDimensionalityAt.SetValue( vectorDimensionality );
347 surfacePointsNormalsDS.Replace( vectorDimensionalityAt.GetAsDataElement() );
348
349 // Vector Accuracy (Type 3)
350 Attribute<0x0066, 0x0020> vectorAccuracyAt;
351 const float * vectorAccuracy = surface->GetVectorAccuracy();
352 if (vectorAccuracy != nullptr)
353 {
354 vectorAccuracyAt.SetValues( vectorAccuracy, vectorDimensionality );
355 surfacePointsNormalsDS.Replace( vectorAccuracyAt.GetAsDataElement() );
356 }
357
358 // Vector Coordinate Data
359 DataElement vectorCoordDataDE( Tag(0x0066, 0x0021) );
360 vectorCoordDataDE.SetVR( VR::OF );
361 const Value & vectorCoordinateDataValue = surface->GetVectorCoordinateData().GetValue();
362 assert( &vectorCoordinateDataValue );
363 vectorCoordDataDE.SetValue( vectorCoordinateDataValue );
364
365 const ByteValue *bv = vectorCoordDataDE.GetByteValue();
366 VL vl;
367 if ( bv )
368 vl = bv->GetLength();
369 else
370 vl.SetToUndefined();
371 vectorCoordDataDE.SetVL( vl );
372
373 if ( ts.IsExplicit() )
374 vectorCoordDataDE.SetVR( VR::OF );
375
376 surfacePointsNormalsDS.Replace( vectorCoordDataDE );
377 }
378 else if (numberofvectors > 0)
379 {
380 gdcmWarningMacro("Triangle strip or fan have no surface points normals");
381 }
382
383 //****** Surface Mesh Primitives *****//
384 // Two exemples :
385 // (0066,0013) SQ (Sequence with undefined length #=1) # u/l, 1 Surface Mesh Primitives Sequence
386 // (fffe,e000) na (Item with undefined length #=1) # u/l, 1 Item
387 // (0066,0042) OL # 0, 1 Long Edge Point Index List
388 // (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem
389 // (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem
390 //
391 // OR
392 //
393 // (0066,0013) SQ (Sequence with undefined length #=1) # u/l, 1 Surface Mesh Primitives Sequence
394 // (fffe,e000) na (Item with undefined length #=1) # u/l, 1 Item
395 // (0066,0026) SQ (Sequence with undefined length #=1) # u/l, 1 // Triangle Strip Sequence
396 // (fffe,e000) na (Item with undefined length #=1) # u/l, 1 Item
397 // (0066,0040) OL # 0, 1 // Long Primitive Point Index List
398 // (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem
399 // ...
400 // (fffe,e000) na (Item with undefined length #=1) # u/l, 1 Item
401 // (0066,0040) OL # 0, 1 // Long Primitive Point Index List
402 // (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem
403 // (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem
404 // (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem
405 // (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem
406
407 // Surface Mesh Primitives Sequence
408 {
409 SmartPointer<SequenceOfItems> surfaceMeshPrimitivesSQ;
410 if( !surfaceDS.FindDataElement( Tag(0x0066, 0x0013) ) )
411 {
412 surfaceMeshPrimitivesSQ = new SequenceOfItems;
413 DataElement detmp( Tag(0x0066, 0x0013) );
414 detmp.SetVR( VR::SQ );
415 detmp.SetValue( *surfaceMeshPrimitivesSQ );
416 detmp.SetVLToUndefined();
417 surfaceDS.Insert( detmp );
418 }
419 surfaceMeshPrimitivesSQ = surfaceDS.GetDataElement( Tag(0x0066, 0x0013) ).GetValueAsSQ();
420 surfaceMeshPrimitivesSQ->SetLengthToUndefined();
421
422 if (surfaceMeshPrimitivesSQ->GetNumberOfItems() < 1) // One itme shall be permitted
423 {
424 Item item;
425 item.SetVLToUndefined();
426 surfaceMeshPrimitivesSQ->AddItem(item);
427 }
428
429 Item & surfaceMeshPrimitivesItem = surfaceMeshPrimitivesSQ->GetItem(1);
430 DataSet & surfaceMeshPrimitivesDS = surfaceMeshPrimitivesItem.GetNestedDataSet();
431
432 //***** Handle "Typed" Point Index List *****//
433 bool insertInSQ = false;
434
435 // Primitive Point Index List
436 Tag typedPrimitiveTag;
437 typedPrimitiveTag.SetGroup(0x0066);
438 DataSet & pointIndexListDS0 = surfaceMeshPrimitivesDS;
439
440 switch (primitiveType)
441 {
442 case MeshPrimitive::VERTEX:
443 // Long Vertex Point Index List
444 typedPrimitiveTag.SetElement(0x0043);
445 break;
446 case MeshPrimitive::EDGE:
447 // Long Edge Point Index List
448 typedPrimitiveTag.SetElement(0x0042);
449 break;
450 case MeshPrimitive::TRIANGLE:
451 // Long Triangle Point Index List
452 typedPrimitiveTag.SetElement(0x0041);
453 break;
454 case MeshPrimitive::TRIANGLE_STRIP:
455 case MeshPrimitive::TRIANGLE_FAN:
456 case MeshPrimitive::LINE:
457 case MeshPrimitive::FACET:
458 // Long Primitive Point Index List
459 typedPrimitiveTag.SetElement(0x0040);
460 insertInSQ = true;
461 break;
462 default:
463 gdcmErrorMacro( "Unknown surface mesh primitives type" );
464 return false;
465 }
466
467 if (insertInSQ)
468 {
469 // Handled "complex" mesh primitives
470
471 Tag typedSequenceTag;
472 typedSequenceTag.SetGroup(0x0066);
473 switch (primitiveType)
474 {
475 case MeshPrimitive::TRIANGLE_STRIP:
476 // Triangle Strip Sequence
477 typedSequenceTag.SetElement(0x0026);
478 break;
479 case MeshPrimitive::TRIANGLE_FAN:
480 // Triangle Fan Sequence
481 typedSequenceTag.SetElement(0x0027);
482 break;
483 case MeshPrimitive::LINE:
484 // Line Sequence
485 typedSequenceTag.SetElement(0x0028);
486 break;
487 case MeshPrimitive::FACET:
488 // Facet Sequence
489 typedSequenceTag.SetElement(0x0034);
490 break;
491 default:
492 gdcmErrorMacro( "Unknown surface mesh primitives type" );
493 return false;
494 }
495
496 // "Typed" Sequence
497 SmartPointer<SequenceOfItems> typedSequenceSQ;
498 if( !surfaceMeshPrimitivesDS.FindDataElement( typedSequenceTag ) )
499 {
500 typedSequenceSQ = new SequenceOfItems;
501 DataElement detmp( typedSequenceTag );
502 detmp.SetVR( VR::SQ );
503 detmp.SetValue( *typedSequenceSQ );
504 detmp.SetVLToUndefined();
505 surfaceMeshPrimitivesDS.Insert( detmp );
506 }
507 typedSequenceSQ = surfaceMeshPrimitivesDS.GetDataElement( typedSequenceTag ).GetValueAsSQ();
508 typedSequenceSQ->SetLengthToUndefined();
509
510 // Fill the Segment Sequence
511 const unsigned int numberOfPrimitives = meshPrimitive->GetNumberOfPrimitivesData();
512 assert( numberOfPrimitives );
513 const size_t nbItems = typedSequenceSQ->GetNumberOfItems();
514 if (nbItems < numberOfPrimitives)
515 {
516 const size_t diff = numberOfPrimitives - nbItems;
517 const size_t nbOfItemToMake = (diff > 0?diff:0);
518 for(unsigned int i = 1; i <= nbOfItemToMake; ++i)
519 {
520 Item item;
521 item.SetVLToUndefined();
522 typedSequenceSQ->AddItem(item);
523 }
524 }
525 // else Should I remove items?
526
527 const MeshPrimitive::PrimitivesData & primitivesData= meshPrimitive->GetPrimitivesData();
528 MeshPrimitive::PrimitivesData::const_iterator it = primitivesData.begin();
529 MeshPrimitive::PrimitivesData::const_iterator itEnd = primitivesData.end();
530 unsigned int i = 1;
531 for (; it != itEnd; it++)
532 {
533 Item & typedSequenceItem = typedSequenceSQ->GetItem(i++);
534 DataSet & pointIndexListDS = typedSequenceItem.GetNestedDataSet();
535
536 // "Typed" Point Index List
537 DataElement typedPointIndexListDE( typedPrimitiveTag );
538
539 const Value & pointIndexListValue = it->GetValue();
540 assert( &pointIndexListValue );
541 typedPointIndexListDE.SetValue( pointIndexListValue );
542
543 const ByteValue * pointIndexListBV = typedPointIndexListDE.GetByteValue();
544 VL pointIndexListVL;
545 if( pointIndexListBV )
546 {
547 pointIndexListVL = pointIndexListBV->GetLength();
548 }
549 else
550 {
551 pointIndexListVL.SetToUndefined();
552 }
553 typedPointIndexListDE.SetVL( pointIndexListVL );
554
555 if ( ts.IsExplicit() )
556 typedPointIndexListDE.SetVR( VR::OL );
557
558 pointIndexListDS.Replace( typedPointIndexListDE );
559 }
560 }
561 else
562 {
563 // Handled "simple" mesh primitives
564
565 // "Typed" Point Index List
566 DataElement typedPointIndexListDE( typedPrimitiveTag );
567
568 const Value & pointIndexListValue = meshPrimitive->GetPrimitiveData().GetValue();
569 assert( &pointIndexListValue );
570 typedPointIndexListDE.SetValue( pointIndexListValue );
571
572 const ByteValue * pointIndexListBV = typedPointIndexListDE.GetByteValue();
573 VL pointIndexListVL;
574 if ( pointIndexListBV )
575 pointIndexListVL = pointIndexListBV->GetLength();
576 else
577 pointIndexListVL.SetToUndefined();
578 typedPointIndexListDE.SetVL( pointIndexListVL );
579
580 if ( ts.IsExplicit() )
581 typedPointIndexListDE.SetVR( VR::OL );
582
583 pointIndexListDS0.Replace( typedPointIndexListDE );
584 }
585
586 //***** Add empty values in unused but required tags (Type 2) *****//
587
588 DataElement emptyOLDE;
589 emptyOLDE.SetVLToUndefined();
590 emptyOLDE.SetVR( VR::OL );
591 SmartPointer<ByteValue> emptyValueOW = new ByteValue;
592 emptyOLDE.SetValue(*emptyValueOW);
593
594 // Long Vertex Point Index List ( Type 2 )
595 Tag vertexPointIndexListTag(0x0066, 0x0043);
596 if( !surfaceMeshPrimitivesDS.FindDataElement( vertexPointIndexListTag ) )
597 {
598 emptyOLDE.SetTag( vertexPointIndexListTag );
599 surfaceMeshPrimitivesDS.Insert( emptyOLDE );
600 }
601
602 // Long Edge Point Index List ( Type 2 )
603 Tag edgePointIndexListTag(0x0066, 0x0042);
604 if( !surfaceMeshPrimitivesDS.FindDataElement( edgePointIndexListTag ) )
605 {
606 emptyOLDE.SetTag( edgePointIndexListTag );
607 surfaceMeshPrimitivesDS.Insert( emptyOLDE );
608 }
609
610 // Long Triangle Point Index List ( Type 2 )
611 Tag trianglePointIndexListTag(0x0066, 0x0041);
612 if( !surfaceMeshPrimitivesDS.FindDataElement( trianglePointIndexListTag ) )
613 {
614 emptyOLDE.SetTag( trianglePointIndexListTag );
615 surfaceMeshPrimitivesDS.Insert( emptyOLDE );
616 }
617
618 DataElement emptySQDE;
619 emptySQDE.SetVLToUndefined();
620 emptySQDE.SetVR( VR::SQ );
621 SmartPointer<SequenceOfItems> emptySequenceSQ = new SequenceOfItems;
622 emptySQDE.SetValue(*emptySequenceSQ);
623
624 // Triangle Strip Sequence ( Type 2 )
625 Tag triangleStripSequenceTag(0x0066, 0x0026);
626 if( !surfaceMeshPrimitivesDS.FindDataElement( triangleStripSequenceTag ) )
627 {
628 emptySQDE.SetTag( triangleStripSequenceTag );
629 surfaceMeshPrimitivesDS.Insert( emptySQDE );
630 }
631
632 // Triangle Fan Sequence ( Type 2 )
633 Tag triangleFanSequenceTag(0x0066, 0x0027);
634 if( !surfaceMeshPrimitivesDS.FindDataElement( triangleFanSequenceTag ) )
635 {
636 emptySQDE.SetTag( triangleFanSequenceTag );
637 surfaceMeshPrimitivesDS.Insert( emptySQDE );
638 }
639
640 // Line Sequence ( Type 2 )
641 Tag lineSequenceTag(0x0066, 0x0028);
642 if( !surfaceMeshPrimitivesDS.FindDataElement( lineSequenceTag ) )
643 {
644 emptySQDE.SetTag( lineSequenceTag );
645 surfaceMeshPrimitivesDS.Insert( emptySQDE );
646 }
647
648 // Facet Sequence ( Type 2 )
649 Tag facetSequenceTag(0x0066, 0x0034);
650 if( !surfaceMeshPrimitivesDS.FindDataElement( facetSequenceTag ) )
651 {
652 emptySQDE.SetTag( facetSequenceTag );
653 surfaceMeshPrimitivesDS.Insert( emptySQDE );
654 }
655
656 }
657 ++numSurface;
658 }
659
660
661 //***** Segment Sequence *****//
662 SmartPointer<SequenceOfItems> segmentsSQ = ds.GetDataElement( Tag(0x0062, 0x0002) ).GetValueAsSQ();
663 Item & segmentIt = segmentsSQ->GetItem( numSegment++ );
664 DataSet & segmentDS = segmentIt.GetNestedDataSet();
665
666 //***** Referenced Surface Sequence *****//
667 SmartPointer<SequenceOfItems> refSurfaceSQ = segmentDS.GetDataElement( Tag(0x0066, 0x002B) ).GetValueAsSQ();
668 SequenceOfItems::Iterator itRefSurface = refSurfaceSQ->Begin();
669 SequenceOfItems::Iterator itEndRefSurface = refSurfaceSQ->End();
670 unsigned int idxSurface = 0;
671 for (; itRefSurface != itEndRefSurface; itRefSurface++)
672 {
673 DataSet & refSurfaceDS = itRefSurface->GetNestedDataSet();
674 SmartPointer< Surface > surface = segment->GetSurface( idxSurface++ );
675
676 //***** Segment Surface Generation Algorithm Identification Sequence *****//
677 {
678 SmartPointer<SequenceOfItems> segmentsAlgoIdSQ;
679 const Tag segmentsAlgoIdTag(0x0066, 0x002D);
680 if( !refSurfaceDS.FindDataElement( segmentsAlgoIdTag ) )
681 {
682 segmentsAlgoIdSQ = new SequenceOfItems;
683 DataElement detmp( segmentsAlgoIdTag );
684 detmp.SetVR( VR::SQ );
685 detmp.SetValue( *segmentsAlgoIdSQ );
686 detmp.SetVLToUndefined();
687 refSurfaceDS.Insert( detmp );
688 }
689 segmentsAlgoIdSQ = refSurfaceDS.GetDataElement( segmentsAlgoIdTag ).GetValueAsSQ();
690 segmentsAlgoIdSQ->SetLengthToUndefined();
691
692 if (segmentsAlgoIdSQ->GetNumberOfItems() < 1)
693 {
694 Item item;
695 item.SetVLToUndefined();
696 segmentsAlgoIdSQ->AddItem(item);
697 }
698
699 Item & segmentsAlgoIdItem = segmentsAlgoIdSQ->GetItem(1);
700 DataSet & segmentsAlgoIdDS = segmentsAlgoIdItem.GetNestedDataSet();
701
702 //***** Algorithm Family Code Sequence *****//
703 //See: PS.3.3 Table 8.8-1 and PS 3.16 Context ID 7162
704 const SegmentHelper::BasicCodedEntry & algoFamily = surface->GetAlgorithmFamily();
705 if (algoFamily.IsEmpty())
706 {
707 gdcmWarningMacro("Segment surface generation algorithm family not specified or incomplete");
708 }
709
710 SmartPointer<SequenceOfItems> algoFamilyCodeSQ;
711 const Tag algoFamilyCodeTag(0x0066, 0x002F);
712
713 if( !segmentsAlgoIdDS.FindDataElement( algoFamilyCodeTag ) )
714 {
715 algoFamilyCodeSQ = new SequenceOfItems;
716 DataElement detmp( algoFamilyCodeTag );
717 detmp.SetVR( VR::SQ );
718 detmp.SetValue( *algoFamilyCodeSQ );
719 detmp.SetVLToUndefined();
720 segmentsAlgoIdDS.Insert( detmp );
721 }
722
723 algoFamilyCodeSQ = segmentsAlgoIdDS.GetDataElement( algoFamilyCodeTag ).GetValueAsSQ();
724 algoFamilyCodeSQ->SetLengthToUndefined();
725
726 // Fill the Algorithm Family Code Sequence
727 if (algoFamilyCodeSQ->GetNumberOfItems() < 1)
728 {
729 Item item;
730 item.SetVLToUndefined();
731 algoFamilyCodeSQ->AddItem(item);
732 }
733
734 Item & algoFamilyCodeItem = algoFamilyCodeSQ->GetItem(1);
735 DataSet & algoFamilyCodeDS = algoFamilyCodeItem.GetNestedDataSet();
736
737 //***** CODE SEQUENCE MACRO ATTRIBUTES *****//
738 {
739 // Code Value (Type 1)
740 Attribute<0x0008, 0x0100> codeValueAt;
741 codeValueAt.SetValue( algoFamily.CV );
742 algoFamilyCodeDS.Replace( codeValueAt.GetAsDataElement() );
743
744 // Coding Scheme (Type 1)
745 Attribute<0x0008, 0x0102> codingSchemeAt;
746 codingSchemeAt.SetValue( algoFamily.CSD );
747 algoFamilyCodeDS.Replace( codingSchemeAt.GetAsDataElement() );
748
749 // Code Meaning (Type 1)
750 Attribute<0x0008, 0x0104> codeMeaningAt;
751 codeMeaningAt.SetValue( algoFamily.CM );
752 algoFamilyCodeDS.Replace( codeMeaningAt.GetAsDataElement() );
753 }
754
755 // Algorithm Version
756 const char * algorithmVersion = surface->GetAlgorithmVersion();
757 if (strcmp(algorithmVersion, "") != 0)
758 {
759 gdcmWarningMacro("No algorithm version specified");
760 }
761 Attribute<0x0066, 0x0031> algorithmVersionAt;
762 algorithmVersionAt.SetValue( algorithmVersion );
763 segmentsAlgoIdDS.Replace( algorithmVersionAt.GetAsDataElement() );
764
765 // Algorithm Name
766 const char * algorithmName = surface->GetAlgorithmName();
767 if (strcmp(algorithmName, "") != 0)
768 {
769 gdcmWarningMacro("No algorithm name specified");
770 }
771 Attribute<0x0066, 0x0036> algorithmNameAt;
772 algorithmNameAt.SetValue( algorithmName );
773 segmentsAlgoIdDS.Replace( algorithmNameAt.GetAsDataElement() );
774 }
775 }
776
777 }
778
779 //** Complete the file **//
780 // Is SOP Class UID defined?
781 if( !ds.FindDataElement( Tag(0x0008, 0x0016) ) )
782 {
783 const char * SOPClassUID = MediaStorage::GetMSString(MediaStorage::SurfaceSegmentationStorage);
784
785 DataElement de( Tag(0x0008, 0x0016) );
786 VL::Type strlenSOPClassUID= (VL::Type)strlen(SOPClassUID);
787 de.SetByteValue( SOPClassUID, strlenSOPClassUID );
788 de.SetVR( Attribute<0x0008, 0x0016>::GetVR() );
789 ds.ReplaceEmpty( de );
790 }
791
792 // Is SOP Instance UID defined?
793 if( !ds.FindDataElement( Tag(0x0008, 0x0018) ) )
794 {
795 UIDGenerator UIDgen;
796 UIDgen.SetRoot( MediaStorage::GetMSString(MediaStorage::SurfaceSegmentationStorage) );
797 const char * SOPInstanceUID = UIDgen.Generate();
798
799 DataElement de( Tag(0x0008, 0x0018) );
800 VL::Type strlenSOPInstanceUID= (VL::Type)strlen(SOPInstanceUID);
801 de.SetByteValue( SOPInstanceUID, strlenSOPInstanceUID );
802 de.SetVR( Attribute<0x0008, 0x0018>::GetVR() );
803 ds.ReplaceEmpty( de );
804 }
805
806 fmi.Clear();
807 {
808 const char *tsuid = TransferSyntax::GetTSString( ts );
809 DataElement de( Tag(0x0002,0x0010) );
810 VL::Type strlenTSUID = (VL::Type)strlen(tsuid);
811 de.SetByteValue( tsuid, strlenTSUID );
812 de.SetVR( Attribute<0x0002, 0x0010>::GetVR() );
813 fmi.Replace( de );
814 fmi.SetDataSetTransferSyntax(ts);
815 }
816 fmi.FillFromDataSet( ds );
817
818 return true;
819 }
820
Write()821 bool SurfaceWriter::Write()
822 {
823 if( !PrepareWrite() )
824 {
825 return false;
826 }
827
828 assert( Stream );
829 if( !Writer::Write() )
830 {
831 return false;
832 }
833
834 return true;
835 }
836
PrepareWritePointMacro(SmartPointer<Surface> surface,DataSet & surfaceDS,const TransferSyntax & ts)837 bool SurfaceWriter::PrepareWritePointMacro(SmartPointer< Surface > surface,
838 DataSet & surfaceDS,
839 const TransferSyntax & ts)
840 {
841 //****** Surface Points *****//
842 // (0066,0011) SQ (Sequence with undefined length #=1) # u/l, 1 Surface Points Sequence
843 // (fffe,e000) na (Item with undefined length #=1) # u/l, 1 Item
844 // (0066,0015) UL # 0, 1 Number Of Surface Points
845 // (0066,0016) OW # 0, 1 Point Coordinates Data
846 // (fffe,e00d) na (ItemDelimitationItem) # 0, 0 ItemDelimitationItem
847 // (fffe,e0dd) na (SequenceDelimitationItem) # 0, 0 SequenceDelimitationItem
848
849 //***** Surface Points Sequence *****//
850 {
851 SmartPointer<SequenceOfItems> surfacePointsSq;
852 if( !surfaceDS.FindDataElement( Tag(0x0066, 0x0011) ) )
853 {
854 surfacePointsSq = new SequenceOfItems;
855 DataElement detmp( Tag(0x0066, 0x0011) );
856 detmp.SetVR( VR::SQ );
857 detmp.SetValue( *surfacePointsSq );
858 detmp.SetVLToUndefined();
859 surfaceDS.Insert( detmp );
860 }
861 surfacePointsSq = surfaceDS.GetDataElement( Tag(0x0066, 0x0011) ).GetValueAsSQ();
862 surfacePointsSq->SetLengthToUndefined();
863
864 if (surfacePointsSq->GetNumberOfItems() < 1) // One item shall be permitted
865 {
866 Item item;
867 item.SetVLToUndefined();
868 surfacePointsSq->AddItem(item);
869 }
870
871 Item & surfacePointsItem = surfacePointsSq->GetItem(1);
872 DataSet & surfacePointsDs = surfacePointsItem.GetNestedDataSet();
873
874 // Point Coordinates Data
875 DataElement pointCoordDataDE( Tag(0x0066, 0x0016) );
876 const Value & pointCoordinateDataValue = surface->GetPointCoordinatesData().GetValue();
877 assert( &pointCoordinateDataValue );
878 pointCoordDataDE.SetValue( pointCoordinateDataValue );
879
880 const ByteValue *bv = pointCoordDataDE.GetByteValue();
881 VL vl;
882 if ( bv )
883 vl = bv->GetLength();
884 else
885 vl.SetToUndefined();
886 pointCoordDataDE.SetVL( vl );
887
888 if ( ts.IsExplicit() )
889 pointCoordDataDE.SetVR( VR::OF );
890
891 surfacePointsDs.Replace( pointCoordDataDE );
892
893 // Number Of Surface Points
894 Attribute<0x0066, 0x0015> numberOfSurfacePointsAt;
895 unsigned long numberOfSurfacePoints = surface->GetNumberOfSurfacePoints();
896 if (numberOfSurfacePoints == 0)
897 numberOfSurfacePoints = bv->GetLength() / (VR::GetLength(VR::OF) * 3);
898 numberOfSurfacePointsAt.SetValue( (unsigned int)numberOfSurfacePoints );
899 surfacePointsDs.Replace( numberOfSurfacePointsAt.GetAsDataElement() );
900
901 // Point Position Accuracy (Type 3)
902 Attribute<0x0066, 0x0017> pointPositionAccuracyAt;
903 const float * pointPositionAccuracy = surface->GetPointPositionAccuracy();
904 if (pointPositionAccuracy != nullptr)
905 {
906 pointPositionAccuracyAt.SetValues( pointPositionAccuracy );
907 surfacePointsDs.Replace( pointPositionAccuracyAt.GetAsDataElement() );
908 }
909
910 // Mean Point Distance (Type 3)
911 Attribute<0x0066, 0x0018> meanPointDistanceAt;
912 float meanPointDistance = surface->GetMeanPointDistance();
913 if (meanPointDistance != 0) // FIXME: user can specified 0 value
914 {
915 meanPointDistanceAt.SetValue( meanPointDistance );
916 surfacePointsDs.Replace( meanPointDistanceAt.GetAsDataElement() );
917 }
918
919 // Maximum Point Distance (Type 3)
920 Attribute<0x0066, 0x0019> maximumPointDistanceAt;
921 float maximumPointDistance = surface->GetMaximumPointDistance();
922 if (maximumPointDistance != 0) // FIXME: user can specified 0 value
923 {
924 maximumPointDistanceAt.SetValue( maximumPointDistance );
925 surfacePointsDs.Replace( maximumPointDistanceAt.GetAsDataElement() );
926 }
927
928 // Point Bounding Box Coordinates (Type 3)
929 Attribute<0x0066, 0x001a> pointsBoundingBoxCoordinatesAt;
930 const float * pointsBoundingBoxCoordinates = surface->GetPointsBoundingBoxCoordinates();
931 if (pointsBoundingBoxCoordinates != nullptr)
932 {
933 pointsBoundingBoxCoordinatesAt.SetValues( pointsBoundingBoxCoordinates );
934 surfacePointsDs.Replace( pointsBoundingBoxCoordinatesAt.GetAsDataElement() );
935 }
936
937 // Axis of Rotation (Type 3)
938 Attribute<0x0066, 0x001b> axisOfRotationAt;
939 const float * axisOfRotation = surface->GetAxisOfRotation();
940 if (axisOfRotation != nullptr)
941 {
942 axisOfRotationAt.SetValues( axisOfRotation );
943 surfacePointsDs.Replace( axisOfRotationAt.GetAsDataElement() );
944 }
945
946 // Center of Rotation (Type 3)
947 Attribute<0x0066, 0x001c> centerOfRotationAt;
948 const float * centerOfRotation = surface->GetCenterOfRotation();
949 if (centerOfRotation != nullptr)
950 {
951 centerOfRotationAt.SetValues( centerOfRotation );
952 surfacePointsDs.Replace( centerOfRotationAt.GetAsDataElement() );
953 }
954
955 return true;
956 }
957 }
958
959 }
960