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 */ 017 018package org.apache.commons.rng.sampling.shape; 019 020import org.apache.commons.rng.UniformRandomProvider; 021import org.apache.commons.rng.sampling.SharedStateObjectSampler; 022 023/** 024 * Generate points <a href="https://mathworld.wolfram.com/TrianglePointPicking.html"> 025 * uniformly distributed within a triangle</a>. 026 * 027 * <ul> 028 * <li> 029 * Uses the algorithm described in: 030 * <blockquote> 031 * Turk, G. <i>Generating random points in triangles</i>. Glassner, A. S. (ed) (1990).<br> 032 * <strong>Graphic Gems</strong> Academic Press, pp. 24-28. 033 * </blockquote> 034 * </li> 035 * </ul> 036 * 037 * <p>Sampling uses:</p> 038 * 039 * <ul> 040 * <li>{@link UniformRandomProvider#nextDouble()} 041 * </ul> 042 * 043 * @since 1.4 044 */ 045public abstract class TriangleSampler implements SharedStateObjectSampler<double[]> { 046 /** The dimension for 2D sampling. */ 047 private static final int TWO_D = 2; 048 /** The dimension for 3D sampling. */ 049 private static final int THREE_D = 3; 050 /** The source of randomness. */ 051 private final UniformRandomProvider rng; 052 053 // The following code defines a plane as the set of all points r: 054 // r = r0 + sv + tw 055 // where s and t range over all real numbers, v and w are given linearly independent 056 // vectors defining the plane, and r0 is an arbitrary (but fixed) point in the plane. 057 // 058 // Sampling from a triangle (a,b,c) is performed when: 059 // s and t are in [0, 1] and s+t <= 1; 060 // r0 is one triangle vertex (a); 061 // and v (b-a) and w (c-a) are vectors from the other two vertices to r0. 062 // 063 // For robustness with large value coordinates the point r is computed without 064 // the requirement to compute v and w which can overflow: 065 // 066 // a + s(b-a) + t(c-a) == a + sb - sa + tc - ta 067 // == a(1 - s - t) + sb + tc 068 // 069 // Assuming the uniform deviates are from the 2^53 dyadic rationals in [0, 1) if s+t <= 1 070 // then 1 - (s+t) is exact. Sampling is then done using: 071 // 072 // if (s + t <= 1): 073 // p = a(1 - (s + t)) + sb + tc 074 // else: 075 // p = a(1 - (1 - s) - (1 - t)) + (1 - s)b + (1 - t)c 076 // p = a(s - 1 + t) + (1 - s)b + (1 - t)c 077 // 078 // Note do not simplify (1 - (1 - s) - (1 - t)) to (s + t - 1) as s+t > 1 and has potential 079 // loss of a single bit of randomness due to rounding. An exact sum is s - 1 + t. 080 081 /** 082 * Sample uniformly from a triangle in 2D. This is an non-array based specialisation of 083 * {@link TriangleSamplerND} for performance. 084 */ 085 private static class TriangleSampler2D extends TriangleSampler { 086 /** The x component of vertex a. */ 087 private final double ax; 088 /** The y component of vertex a. */ 089 private final double ay; 090 /** The x component of vertex b. */ 091 private final double bx; 092 /** The y component of vertex b. */ 093 private final double by; 094 /** The x component of vertex c. */ 095 private final double cx; 096 /** The y component of vertex c. */ 097 private final double cy; 098 099 /** 100 * @param rng Source of randomness. 101 * @param a The first vertex. 102 * @param b The second vertex. 103 * @param c The third vertex. 104 */ 105 TriangleSampler2D(UniformRandomProvider rng, double[] a, double[] b, double[] c) { 106 super(rng); 107 ax = a[0]; 108 ay = a[1]; 109 bx = b[0]; 110 by = b[1]; 111 cx = c[0]; 112 cy = c[1]; 113 } 114 115 /** 116 * @param rng Generator of uniformly distributed random numbers 117 * @param source Source to copy. 118 */ 119 TriangleSampler2D(UniformRandomProvider rng, TriangleSampler2D source) { 120 super(rng); 121 ax = source.ax; 122 ay = source.ay; 123 bx = source.bx; 124 by = source.by; 125 cx = source.cx; 126 cy = source.cy; 127 } 128 129 @Override 130 public double[] createSample(double p1msmt, double s, double t) { 131 return new double[] {p1msmt * ax + s * bx + t * cx, 132 p1msmt * ay + s * by + t * cy}; 133 } 134 135 @Override 136 public TriangleSampler withUniformRandomProvider(UniformRandomProvider rng) { 137 return new TriangleSampler2D(rng, this); 138 } 139 } 140 141 /** 142 * Sample uniformly from a triangle in 3D. This is an non-array based specialisation of 143 * {@link TriangleSamplerND} for performance. 144 */ 145 private static class TriangleSampler3D extends TriangleSampler { 146 /** The x component of vertex a. */ 147 private final double ax; 148 /** The y component of vertex a. */ 149 private final double ay; 150 /** The z component of vertex a. */ 151 private final double az; 152 /** The x component of vertex b. */ 153 private final double bx; 154 /** The y component of vertex b. */ 155 private final double by; 156 /** The z component of vertex b. */ 157 private final double bz; 158 /** The x component of vertex c. */ 159 private final double cx; 160 /** The y component of vertex c. */ 161 private final double cy; 162 /** The z component of vertex c. */ 163 private final double cz; 164 165 /** 166 * @param rng Source of randomness. 167 * @param a The first vertex. 168 * @param b The second vertex. 169 * @param c The third vertex. 170 */ 171 TriangleSampler3D(UniformRandomProvider rng, double[] a, double[] b, double[] c) { 172 super(rng); 173 ax = a[0]; 174 ay = a[1]; 175 az = a[2]; 176 bx = b[0]; 177 by = b[1]; 178 bz = b[2]; 179 cx = c[0]; 180 cy = c[1]; 181 cz = c[2]; 182 } 183 184 /** 185 * @param rng Generator of uniformly distributed random numbers 186 * @param source Source to copy. 187 */ 188 TriangleSampler3D(UniformRandomProvider rng, TriangleSampler3D source) { 189 super(rng); 190 ax = source.ax; 191 ay = source.ay; 192 az = source.az; 193 bx = source.bx; 194 by = source.by; 195 bz = source.bz; 196 cx = source.cx; 197 cy = source.cy; 198 cz = source.cz; 199 } 200 201 @Override 202 public double[] createSample(double p1msmt, double s, double t) { 203 return new double[] {p1msmt * ax + s * bx + t * cx, 204 p1msmt * ay + s * by + t * cy, 205 p1msmt * az + s * bz + t * cz}; 206 } 207 208 @Override 209 public TriangleSampler withUniformRandomProvider(UniformRandomProvider rng) { 210 return new TriangleSampler3D(rng, this); 211 } 212 } 213 214 /** 215 * Sample uniformly from a triangle in ND. 216 */ 217 private static class TriangleSamplerND extends TriangleSampler { 218 /** The first vertex. */ 219 private final double[] a; 220 /** The second vertex. */ 221 private final double[] b; 222 /** The third vertex. */ 223 private final double[] c; 224 225 /** 226 * @param rng Source of randomness. 227 * @param a The first vertex. 228 * @param b The second vertex. 229 * @param c The third vertex. 230 */ 231 TriangleSamplerND(UniformRandomProvider rng, double[] a, double[] b, double[] c) { 232 super(rng); 233 // Defensive copy 234 this.a = a.clone(); 235 this.b = b.clone(); 236 this.c = c.clone(); 237 } 238 239 /** 240 * @param rng Generator of uniformly distributed random numbers 241 * @param source Source to copy. 242 */ 243 TriangleSamplerND(UniformRandomProvider rng, TriangleSamplerND source) { 244 super(rng); 245 // Shared state is immutable 246 a = source.a; 247 b = source.b; 248 c = source.c; 249 } 250 251 @Override 252 public double[] createSample(double p1msmt, double s, double t) { 253 final double[] x = new double[a.length]; 254 for (int i = 0; i < x.length; i++) { 255 x[i] = p1msmt * a[i] + s * b[i] + t * c[i]; 256 } 257 return x; 258 } 259 260 @Override 261 public TriangleSampler withUniformRandomProvider(UniformRandomProvider rng) { 262 return new TriangleSamplerND(rng, this); 263 } 264 } 265 266 /** 267 * @param rng Source of randomness. 268 */ 269 TriangleSampler(UniformRandomProvider rng) { 270 this.rng = rng; 271 } 272 273 /** 274 * @return a random Cartesian coordinate within the triangle. 275 */ 276 @Override 277 public double[] sample() { 278 final double s = rng.nextDouble(); 279 final double t = rng.nextDouble(); 280 final double spt = s + t; 281 if (spt > 1) { 282 // Transform: s1 = 1 - s; t1 = 1 - t. 283 // Compute: 1 - s1 - t1 284 // Do not assume (1 - (1-s) - (1-t)) is (s + t - 1), i.e. (spt - 1.0), 285 // to avoid loss of a random bit due to rounding when s + t > 1. 286 // An exact sum is (s - 1 + t). 287 return createSample(s - 1.0 + t, 1.0 - s, 1.0 - t); 288 } 289 // Here s + t is exact so can be subtracted to make 1 - s - t 290 return createSample(1.0 - spt, s, t); 291 } 292 293 /** 294 * Creates the sample given the random variates {@code s} and {@code t} in the 295 * interval {@code [0, 1]} and {@code s + t <= 1}. The sum {@code 1 - s - t} is provided. 296 * The sample can be obtained from the triangle abc using: 297 * <pre> 298 * p = a(1 - s - t) + sb + tc 299 * </pre> 300 * 301 * @param p1msmt plus 1 minus s minus t (1 - s - t) 302 * @param s the first variate s 303 * @param t the second variate t 304 * @return the sample 305 */ 306 protected abstract double[] createSample(double p1msmt, double s, double t); 307 308 /** {@inheritDoc} */ 309 // Redeclare the signature to return a TriangleSampler not a SharedStateObjectSampler<double[]> 310 @Override 311 public abstract TriangleSampler withUniformRandomProvider(UniformRandomProvider rng); 312 313 /** 314 * Create a triangle sampler with vertices {@code a}, {@code b} and {@code c}. 315 * Sampled points are uniformly distributed within the triangle. 316 * 317 * <p>Sampling is supported in dimensions of 2 or above. Samples will lie in the 318 * plane (2D Euclidean space) defined by using the three triangle vertices to 319 * create two vectors starting at a point in the plane and orientated in 320 * different directions along the plane. 321 * 322 * <p>No test for collinear points is performed. If the points are collinear the sampling 323 * distribution is undefined. 324 * 325 * @param rng Source of randomness. 326 * @param a The first vertex. 327 * @param b The second vertex. 328 * @param c The third vertex. 329 * @return the sampler 330 * @throws IllegalArgumentException If the vertices do not have the same 331 * dimension; the dimension is less than 2; or vertices have non-finite coordinates 332 */ 333 public static TriangleSampler of(UniformRandomProvider rng, 334 double[] a, 335 double[] b, 336 double[] c) { 337 final int dimension = a.length; 338 if (dimension != b.length || dimension != c.length) { 339 throw new IllegalArgumentException( 340 new StringBuilder("Mismatch of vertex dimensions: ").append(dimension).append(',') 341 .append(b.length).append(',') 342 .append(c.length).toString()); 343 } 344 // Detect non-finite vertices 345 Coordinates.requireFinite(a, "Vertex a"); 346 Coordinates.requireFinite(b, "Vertex b"); 347 Coordinates.requireFinite(c, "Vertex c"); 348 // Low dimension specialisations 349 if (dimension == TWO_D) { 350 return new TriangleSampler2D(rng, a, b, c); 351 } else if (dimension == THREE_D) { 352 return new TriangleSampler3D(rng, a, b, c); 353 } else if (dimension > THREE_D) { 354 return new TriangleSamplerND(rng, a, b, c); 355 } 356 // Less than 2D 357 throw new IllegalArgumentException( 358 "Unsupported dimension: " + dimension); 359 } 360}