001/*******************************************************************************
002 * This software is provided as a supplement to the authors' textbooks on digital
003 * image processing published by Springer-Verlag in various languages and editions.
004 * Permission to use and distribute this software is granted under the BSD 2-Clause
005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause).
006 * Copyright (c) 2006-2023 Wilhelm Burger, Mark J. Burge. All rights reserved.
007 * Visit https://imagingbook.com for additional details.
008 ******************************************************************************/
009package imagingbook.common.geometry.fitting.ellipse.utils;
010
011import imagingbook.common.geometry.basic.Pnt2d;
012import imagingbook.common.geometry.ellipse.GeometricEllipse;
013
014import java.util.Random;
015
016import static imagingbook.common.math.Arithmetic.mod;
017import static java.lang.Math.PI;
018
019/**
020 * Utility class for picking random points on a given ellipse.
021 * 
022 * @author WB
023 * @version 2022/11/17
024 */
025public class EllipseSampler {
026        
027        private final Random rg;
028        
029        private final GeometricEllipse ellipse;
030        
031        public EllipseSampler(GeometricEllipse ellipse) {
032                this.ellipse = ellipse;
033                this.rg = new Random();
034        }
035        
036        public EllipseSampler(GeometricEllipse ellipse, long seed) {
037                this.ellipse = ellipse;
038                this.rg = new Random(seed);
039        }
040
041        /**
042         * Creates and returns an array of 2D points sampled on the ellipse associated with this {@link EllipseSampler}.
043         * Random Gaussian noise (with standard deviation sigma) is added to the individual x/y coordinates.
044         *
045         * @param n number of points
046         * @param startAngle initial angle (radians)
047         * @param arcAngle arc angle (radians)
048         * @param sigma amount of random noise
049         * @return an array of sample points
050         */
051        public Pnt2d[] getPoints(int n, double startAngle, double arcAngle, double sigma) {
052                Pnt2d[] points = new Pnt2d[n];
053                                
054                double xc = ellipse.xc;
055                double yc = ellipse.yc;
056                double ra = ellipse.ra;
057                double rb = ellipse.rb;
058                double theta = ellipse.theta;
059                
060                startAngle = mod(startAngle, 2 * PI);   
061                arcAngle   = mod(arcAngle, 2 * PI);
062                if (arcAngle == 0)
063                        arcAngle = 2 * PI;
064
065//              double dAngle;
066//              if (endAngle > startAngle) {
067//                      dAngle = endAngle - startAngle;
068//              }
069//              else if (endAngle < startAngle) {
070//                      dAngle = endAngle + 2 * PI - startAngle;
071//              }
072//              else {  // endAngle == startAngle
073//                      dAngle = 2 * PI;
074//              }
075
076                final double cosTh = Math.cos(theta);
077                final double sinTh = Math.sin(theta);
078                
079                for (int i = 0; i < n; i++) {
080                        double alpha = startAngle + arcAngle * i / n;
081                        double x0 = ra * Math.cos(alpha) + sigma * rg.nextGaussian();
082                        double y0 = rb * Math.sin(alpha) + sigma * rg.nextGaussian();
083                        double x = x0 * cosTh - y0 * sinTh + xc;
084                        double y = x0 * sinTh + y0 * cosTh + yc;
085                        points[i] = Pnt2d.from(x, y);
086                }
087                return points;
088        }
089
090}