View Javadoc
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  
18  package org.apache.commons.math4.examples.sofm.chineserings;
19  
20  import java.util.Iterator;
21  import java.util.NoSuchElementException;
22  
23  import org.apache.commons.rng.UniformRandomProvider;
24  import org.apache.commons.rng.simple.RandomSource;
25  import org.apache.commons.rng.sampling.UnitSphereSampler;
26  import org.apache.commons.rng.sampling.distribution.ContinuousUniformSampler;
27  import org.apache.commons.geometry.euclidean.threed.Vector3D;
28  import org.apache.commons.geometry.euclidean.threed.rotation.Rotation3D;
29  import org.apache.commons.geometry.euclidean.threed.rotation.QuaternionRotation;
30  
31  /**
32   * Class that creates two intertwined rings in 3D-space.
33   * Each ring is composed of a cloud of points.
34   */
35  class ChineseRings {
36      /** Points in the two rings. */
37      private final Vector3D[] points;
38  
39      /**
40       * @param orientationRing1 Vector othogonal to the plane containing the
41       * first ring.
42       * @param radiusRing1 Radius of the first ring.
43       * @param halfWidthRing1 Half-width of the first ring.
44       * @param radiusRing2 Radius of the second ring.
45       * @param halfWidthRing2 Half-width of the second ring.
46       * @param numPointsRing1 Number of points in the first ring.
47       * @param numPointsRing2 Number of points in the second ring.
48       */
49      ChineseRings(Vector3D orientationRing1,
50                   double radiusRing1,
51                   double halfWidthRing1,
52                   double radiusRing2,
53                   double halfWidthRing2,
54                   int numPointsRing1,
55                   int numPointsRing2) {
56          // First ring (centered at the origin).
57          final Vector3D[] firstRing = new Vector3D[numPointsRing1];
58          // Second ring (centered around the first ring).
59          final Vector3D[] secondRing = new Vector3D[numPointsRing2];
60  
61          final UniformRandomProvider rng = RandomSource.WELL_19937_C.create();
62  
63          // Create two rings lying in xy-plane.
64          final UnitSphereSampler unit = UnitSphereSampler.of(rng, 2);
65  
66          final ContinuousUniformSampler radius1
67              = new ContinuousUniformSampler(rng,
68                                             radiusRing1 - halfWidthRing1,
69                                             radiusRing1 + halfWidthRing1);
70          final ContinuousUniformSampler widthRing1
71              = new ContinuousUniformSampler(rng,
72                                             -halfWidthRing1,
73                                             halfWidthRing1);
74  
75          for (int i = 0; i < numPointsRing1; i++) {
76              final double[] v = unit.sample();
77              final double r = radius1.sample();
78              // First ring is in the xy-plane, centered at (0, 0, 0).
79              firstRing[i] = Vector3D.of(v[0] * r,
80                                         v[1] * r,
81                                         widthRing1.sample());
82          }
83  
84          final ContinuousUniformSampler radius2
85              = new ContinuousUniformSampler(rng,
86                                             radiusRing2 - halfWidthRing2,
87                                             radiusRing2 + halfWidthRing2);
88          final ContinuousUniformSampler widthRing2
89              = new ContinuousUniformSampler(rng,
90                                             -halfWidthRing2,
91                                             halfWidthRing2);
92  
93          for (int i = 0; i < numPointsRing2; i++) {
94              final double[] v = unit.sample();
95              final double r = radius2.sample();
96              // Second ring is in the xz-plane, centered at (radiusRing1, 0, 0).
97              secondRing[i] = Vector3D.of(radiusRing1 + v[0] * r,
98                                          widthRing2.sample(),
99                                          v[1] * r);
100         }
101 
102         // Move first and second rings into position.
103         final Rotation3D rot = QuaternionRotation.createVectorRotation(Vector3D.Unit.PLUS_Z,
104                                                                        orientationRing1.normalize());
105         int count = 0;
106         points = new Vector3D[numPointsRing1 + numPointsRing2];
107         for (int i = 0; i < numPointsRing1; i++) {
108             points[count++] = rot.apply(firstRing[i]);
109         }
110         for (int i = 0; i < numPointsRing2; i++) {
111             points[count++] = rot.apply(secondRing[i]);
112         }
113     }
114 
115     /**
116      * Gets all the points.
117      *
118      * @return the points
119      */
120     public Vector3D[] getPoints() {
121         return points.clone();
122     }
123 
124     /**
125      * Creates an iterable that will present the points coordinates.
126      *
127      * @return the iterable.
128      */
129     public Iterable<double[]> createIterable() {
130         return () -> new Iterator<double[]>() {
131                 /** Data. */
132                 private final Vector3D[] points = getPoints();
133                 /** Number of samples. */
134                 private int n;
135 
136                 /** {@inheritDoc} */
137                 @Override
138                 public boolean hasNext() {
139                     return n < points.length;
140                 }
141 
142                 /** {@inheritDoc} */
143                 @Override
144                 public double[] next() {
145                     if (!hasNext()) {
146                         throw new NoSuchElementException();
147                     }
148                     return points[n++].toArray();
149                 }
150 
151                 /** {@inheritDoc} */
152                 @Override
153                 public void remove() {
154                     throw new UnsupportedOperationException();
155                 }
156             };
157     }
158 }