Skip to content

Commit 0aa5e76

Browse files
authored
Merge pull request #9 from dcmjs-org/migrating-to-scoord3d
Migrating to scoord3d
2 parents 7027893 + 690320b commit 0aa5e76

File tree

6 files changed

+92
-79
lines changed

6 files changed

+92
-79
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,5 @@ typings/
6161
.next
6262
node_modules/
6363
build/
64-
yarn.lock
64+
yarn.lock
65+
.vscode/

src/api.js

Lines changed: 44 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -31,34 +31,34 @@ import {
3131
Polyline,
3232
Circle,
3333
Ellipse
34-
} from './scoord.js';
34+
} from './scoord3d.js';
3535

3636
import DICOMwebClient from 'dicomweb-client/build/dicomweb-client.js'
3737

3838

39-
function _geometry2Scoord(geometry) {
39+
function _geometry2Scoord3d(geometry) {
4040
const type = geometry.getType();
4141
if (type === 'Point') {
4242
let coordinates = geometry.getCoordinates();
43-
coordinates = _geometryCoordinates2scoordCoordinates(coordinates);
43+
coordinates = _geometryCoordinates2scoord3dCoordinates(coordinates);
4444
return new Point(coordinates);
4545
} else if (type === 'Polygon') {
4646
/*
4747
* The first linear ring of the array defines the outer-boundary (surface).
4848
* Each subsequent linear ring defines a hole in the surface.
4949
*/
5050
let coordinates = geometry.getCoordinates()[0].map(c => {
51-
return _geometryCoordinates2scoordCoordinates(c);
51+
return _geometryCoordinates2scoord3dCoordinates(c);
5252
});
5353
return new Polyline(coordinates);
5454
} else if (type === 'LineString') {
5555
let coordinates = geometry.getCoordinates().map(c => {
56-
return _geometryCoordinates2scoordCoordinates(c);
56+
return _geometryCoordinates2scoord3dCoordinates(c);
5757
});
5858
return new Polyline(coordinates);
5959
} else if (type === 'Circle') {
6060
// TODO: Circle may actually represent a Polyline
61-
let center = _geometryCoordinates2scoordCoordinates(geometry.getCenter());
61+
let center = _geometryCoordinates2scoord3dCoordinates(geometry.getCenter());
6262
let radius = geometry.getRadius();
6363
return new Circle(center, radius);
6464
} else {
@@ -68,41 +68,38 @@ function _geometry2Scoord(geometry) {
6868
}
6969

7070

71-
function _scoord2Geometry(scoord) {
72-
const type = scoord.graphicType;
73-
const data = scoord.graphicData;
71+
function _scoord3d2Geometry(scoord3d) {
72+
const type = scoord3d.graphicType;
73+
const data = scoord3d.graphicData;
74+
console.log(data)
7475
if (type === 'POINT') {
75-
let coordinates = _scoordCoordinates2geometryCoordinates(data);
76+
let coordinates = _scoord3dCoordinates2geometryCoordinates(data);
7677
return new PointGeometry(coordinates);
7778
} else if (type === 'POLYLINE') {
7879
const coordinates = data.map(d => {
79-
return _scoordCoordinates2geometryCoordinates(d);
80+
return _scoord3dCoordinates2geometryCoordinates(d);
8081
});
81-
let isClosed = (
82-
data[0][0] === data[data.length-1][0] &&
83-
data[0][1] === data[data.length-1][1]
84-
);
85-
if (isClosed) {
86-
// Polygon requires inner linear ring and an outer ring.
87-
return new PolygonGeometry([coordinates]);
88-
} else {
89-
return new LineStringGeometry(coordinates);
90-
}
82+
return new LineStringGeometry(coordinates);
83+
} else if(type === 'POLYGON'){
84+
const coordinates = data.map(d => {
85+
return _scoord3dCoordinates2geometryCoordinates(d);
86+
});
87+
return new PolygonGeometry([coordinates]);
9188
} else if (type === 'CIRCLE') {
92-
let center = _scoordCoordinates2geometryCoordinates(scoord.centerCoordinates);
93-
let radius = scoord.radius;
89+
let center = _scoord3dCoordinates2geometryCoordinates(scoord3d.centerCoordinates);
90+
let radius = scoord3d.radius;
9491
return new CircleGeometry(center, radius);
9592
} else {
9693
console.error(`unsupported graphic type "${type}"`)
9794
}
9895
}
9996

10097

101-
function _geometryCoordinates2scoordCoordinates(coordinates) {
98+
function _geometryCoordinates2scoord3dCoordinates(coordinates) {
10299
return [coordinates[0] + 1, -coordinates[1]]
103100
}
104101

105-
function _scoordCoordinates2geometryCoordinates(coordinates) {
102+
function _scoord3dCoordinates2geometryCoordinates(coordinates) {
106103
return [coordinates[0] - 1, -coordinates[1]]
107104
}
108105

@@ -120,17 +117,6 @@ function coordinateFormatFunction(coordinates, pyramid) {
120117
return(coordinates);
121118
}
122119

123-
function _getROIByFeature(feature, pyramid, coordinateSystem) {
124-
const geometry = feature.getGeometry();
125-
let scoord = _geometry2Scoord(geometry);
126-
if(pyramid !== undefined && coordinateSystem === 'mm'){
127-
scoord = coordinateFormatFunction(scoord.coordinates, pyramid);
128-
}
129-
const properties = feature.getProperties();
130-
delete properties["geometry"];
131-
return new ROI({ scoord, properties, coordinateSystem });
132-
}
133-
134120
const _usewebgl = Symbol('usewebgl');
135121
const _map = Symbol('map');
136122
const _features = Symbol('features');
@@ -723,13 +709,12 @@ class VLWholeSlideMicroscopyImageViewer {
723709
return this[_interaction].modify !== undefined;
724710
}
725711

726-
getAllROIs(coordinateSystem='totalPixelMatrix') {
727-
const features = this[_features];
712+
getAllROIs() {
728713
let rois = [];
729-
if (features !== undefined) {
730-
features.forEach(feature => {
731-
rois.push(_getROIByFeature(feature, this.pyramid , coordinateSystem));
732-
});
714+
if (this.numberOfROIs > 0) {
715+
for(let index = 0; index < this.numberOfROIs; index++){
716+
rois.push(this.getROI(index));
717+
}
733718
}
734719
return rois;
735720
}
@@ -738,33 +723,43 @@ class VLWholeSlideMicroscopyImageViewer {
738723
return this[_features].getLength();
739724
}
740725

741-
getROI(index, coordinateSystem='totalPixelMatrix') {
726+
getROI(index) {
742727
const feature = this[_features].item(index);
743728
let roi = {};
744-
if (feature !== undefined) {
745-
roi = _getROIByFeature(feature, this.pyramid, coordinateSystem);
729+
if (feature !== undefined) {
730+
const geometry = feature.getGeometry();
731+
let scoord3d = _geometry2Scoord3d(geometry);
732+
// This is to uniform the ROI format in an array of arrays. When it is a point the representation
733+
// is a single array with x and y coords
734+
if(scoord3d.coordinates.length === 2){
735+
scoord3d.coordinates = [scoord3d.coordinates]
736+
}
737+
scoord3d.coordinates.map(coord => {return coordinateFormatFunction(coord, this.pyramid)});
738+
const properties = feature.getProperties();
739+
delete properties['geometry'];
740+
return new ROI({scoord3d, properties});
746741
}
747742
return roi;
748743
}
749744

750745
popROI() {
751746
const feature = this[_features].pop();
752747
const geometry = feature.getGeometry()
753-
const scoord = _geometry2Scoord(geometry);
748+
const scoord3d = _geometry2Scoord3d(geometry);
754749
const properties = feature.getProperties();
755750
delete properties['geometry'];
756-
return new ROI({scoord, properties});
751+
return new ROI({scoord3d, properties});
757752
}
758753

759754
addROI(item) {
760-
const geometry = _scoord2Geometry(item.scoord);
755+
const geometry = _scoord3d2Geometry(item.scoord3d);
761756
const feature = new Feature(geometry);
762757
feature.setProperties(item.properties, true);
763758
this[_features].push(feature);
764759
}
765760

766761
updateROI(index, item) {
767-
const geometry = _scoord2Geometry(item.scoord);
762+
const geometry = _scoord3d2Geometry(item.scoord3d);
768763
const feature = new Feature(geometry);
769764
feature.setProperties(item.properties, true);
770765
this[_features].setAt(index, feature);

src/dicom-microscopy-viewer.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ import {
66
Polyline,
77
Circle,
88
Ellipse,
9-
} from './scoord.js';
9+
} from './scoord3d.js';
1010

1111
let api = {
1212
VLWholeSlideMicroscopyImageViewer,
1313
};
14-
let scoord = {
14+
let scoord3d = {
1515
Point,
1616
Multipoint,
1717
Polyline,
@@ -22,4 +22,4 @@ let roi = {
2222
ROI,
2323
}
2424

25-
export { api, scoord, roi };
25+
export { api, scoord3d, roi };

src/roi.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,15 @@
33
class ROI {
44

55
/* @constructor
6-
* @param{Scoord} scoord spatial coordinates
6+
* @param{Scoord3D} scoord3d spatial coordinates
77
* @param{Object} properties qualititative evaluations
88
*/
99
constructor(options) {
10-
if (!('scoord' in options)) {
10+
if (!('scoord3d' in options)) {
1111
console.error('spatial coordinates are required for ROI')
1212
}
13-
this.scoord = options.scoord;
13+
this.scoord3d = options.scoord3d;
1414
this.properties = options.properties ? options.properties : {};
15-
this.coordinateSystem = options.coordinateSystem;
1615
}
1716

1817
}

src/scoord.js renamed to src/scoord3d.js

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Spatial coordinates of a geometric region of interest (ROI) in the DICOM
33
* image coordinate system.
44
*/
5-
class Scoord {
5+
class Scoord3D {
66

77
constructor() {}
88

@@ -14,21 +14,17 @@ class Scoord {
1414
throw new Error('Propotype property "graphicType" must be implemented.');
1515
}
1616

17-
get pixelOriginInterpretation() {
18-
// FRAME or VOLUME
19-
/* TODO: Consider relative to frame instead of total pixel matrix.
20-
* This would complicate scenarios where graphics span multiple frames.
21-
*/
22-
return 'VOLUME';
17+
get referencedFrameOfReferenceUID() {
18+
throw new Error('Propotype property "referencedFrameOfReferenceUID" must be implemented.');
19+
}
20+
21+
get fiducialUID() {
22+
throw new Error('Propotype property "fiducialUID" must be implemented.');
2323
}
24-
25-
// get fiducialUID() {
26-
// }
2724

2825
}
2926

30-
31-
class Point extends Scoord {
27+
class Point extends Scoord3D {
3228

3329
constructor(coordinates) {
3430
super()
@@ -46,7 +42,7 @@ class Point extends Scoord {
4642
}
4743

4844

49-
class Multipoint extends Scoord {
45+
class Multipoint extends Scoord3D {
5046

5147
constructor(coordinates) {
5248
super()
@@ -64,7 +60,7 @@ class Multipoint extends Scoord {
6460
}
6561

6662

67-
class Polyline extends Scoord {
63+
class Polyline extends Scoord3D {
6864

6965
constructor(coordinates) {
7066
super()
@@ -88,8 +84,31 @@ class Polyline extends Scoord {
8884

8985
}
9086

87+
class Polygon extends Scoord3D {
88+
89+
constructor(coordinates) {
90+
super()
91+
this.coordinates = coordinates
92+
}
93+
94+
get graphicData() {
95+
/*
96+
* A polygon is defined a series of connected line segments with ordered vertices
97+
* denoted by (x,y,z) triplets, where the first and last vertices shall be the same
98+
* forming a polygon; the points shall be coplanar
99+
*/
100+
// TODO: sort coordinates, make sure that the first and last vertices are the same
101+
return this.coordinates;
102+
}
103+
104+
get graphicType() {
105+
return 'POLYGON';
106+
}
107+
108+
}
109+
91110

92-
class Circle extends Scoord {
111+
class Circle extends Scoord3D {
93112

94113
constructor(centerCoordinates, radius) {
95114
super()
@@ -115,7 +134,7 @@ class Circle extends Scoord {
115134
}
116135

117136

118-
class Ellipse extends Scoord {
137+
class Ellipse extends Scoord3D {
119138

120139
constructor(majorAxisEndpointCoordinates, minorAxisEndpointCoordinates) {
121140
super()
@@ -143,5 +162,5 @@ class Ellipse extends Scoord {
143162
}
144163

145164

146-
export { Point, Multipoint, Polyline, Circle, Ellipse };
165+
export { Point, Multipoint, Polyline, Polygon, Circle, Ellipse };
147166

test/api.spec.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
const chai = require('chai');
22
const assert = require('assert');
3-
const roi = require
43

54
chai.should();
65

@@ -35,15 +34,15 @@ describe('dicomMicroscopyViewer.api.VLWholeSlideMicroscopyImageViewer', ()=> {
3534
[20967.16433027939, 16899.356324511322],
3635
[18802.104222888596, 17666.917976278484]
3736
];
38-
const scoord = {
37+
const scoord3d = {
3938
coordinates: coordinates,
4039
graphicData : coordinates,
4140
graphicType: "POLYLINE"
4241
};
4342
let properties = {};
44-
const roi = new dicomMicroscopyViewer.roi.ROI({scoord, properties});
43+
const roi = new dicomMicroscopyViewer.roi.ROI({scoord3d, properties});
4544
viewer.addROI(roi);
46-
assert.deepEqual(viewer.getROI(0).coordinateSystem, 'totalPixelMatrix');
47-
assert.deepEqual(viewer.getROI(0).scoord.coordinates, coordinates);
45+
console.log(viewer.getROI(0))
46+
assert.deepEqual(viewer.getROI(0).scoord3d.coordinates, coordinates);
4847
})
4948
});

0 commit comments

Comments
 (0)