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}