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.line; 010 011import imagingbook.common.geometry.basic.Pnt2d; 012import imagingbook.common.math.Arithmetic; 013import imagingbook.common.math.Matrix; 014 015import java.util.Locale; 016 017import static imagingbook.common.math.Arithmetic.sqr; 018 019/** 020 * <p> 021 * This class represents a line in parametric form: x = s + t v, where s is a start point on the line, v is a direction 022 * vector, and t is a real variable. Instances are immutable. See Sec. 10.1 and Appendix F.1 of [1] for details. 023 * </p> 024 * <p> 025 * [1] W. Burger, M.J. Burge, <em>Digital Image Processing – An Algorithmic Introduction</em>, 3rd ed, Springer 026 * (2022). 027 * </p> 028 * 029 * @author WB 030 * @version 2022/11/18 031 */ 032public class ParametricLine { 033 034 private final double[] s, v; 035 036 public ParametricLine(double[] s, double[] v) { 037 if (s.length != 2 || v.length != 2) { 038 throw new IllegalArgumentException("vectors s, v must be of length 2"); 039 } 040 if (Arithmetic.isZero(Matrix.normL2squared(v))) { 041 throw new IllegalArgumentException("direction vector (v) must be nonzero"); 042 } 043 this.s = s.clone(); 044 this.v = v.clone(); 045 } 046 047 public double[] getS() { 048 return s; 049 } 050 051 public double[] getV() { 052 return v; 053 } 054 055 // -------------------------- 056 057 public static ParametricLine from(AlgebraicLine al) { 058 double[] p = al.getParameters(); 059 // not needed, since algebraic lines are always normalized (i.e. A^2 + B^2 = 1) 060 double norm2 = sqr(p[0]) + sqr(p[1]); // = A^2 + B^2 061 double scale = -p[2] / norm2; // = -C / ||(A,B)||^2 062 double[] s = {scale * p[0], scale * p[1]}; // = (-CA, -CB) 063 double[] v = {-p[1], p[0]}; // = (-B, A) 064 return new ParametricLine(s, v); 065 } 066 067 @Override 068 public String toString() { 069 return String.format(Locale.US, "%s <s=[%.3f, %.3f], v=[%.3f, %.3f]>", 070 this.getClass().getSimpleName(), s[0], s[1], v[0], v[1]); 071 } 072 073 // -------------------------- 074 075 public static void main (String[] args) { 076 Pnt2d p1 = Pnt2d.from(1, 2); 077 Pnt2d p2 = Pnt2d.from(4, 3); 078 079 AlgebraicLine al1 = AlgebraicLine.from(p1, p2); 080 System.out.println("al1 = " + al1); 081 082 ParametricLine pl = ParametricLine.from(al1); 083 System.out.println("pl = " + pl); 084 085 AlgebraicLine al2 = AlgebraicLine.from(pl); 086 System.out.println("al2 = " + al2); 087 088 System.out.println("al1 = al2 ? " + al1.equals(al2, 1e-6)); 089 } 090 091 /* 092 al1 = AlgebraicLine <a=-0.316, b=0.949, c=-1.581> 093 pl = ParametricLine <s=[-0.500, 1.500], v=[-0.949, -0.316]> 094 al2 = AlgebraicLine <a=0.316, b=-0.949, c=1.581> 095 al1 = al2 ? true 096 */ 097 098}