1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.math4.legacy.analysis.interpolation;
18
19 import java.util.List;
20 import java.util.ArrayList;
21 import org.apache.commons.numbers.core.Norm;
22 import org.apache.commons.numbers.angle.CosAngle;
23 import org.apache.commons.rng.sampling.UnitSphereSampler;
24 import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
25 import org.apache.commons.math4.legacy.exception.NotPositiveException;
26 import org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException;
27 import org.apache.commons.math4.legacy.exception.MaxCountExceededException;
28 import org.apache.commons.math4.legacy.exception.OutOfRangeException;
29 import org.apache.commons.math4.core.jdkmath.JdkMath;
30 import org.apache.commons.math4.legacy.core.MathArrays;
31
32
33
34
35
36
37 public class InterpolatingMicrosphere {
38
39 private final List<Facet> microsphere;
40
41 private final List<FacetData> microsphereData;
42
43 private final int dimension;
44
45 private final int size;
46
47 private final double maxDarkFraction;
48
49 private final double darkThreshold;
50
51 private final double background;
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74 protected InterpolatingMicrosphere(int dimension,
75 int size,
76 double maxDarkFraction,
77 double darkThreshold,
78 double background) {
79 if (dimension <= 0) {
80 throw new NotStrictlyPositiveException(dimension);
81 }
82 if (size <= 0) {
83 throw new NotStrictlyPositiveException(size);
84 }
85 if (maxDarkFraction < 0 ||
86 maxDarkFraction > 1) {
87 throw new OutOfRangeException(maxDarkFraction, 0, 1);
88 }
89 if (darkThreshold < 0) {
90 throw new NotPositiveException(darkThreshold);
91 }
92
93 this.dimension = dimension;
94 this.size = size;
95 this.maxDarkFraction = maxDarkFraction;
96 this.darkThreshold = darkThreshold;
97 this.background = background;
98 microsphere = new ArrayList<>(size);
99 microsphereData = new ArrayList<>(size);
100 }
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124 public InterpolatingMicrosphere(int dimension,
125 int size,
126 double maxDarkFraction,
127 double darkThreshold,
128 double background,
129 UnitSphereSampler rand) {
130 this(dimension, size, maxDarkFraction, darkThreshold, background);
131
132
133
134 for (int i = 0; i < size; i++) {
135 add(rand.sample(), false);
136 }
137 }
138
139
140
141
142
143
144 protected InterpolatingMicrosphere(InterpolatingMicrosphere other) {
145 dimension = other.dimension;
146 size = other.size;
147 maxDarkFraction = other.maxDarkFraction;
148 darkThreshold = other.darkThreshold;
149 background = other.background;
150
151
152 microsphere = other.microsphere;
153
154
155 microsphereData = new ArrayList<>(size);
156 for (FacetData fd : other.microsphereData) {
157 microsphereData.add(new FacetData(fd.illumination(), fd.sample()));
158 }
159 }
160
161
162
163
164
165
166 public InterpolatingMicrosphere copy() {
167 return new InterpolatingMicrosphere(this);
168 }
169
170
171
172
173
174
175 public int getDimension() {
176 return dimension;
177 }
178
179
180
181
182
183
184 public int getSize() {
185 return size;
186 }
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208 public double value(double[] point,
209 double[][] samplePoints,
210 double[] sampleValues,
211 double exponent,
212 double noInterpolationTolerance) {
213 if (exponent < 0) {
214 throw new NotPositiveException(exponent);
215 }
216
217 clear();
218
219
220
221 final int numSamples = samplePoints.length;
222 for (int i = 0; i < numSamples; i++) {
223
224 final double[] diff = MathArrays.ebeSubtract(samplePoints[i], point);
225 final double diffNorm = Norm.L2.of(diff);
226
227 if (JdkMath.abs(diffNorm) < noInterpolationTolerance) {
228
229
230 return sampleValues[i];
231 }
232
233 final double weight = JdkMath.pow(diffNorm, -exponent);
234 illuminate(diff, sampleValues[i], weight);
235 }
236
237 return interpolate();
238 }
239
240
241
242
243
244
245
246
247
248
249
250
251 protected void add(double[] normal,
252 boolean copy) {
253 if (microsphere.size() >= size) {
254 throw new MaxCountExceededException(size);
255 }
256 if (normal.length > dimension) {
257 throw new DimensionMismatchException(normal.length, dimension);
258 }
259
260 microsphere.add(new Facet(copy ? normal.clone() : normal));
261 microsphereData.add(new FacetData(0d, 0d));
262 }
263
264
265
266
267
268
269
270 private double interpolate() {
271
272 int darkCount = 0;
273
274 double value = 0;
275 double totalWeight = 0;
276 for (FacetData fd : microsphereData) {
277 final double iV = fd.illumination();
278 if (iV != 0d) {
279 value += iV * fd.sample();
280 totalWeight += iV;
281 } else {
282 ++darkCount;
283 }
284 }
285
286 final double darkFraction = darkCount / (double) size;
287
288 return darkFraction <= maxDarkFraction ?
289 value / totalWeight :
290 background;
291 }
292
293
294
295
296
297
298
299
300
301 private void illuminate(double[] sampleDirection,
302 double sampleValue,
303 double weight) {
304 for (int i = 0; i < size; i++) {
305 final double[] n = microsphere.get(i).getNormal();
306 final double cos = CosAngle.value(n, sampleDirection);
307
308 if (cos > 0) {
309 final double illumination = cos * weight;
310
311 if (illumination > darkThreshold &&
312 illumination > microsphereData.get(i).illumination()) {
313 microsphereData.set(i, new FacetData(illumination, sampleValue));
314 }
315 }
316 }
317 }
318
319
320
321
322 private void clear() {
323 for (int i = 0; i < size; i++) {
324 microsphereData.set(i, new FacetData(0d, 0d));
325 }
326 }
327
328
329
330
331 private static final class Facet {
332
333 private final double[] normal;
334
335
336
337
338
339 Facet(double[] n) {
340 normal = n;
341 }
342
343
344
345
346
347
348 public double[] getNormal() {
349 return normal;
350 }
351 }
352
353
354
355
356 private static final class FacetData {
357
358 private final double illumination;
359
360 private final double sample;
361
362
363
364
365
366 FacetData(double illumination, double sample) {
367 this.illumination = illumination;
368 this.sample = sample;
369 }
370
371
372
373
374
375 public double illumination() {
376 return illumination;
377 }
378
379
380
381
382
383 public double sample() {
384 return sample;
385 }
386 }
387 }