001package conexp.fx.core.math; 002 003/* 004 * #%L 005 * Concept Explorer FX 006 * %% 007 * Copyright (C) 2010 - 2023 Francesco Kriegel 008 * %% 009 * This program is free software: you can redistribute it and/or modify 010 * it under the terms of the GNU General Public License as 011 * published by the Free Software Foundation, either version 3 of the 012 * License, or (at your option) any later version. 013 * 014 * This program is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 017 * GNU General Public License for more details. 018 * 019 * You should have received a copy of the GNU General Public 020 * License along with this program. If not, see 021 * <http://www.gnu.org/licenses/gpl-3.0.html>. 022 * #L% 023 */ 024 025import java.util.Collection; 026import java.util.NoSuchElementException; 027 028import com.sun.javafx.geom.Line2D; 029 030import javafx.geometry.Point2D; 031import javafx.geometry.Point3D; 032 033public final class Points { 034 035// public static final Point3D ZERO = new Point3D(0, 0, 0); 036// public static final Point3D ONE_X = new Point3D(1, 0, 0); 037// public static final Point3D ONE_Y = new Point3D(0, 1, 0); 038// public static final Point3D ONE_Z = new Point3D(0, 0, 1); 039 040 public static final Point2D 041 projectOnCircle(final double cx, final double cy, final double r, final double px, final double py) { 042 final double dx = px - cx; 043 final double dy = py - cy; 044 final double l = Math.sqrt(dx * dx + dy * dy); 045 final double f = r / l; 046 final double x = cx + f * dx; 047 final double y = cx + f * dy; 048 return new Point2D(x, y); 049 } 050 051 public static final Point3D absoluteSum(final Collection<Point3D> c) { 052 double x = 0d; 053 double y = 0d; 054 double z = 0d; 055 for (Point3D p : c) { 056 x += Math.abs(p.getX()); 057 y += Math.abs(p.getY()); 058 z += Math.abs(p.getZ()); 059 } 060 return new Point3D(x, y, z); 061 } 062 063 public static final Point3D rotate(final Point3D point, final double angle) { 064 final double x = point.getX() * Math.cos(angle) + point.getZ() * Math.sin(angle); 065 final double y = point.getY(); 066 final double z = -point.getX() * Math.sin(angle) + point.getZ() * Math.cos(angle); 067 return new Point3D(x, y, z); 068 } 069 070// public static final Point3D plus(final Point3D p, final Point3D q) { 071// return Point3DBuilder.create().x(p.getX() + q.getX()).y(p.getY() + q.getY()).z(p.getZ() + q.getZ()).build(); 072// } 073// 074// public static final Point3D plus(final Point3D p, final double dx, final double dy) { 075// if (dy == 0d && dy == 0d) 076// return p; 077// else 078// return new Point3D(p.getX() + dx, p.getY() + dy, p.getZ()); 079// } 080// 081// public static final Point2D plus(final Point2D p, final double dx, final double dy) { 082// if (dy == 0d && dy == 0d) 083// return p; 084// else 085// return new Point2D(p.getX() + dx, p.getY() + dy); 086// } 087// 088// public static final Point3D plus(final Point3D p, final double dx, final double dy, final double dz) { 089// if (dy == 0d && dy == 0d && dz == 0d) 090// return p; 091// else 092// return new Point3D(p.getX() + dx, p.getY() + dy, p.getZ() + dz); 093// } 094// 095// public static final Point3D minus(final Point3D p, final Point3D q) { 096// return Point3DBuilder.create().x(p.getX() - q.getX()).y(p.getY() - q.getY()).z(p.getZ() - q.getZ()).build(); 097// } 098// 099// public static final Point3D multiply(final double f, final Point3D p) { 100// return Point3DBuilder.create().x(p.getX() * f).y(p.getY() * f).z(p.getZ() * f).build(); 101// } 102// 103// public static final Point3D divide(final Point3D p, final double f) { 104// return Point3DBuilder.create().x(p.getX() / f).y(p.getY() / f).z(p.getZ() / f).build(); 105// } 106 107// public static final double scalarProduct(final Point3D p, final Point3D q) { 108// return p.getX() * q.getX() + p.getY() * q.getY() + p.getZ() * q.getZ(); 109// } 110 111// public static final Point3D crossProduct(final Point3D p, final Point3D q) { 112// return Point3DBuilder 113// .create() 114// .x(p.getY() * q.getZ() - p.getZ() * q.getY()) 115// .y(p.getZ() * q.getX() - p.getX() * q.getZ()) 116// .z(p.getX() * q.getY() - p.getY() * q.getX()) 117// .build(); 118// } 119 120// public static final double length(final Point3D p) { 121// return Math.sqrt(scalarProduct(p, p)); 122// } 123 124 public static final double pointSegmentDistance(final Point3D p, final Point3D q1, final Point3D q2) { 125 if (p.equals(q1) || p.equals(q2)) 126 return 0; 127 if (q1.equals(q2)) 128 return p.distance(q1); 129 // line segment: g = q1 + t * (q2 - q1), 0 <= t <= 1 130 final Point3D q = q2.subtract(q1); 131 // projection on line: P(p,g) = q1 + t * q 132 final double t = p.subtract(q1).dotProduct(q) / q.dotProduct(q); 133 if (t < 0) 134 return p.distance(q1); 135 else if (t > 1) 136 return p.distance(q2); 137 else 138 return p.distance(q1.add(q.multiply(t))); 139 } 140 141 public static final boolean intersectX(final Point3D p1, final Point3D p2, final Point3D q1, final Point3D q2) { 142 return Math.max(p1.getX(), p2.getX()) >= Math.min(q1.getX(), q2.getX()) 143 && Math.max(q1.getX(), q2.getX()) >= Math.min(p1.getX(), p2.getX()); 144 } 145 146 public static final boolean intersectY(final Point3D p1, final Point3D p2, final Point3D q1, final Point3D q2) { 147 return Math.max(p1.getY(), p2.getY()) >= Math.min(q1.getY(), q2.getY()) 148 && Math.max(q1.getY(), q2.getY()) >= Math.min(p1.getY(), p2.getY()); 149 } 150 151 public static final boolean intersectXY(Point3D p1, Point3D p2, Point3D q1, Point3D q2) { 152 return new Line2D((float) p1.getX(), (float) p1.getY(), (float) p2.getX(), (float) p2.getY()) 153 .intersectsLine(new Line2D((float) q1.getX(), (float) q1.getY(), (float) q2.getX(), (float) q2.getY())); 154 } 155 156 public static final Point2D projectXY(final Point3D p) { 157 return new Point2D(p.getX(), p.getY()); 158 } 159 160 public static final boolean 161 intersectingLineSegments(final Point3D p1, final Point3D p2, final Point3D q1, final Point3D q2) { 162 // line segment: g = p1 + s * (p2 - p1), 0 <= s <= 1 163 // line segment: h = q1 + t * (q2 - q1), 0 <= t <= 1 164 final Point3D p = p2.subtract(p1); 165 final Point3D q = q2.subtract(q1); 166 // equation: p1 + s * p = q1 + t * q 167 // ==> s * p = (q1 - p1) + t * q 168 // ==> t * q = (p1 - q1) + s * p 169 // ==> s * (p X q) = (q1 - p1) X q 170 // ==> t * (q X p) = (p1 - q1) X p 171 // g intersects h iff there is a solution (s,t) in [0,1]x[0,1] 172 final Point3D c1 = p.crossProduct(q); 173 final Point3D c2 = q1.subtract(p1).crossProduct(q); 174 final Point3D d1 = q.crossProduct(p); 175 final Point3D d2 = p1.subtract(q1).crossProduct(p); 176 // ==> s * c1 = c2 177 // ==> t * d1 = d2 178 try { 179 final double s = solve(c1, c2); 180 if (s < 0 || s > 1) 181 return false; 182 final double t = solve(d1, d2); 183 if (t >= 0 && t <= 1) 184 return true; 185 } catch (NoSuchElementException e) {} 186 return false; 187 } 188 189 /** 190 * @param p 191 * @param q 192 * @return double value t such that t * p == q 193 * @throws NoSuchElementException 194 */ 195 public static final double solve(final Point3D p, final Point3D q) throws NoSuchElementException { 196 if (p.normalize().equals(q.normalize())) { 197 if (p.equals(Point3D.ZERO)) 198 return 1; 199 return q.magnitude() / p.magnitude(); 200 } 201// if (p.getX() == 0 && q.getX() != 0) 202// throw new NoSuchElementException(); 203// if (p.getY() == 0 && q.getY() != 0) 204// throw new NoSuchElementException(); 205// if (p.getZ() == 0 && q.getZ() != 0) 206// throw new NoSuchElementException(); 207// if (q.getX() == 0 && q.getY() == 0 && q.getZ() == 0) 208// return 1d; 209// if (p.getX() != 0 && q.getX() != 0) { 210// final double t = q.getX() / p.getX(); 211// if (t * p.getY() == q.getY() && t * p.getZ() == q.getZ()) 212// return t; 213// } else if (p.getY() != 0 && q.getY() != 0) { 214// final double t = q.getY() / p.getY(); 215// if (t * p.getX() == q.getX() && t * p.getZ() == q.getZ()) 216// return t; 217// } else if (p.getZ() != 0 && q.getZ() != 0) { 218// final double t = q.getZ() / p.getZ(); 219// if (t * p.getX() == q.getX() && t * p.getY() == q.getY()) 220// return t; 221// } 222 throw new NoSuchElementException(); 223 } 224 225 public static final boolean 226 parallelLineSegments(final Point3D p1, final Point3D p2, final Point3D q1, final Point3D q2) { 227 final Point3D p = p2.subtract(p1); 228 final Point3D q = q2.subtract(q1); 229 // length(minus(divide(p, length(p)), divide(q, length(q)))) 230 if (p.normalize().crossProduct(q.normalize()).magnitude() < 0.01d) 231 return true; 232 return false; 233 } 234 235 public static final double cosAngle(final Point3D p1, final Point3D p2, final Point3D q1, final Point3D q2) { 236 final Point3D p = p2.subtract(p1); 237 final Point3D q = q2.subtract(q1); 238 return p.normalize().dotProduct(q.normalize()); 239 } 240 241// public static final Function<Point3D, Double> X_PROJECTION = new Function<Point3D, Double>() { 242// 243// @Override 244// public final Double apply(final Point3D point) { 245// return point.getX(); 246// } 247// }; 248// public static final Function<Point3D, Double> Y_PROJECTION = new Function<Point3D, Double>() { 249// 250// @Override 251// public final Double apply(final Point3D point) { 252// return point.getY(); 253// } 254// }; 255// public static final Function<Point3D, Double> Z_PROJECTION = new Function<Point3D, Double>() { 256// 257// @Override 258// public final Double apply(final Point3D point) { 259// return point.getZ(); 260// } 261// }; 262// public static final Function<Point3D, Point3D> XY_PROJECTION = new Function<Point3D, Point3D>() { 263// 264// @Override 265// public final Point3D apply(final Point3D point) { 266// return Point3DBuilder 267// .create() 268// .x(point.getX()) 269// .y(point.getY()) 270// .z(0) 271// .build(); 272// } 273// }; 274 275 /** 276 * Returns t such that p = q1 + t * (q2 - q1) 277 * 278 * @param p 279 * @param q1 280 * @param q2 281 * @return 282 */ 283 public static final double solve(final Point3D p, final Point3D q1, final Point3D q2) { 284 final Point3D q = q2.subtract(q1); 285 final Point3D x = p.subtract(q1); 286 return x.dotProduct(q) / q.dotProduct(q); 287 } 288 289 public static final Point3D projectToLine(final Point3D p, final Point3D q1, final Point3D q2) { 290 final double t = solve(p, q1, q2); 291 return q1.add(q2.subtract(q1).multiply(t)); 292 } 293 294 public static final Point3D projectToLineSegment(final Point3D p, final Point3D q1, final Point3D q2) { 295 final double t = Math.min(Math.max(solve(p, q1, q2), 0), 1); 296 return q1.add(q2.subtract(q1).multiply(t)); 297 } 298 299 public static final Point3D shortestVectorFromLineSegment(final Point3D p, final Point3D q1, final Point3D q2) { 300 return p.subtract(projectToLineSegment(p, q1, q2)); 301 } 302 303}