001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.math3.analysis.interpolation; 018 019import java.util.ArrayList; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Map; 023 024import org.apache.commons.math3.analysis.MultivariateFunction; 025import org.apache.commons.math3.exception.DimensionMismatchException; 026import org.apache.commons.math3.exception.NoDataException; 027import org.apache.commons.math3.exception.NullArgumentException; 028import org.apache.commons.math3.linear.ArrayRealVector; 029import org.apache.commons.math3.linear.RealVector; 030import org.apache.commons.math3.random.UnitSphereRandomVectorGenerator; 031import org.apache.commons.math3.util.FastMath; 032 033/** 034 * Interpolating function that implements the 035 * <a href="http://www.dudziak.com/microsphere.php">Microsphere Projection</a>. 036 * 037 * @deprecated Code will be removed in 4.0. Use {@link InterpolatingMicrosphere} 038 * and {@link MicrosphereProjectionInterpolator} instead. 039 */ 040@Deprecated 041public class MicrosphereInterpolatingFunction 042 implements MultivariateFunction { 043 /** 044 * Space dimension. 045 */ 046 private final int dimension; 047 /** 048 * Internal accounting data for the interpolation algorithm. 049 * Each element of the list corresponds to one surface element of 050 * the microsphere. 051 */ 052 private final List<MicrosphereSurfaceElement> microsphere; 053 /** 054 * Exponent used in the power law that computes the weights of the 055 * sample data. 056 */ 057 private final double brightnessExponent; 058 /** 059 * Sample data. 060 */ 061 private final Map<RealVector, Double> samples; 062 063 /** 064 * Class for storing the accounting data needed to perform the 065 * microsphere projection. 066 */ 067 private static class MicrosphereSurfaceElement { 068 /** Normal vector characterizing a surface element. */ 069 private final RealVector normal; 070 /** Illumination received from the brightest sample. */ 071 private double brightestIllumination; 072 /** Brightest sample. */ 073 private Map.Entry<RealVector, Double> brightestSample; 074 075 /** 076 * @param n Normal vector characterizing a surface element 077 * of the microsphere. 078 */ 079 MicrosphereSurfaceElement(double[] n) { 080 normal = new ArrayRealVector(n); 081 } 082 083 /** 084 * Return the normal vector. 085 * @return the normal vector 086 */ 087 RealVector normal() { 088 return normal; 089 } 090 091 /** 092 * Reset "illumination" and "sampleIndex". 093 */ 094 void reset() { 095 brightestIllumination = 0; 096 brightestSample = null; 097 } 098 099 /** 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}