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);
136 for (
unsigned int i = 0; i < IV.rows(); ++i)
138 unsigned int j = ((i + 1) % (
int)IV.rows());
139 const Eigen::RowVector2d &q1 = IV.row(i).head<2>();
140 const Eigen::RowVector2d &q2 = IV.row(j).head<2>();
159 Eigen::MatrixXi F(1, OV.rows());
160 for (
int i = 0; i < OV.rows(); ++i)
165 igl::barycenter(OV, F, BC);
166 int n = std::min(3, (
int)BC.cols());
168 bary.head(n) = BC.row(0).head(n);
177#ifdef POLYFEM_WITH_CLIPPER
178 using namespace ClipperLib;
183 co.AddPath(PolygonClipping::toClipperPolygon(IV), jtSquare, etClosedPolygon);
187 co.Execute(solution, cInt(eps * DOUBLE_TO_INT_SCALE_FACTOR));
188 assert(solution.size() == 1);
191 OV = PolygonClipping::fromClipperPolygon(solution.front());
193 throw std::runtime_error(
"Compile with clipper!");
202 typedef Eigen::RowVector2d Vec2d;
204 double inline det(Vec2d u, Vec2d v)
206 return u[0] * v[1] - u[1] * v[0];
210 bool intersect_segment(
const Vec2d &a,
const Vec2d &b, Vec2d c, Vec2d d, Vec2d &ans)
212 const double eps = 1e-10;
213 double x = det(c - a, d - c);
214 double y = det(b - a, a - c);
215 double z = det(b - a, d - c);
216 if (std::abs(
z) < eps ||
x * z < 0 || x * z >
z *
z ||
y * z < 0 || y * z >
z *
z)
218 ans = c + (d - c) *
y /
z;
222 bool is_point_inside(
const Eigen::MatrixXd &poly,
const Vec2d &outside,
const Vec2d &query)
225 bool tmp, ans =
false;
226 for (
long i = 0; i < poly.rows(); ++i)
229 tmp = intersect_segment(query, outside, poly.row(i), poly.row((i + 1) % n), m);
241 assert(IV.cols() == 2);
242 Eigen::RowVector2d minV = IV.colwise().minCoeff().array();
243 Eigen::RowVector2d maxV = IV.colwise().maxCoeff().array();
244 Eigen::RowVector2d center = 0.5 * (maxV + minV);
245 Eigen::RowVector2d outside(2.0 * minV(0) - center(0), center(1));
246 inside.resize(Q.rows());
248 for (
long i = 0; i < Q.rows(); ++i)
250 inside[i] = is_point_inside(IV, outside, Q.row(i));
263 assert(poly.rows() >= 2);
266 auto length = [&](
int i) {
267 return (poly.row(i) - poly.row((i + 1) % n)).norm();
272 double max_length = 0;
273 double total_length = 0;
274 for (
int i = 0; i < n; ++i)
276 double len = length(i);
278 if (len > max_length)
287 S.resize(num_samples, poly.cols());
288 double spacing = total_length / num_samples;
289 double offset = length(i0) / 2.0;
290 double distance_to_next = length(i0);
291 for (
int s = 0, i = i0; s < num_samples; ++s)
293 double distance_to_sample = s * spacing + offset;
294 while (distance_to_sample > distance_to_next)
297 distance_to_next += length(i);
299 double t = (distance_to_next - distance_to_sample) / length(i);
300 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.