4#include <geogram/numerics/predicates.h>
5#include <igl/barycenter.h>
12 inline GEO::Sign point_is_in_half_plane(
13 const Eigen::RowVector2d &p,
const Eigen::RowVector2d &q1,
const Eigen::RowVector2d &q2)
15 return GEO::PCK::orient_2d(q1.data(), q2.data(), p.data());
18 inline bool intersect_segments(
19 const Eigen::RowVector2d &p1,
const Eigen::RowVector2d &p2,
20 const Eigen::RowVector2d &q1,
const Eigen::RowVector2d &q2,
21 Eigen::RowVector2d &result)
24 Eigen::RowVector2d Vp = p2 - p1;
25 Eigen::RowVector2d Vq = q2 - q1;
26 Eigen::RowVector2d pq = q1 - p1;
33 double delta = a * d - b * c;
39 double tp = (d * pq(0) - b * pq(1)) / delta;
41 result << (1.0 - tp) * p1(0) + tp * p2(0),
42 (1.0 - tp) * p1(1) + tp * p2(1);
52 const Eigen::RowVector2d &q1,
const Eigen::RowVector2d &q2, Eigen::MatrixXd &P_out)
55 assert(P_in.cols() == 2);
56 std::vector<Eigen::RowVector2d> result;
66 if (point_is_in_half_plane(P_in.row(0), q1, q2))
78 Eigen::RowVector2d prev_p = P_in.row(P_in.rows() - 1);
79 Sign prev_status = point_is_in_half_plane(prev_p, q1, q2);
81 for (
unsigned int i = 0; i < P_in.rows(); ++i)
83 Eigen::RowVector2d p = P_in.row(i);
84 Sign status = point_is_in_half_plane(p, q1, q2);
85 if (status != prev_status && status != ZERO && prev_status != ZERO)
87 Eigen::RowVector2d intersect;
88 if (intersect_segments(prev_p, p, q1, q2, intersect))
90 result.push_back(intersect);
109 prev_status = status;
112 P_out.resize((
int)result.size(), 2);
113 for (
size_t i = 0; i < result.size(); ++i)
115 P_out.row((
int)i) = result[i];
123 assert(IV.cols() == 2 || IV.cols() == 3);
126 Eigen::MatrixXd src, dst;
127 const auto &minV = IV.colwise().minCoeff().array();
128 const auto &maxV = IV.colwise().maxCoeff().array();
130 src.row(0) << minV(0), minV(1);
131 src.row(1) << maxV(0), minV(1);
132 src.row(2) << maxV(0), maxV(1);
133 src.row(3) << minV(0), maxV(1);
138 for (
unsigned int i = 0; i < IV.rows(); ++i)
140 unsigned int j = ((i + 1) % (
int)IV.rows());
141 const Eigen::RowVector2d &q1 = IV.row(i).head<2>();
142 const Eigen::RowVector2d &q2 = IV.row(j).head<2>();
161 Eigen::MatrixXi F(1, OV.rows());
162 for (
int i = 0; i < OV.rows(); ++i)
167 igl::barycenter(OV, F, BC);
170 int n = std::min(3, (
int)BC.cols());
172 bary.head(n) = BC.row(0).head(n);
181#ifdef POLYFEM_WITH_CLIPPER
182 using namespace ClipperLib;
187 co.AddPath(PolygonClipping::toClipperPolygon(IV), jtSquare, etClosedPolygon);
191 co.Execute(solution, cInt(eps * DOUBLE_TO_INT_SCALE_FACTOR));
192 assert(solution.size() == 1);
195 OV = PolygonClipping::fromClipperPolygon(solution.front());
197 throw std::runtime_error(
"Compile with clipper!");
206 typedef Eigen::RowVector2d Vec2d;
208 double inline det(Vec2d u, Vec2d v)
210 return u[0] * v[1] - u[1] * v[0];
214 bool intersect_segment(
const Vec2d &a,
const Vec2d &b, Vec2d c, Vec2d d, Vec2d &ans)
216 const double eps = 1e-10;
217 double x = det(c - a, d - c);
218 double y = det(b - a, a - c);
219 double z = det(b - a, d - c);
220 if (std::abs(
z) < eps ||
x * z < 0 || x * z >
z *
z ||
y * z < 0 || y * z >
z *
z)
222 ans = c + (d - c) *
y /
z;
226 bool is_point_inside(
const Eigen::MatrixXd &poly,
const Vec2d &outside,
const Vec2d &query)
229 bool tmp, ans =
false;
230 for (
long i = 0; i < poly.rows(); ++i)
233 tmp = intersect_segment(query, outside, poly.row(i), poly.row((i + 1) % n), m);
245 assert(IV.cols() == 2);
246 Eigen::RowVector2d minV = IV.colwise().minCoeff().array();
247 Eigen::RowVector2d maxV = IV.colwise().maxCoeff().array();
248 Eigen::RowVector2d center = 0.5 * (maxV + minV);
249 Eigen::RowVector2d outside(2.0 * minV(0) - center(0), center(1));
250 inside.resize(Q.rows());
252 for (
long i = 0; i < Q.rows(); ++i)
254 inside[i] = is_point_inside(IV, outside, Q.row(i));
267 assert(poly.rows() >= 2);
270 auto length = [&](
int i) {
271 return (poly.row(i) - poly.row((i + 1) % n)).norm();
276 double max_length = 0;
277 double total_length = 0;
278 for (
int i = 0; i < n; ++i)
280 double len = length(i);
282 if (len > max_length)
291 S.resize(num_samples, poly.cols());
292 double spacing = total_length / num_samples;
293 double offset = length(i0) / 2.0;
294 double distance_to_next = length(i0);
295 for (
int s = 0, i = i0; s < num_samples; ++s)
297 double distance_to_sample = s * spacing + offset;
298 while (distance_to_sample > distance_to_next)
301 distance_to_next += length(i);
303 double t = (distance_to_next - distance_to_sample) / length(i);
304 S.row(s) = t * poly.row(i) + (1.0 - t) * poly.row((i + 1) % n);
void clip_polygon_by_half_plane(const Eigen::MatrixXd &P, const Eigen::RowVector2d &q1, const Eigen::RowVector2d &q2, Eigen::MatrixXd &result)
Clip a polygon by a half-plane.
void sample_polygon(const Eigen::MatrixXd &IV, int num_samples, Eigen::MatrixXd &S)
Sample points on a polygon, evenly spaced from each other.
void offset_polygon(const Eigen::MatrixXd &IV, Eigen::MatrixXd &OV, double eps)
Compute offset polygon.
void compute_visibility_kernel(const Eigen::MatrixXd &IV, Eigen::MatrixXd &OV)
Determine the kernel of the given polygon.
bool is_star_shaped(const Eigen::MatrixXd &IV, Eigen::RowVector3d &bary)
Determine whether a polygon is star-shaped or not.
int is_inside(const Eigen::MatrixXd &IV, const Eigen::MatrixXd &Q, std::vector< bool > &inside)
Compute whether points are inside a polygon.