All Downloads are FREE. Search and download functionalities are using the official Maven repository.

georegression.geometry.polygon.AreaIntersectionPolygon2D_F64 Maven / Gradle / Ivy

/*
 * Copyright (C) 2022, Peter Abeles. All Rights Reserved.
 *
 * This file is part of Geometric Regression Library (GeoRegression).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package georegression.geometry.polygon;

import georegression.geometry.UtilPolygons2D_F64;
import georegression.struct.point.Point2D_I32;
import georegression.struct.shapes.Polygon2D_F64;
import georegression.struct.shapes.Rectangle2D_F64;
import org.ddogleg.struct.DogArray;

/**
 * 

* Computes the area of intersection between two convex polygons. Port of code found at [1] and Java version by Lagado. *

* * [1] http://www.cap-lore.com/MathPhys/IP/ * * @author Peter Abeles */ @SuppressWarnings("NullAway.Init") public class AreaIntersectionPolygon2D_F64 { static final double gamut = (double)500000000.0; static final double mid = gamut/(double)2.0; private long ssss; private double sclx; private double scly; // Internal workspace DogArray ipa = new DogArray<>(Vertex::new, Vertex::reset); DogArray ipb = new DogArray<>(Vertex::new, Vertex::reset); Rectangle2D_F64 bbox = new Rectangle2D_F64(); /** *

Computes the area of the intersection between the two polygons.

* * Note: the area result has little more accuracy than a float * This is true even if the polygon is specified with doubles. * * @param a Polygon A * @param b Polygon B * @return area of intersection. Negative if the order (CW vs CCW) do not match. */ public double computeArea( Polygon2D_F64 a, Polygon2D_F64 b ) { ssss = 0; sclx = 0; scly = 0; return inter(a, b); } //-------------------------------------------------------------------------- static class Rng { int mn; int mx; Rng( int mn, int mx ) { this.mn = mn; this.mx = mx; } } static class Vertex { Point2D_I32 ip; Rng rx; Rng ry; int in; @SuppressWarnings("NullAway") public void reset() { ip = null; rx = null; ry = null; in = 0; } } //-------------------------------------------------------------------------- private static void range( Polygon2D_F64 points, Rectangle2D_F64 bbox ) { UtilPolygons2D_F64.bounding(points, bbox); } private static long area( Point2D_I32 a, Point2D_I32 p, Point2D_I32 q ) { return (long)p.x*q.y - (long)p.y*q.x + (long)a.x*(p.y - q.y) + (long)a.y*(q.x - p.x); } private static boolean ovl( Rng p, Rng q ) { return p.mn < q.mx && q.mn < p.mx; } private void cntrib( int f_x, int f_y, int t_x, int t_y, int w ) { ssss += (long)w*(t_x - f_x)*(t_y + f_y)/2; } private void fit( Polygon2D_F64 x, Vertex[] ix, int fudge, Rectangle2D_F64 B ) { int c = x.size(); while (c-- > 0) { ix[c] = new Vertex(); ix[c].ip = new Point2D_I32(); ix[c].ip.x = ((int)((x.get(c).getX() - B.p0.x)*sclx - mid) & ~7) | fudge | (c & 1); ix[c].ip.y = ((int)((x.get(c).getY() - B.p0.y)*scly - mid) & ~7) | fudge; } ix[0].ip.y += x.size() & 1; ix[x.size()] = ix[0]; c = x.size(); while (c-- > 0) { ix[c].rx = ix[c].ip.x < ix[c + 1].ip.x ? new Rng(ix[c].ip.x, ix[c + 1].ip.x) : new Rng(ix[c + 1].ip.x, ix[c].ip.x); ix[c].ry = ix[c].ip.y < ix[c + 1].ip.y ? new Rng(ix[c].ip.y, ix[c + 1].ip.y) : new Rng(ix[c + 1].ip.y, ix[c].ip.y); ix[c].in = 0; } } private void cross( Vertex a, Vertex b, Vertex c, Vertex d, double a1, double a2, double a3, double a4 ) { double r1 = a1/((double) a1 + a2); double r2 = a3/((double) a3 + a4); cntrib((int)(a.ip.x + r1*(b.ip.x - a.ip.x)), (int)(a.ip.y + r1*(b.ip.y - a.ip.y)), b.ip.x, b.ip.y, 1); cntrib(d.ip.x, d.ip.y, (int)(c.ip.x + r2*(d.ip.x - c.ip.x)), (int)(c.ip.y + r2*(d.ip.y - c.ip.y)), 1); ++a.in; --c.in; } private void inness( Vertex[] P, int cP, Vertex[] Q, int cQ ) { int s = 0; int c = cQ; Point2D_I32 p = P[0].ip; while (c-- > 0) { if (Q[c].rx.mn < p.x && p.x < Q[c].rx.mx) { boolean sgn = 0 < area(p, Q[c].ip, Q[c + 1].ip); s += (sgn != Q[c].ip.x < Q[c + 1].ip.x) ? 0 : (sgn ? -1 : 1); } } for (int j = 0; j < cP; ++j) { if (s != 0) cntrib(P[j].ip.x, P[j].ip.y, P[j + 1].ip.x, P[j + 1].ip.y, s); s += P[j].in; } } //------------------------------------------------------------------------- private double inter( Polygon2D_F64 a, Polygon2D_F64 b ) { if (a.size() < 3 || b.size() < 3) return 0; // Recycle / allocate memory ipa.reset().resize(a.size() + 1); ipb.reset().resize(b.size() + 1); Vertex[] ipa = this.ipa.data; Vertex[] ipb = this.ipb.data; bbox.setTo(Double.MAX_VALUE, Double.MAX_VALUE, -Double.MAX_VALUE, -Double.MAX_VALUE); range(a, bbox); range(b, bbox); double rngx = bbox.p1.x - bbox.p0.x; sclx = gamut/rngx; double rngy = bbox.p1.y - bbox.p0.y; scly = gamut/rngy; double ascale = sclx*scly; fit(a, ipa, 0, bbox); fit(b, ipb, 2, bbox); for (int j = 0; j < a.size(); ++j) { for (int k = 0; k < b.size(); ++k) { if (ovl(ipa[j].rx, ipb[k].rx) && ovl(ipa[j].ry, ipb[k].ry)) { long a1 = -area(ipa[j].ip, ipb[k].ip, ipb[k + 1].ip); long a2 = area(ipa[j + 1].ip, ipb[k].ip, ipb[k + 1].ip); boolean o = a1 < 0; if (o == a2 < 0) { long a3 = area(ipb[k].ip, ipa[j].ip, ipa[j + 1].ip); long a4 = -area(ipb[k + 1].ip, ipa[j].ip, ipa[j + 1].ip); if (a3 < 0 == a4 < 0) { if (o) cross(ipa[j], ipa[j + 1], ipb[k], ipb[k + 1], a1, a2, a3, a4); else cross(ipb[k], ipb[k + 1], ipa[j], ipa[j + 1], a3, a4, a1, a2); } } } } } inness(ipa, a.size(), ipb, b.size()); inness(ipb, b.size(), ipa, a.size()); return ssss/ascale; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy