1 | /*
|
---|
2 | * Licensed to the Apache Software Foundation (ASF) under one or more
|
---|
3 | * contributor license agreements. See the NOTICE file distributed with
|
---|
4 | * this work for additional information regarding copyright ownership.
|
---|
5 | * The ASF licenses this file to You under the Apache License, Version 2.0
|
---|
6 | * (the "License"); you may not use this file except in compliance with
|
---|
7 | * the License. You may obtain a copy of the License at
|
---|
8 | *
|
---|
9 | * http://www.apache.org/licenses/LICENSE-2.0
|
---|
10 | *
|
---|
11 | * Unless required by applicable law or agreed to in writing, software
|
---|
12 | * distributed under the License is distributed on an "AS IS" BASIS,
|
---|
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
---|
14 | * See the License for the specific language governing permissions and
|
---|
15 | * limitations under the License.
|
---|
16 | */
|
---|
17 | package agents.anac.y2019.harddealer.math3.analysis.interpolation;
|
---|
18 |
|
---|
19 | import java.util.ArrayList;
|
---|
20 | import java.util.HashMap;
|
---|
21 | import java.util.List;
|
---|
22 | import java.util.Map;
|
---|
23 |
|
---|
24 | import agents.anac.y2019.harddealer.math3.analysis.MultivariateFunction;
|
---|
25 | import agents.anac.y2019.harddealer.math3.exception.DimensionMismatchException;
|
---|
26 | import agents.anac.y2019.harddealer.math3.exception.NoDataException;
|
---|
27 | import agents.anac.y2019.harddealer.math3.exception.NullArgumentException;
|
---|
28 | import agents.anac.y2019.harddealer.math3.linear.ArrayRealVector;
|
---|
29 | import agents.anac.y2019.harddealer.math3.linear.RealVector;
|
---|
30 | import agents.anac.y2019.harddealer.math3.random.UnitSphereRandomVectorGenerator;
|
---|
31 | import agents.anac.y2019.harddealer.math3.util.FastMath;
|
---|
32 |
|
---|
33 | /**
|
---|
34 | * Interpolating function that implements the
|
---|
35 | * <a href="http://www.dudziak.com/microsphere.php">Microsphere Projection</a>.
|
---|
36 | *
|
---|
37 | * @deprecated Code will be removed in 4.0. Use {@link InterpolatingMicrosphere}
|
---|
38 | * and {@link MicrosphereProjectionInterpolator} instead.
|
---|
39 | */
|
---|
40 | @Deprecated
|
---|
41 | public class MicrosphereInterpolatingFunction
|
---|
42 | implements MultivariateFunction {
|
---|
43 | /**
|
---|
44 | * Space dimension.
|
---|
45 | */
|
---|
46 | private final int dimension;
|
---|
47 | /**
|
---|
48 | * Internal accounting data for the interpolation algorithm.
|
---|
49 | * Each element of the list corresponds to one surface element of
|
---|
50 | * the microsphere.
|
---|
51 | */
|
---|
52 | private final List<MicrosphereSurfaceElement> microsphere;
|
---|
53 | /**
|
---|
54 | * Exponent used in the power law that computes the weights of the
|
---|
55 | * sample data.
|
---|
56 | */
|
---|
57 | private final double brightnessExponent;
|
---|
58 | /**
|
---|
59 | * Sample data.
|
---|
60 | */
|
---|
61 | private final Map<RealVector, Double> samples;
|
---|
62 |
|
---|
63 | /**
|
---|
64 | * Class for storing the accounting data needed to perform the
|
---|
65 | * microsphere projection.
|
---|
66 | */
|
---|
67 | private static class MicrosphereSurfaceElement {
|
---|
68 | /** Normal vector characterizing a surface element. */
|
---|
69 | private final RealVector normal;
|
---|
70 | /** Illumination received from the brightest sample. */
|
---|
71 | private double brightestIllumination;
|
---|
72 | /** Brightest sample. */
|
---|
73 | private Map.Entry<RealVector, Double> brightestSample;
|
---|
74 |
|
---|
75 | /**
|
---|
76 | * @param n Normal vector characterizing a surface element
|
---|
77 | * of the microsphere.
|
---|
78 | */
|
---|
79 | MicrosphereSurfaceElement(double[] n) {
|
---|
80 | normal = new ArrayRealVector(n);
|
---|
81 | }
|
---|
82 |
|
---|
83 | /**
|
---|
84 | * Return the normal vector.
|
---|
85 | * @return the normal vector
|
---|
86 | */
|
---|
87 | RealVector normal() {
|
---|
88 | return normal;
|
---|
89 | }
|
---|
90 |
|
---|
91 | /**
|
---|
92 | * Reset "illumination" and "sampleIndex".
|
---|
93 | */
|
---|
94 | void reset() {
|
---|
95 | brightestIllumination = 0;
|
---|
96 | brightestSample = null;
|
---|
97 | }
|
---|
98 |
|
---|
99 | /**
|
---|
100 | * Store the illumination and index of the brightest sample.
|
---|
101 | * @param illuminationFromSample illumination received from sample
|
---|
102 | * @param sample current sample illuminating the element
|
---|
103 | */
|
---|
104 | void store(final double illuminationFromSample,
|
---|
105 | final Map.Entry<RealVector, Double> sample) {
|
---|
106 | if (illuminationFromSample > this.brightestIllumination) {
|
---|
107 | this.brightestIllumination = illuminationFromSample;
|
---|
108 | this.brightestSample = sample;
|
---|
109 | }
|
---|
110 | }
|
---|
111 |
|
---|
112 | /**
|
---|
113 | * Get the illumination of the element.
|
---|
114 | * @return the illumination.
|
---|
115 | */
|
---|
116 | double illumination() {
|
---|
117 | return brightestIllumination;
|
---|
118 | }
|
---|
119 |
|
---|
120 | /**
|
---|
121 | * Get the sample illuminating the element the most.
|
---|
122 | * @return the sample.
|
---|
123 | */
|
---|
124 | Map.Entry<RealVector, Double> sample() {
|
---|
125 | return brightestSample;
|
---|
126 | }
|
---|
127 | }
|
---|
128 |
|
---|
129 | /**
|
---|
130 | * @param xval Arguments for the interpolation points.
|
---|
131 | * {@code xval[i][0]} is the first component of interpolation point
|
---|
132 | * {@code i}, {@code xval[i][1]} is the second component, and so on
|
---|
133 | * until {@code xval[i][d-1]}, the last component of that interpolation
|
---|
134 | * point (where {@code dimension} is thus the dimension of the sampled
|
---|
135 | * space).
|
---|
136 | * @param yval Values for the interpolation points.
|
---|
137 | * @param brightnessExponent Brightness dimming factor.
|
---|
138 | * @param microsphereElements Number of surface elements of the
|
---|
139 | * microsphere.
|
---|
140 | * @param rand Unit vector generator for creating the microsphere.
|
---|
141 | * @throws DimensionMismatchException if the lengths of {@code yval} and
|
---|
142 | * {@code xval} (equal to {@code n}, the number of interpolation points)
|
---|
143 | * do not match, or the the arrays {@code xval[0]} ... {@code xval[n]},
|
---|
144 | * have lengths different from {@code dimension}.
|
---|
145 | * @throws NoDataException if there an array has zero-length.
|
---|
146 | * @throws NullArgumentException if an argument is {@code null}.
|
---|
147 | */
|
---|
148 | public MicrosphereInterpolatingFunction(double[][] xval,
|
---|
149 | double[] yval,
|
---|
150 | int brightnessExponent,
|
---|
151 | int microsphereElements,
|
---|
152 | UnitSphereRandomVectorGenerator rand)
|
---|
153 | throws DimensionMismatchException,
|
---|
154 | NoDataException,
|
---|
155 | NullArgumentException {
|
---|
156 | if (xval == null ||
|
---|
157 | yval == null) {
|
---|
158 | throw new NullArgumentException();
|
---|
159 | }
|
---|
160 | if (xval.length == 0) {
|
---|
161 | throw new NoDataException();
|
---|
162 | }
|
---|
163 | if (xval.length != yval.length) {
|
---|
164 | throw new DimensionMismatchException(xval.length, yval.length);
|
---|
165 | }
|
---|
166 | if (xval[0] == null) {
|
---|
167 | throw new NullArgumentException();
|
---|
168 | }
|
---|
169 |
|
---|
170 | dimension = xval[0].length;
|
---|
171 | this.brightnessExponent = brightnessExponent;
|
---|
172 |
|
---|
173 | // Copy data samples.
|
---|
174 | samples = new HashMap<RealVector, Double>(yval.length);
|
---|
175 | for (int i = 0; i < xval.length; ++i) {
|
---|
176 | final double[] xvalI = xval[i];
|
---|
177 | if (xvalI == null) {
|
---|
178 | throw new NullArgumentException();
|
---|
179 | }
|
---|
180 | if (xvalI.length != dimension) {
|
---|
181 | throw new DimensionMismatchException(xvalI.length, dimension);
|
---|
182 | }
|
---|
183 |
|
---|
184 | samples.put(new ArrayRealVector(xvalI), yval[i]);
|
---|
185 | }
|
---|
186 |
|
---|
187 | microsphere = new ArrayList<MicrosphereSurfaceElement>(microsphereElements);
|
---|
188 | // Generate the microsphere, assuming that a fairly large number of
|
---|
189 | // randomly generated normals will represent a sphere.
|
---|
190 | for (int i = 0; i < microsphereElements; i++) {
|
---|
191 | microsphere.add(new MicrosphereSurfaceElement(rand.nextVector()));
|
---|
192 | }
|
---|
193 | }
|
---|
194 |
|
---|
195 | /**
|
---|
196 | * @param point Interpolation point.
|
---|
197 | * @return the interpolated value.
|
---|
198 | * @throws DimensionMismatchException if point dimension does not math sample
|
---|
199 | */
|
---|
200 | public double value(double[] point) throws DimensionMismatchException {
|
---|
201 | final RealVector p = new ArrayRealVector(point);
|
---|
202 |
|
---|
203 | // Reset.
|
---|
204 | for (MicrosphereSurfaceElement md : microsphere) {
|
---|
205 | md.reset();
|
---|
206 | }
|
---|
207 |
|
---|
208 | // Compute contribution of each sample points to the microsphere elements illumination
|
---|
209 | for (Map.Entry<RealVector, Double> sd : samples.entrySet()) {
|
---|
210 |
|
---|
211 | // Vector between interpolation point and current sample point.
|
---|
212 | final RealVector diff = sd.getKey().subtract(p);
|
---|
213 | final double diffNorm = diff.getNorm();
|
---|
214 |
|
---|
215 | if (FastMath.abs(diffNorm) < FastMath.ulp(1d)) {
|
---|
216 | // No need to interpolate, as the interpolation point is
|
---|
217 | // actually (very close to) one of the sampled points.
|
---|
218 | return sd.getValue();
|
---|
219 | }
|
---|
220 |
|
---|
221 | for (MicrosphereSurfaceElement md : microsphere) {
|
---|
222 | final double w = FastMath.pow(diffNorm, -brightnessExponent);
|
---|
223 | md.store(cosAngle(diff, md.normal()) * w, sd);
|
---|
224 | }
|
---|
225 |
|
---|
226 | }
|
---|
227 |
|
---|
228 | // Interpolation calculation.
|
---|
229 | double value = 0;
|
---|
230 | double totalWeight = 0;
|
---|
231 | for (MicrosphereSurfaceElement md : microsphere) {
|
---|
232 | final double iV = md.illumination();
|
---|
233 | final Map.Entry<RealVector, Double> sd = md.sample();
|
---|
234 | if (sd != null) {
|
---|
235 | value += iV * sd.getValue();
|
---|
236 | totalWeight += iV;
|
---|
237 | }
|
---|
238 | }
|
---|
239 |
|
---|
240 | return value / totalWeight;
|
---|
241 | }
|
---|
242 |
|
---|
243 | /**
|
---|
244 | * Compute the cosine of the angle between 2 vectors.
|
---|
245 | *
|
---|
246 | * @param v Vector.
|
---|
247 | * @param w Vector.
|
---|
248 | * @return the cosine of the angle between {@code v} and {@code w}.
|
---|
249 | */
|
---|
250 | private double cosAngle(final RealVector v, final RealVector w) {
|
---|
251 | return v.dotProduct(w) / (v.getNorm() * w.getNorm());
|
---|
252 | }
|
---|
253 | }
|
---|