@@ -10,8 +10,10 @@ Outline <- R6Class("Outline",
10
10
# #' @field P A N-by-2 matrix of points of the \code{Outline}
11
11
# #' arranged in anticlockwise order
12
12
P = NULL ,
13
- # #' @field scale The length of one unit of \code{P} in arbitrary units
14
- scale = NULL ,
13
+ # #' @field scale The length of one unit of \code{P} in the X-Y plane in arbitrary units
14
+ scale = NULL ,
15
+ # #' @field scalez The length of one unit of \code{P} in the Z-direction in arbitrary units
16
+ scalez = NULL ,
15
17
# #' @field units String giving units of scaled P, e.g. \dQuote{um}
16
18
units = NA ,
17
19
# #' @field gf For each row of \code{P}, the index of \code{P} that
@@ -25,19 +27,32 @@ Outline <- R6Class("Outline",
25
27
h = NULL ,
26
28
# #' @field im An image as a \code{raster} object
27
29
im = NULL ,
30
+ # #' @field dm Depthmap, with same dimensions as \code{im}, which indicates
31
+ # #' height of each pixel in Z-direction
32
+ dm = NULL ,
28
33
# #' @field A.fragments Areas of fragments
29
34
A.fragments = NULL ,
30
35
# #' @description Construct an outline object. This sanitises the
31
36
# #' input points \code{P}.
32
37
# #' @param fragments A list of N-by-2 matrix of points for each fragment of the \code{Outline}
33
38
# #' @param scale The length of one unit of \code{P} in arbitrary units
34
39
# #' @param im The image as a \code{raster} object
40
+ # #' @param scalez The length of one unit of \code{P} in the Z-direction in arbitrary units. If \code{NA}, the depthmap is ignored.
41
+ # #' @param dm Depthmap, with same dimensions as \code{im}, which indicates
42
+ # #' height of each pixel in Z-direction
35
43
# #' @param units String giving units of scaled P, e.g. \dQuote{um}
36
- initialize = function (fragments = list (), scale = NA , im = NULL , units = NA ) {
37
- self $ P <- matrix (0 , 0 , 3 )
38
- colnames(self $ P ) <- c(" X" , " Y" , " FID" )
44
+ initialize = function (fragments = list (), scale = NA , im = NULL , scalez = NA , dm = NULL , units = NA ) {
45
+ self $ P <- matrix (0 , 0 , 4 )
46
+ colnames(self $ P ) <- c(" X" , " Y" , " Z" , " FID" )
47
+ if (! is.null(dm ) & ! is.null(im )) {
48
+ if (all(dim(im ) != dim(dm ))) {
49
+ stop(" Image and depthmap must have the same dimensions" )
50
+ }
51
+ }
39
52
self $ im <- im
53
+ self $ dm <- dm
40
54
self $ scale <- scale
55
+ self $ scalez <- scalez
41
56
self $ units <- sub(units , " um" , " \U 00B5m" )
42
57
if (! is.list(fragments )) {
43
58
fragments <- list (fragments )
@@ -60,6 +75,9 @@ Outline <- R6Class("Outline",
60
75
# #' @description Image setter
61
76
# #' @param im An image as a \code{raster} object
62
77
replaceImage = function (im ) {
78
+ if (all(dim(im ) != dim(self $ dm ))) {
79
+ stop(" Image and depthmap must have the same dimensions" )
80
+ }
63
81
self $ im <- im
64
82
},
65
83
# #' @description Map the point IDs of a \link{Fragment} on the
@@ -84,22 +102,42 @@ Outline <- R6Class("Outline",
84
102
y [pids [nna ]] <- pids [x [nna ]]
85
103
return (y )
86
104
},
105
+ # #' @description Get depth of points P
106
+ # #' @param P matrix containing unscaled X-Y coordinates of points
107
+ # #' @return Vector of unscaled Z coordinates of points P
108
+ getDepth = function (P ) {
109
+ if (! is.null(self $ dm )) {
110
+ Z <- interpolate.image(self $ dm , P , invert.y = TRUE )
111
+ } else {
112
+ Z <- rep(0 , nrow(P ))
113
+ }
114
+ return (Z )
115
+ },
116
+
87
117
# #' @description Add points to the outline register of points
88
- # #' @param P 2 column matrix of points to add
89
- # #' @param fid fragment id of the points
118
+ # #' @param P 2 or 3 column matrix of points to add
119
+ # #' @param fid ID of fragment to which to add the points
90
120
# #' @return The ID of each added point in the register. If points already
91
121
# #' exist a point will not be created in the register,
92
122
# #' but an ID will be returned
93
123
addPoints = function (P , fid ) {
94
- if (! is.matrix(P )) {
95
- if (length(P ) == 2 ) {
124
+ if (! (is.vector(P ) | is.matrix(P ))) {
125
+ stop(" P must be a matrix with 2 or 3 columns, or a vector of length 2 or 3" )
126
+ }
127
+ if (is.vector(P )) {
128
+ if (length(P ) == 2 | length(P ) == 3 ) {
96
129
P <- matrix (P , nrow = 1 )
97
130
} else {
98
- stop(" P must be a matrix or vector of length 2" )
131
+ stop(" P should be vector of length 2 or 3" )
132
+ }
133
+ }
134
+ if (is.matrix(P )) {
135
+ if (! (ncol(P ) == 2 | ncol(P ) == 3 )) {
136
+ stop(" P should be a matrix with 2 or 3 columns" )
99
137
}
100
138
}
101
- if (! ( ncol(P ) == 2 ) ) {
102
- stop( " P must have 2 (X, Y) columns " )
139
+ if (ncol(P ) == 2 ) {
140
+ P <- cbind( P , self $ getDepth( P ) )
103
141
}
104
142
pids <- rep(NA , nrow(P ))
105
143
for (i in (1 : nrow(P ))) {
@@ -109,7 +147,7 @@ Outline <- R6Class("Outline",
109
147
self $ h [1 ] <- 1
110
148
} else {
111
149
# # Check point doesn't already exist
112
- id <- which(apply(t(self $ P [,- 3 ,drop = FALSE ]) == P [i ,], 2 , all ))
150
+ id <- which(apply(t(self $ P [,1 : 2 ,drop = FALSE ]) == P [i ,1 : 2 ], 2 , all ))
113
151
if (length(id ) > 1 ) {
114
152
stop(paste(" Point register has duplicates" , self $ P [id ,], collapse = " , " ))
115
153
}
@@ -139,7 +177,7 @@ Outline <- R6Class("Outline",
139
177
# #' @param fid fragment id of the points
140
178
# #' @return Vector of points
141
179
getFragmentPoints = function (fid ) {
142
- return (self $ P [self $ getFragmentPointIDs(fid ),c(" X" , " Y" )])
180
+ return (self $ P [self $ getFragmentPointIDs(fid ),c(" X" , " Y" , " Z " )])
143
181
},
144
182
# #' @description Get fragment
145
183
# #' @param fid Fragment ID
@@ -167,18 +205,36 @@ Outline <- R6Class("Outline",
167
205
return (unique(self $ P [," FID" ]))
168
206
},
169
207
# #' @description Get unscaled mesh points
208
+ # #' @param pids IDs of point to return
209
+ # #' @return Matrix with columns \code{X}, \code{Y} and \code{Z}
210
+ getPoints = function (pids = NULL ) {
211
+ if (! is.null(pids )) {
212
+ return (self $ P [pids ,c(" X" , " Y" , " Z" )])
213
+ }
214
+ return (self $ P [,c(" X" , " Y" , " Z" )])
215
+ },
216
+ # #' @description Get X-Y coordinates of unscaled mesh points
217
+ # #' @param pids IDs of point to return
170
218
# #' @return Matrix with columns \code{X} and \code{Y}
171
- getPoints = function () {
219
+ getPointsXY = function (pids = NULL ) {
220
+ if (! is.null(pids )) {
221
+ return (self $ P [pids ,c(" X" , " Y" )])
222
+ }
172
223
return (self $ P [,c(" X" , " Y" )])
173
224
},
174
225
# #' @description Get scaled mesh points
175
226
# #' @return Matrix with columns \code{X} and \code{Y} which is
176
227
# #' exactly \code{scale} times the matrix returned by \code{getPoints}
177
228
getPointsScaled = function () {
178
229
if (is.na(self $ scale )) {
179
- return (self $ P [,c(" X" , " Y" )])
230
+ return (self $ P [,c(" X" , " Y" , " Z" )])
231
+ }
232
+ if (is.na(self $ scalez )) {
233
+ return (cbind(self $ scale * self $ P [,c(" X" , " Y" )],
234
+ Z = 0 ))
180
235
}
181
- return (cbind(self $ scale * self $ P [,c(" X" , " Y" )]))
236
+ return (cbind(self $ scale * self $ P [,c(" X" , " Y" )],
237
+ Z = self $ scalez * self $ P [," Z" ]))
182
238
},
183
239
# #' @description Get set of points on rim
184
240
# #' @return Vector of point IDs, i.e. indices of the rows in
0 commit comments