TriangleSampler.java

  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 org.apache.commons.rng.sampling.shape;

  18. import org.apache.commons.rng.UniformRandomProvider;
  19. import org.apache.commons.rng.sampling.SharedStateObjectSampler;

  20. /**
  21.  * Generate points <a href="https://mathworld.wolfram.com/TrianglePointPicking.html">
  22.  * uniformly distributed within a triangle</a>.
  23.  *
  24.  * <ul>
  25.  *  <li>
  26.  *   Uses the algorithm described in:
  27.  *   <blockquote>
  28.  *    Turk, G. <i>Generating random points in triangles</i>. Glassner, A. S. (ed) (1990).<br>
  29.  *    <strong>Graphic Gems</strong> Academic Press, pp. 24-28.
  30.  *   </blockquote>
  31.  *  </li>
  32.  * </ul>
  33.  *
  34.  * <p>Sampling uses:</p>
  35.  *
  36.  * <ul>
  37.  *   <li>{@link UniformRandomProvider#nextDouble()}
  38.  * </ul>
  39.  *
  40.  * @since 1.4
  41.  */
  42. public abstract class TriangleSampler implements SharedStateObjectSampler<double[]> {
  43.     /** The dimension for 2D sampling. */
  44.     private static final int TWO_D = 2;
  45.     /** The dimension for 3D sampling. */
  46.     private static final int THREE_D = 3;
  47.     /** The source of randomness. */
  48.     private final UniformRandomProvider rng;

  49.     // The following code defines a plane as the set of all points r:
  50.     // r = r0 + sv + tw
  51.     // where s and t range over all real numbers, v and w are given linearly independent
  52.     // vectors defining the plane, and r0 is an arbitrary (but fixed) point in the plane.
  53.     //
  54.     // Sampling from a triangle (a,b,c) is performed when:
  55.     // s and t are in [0, 1] and s+t <= 1;
  56.     // r0 is one triangle vertex (a);
  57.     // and v (b-a) and w (c-a) are vectors from the other two vertices to r0.
  58.     //
  59.     // For robustness with large value coordinates the point r is computed without
  60.     // the requirement to compute v and w which can overflow:
  61.     //
  62.     // a + s(b-a) + t(c-a) == a + sb - sa + tc - ta
  63.     //                     == a(1 - s - t) + sb + tc
  64.     //
  65.     // Assuming the uniform deviates are from the 2^53 dyadic rationals in [0, 1) if s+t <= 1
  66.     // then 1 - (s+t) is exact. Sampling is then done using:
  67.     //
  68.     // if (s + t <= 1):
  69.     //    p = a(1 - (s + t)) + sb + tc
  70.     // else:
  71.     //    p = a(1 - (1 - s) - (1 - t)) + (1 - s)b + (1 - t)c
  72.     //    p = a(s - 1 + t) + (1 - s)b + (1 - t)c
  73.     //
  74.     // Note do not simplify (1 - (1 - s) - (1 - t)) to (s + t - 1) as s+t > 1 and has potential
  75.     // loss of a single bit of randomness due to rounding. An exact sum is s - 1 + t.

  76.     /**
  77.      * Sample uniformly from a triangle in 2D. This is an non-array based specialisation of
  78.      * {@link TriangleSamplerND} for performance.
  79.      */
  80.     private static final class TriangleSampler2D extends TriangleSampler {
  81.         /** The x component of vertex a. */
  82.         private final double ax;
  83.         /** The y component of vertex a. */
  84.         private final double ay;
  85.         /** The x component of vertex b. */
  86.         private final double bx;
  87.         /** The y component of vertex b. */
  88.         private final double by;
  89.         /** The x component of vertex c. */
  90.         private final double cx;
  91.         /** The y component of vertex c. */
  92.         private final double cy;

  93.         /**
  94.          * @param rng Source of randomness.
  95.          * @param a The first vertex.
  96.          * @param b The second vertex.
  97.          * @param c The third vertex.
  98.          */
  99.         TriangleSampler2D(UniformRandomProvider rng, double[] a, double[] b, double[] c) {
  100.             super(rng);
  101.             ax = a[0];
  102.             ay = a[1];
  103.             bx = b[0];
  104.             by = b[1];
  105.             cx = c[0];
  106.             cy = c[1];
  107.         }

  108.         /**
  109.          * @param rng Generator of uniformly distributed random numbers
  110.          * @param source Source to copy.
  111.          */
  112.         TriangleSampler2D(UniformRandomProvider rng, TriangleSampler2D source) {
  113.             super(rng);
  114.             ax = source.ax;
  115.             ay = source.ay;
  116.             bx = source.bx;
  117.             by = source.by;
  118.             cx = source.cx;
  119.             cy = source.cy;
  120.         }

  121.         @Override
  122.         public double[] createSample(double p1msmt, double s, double t) {
  123.             return new double[] {p1msmt * ax + s * bx + t * cx,
  124.                                  p1msmt * ay + s * by + t * cy};
  125.         }

  126.         @Override
  127.         public TriangleSampler withUniformRandomProvider(UniformRandomProvider rng) {
  128.             return new TriangleSampler2D(rng, this);
  129.         }
  130.     }

  131.     /**
  132.      * Sample uniformly from a triangle in 3D. This is an non-array based specialisation of
  133.      * {@link TriangleSamplerND} for performance.
  134.      */
  135.     private static final class TriangleSampler3D extends TriangleSampler {
  136.         /** The x component of vertex a. */
  137.         private final double ax;
  138.         /** The y component of vertex a. */
  139.         private final double ay;
  140.         /** The z component of vertex a. */
  141.         private final double az;
  142.         /** The x component of vertex b. */
  143.         private final double bx;
  144.         /** The y component of vertex b. */
  145.         private final double by;
  146.         /** The z component of vertex b. */
  147.         private final double bz;
  148.         /** The x component of vertex c. */
  149.         private final double cx;
  150.         /** The y component of vertex c. */
  151.         private final double cy;
  152.         /** The z component of vertex c. */
  153.         private final double cz;

  154.         /**
  155.          * @param rng Source of randomness.
  156.          * @param a The first vertex.
  157.          * @param b The second vertex.
  158.          * @param c The third vertex.
  159.          */
  160.         TriangleSampler3D(UniformRandomProvider rng, double[] a, double[] b, double[] c) {
  161.             super(rng);
  162.             ax = a[0];
  163.             ay = a[1];
  164.             az = a[2];
  165.             bx = b[0];
  166.             by = b[1];
  167.             bz = b[2];
  168.             cx = c[0];
  169.             cy = c[1];
  170.             cz = c[2];
  171.         }

  172.         /**
  173.          * @param rng Generator of uniformly distributed random numbers
  174.          * @param source Source to copy.
  175.          */
  176.         TriangleSampler3D(UniformRandomProvider rng, TriangleSampler3D source) {
  177.             super(rng);
  178.             ax = source.ax;
  179.             ay = source.ay;
  180.             az = source.az;
  181.             bx = source.bx;
  182.             by = source.by;
  183.             bz = source.bz;
  184.             cx = source.cx;
  185.             cy = source.cy;
  186.             cz = source.cz;
  187.         }

  188.         @Override
  189.         public double[] createSample(double p1msmt, double s, double t) {
  190.             return new double[] {p1msmt * ax + s * bx + t * cx,
  191.                                  p1msmt * ay + s * by + t * cy,
  192.                                  p1msmt * az + s * bz + t * cz};
  193.         }

  194.         @Override
  195.         public TriangleSampler withUniformRandomProvider(UniformRandomProvider rng) {
  196.             return new TriangleSampler3D(rng, this);
  197.         }
  198.     }

  199.     /**
  200.      * Sample uniformly from a triangle in ND.
  201.      */
  202.     private static final class TriangleSamplerND extends TriangleSampler {
  203.         /** The first vertex. */
  204.         private final double[] a;
  205.         /** The second vertex. */
  206.         private final double[] b;
  207.         /** The third vertex. */
  208.         private final double[] c;

  209.         /**
  210.          * @param rng Source of randomness.
  211.          * @param a The first vertex.
  212.          * @param b The second vertex.
  213.          * @param c The third vertex.
  214.          */
  215.         TriangleSamplerND(UniformRandomProvider rng, double[] a, double[] b, double[] c) {
  216.             super(rng);
  217.             // Defensive copy
  218.             this.a = a.clone();
  219.             this.b = b.clone();
  220.             this.c = c.clone();
  221.         }

  222.         /**
  223.          * @param rng Generator of uniformly distributed random numbers
  224.          * @param source Source to copy.
  225.          */
  226.         TriangleSamplerND(UniformRandomProvider rng, TriangleSamplerND source) {
  227.             super(rng);
  228.             // Shared state is immutable
  229.             a = source.a;
  230.             b = source.b;
  231.             c = source.c;
  232.         }

  233.         @Override
  234.         public double[] createSample(double p1msmt, double s, double t) {
  235.             final double[] x = new double[a.length];
  236.             for (int i = 0; i < x.length; i++) {
  237.                 x[i] = p1msmt * a[i] + s * b[i] + t * c[i];
  238.             }
  239.             return x;
  240.         }

  241.         @Override
  242.         public TriangleSampler withUniformRandomProvider(UniformRandomProvider rng) {
  243.             return new TriangleSamplerND(rng, this);
  244.         }
  245.     }

  246.     /**
  247.      * @param rng Source of randomness.
  248.      */
  249.     TriangleSampler(UniformRandomProvider rng) {
  250.         this.rng = rng;
  251.     }

  252.     /**
  253.      * @return a random Cartesian coordinate within the triangle.
  254.      */
  255.     @Override
  256.     public double[] sample() {
  257.         final double s = rng.nextDouble();
  258.         final double t = rng.nextDouble();
  259.         final double spt = s + t;
  260.         if (spt > 1) {
  261.             // Transform: s1 = 1 - s; t1 = 1 - t.
  262.             // Compute: 1 - s1 - t1
  263.             // Do not assume (1 - (1-s) - (1-t)) is (s + t - 1), i.e. (spt - 1.0),
  264.             // to avoid loss of a random bit due to rounding when s + t > 1.
  265.             // An exact sum is (s - 1 + t).
  266.             return createSample(s - 1.0 + t, 1.0 - s, 1.0 - t);
  267.         }
  268.         // Here s + t is exact so can be subtracted to make 1 - s - t
  269.         return createSample(1.0 - spt, s, t);
  270.     }

  271.     /**
  272.      * Creates the sample given the random variates {@code s} and {@code t} in the
  273.      * interval {@code [0, 1]} and {@code s + t <= 1}. The sum {@code 1 - s - t} is provided.
  274.      * The sample can be obtained from the triangle abc using:
  275.      * <pre>
  276.      * p = a(1 - s - t) + sb + tc
  277.      * </pre>
  278.      *
  279.      * @param p1msmt plus 1 minus s minus t (1 - s - t)
  280.      * @param s the first variate s
  281.      * @param t the second variate t
  282.      * @return the sample
  283.      */
  284.     protected abstract double[] createSample(double p1msmt, double s, double t);

  285.     /** {@inheritDoc} */
  286.     // Redeclare the signature to return a TriangleSampler not a SharedStateObjectSampler<double[]>
  287.     @Override
  288.     public abstract TriangleSampler withUniformRandomProvider(UniformRandomProvider rng);

  289.     /**
  290.      * Create a triangle sampler with vertices {@code a}, {@code b} and {@code c}.
  291.      * Sampled points are uniformly distributed within the triangle.
  292.      *
  293.      * <p>Sampling is supported in dimensions of 2 or above. Samples will lie in the
  294.      * plane (2D Euclidean space) defined by using the three triangle vertices to
  295.      * create two vectors starting at a point in the plane and orientated in
  296.      * different directions along the plane.
  297.      *
  298.      * <p>No test for collinear points is performed. If the points are collinear the sampling
  299.      * distribution is undefined.
  300.      *
  301.      * @param rng Source of randomness.
  302.      * @param a The first vertex.
  303.      * @param b The second vertex.
  304.      * @param c The third vertex.
  305.      * @return the sampler
  306.      * @throws IllegalArgumentException If the vertices do not have the same
  307.      * dimension; the dimension is less than 2; or vertices have non-finite coordinates
  308.      */
  309.     public static TriangleSampler of(UniformRandomProvider rng,
  310.                                      double[] a,
  311.                                      double[] b,
  312.                                      double[] c) {
  313.         final int dimension = a.length;
  314.         if (dimension != b.length || dimension != c.length) {
  315.             throw new IllegalArgumentException(
  316.                     new StringBuilder("Mismatch of vertex dimensions: ").append(dimension).append(',')
  317.                                                                         .append(b.length).append(',')
  318.                                                                         .append(c.length).toString());
  319.         }
  320.         // Detect non-finite vertices
  321.         Coordinates.requireFinite(a, "Vertex a");
  322.         Coordinates.requireFinite(b, "Vertex b");
  323.         Coordinates.requireFinite(c, "Vertex c");
  324.         // Low dimension specialisations
  325.         if (dimension == TWO_D) {
  326.             return new TriangleSampler2D(rng, a, b, c);
  327.         } else if (dimension == THREE_D) {
  328.             return new TriangleSampler3D(rng, a, b, c);
  329.         } else if (dimension > THREE_D) {
  330.             return new TriangleSamplerND(rng, a, b, c);
  331.         }
  332.         // Less than 2D
  333.         throw new IllegalArgumentException(
  334.                 "Unsupported dimension: " + dimension);
  335.     }
  336. }