Files
2026-01-23 17:03:45 +08:00

178 lines
4.9 KiB
C++

/*
1 Poly.cpp is an interface for external polygon libraries.
This is mainly used to calculate polygon area and intersections.
Currently, boost is utilized to perform these calculations.
*/
#include "interpolate.h"
#include <cfloat>
/*
1.1 Constructor, which creates a polygon from the given face and
transformations.
*/
Poly::Poly(Face& f, double offx, double offy, double scalex, double scaley,
bool withholes) {
Pt off(offx, offy);
Pt scale(scalex, scaley);
for (unsigned int i = 0; i < f.v.size(); i++) {
Pt p = ((f.v[i].s) - off) * scale / SCALEOUT;
points.push_back(p);
}
}
/*
1.2 ~Area~ returns the area of this Poly object.
*/
double Poly::Area() {
return inter(points, points);
}
/*
1.3 ~IntersectionArea~ returns the intersecting area of this Poly object
with the given Poly object.
*/
double Poly::IntersectionArea(Poly& p) {
cerr << "Calling inter...\n";
double ret = inter(p.points, points);
cerr << "Finished inter...\n";
return ret;
}
void Poly::range(vector<Pt>& points, int c, Box& bbox) {
while (c-- > 0) {
bbox.min.x = min(bbox.min.x, points[c].x);
bbox.min.y = min(bbox.min.y, points[c].y);
bbox.max.x = max(bbox.max.x, points[c].x);
bbox.max.y = max(bbox.max.y, points[c].y);
}
}
double Poly::area(IPoint a, IPoint p, IPoint 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);
}
bool Poly::ovl(Rng p, Rng q) {
return (p.mn < q.mx) && (q.mn < p.mx);
}
void Poly::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;
}
void Poly::fit(vector<Pt>& x, int cx, vector<Vertex>& ix, int fudge, Box& b) {
int c = cx;
while (c-- > 0) {
Vertex v;
v.ip.x = ((int)((x[c].x - b.min.x) * sclx - mid) & ~7) | fudge|(c & 1);
v.ip.y = ((int)((x[c].y - b.min.y) * scly - mid) & ~7) | fudge;
ix[c] = v;
}
ix[0].ip.y += (cx&1);
ix[cx] = ix[0];
c = cx;
while (c-- > 0) {
ix[c].rx = ix[c].ip.x < ix[c + 1].ip.x ?
Rng(ix[c].ip.x, ix[c + 1].ip.x) : Rng(ix[c + 1].ip.x, ix[c].ip.x);
ix[c].ry = ix[c].ip.y < ix[c + 1].ip.y ?
Rng(ix[c].ip.y, ix[c + 1].ip.y) : Rng(ix[c + 1].ip.y, ix[c].ip.y);
ix[c].in = 0;
}
}
void Poly::cross(Vertex& a, Vertex& b, Vertex& c, Vertex& d,
double a1, double a2, double a3, double a4) {
double r1 = a1 / (a1 + a2);
double r2 = a3 / (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--;
}
void Poly::inness(vector<Vertex>& P, int cP, vector<Vertex>& Q, int cQ) {
int s = 0;
int c = cQ;
IPoint p = P[0].ip;
while (c-- > 0) {
if (Q[c].rx.mn < p.x && p.x < Q[c].rx.mx) {
bool 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;
}
}
double Poly::inter(vector<Pt> a, vector<Pt> b) {
sclx = scly = ssss = 0.0;
int na = a.size();
int nb = b.size();
vector<Vertex> ipa(na + 1, Vertex());
vector<Vertex> ipb(nb + 1, Vertex());
Box bbox(Pt(DBL_MAX, DBL_MAX), Pt(-DBL_MAX, -DBL_MAX));
if (na < 3 || nb < 3)
return 0;
range(a, na, bbox);
range(b, nb, bbox);
double rngx = bbox.max.x - bbox.min.x;
sclx = gamut / rngx;
double rngy = bbox.max.y - bbox.min.y;
scly = gamut / rngy;
double ascale = sclx * scly;
fit(a, na, ipa, 0, bbox);
fit(b, nb, ipb, 2, bbox);
for (int j = 0; j < na; j++) {
for (int k = 0; k < nb; 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);
bool 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, na, ipb, nb);
inness(ipb, nb, ipa, na);
return ssss / ascale;
}