/** * ORIPA - Origami Pattern Editor * Copyright (C) 2005-2009 Jun Mitani http://mitani.cs.tsukuba.ac.jp/ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package oripa.geom; import javax.vecmath.Vector2d; import javax.vecmath.Vector3d; import oripa.fold.OriFace; import oripa.fold.OriHalfedge; import oripa.value.OriLine; public class GeomUtil { public final static double EPS = 1.0e-6; public static double Distance(Vector2d p0, Vector2d p1) { return Distance(p0.x, p0.y, p1.x, p1.y); } public static double DistanceSquared(Vector2d p0, Vector2d p1) { return DistanceSquared(p0.x, p0.y, p1.x, p1.y); } public static double DistanceSquared(double x0, double y0, double x1, double y1) { return (x0 - x1) * (x0 - x1) + (y0 - y1) * (y0 - y1); } // Determine if p3 is in p1~p2 range public static boolean isRange(double p1, double p2, double p3) { return Math.min(p1, p2) <= p3 && p3 <= Math.max(p1, p2); } public static boolean isRange(Vector2d p1, Vector2d p2, Vector2d p3) { return isRange(p1.x, p2.x, p3.x) && isRange(p1.y, p2.y, p3.y); } public static boolean isParallel(Vector2d dir0, Vector2d dir1) { // tolerance of 1 degree return dir0.angle(dir1) < Math.PI / 180 || dir0.angle(dir1) > Math.PI * 179.0 / 180; } public static boolean isSegmentsCross(Vector2d p0, Vector2d p1, Vector2d q0, Vector2d q1) { // Rough check // Check by coordinates x if (p0.x >= p1.x) { if ((p0.x < q0.x && p0.x < q1.x) || (p1.x > q0.x && p1.x > q1.x)) { return false; } } else { if ((p1.x < q0.x && p1.x < q1.x) || (p0.x > q0.x && p0.x > q1.x)) { return false; } } // checked by the coordinate y if (p0.y >= p1.y) { if ((p0.y < q0.y && p0.y < q1.y) || (p1.y > q0.y && p1.y > q1.y)) { return false; } } else { if ((p1.y < q0.y && p1.y < q1.y) || (p0.y > q0.y && p0.y > q1.y)) { return false; } } // >= 0.0 means that when p0 == q0, for example, returns false if (((p0.x - p1.x) * (q0.y - p0.y) + (p0.y - p1.y) * (p0.x - q0.x)) * ((p0.x - p1.x) * (q1.y - p0.y) + (p0.y - p1.y) * (p0.x - q1.x)) >= 0.0) { return false; } if (((q0.x - q1.x) * (p0.y - q0.y) + (q0.y - q1.y) * (q0.x - p0.x)) * ((q0.x - q1.x) * (p1.y - q0.y) + (q0.y - q1.y) * (q0.x - p1.x)) >= 0.0) { return false; } return true; } public static boolean isLineSegmentsOverlap(Vector2d s0, Vector2d e0, Vector2d s1, Vector2d e1) { // Whether or not is parallel Vector2d dir0 = new Vector2d(e0); dir0.sub(s0); Vector2d dir1 = new Vector2d(e1); dir1.sub(s1); if (!isParallel(dir0, dir1)) { return false; } int cnt = 0; if (DistancePointToSegment(s0, s1, e1) < EPS) { cnt++; } if (DistancePointToSegment(e0, s1, e1) < EPS) { cnt++; } if (DistancePointToSegment(s1, s0, e0) < EPS) { cnt++; } if (DistancePointToSegment(e1, s0, e0) < EPS) { cnt++; } if (cnt >= 2) { return true; } return false; } //Calculate the intersection of p1-p2 and p3-p4. //The return value is: 0 when there is no intersection, // 1 when intersect (ap1 is the intersection), // 2 when they are parallel and ap1-ap2 is the intersection public static int getCrossPoint(Vector2d ap1, Vector2d ap2, Vector2d p1, Vector2d p2, Vector2d p3, Vector2d p4) { if (Distance(p1, p3) < EPS && Distance(p2, p4) < EPS) { ap1.set(p1); ap2.set(p2); return 2; } if (Distance(p1, p4) < EPS && Distance(p2, p3) < EPS) { ap1.set(p1); ap2.set(p2); return 2; } double a1, a2, b1, b2; a1 = (p1.y - p2.y); a2 = (p3.y - p4.y); b1 = (p2.x - p1.x); b2 = (p4.x - p3.x); if (Math.abs(a1 * b2 - a2 * b1) < EPS) { if (Math.max(p1.x, p2.x) < Math.min(p3.x, p4.x) || Math.max(p1.y, p2.y) < Math.min(p3.y, p4.y) || Math.max(p3.x, p4.x) < Math.min(p1.x, p2.x) || Math.max(p3.y, p4.y) < Math.min(p1.y, p2.y)) { return 0; } if (isRange(p3, p4, p1)) { ap1.set(p1); } else if (isRange(p1, p4, p3)) { ap1.set(p3); } else if (isRange(p1, p3, p4)) { ap1.set(p4); } else { return 0; } if (isRange(p3, p4, p2)) { ap2.set(p2); } else if (isRange(p2, p4, p3)) { ap2.set(p3); } else if (isRange(p2, p3, p4)) { ap2.set(p4); } else { return 0; } return 2; } double c1, c2; c1 = p1.x * p2.y - p2.x * p1.y; c2 = p3.x * p4.y - p4.x * p3.y; ap1.x = (b1 * c2 - b2 * c1) / (a1 * b2 - a2 * b1); ap1.y = (a1 * c2 - a2 * c1) / (a2 * b1 - a1 * b2); if (isRange(p1, p2, ap1) && isRange(p3, p4, ap1)) { return 1; } else { return 0; } } public static boolean isSameLineSegment(OriLine l0, OriLine l1) { if (Distance(l0.p0, l1.p0) < EPS && Distance(l0.p1, l1.p1) < EPS) { return true; } if (Distance(l0.p0, l1.p1) < EPS && Distance(l0.p1, l1.p0) < EPS) { return true; } return false; } // Returns false if nothing is in the clip area public static boolean clipLine(OriLine l, double halfWidth) { Vector2d p = new Vector2d(l.p0); Vector2d dir = new Vector2d(); dir.sub(l.p1, l.p0); // If horizontal if (Math.abs(dir.y) < EPS) { if (p.y < -halfWidth || p.y > halfWidth) { return false; } l.p0.set(-halfWidth, p.y); l.p1.set(halfWidth, p.y); return true; } // If vertical if (Math.abs(dir.x) < EPS) { if (p.x < -halfWidth || p.x > halfWidth) { return false; } l.p0.set(p.x, -halfWidth); l.p1.set(p.x, halfWidth); return true; } // If you do not have any horizontal vertical // Cut down { double up_t = (halfWidth - p.y) / dir.y; double up_x = p.x + up_t * dir.x; if (up_x < -halfWidth) { double left_t = (-halfWidth - p.x) / dir.x; double left_y = p.y + left_t * dir.y; if (left_y < -halfWidth) { return false; } l.p0.set(-halfWidth, left_y); } else if (up_x > halfWidth) { double right_t = (halfWidth - p.x) / dir.x; double right_y = p.y + right_t * dir.y; if (right_y < -halfWidth) { return false; } l.p0.set(halfWidth, right_y); } else { l.p0.set(up_x, halfWidth); } } { double down_t = (-halfWidth - p.y) / dir.y; double down_x = p.x + down_t * dir.x; if (down_x < -halfWidth) { double left_t = (-halfWidth - p.x) / dir.x; double left_y = p.y + left_t * dir.y; if (left_y < -halfWidth) { return false; } l.p1.set(-halfWidth, left_y); } else if (down_x > halfWidth) { double right_t = (halfWidth - p.x) / dir.x; double right_y = p.y + right_t * dir.y; if (right_y < -halfWidth) { return false; } l.p1.set(halfWidth, right_y); } else { l.p1.set(down_x, -halfWidth); } } return true; } public static OriLine getVerticalLine(Vector2d v, OriLine line, int type) { double x0 = line.p0.x; double y0 = line.p0.y; double x1 = line.p1.x; double y1 = line.p1.y; double px = v.x; double py = v.y; Vector2d sub0, sub1, sub, sub0b; sub0 = new Vector2d(x0 - px, y0 - py); sub1 = new Vector2d(x1 - px, y1 - py); sub0b = new Vector2d(-sub0.x, -sub0.y); sub = new Vector2d(x1 - x0, y1 - y0); double t = ((sub.x * sub0b.x) + (sub.y * sub0b.y)) / ((sub.x * sub.x) + (sub.y * sub.y)); return new OriLine(x0 + t * sub.x, y0 + t * sub.y, px, py, type); } public static Vector2d getIncenter(Vector2d v0, Vector2d v1, Vector2d v2) { double l0 = Distance(v1, v2); double l1 = Distance(v0, v2); double l2 = Distance(v0, v1); Vector2d vc = new Vector2d(); vc.x = (v0.x * l0 + v1.x * l1 + v2.x * l2) / (l0 + l1 + l2); vc.y = (v0.y * l0 + v1.y * l1 + v2.y * l2) / (l0 + l1 + l2); return vc; } public static Vector2d getBisectorVec(Vector2d v0, Vector2d v1, Vector2d v2) { Vector2d v0_v1 = new Vector2d(); v0_v1.sub(v0, v1); v0_v1.normalize(); Vector2d v2_v1 = new Vector2d(); v2_v1.sub(v2, v1); v2_v1.normalize(); return new Vector2d(v0_v1.x + v2_v1.x, v0_v1.y + v2_v1.y); } public static Vector2d getSymmetricPoint(Vector2d p, Vector2d sp, Vector2d ep) { Vector2d cp = getNearestPointToLine(p, sp, ep); return new Vector2d(2 * cp.x - p.x, 2 * cp.y - p.y); } // Returns the intersection of the semi straight line and the line segment. // Null if not intersect public static Vector2d getCrossPoint(Ray ray, Segment seg) { Vector2d p0 = new Vector2d(ray.p); Vector2d d0 = new Vector2d(ray.dir); Vector2d d1 = new Vector2d(seg.p1.x - seg.p0.x, seg.p1.y - seg.p0.y); Vector2d diff = new Vector2d(seg.p0.x - p0.x, seg.p0.y - p0.y); double det = d1.x * d0.y - d1.y * d0.x; double epsilon = 1.0e-6; if (det * det > epsilon * d0.lengthSquared() * d1.lengthSquared()) { // Lines intersect in a single point. Return both s and t values for // use by calling functions. double invDet = 1.0 / det; double s = (d1.x * diff.y - d1.y * diff.x) * invDet; double t = (d0.x * diff.y - d0.y * diff.x) * invDet; if (t < 0.0 - epsilon || t > 1.0 + epsilon) { return null; } else if (s < 0.0 - epsilon) { return null; } else { Vector2d cp = new Vector2d(); cp.x = (1.0 - t) * seg.p0.x + t * seg.p1.x; cp.y = (1.0 - t) * seg.p0.y + t * seg.p1.y; return cp; } } return null; } // Compute the intersection of straight lines public static Vector2d getCrossPoint(Line l0, Line l1) { Vector2d p0 = new Vector2d(l0.p); Vector2d p1 = new Vector2d(l0.p); p1.add(l0.dir); Vector2d d0 = new Vector2d(p1.x - p0.x, p1.y - p0.y); Vector2d d1 = new Vector2d(l1.dir); Vector2d diff = new Vector2d(l1.p.x - p0.x, l1.p.y - p0.y); double det = d1.x * d0.y - d1.y * d0.x; double epsilon = 1.0e-6; if (det * det > epsilon * d0.lengthSquared() * d1.lengthSquared()) { // Lines intersect in a single point. Return both s and t values for // use by calling functions. double invDet = 1.0 / det; double s = (d1.x * diff.y - d1.y * diff.x) * invDet; double t = (d0.x * diff.y - d0.y * diff.x) * invDet; Vector2d cp = new Vector2d(); cp.x = (1.0 - t) * l1.p.x + t * (l1.p.x + l1.dir.x); cp.y = (1.0 - t) * l1.p.y + t * (l1.p.y + l1.dir.y); return cp; } return null; } public static OriLine getLineByValue(Vector2d sv, double length, double deg_angle, int type) { Vector2d ev = new Vector2d(sv); double rad_angle = Math.toRadians(deg_angle); Vector2d dir = new Vector2d(length * Math.cos(rad_angle), length * Math.sin(rad_angle)); ev.add(dir); return new OriLine(sv, ev, type); } private static Vector2d getNearestPointToLine(Vector2d p, Vector2d sp, Vector2d ep) { double x0 = sp.x; double y0 = sp.y; double x1 = ep.x; double y1 = ep.y; double px = p.x; double py = p.y; Vector2d sub0, sub, sub0b; sub0 = new Vector2d(x0 - px, y0 - py); sub0b = new Vector2d(-sub0.x, -sub0.y); sub = new Vector2d(x1 - x0, y1 - y0); double t = ((sub.x * sub0b.x) + (sub.y * sub0b.y)) / ((sub.x * sub.x) + (sub.y * sub.y)); return new Vector2d(x0 + t * sub.x, y0 + t * sub.y); } public static double DistancePointToSegment(Vector2d p, Vector2d sp, Vector2d ep) { double x0 = sp.x; double y0 = sp.y; double x1 = ep.x; double y1 = ep.y; double px = p.x; double py = p.y; Vector2d sub0, sub, sub0b; sub0 = new Vector2d(x0 - px, y0 - py); sub0b = new Vector2d(-sub0.x, -sub0.y); sub = new Vector2d(x1 - x0, y1 - y0); double t = ((sub.x * sub0b.x) + (sub.y * sub0b.y)) / ((sub.x * sub.x) + (sub.y * sub.y)); if (t < 0.0) { return Distance(px, py, x0, y0); } else if (t > 1.0) { return Distance(px, py, x1, y1); } else { return Distance(x0 + t * sub.x, y0 + t * sub.y, px, py); } } public static double DistancePointToSegment(Vector2d p, Vector2d sp, Vector2d ep, Vector2d nearestPoint) { double x0 = sp.x; double y0 = sp.y; double x1 = ep.x; double y1 = ep.y; double px = p.x; double py = p.y; Vector2d sub0, sub, sub0b; sub0 = new Vector2d(x0 - px, y0 - py); sub0b = new Vector2d(-sub0.x, -sub0.y); sub = new Vector2d(x1 - x0, y1 - y0); double t = ((sub.x * sub0b.x) + (sub.y * sub0b.y)) / ((sub.x * sub.x) + (sub.y * sub.y)); if (t < 0.0) { nearestPoint.set(sp); return Distance(px, py, x0, y0); } else if (t > 1.0) { nearestPoint.set(ep); return Distance(px, py, x1, y1); } else { nearestPoint.set(x0 + t * sub.x, y0 + t * sub.y); return Distance(x0 + t * sub.x, y0 + t * sub.y, px, py); } } public static double DistancePointToLine(Vector2d p, Line line) { double x0 = line.p.x; double y0 = line.p.y; double x1 = line.p.x + line.dir.x; double y1 = line.p.y + line.dir.y; double px = p.x; double py = p.y; Vector2d sub0, sub, sub0b; sub0 = new Vector2d(x0 - px, y0 - py); sub0b = new Vector2d(-sub0.x, -sub0.y); sub = new Vector2d(x1 - x0, y1 - y0); double t = ((sub.x * sub0b.x) + (sub.y * sub0b.y)) / ((sub.x * sub.x) + (sub.y * sub.y)); return Distance(x0 + t * sub.x, y0 + t * sub.y, px, py); } // (Including endpoints) intersection between two line segments public static Vector2d getCrossPoint(Vector2d p0, Vector2d p1, Vector2d q0, Vector2d q1) { Vector2d d0 = new Vector2d(p1.x - p0.x, p1.y - p0.y); Vector2d d1 = new Vector2d(q1.x - q0.x, q1.y - q0.y); Vector2d diff = new Vector2d(q0.x - p0.x, q0.y - p0.y); double det = d1.x * d0.y - d1.y * d0.x; double epsilon = 1.0e-6; if (det * det > epsilon * d0.lengthSquared() * d1.lengthSquared()) { // Lines intersect in a single point. Return both s and t values for // use by calling functions. double invDet = 1.0 / det; double s = (d1.x * diff.y - d1.y * diff.x) * invDet; double t = (d0.x * diff.y - d0.y * diff.x) * invDet; if (t < 0.0 - epsilon || t > 1.0 + epsilon) { return null; } else if (s < 0.0 - epsilon || s > 1.0 + epsilon) { return null; } else { Vector2d cp = new Vector2d(); cp.x = (1.0 - t) * q0.x + t * q1.x; cp.y = (1.0 - t) * q0.y + t * q1.y; return cp; } } return null; } public static Vector2d getCrossPoint(OriLine l0, OriLine l1) { return getCrossPoint(l0, l1, 1.0e-6); } private static Vector2d getCrossPoint(OriLine l0, OriLine l1, double epsilon) { Vector2d p0 = new Vector2d(l0.p0); Vector2d p1 = new Vector2d(l0.p1); Vector2d d0 = new Vector2d(p1.x - p0.x, p1.y - p0.y); Vector2d d1 = new Vector2d(l1.p1.x - l1.p0.x, l1.p1.y - l1.p0.y); Vector2d diff = new Vector2d(l1.p0.x - p0.x, l1.p0.y - p0.y); double det = d1.x * d0.y - d1.y * d0.x; if (det * det > epsilon * d0.lengthSquared() * d1.lengthSquared()) { // Lines intersect in a single point. Return both s and t values for // use by calling functions. double invDet = 1.0 / det; double s = (d1.x * diff.y - d1.y * diff.x) * invDet; double t = (d0.x * diff.y - d0.y * diff.x) * invDet; if (t < 0.0 - epsilon || t > 1.0 + epsilon) { return null; } else if (s < 0.0 - epsilon || s > 1.0 + epsilon) { return null; } else { Vector2d cp = new Vector2d(); cp.x = (1.0 - t) * l1.p0.x + t * l1.p1.x; cp.y = (1.0 - t) * l1.p0.y + t * l1.p1.y; return cp; } } return null; } // Obtain the parameters for the intersection of the segments p0-p1 and q0-q1 // The param stores the position of the intersection // Returns false if parallel public static boolean getCrossPointParam(Vector2d p0, Vector2d p1, Vector2d q0, Vector2d q1, double[] param) { Vector2d d0 = new Vector2d(p1.x - p0.x, p1.y - p0.y); Vector2d d1 = new Vector2d(q1.x - q0.x, q1.y - q0.y); Vector2d diff = new Vector2d(q0.x - p0.x, q0.y - p0.y); double det = d1.x * d0.y - d1.y * d0.x; double epsilon = 1.0e-6; if (det * det > epsilon * d0.lengthSquared() * d1.lengthSquared()) { // Lines intersect in a single point. Return both s and t values for // use by calling functions. double invDet = 1.0 / det; param[0] = (d1.x * diff.y - d1.y * diff.x) * invDet; param[1] = (d0.x * diff.y - d0.y * diff.x) * invDet; return true; } return false; } public static boolean isRightSide(Vector2d p, Line line) { Vector3d lineDir = new Vector3d(line.dir.x, line.dir.y, 0); Vector3d pointDir = new Vector3d(p.x - line.p.x, p.y - line.p.y, 0); Vector3d crossVec = new Vector3d(); crossVec.cross(pointDir, lineDir); return crossVec.z > 0; } public static double Distance(Vector2d p, Line line, double[] param) { Vector2d sub0, sub, sub0b; double x0 = line.p.x; double y0 = line.p.y; double x1 = line.p.x + line.dir.x; double y1 = line.p.y + line.dir.y; double px = p.x; double py = p.y; sub0 = new Vector2d(x0 - px, y0 - py); sub0b = new Vector2d(-sub0.x, -sub0.y); sub = new Vector2d(x1 - x0, y1 - y0); double t = ((sub.x * sub0b.x) + (sub.y * sub0b.y)) / ((sub.x * sub.x) + (sub.y * sub.y)); param[0] = t; return Distance(x0 + t * sub.x, y0 + t * sub.y, px, py); } // Investigate the relationship between the point q with the segment p0, p1 public static boolean CCWcheck(Vector2d p0, Vector2d p1, Vector2d q) { double dx1, dx2, dy1, dy2; dx1 = p1.x - p0.x; dy1 = p1.y - p0.y; dx2 = q.x - p0.x; dy2 = q.y - p0.y; if (dx1 * dy2 > dy1 * dx2) { return true; } return false; } public static double Distance(Vector2d p, Line line) { Vector2d sub0, sub, sub0b; double x0 = line.p.x; double y0 = line.p.y; double x1 = line.p.x + line.dir.x; double y1 = line.p.y + line.dir.y; double px = p.x; double py = p.y; sub0 = new Vector2d(x0 - px, y0 - py); sub0b = new Vector2d(-sub0.x, -sub0.y); sub = new Vector2d(x1 - x0, y1 - y0); double t = ((sub.x * sub0b.x) + (sub.y * sub0b.y)) / ((sub.x * sub.x) + (sub.y * sub.y)); return Distance(x0 + t * sub.x, y0 + t * sub.y, px, py); } private static double Distance(double x0, double y0, double x1, double y1) { return Math.sqrt((x0 - x1) * (x0 - x1) + (y0 - y1) * (y0 - y1)); } public static boolean isContainsPointFoldedFace(OriFace face, Vector2d v, double eps) { int heNum = face.halfedges.size(); // If its on the faces edge, return false for (int i = 0; i < heNum; i++) { OriHalfedge he = face.halfedges.get(i); if (GeomUtil.DistancePointToSegment(v, he.positionAfterFolded, he.next.positionAfterFolded) < eps) { return false; } } OriHalfedge baseHe = face.halfedges.get(0); boolean baseFlg = GeomUtil.CCWcheck(baseHe.positionAfterFolded, baseHe.next.positionAfterFolded, v); for (int i = 1; i < heNum; i++) { OriHalfedge he = face.halfedges.get(i); if (GeomUtil.CCWcheck(he.positionAfterFolded, he.next.positionAfterFolded, v) != baseFlg) { return false; } } return true; } // private static int whichSide(Triangle tri, Vector2d P, Vector2d D) { // // // Vertices are projected to the form P+t*D. Return value is +1 if all // // t > 0, -1 if all t < 0, 0 otherwise, in which case the line splits the // // triangle. // // int iPositive = 0, iNegative = 0, iZero = 0; // // for (int i = 0; i < 3; i++) { // Vector2d vi_p = new Vector2d(); // vi_p.set(tri.p[i].x - P.x, tri.p[i].y - P.y); // double fT = D.dot(vi_p); // // if (fT > 0.0f) { // iPositive++; // } else if (fT < 0.0f) { // iNegative++; // } else { // iZero++; // } // // if (iPositive > 0 && iNegative > 0) { // return 0; // } // } // // return (iZero == 0 ? (iPositive > 0 ? 1 : -1) : 0); // } public static boolean isFaceOverlap(OriFace face0, OriFace face1, double eps) { Vector2d center0 = new Vector2d(); Vector2d center1 = new Vector2d(); // If the vertices of face0 are on face1, true for (OriHalfedge he : face0.halfedges) { if (isContainsPointFoldedFace(face1, he.positionAfterFolded, eps)) { return true; } center0.add(he.positionAfterFolded); } // If the vertices of face1 are on face0, true for (OriHalfedge he : face1.halfedges) { if (isContainsPointFoldedFace(face0, he.positionAfterFolded, eps)) { return true; } center1.add(he.positionAfterFolded); } center0.scale(1.0 / face0.halfedges.size()); // If the gravity center of face0 is on face1, true if (isContainsPointFoldedFace(face1, center0, eps)) { return true; } center1.scale(1.0 / face1.halfedges.size()); // If the gravity center of face1 is on face0, true if (isContainsPointFoldedFace(face0, center1, eps)) { return true; } // If the outline of face0 intersects face1`s, true for (OriHalfedge he0 : face0.halfedges) { if (isLineCrossFace(face1, he0, eps)) { return true; } } for (OriHalfedge he1 : face1.halfedges) { if (isLineCrossFace(face0, he1, eps)) { return true; } } return false; } public static boolean isLineCrossFace(OriFace face, OriHalfedge heg, double eps) { Vector2d p1 = heg.positionAfterFolded; Vector2d p2 = heg.next.positionAfterFolded; Vector2d dir = new Vector2d(); dir.sub(p2, p1); Line heLine = new Line(p1, dir); for (OriHalfedge he : face.halfedges) { // About the relationship of each outline`s segment if (GeomUtil.DistancePointToLine(he.positionAfterFolded, heLine) < eps && GeomUtil.DistancePointToLine(he.next.positionAfterFolded, heLine) < eps) { return false; } } Vector2d preCrossPoint = null; for (OriHalfedge he : face.halfedges) { Vector2d cp = GeomUtil.getCrossPoint(he.positionAfterFolded, he.next.positionAfterFolded, heg.positionAfterFolded, heg.next.positionAfterFolded); if (cp == null) { continue; } if (preCrossPoint == null) { preCrossPoint = cp; } else { if (GeomUtil.Distance(cp, preCrossPoint) > eps) { // Intersects at least in two places return true; } } } // If at least one of the endpoints is fully contained if (GeomUtil.isContainsPointFoldedFace(face, heg.positionAfterFolded, eps)) { return true; } if (GeomUtil.isContainsPointFoldedFace(face, heg.next.positionAfterFolded, eps)) { return true; } return false; } }