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.basic; 010 011import imagingbook.common.geometry.basic.Pnt2d.PntDouble; 012import imagingbook.common.geometry.basic.Pnt2d.PntInt; 013 014import java.util.Arrays; 015 016/** 017 * Defines static methods for manipulating 2D points. 018 */ 019public abstract class PntUtils { 020 021 private PntUtils() {} 022 023 /** 024 * Simply counts the points in the specified point set. 025 * @param pts an {@link Iterable} of {@link Pnt2d} instances 026 * @return the number of points 027 */ 028 public static int count(Iterable<Pnt2d> pts) { 029 int n = 0; 030 for (Pnt2d p : pts) { 031 n++; 032 } 033 return n; 034 } 035 036 /** 037 * Calculates and returns the centroid of the specified point set. 038 * 039 * @param pts an {@link Iterable} of {@link Pnt2d} instances 040 * @return the centroid (as a {@link Pnt2d} instance) 041 */ 042 public static Pnt2d centroid(Iterable<Pnt2d> pts) { 043 double sx = 0; 044 double sy = 0; 045 int n = 0; 046 for (Pnt2d p : pts) { 047 sx = sx + p.getX(); 048 sy = sy + p.getY(); 049 n++; 050 } 051 if (n == 0) { 052 throw new IllegalArgumentException("at least one point is required for centroid calculation"); 053 } 054 return Pnt2d.from(sx/n, sy/n); 055 } 056 057 /** 058 * Calculates and returns the centroid of the specified point set. 059 * 060 * @param pts an array of {@link Pnt2d} instances 061 * @return the centroid (as a {@link Pnt2d} instance) 062 */ 063 public static Pnt2d centroid(Pnt2d[] pts) { 064 return centroid(() -> Arrays.stream(pts).iterator()); 065 } 066 067 // ------------------------------------------------------------------- 068 069 /** 070 * Converts a given point array {@code Pnt2d[n]} to a 2D double array {@code double[n][2]}, with x-coordinates in 071 * column 0 and y-coordinates in column 1. 072 * 073 * @param pts the point array 074 * @return a 2D double array 075 */ 076 public static double[][] toDoubleArray(Pnt2d[] pts) { 077 final int n = pts.length; 078 double[][] pa = new double[n][2]; 079 for (int i = 0; i < n; i++) { 080 pa[i][0] = pts[i].getX(); 081 pa[i][1] = pts[i].getY(); 082 } 083 return pa; 084 } 085 086 /** 087 * Converts a given 2D double array {@code double[n][2]} to a point array {@code Pnt2d[n]}, taking x-coordinates 088 * from column 0 and y-coordinates from column 1. 089 * 090 * @param da a 2D double array 091 * @return a point array 092 */ 093 public static Pnt2d[] fromDoubleArray(double[][] da) { 094 final int n = da.length; 095 Pnt2d[] pts = new Pnt2d[n]; 096 for (int i = 0; i < n; i++) { 097 pts[i] = Pnt2d.from(da[i]); 098 } 099 return pts; 100 } 101 102 // ------------------------------------------------------------------- 103 104 /** 105 * Creates and returns an array of {@link Pnt2d.PntInt} points from the given sequence of {@code int} coordinate 106 * values, interpreted as x/y pairs. Throws an exception if the length of the coordinate sequence is not even. Usage 107 * example: 108 * <pre> 109 * Pnt2d[] pts = PntUtils.makeIntPoints(1, 2, 3, 4, 5, 6); 110 * // gives [PntInt(1, 2), PntInt(3, 4), PntInt(5, 6)] 111 * </pre> 112 * 113 * @param xy an even-numbered sequence of point coordinates 114 * @return an array of {@link Pnt2d.PntInt} points 115 */ 116 public static Pnt2d[] makeIntPoints(int... xy) { 117 if (xy.length % 2 != 0) { 118 throw new IllegalArgumentException("even number of point coordinates required"); 119 } 120 final int n = xy.length/2; 121 Pnt2d[] pts = new Pnt2d[n]; 122 for (int i = 0, j = 0; i < n; i++, j += 2) { 123 pts[i] = new PntInt(xy[j], xy[j + 1]); 124 } 125 return pts; 126 } 127 128 /** 129 * Creates and returns an array of {@link Pnt2d.PntDouble} points from the given sequence of {@code double} 130 * coordinate values, interpreted as x/y pairs. Throws an exception if the length of the coordinate sequence is not 131 * even. Usage example: 132 * <pre> 133 * Pnt2d[] pts = PntUtils.makeDoublePoints(1, 2, 3, 4, 5, 6); 134 * // gives [PntDouble(1.0, 2.0), PntDouble(3.0, 4.0), PntDouble(5.0, 6.0)] 135 * </pre> 136 * 137 * @param xy an even-numbered sequence of point coordinates 138 * @return an array of {@link Pnt2d.PntDouble} points 139 */ 140 public static Pnt2d[] makeDoublePoints(double... xy) { 141 if (xy.length % 2 != 0) { 142 throw new IllegalArgumentException("even number of point coordinates required"); 143 } 144 final int n = xy.length/2; 145 Pnt2d[] pts = new Pnt2d[n]; 146 for (int i = 0, j = 0; i < n; i++, j += 2) { 147 pts[i] = new PntDouble(xy[j], xy[j + 1]); 148 } 149 return pts; 150 } 151 152}