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.circle.algebraic; 010 011 012import imagingbook.common.geometry.basic.Pnt2d; 013import imagingbook.common.math.Arithmetic; 014 015import static imagingbook.common.math.Arithmetic.sqr; 016 017/** 018 * <p> 019 * Performs an exact circle fit to 3 given (non-collinear) points. If the fit is unsuccessful, {@link #getParameters()} 020 * returns {@code null}. See the Appendix of [1] (Sec. F.2.2) for details. 021 * </p> 022 * <p> 023 * [1] W. Burger, M.J. Burge, <em>Digital Image Processing – An Algorithmic Introduction</em>, 3rd ed, Springer 024 * (2022). 025 * </p> 026 * 027 * @author WB 028 * @version 2022/11/17 029 */ 030public class CircleFit3Points implements CircleFitAlgebraic { 031 032 private final double[] p; 033 034 @Override 035 public double[] getParameters() { 036 return this.p; 037 } 038 039 /** 040 * Constructor. Fits a circle to the specified three sample points. If the fit is unsuccessful, 041 * {@link #getParameters()} returns {@code null}. 042 * 043 * @param p0 sample point 1 044 * @param p1 sample point 2 045 * @param p2 sample point 3 046 */ 047 public CircleFit3Points(Pnt2d p0, Pnt2d p1, Pnt2d p2) { 048 final double x0 = p0.getX(); 049 final double y0 = p0.getY(); 050 final double x1 = p1.getX(); 051 final double y1 = p1.getY(); 052 final double x2 = p2.getX(); 053 final double y2 = p2.getY(); 054 055 final double A = x0 * (y1 - y2) - y0 * (x1 - x2) + x1 * y2 - x2 * y1; 056 if (Arithmetic.isZero(A)) { 057 this.p = null; 058 } 059 else { 060 final double R0 = sqr(x0) + sqr(y0); 061 final double R1 = sqr(x1) + sqr(y1); 062 final double R2 = sqr(x2) + sqr(y2); 063 final double B = R0 * (y2 - y1) + R1 * (y0 - y2) + R2 * (y1 - y0); 064 final double C = R0 * (x1 - x2) + R1 * (x2 - x0) + R2 * (x0 - x1); 065 final double D = R0 * (x2 * y1 - x1 * y2) + R1 * (x0 * y2 - x2 * y0) + R2 * (x1 * y0 - x0 * y1); 066 this.p = new double[] {A, B, C, D}; 067 } 068 } 069 070 /** 071 * Constructor. Fits a circle to the specified three sample points, passed as an array of points. Only the first 072 * three points are used, additional points are ignored. If the fit is unsuccessful, {@link #getParameters()} 073 * returns {@code null}. 074 * 075 * @param pts three or more sample points 076 */ 077 public CircleFit3Points(Pnt2d[] pts) { 078 this(pts[0], pts[1], pts[2]); 079 } 080 081 082}