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 }