-
Notifications
You must be signed in to change notification settings - Fork 107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
#2412 - User can't correctly save (or make a layout) to RDF/RXN reaction several products or with separate positioned molecules #2717
base: master
Are you sure you want to change the base?
Changes from 17 commits
36a43a5
0250bf9
fae6434
5119605
abdef6a
65cdf25
3677290
72912cd
4ee56c0
c7fd02f
0cc03e8
8ffa3be
792403a
87b661e
2d0f08e
c02ddbc
008d2cc
60af8c6
496cf10
c0a2308
0654e27
55983b7
2fe842c
cf38192
2f50790
e34c99f
5437b37
be0090c
c8d7bf6
729a3f8
7817c01
951eaca
68a4bce
fdfd7ec
1d7adea
95cff12
4099878
a0d2576
5685a5c
c70713f
84360d7
b992b90
94f043b
d6ec3ca
b68a6d3
43f7f37
9bb518d
deedf03
1edc9c1
bc8b287
99d0629
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,7 @@ | |
#include <cmath> | ||
#include <iostream> | ||
#include <limits> | ||
#include <vector> | ||
|
||
#include "base_c/defs.h" | ||
#include "base_cpp/exception.h" | ||
|
@@ -279,6 +280,7 @@ namespace indigo | |
DLLEXPORT void rotateL(float si, float co); | ||
DLLEXPORT void rotateL(Vec2f vec); | ||
DLLEXPORT void rotateAroundSegmentEnd(const Vec2f& a, const Vec2f& b, float angle); | ||
DLLEXPORT float relativeCross(const Vec2f& a, const Vec2f& b); | ||
|
||
DLLEXPORT static float distSqr(const Vec2f& a, const Vec2f& b); | ||
DLLEXPORT static float dist(const Vec2f& a, const Vec2f& b); | ||
|
@@ -822,5 +824,234 @@ namespace indigo | |
float _d; | ||
}; | ||
|
||
inline bool isPointInPolygon(const Vec2f& p, const std::vector<Vec2f>& poly) | ||
{ | ||
bool in = false; | ||
even1024 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
for (size_t i = 0, n = poly.size(); i < n; ++i) | ||
{ | ||
size_t j = (i + 1) % n; | ||
bool intersect = | ||
((poly[i].y > p.y) != (poly[j].y > p.y)) && (p.x < (poly[j].x - poly[i].x) * (p.y - poly[i].y) / (poly[j].y - poly[i].y) + poly[i].x); | ||
if (intersect) | ||
in = !in; | ||
} | ||
return in; | ||
} | ||
|
||
inline std::vector<Vec2f> getPointsInsidePolygon(const std::vector<Vec2f>& polygon1, const std::vector<Vec2f>& polygon2) | ||
{ | ||
std::vector<Vec2f> result; | ||
result.reserve(polygon1.size()); | ||
std::copy_if(polygon1.begin(), polygon1.end(), std::back_inserter(result), [&](auto& p) { return isPointInPolygon(p, polygon2); }); | ||
return result; | ||
} | ||
|
||
inline bool isPointInConvexPolygon(const Vec2f& p, const std::vector<Vec2f>& poly) | ||
even1024 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
auto cross = [](const Vec2f& a, const Vec2f& b, const Vec2f& c) { return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x); }; | ||
bool sign = cross(poly.back(), poly[0], p) < 0; | ||
for (size_t i = 0, n = poly.size(); i < n; ++i) | ||
if ((cross(poly[i], poly[(i + 1) % n], p) < 0) != sign) | ||
return false; | ||
return true; | ||
} | ||
|
||
inline std::vector<Vec2f> getPointsInsideConvexPolygon(const std::vector<Vec2f>& polygon1, const std::vector<Vec2f>& polygon2) | ||
{ | ||
std::vector<Vec2f> result; | ||
result.reserve(polygon1.size()); | ||
std::copy_if(polygon1.begin(), polygon1.end(), std::back_inserter(result), [&](auto& p) { return isPointInConvexPolygon(p, polygon2); }); | ||
return result; | ||
} | ||
|
||
inline float convexPolygonArea(const std::vector<Vec2f>& poly) | ||
even1024 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
float area = 0.0f; | ||
size_t n = poly.size(); | ||
for (size_t i = 0; i < n; ++i) | ||
{ | ||
const Vec2f& p1 = poly[i]; | ||
const Vec2f& p2 = poly[(i + 1) % n]; | ||
area += Vec2f::cross(p1, p2); | ||
} | ||
return std::abs(area) * 0.5f; | ||
} | ||
|
||
inline bool isInside(const Vec2f& edgeStart, const Vec2f& edgeEnd, const Vec2f& point) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it should be named isNormalNegative, and comment about "check sign of normal to (edgeStart,edgeEnd),(edgeStart,point)" will be usefull |
||
{ | ||
return (edgeEnd.x - edgeStart.x) * (point.y - edgeStart.y) - (edgeEnd.y - edgeStart.y) * (point.x - edgeStart.x) <= 0; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it just |
||
} | ||
|
||
inline bool convexPolygonsIntersect(const std::vector<Vec2f>& poly1, const std::vector<Vec2f>& poly2) | ||
{ | ||
auto project = [](const std::vector<Vec2f>& poly, const Vec2f& axis) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add comment that this is "Separating axis collision detection" |
||
auto [min, max] = std::minmax_element(poly.begin(), poly.end(), [&](const Vec2f& a, const Vec2f& b) { return a * axis < b * axis; }); | ||
return std::pair{*min * axis, *max * axis}; | ||
}; | ||
|
||
auto overlap = [](const auto& range1, const auto& range2) { return !(range1.first > range2.second || range2.first > range1.second); }; | ||
|
||
auto getNormals = [](const std::vector<Vec2f>& poly) { | ||
std::vector<Vec2f> normals; | ||
for (size_t i = 0; i < poly.size(); ++i) | ||
{ | ||
Vec2f edge = poly[(i + 1) % poly.size()] - poly[i]; | ||
normals.emplace_back(-edge.y, edge.x); | ||
} | ||
return normals; | ||
}; | ||
|
||
auto normals1 = getNormals(poly1), normals2 = getNormals(poly2); | ||
for (const auto& normal : normals1) | ||
if (!overlap(project(poly1, normal), project(poly2, normal))) | ||
return false; | ||
for (const auto& normal : normals2) | ||
if (!overlap(project(poly1, normal), project(poly2, normal))) | ||
return false; | ||
return true; | ||
} | ||
|
||
inline Vec2f computeIntersection(const Vec2f& s, const Vec2f& e, const Vec2f& edgeStart, const Vec2f& edgeEnd) | ||
{ | ||
float dx1 = e.x - s.x, dy1 = e.y - s.y, dx2 = edgeEnd.x - edgeStart.x, dy2 = edgeEnd.y - edgeStart.y; | ||
float det = dx1 * dy2 - dy1 * dx2; | ||
if (std::abs(det) < 1e-6) | ||
return s; | ||
float t = ((edgeStart.x - s.x) * dy2 - (edgeStart.y - s.y) * dx2) / det; | ||
return {s.x + t * dx1, s.y + t * dy1}; | ||
} | ||
|
||
inline std::vector<Vec2f> convexClip(const std::vector<Vec2f>& subject, const std::vector<Vec2f>& clip) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add comment that this is "Sutherland–Hodgman algorithm" |
||
{ | ||
std::vector<Vec2f> result = subject; | ||
for (size_t i = 0; i < clip.size(); ++i) | ||
{ | ||
const Vec2f& edgeStart = clip[i]; | ||
const Vec2f& edgeEnd = clip[(i + 1) % clip.size()]; | ||
std::vector<Vec2f> input = std::move(result); | ||
result.clear(); | ||
if (input.empty()) | ||
break; | ||
Vec2f prev = input.back(); | ||
for (const Vec2f& curr : input) | ||
{ | ||
if (isInside(edgeStart, edgeEnd, curr)) | ||
{ | ||
if (!isInside(edgeStart, edgeEnd, prev)) | ||
result.push_back(computeIntersection(prev, curr, edgeStart, edgeEnd)); | ||
result.push_back(curr); | ||
} | ||
else if (isInside(edgeStart, edgeEnd, prev)) | ||
{ | ||
result.push_back(computeIntersection(prev, curr, edgeStart, edgeEnd)); | ||
} | ||
prev = curr; | ||
} | ||
} | ||
return result; | ||
} | ||
|
||
inline float computeConvexContainment(const std::vector<Vec2f>& poly1, const std::vector<Vec2f>& poly2) | ||
{ | ||
float area = convexPolygonArea(poly1); | ||
if (area == 0.0f) | ||
return 0.0f; | ||
std::vector<Vec2f> intersection = convexClip(poly1, poly2); | ||
if (intersection.empty()) | ||
return 0.0f; | ||
float intersectionArea = convexPolygonArea(intersection); | ||
if (std::abs(intersectionArea - area) < 1e-6f) | ||
return 1.0f; | ||
return intersectionArea / area; | ||
} | ||
|
||
inline float distancePointToEdge(const Vec2f& p, const Vec2f& a, const Vec2f& b) | ||
{ | ||
Vec2f ab = b - a; | ||
Vec2f ap = p - a; | ||
float t = Vec2f::dot(ap, ab) / Vec2f::dot(ab, ab); | ||
t = std::max(0.0f, std::min(1.0f, t)); | ||
Vec2f projection(a.x + ab.x * t, a.y + ab.y * t); | ||
Vec2f diff = p - projection; | ||
return diff.length(); | ||
} | ||
|
||
inline float computeConvexDistance(const std::vector<Vec2f>& poly1, const std::vector<Vec2f>& poly2) | ||
{ | ||
float minDist = std::numeric_limits<float>::max(); | ||
for (const auto& p : poly1) | ||
{ | ||
float dist = std::numeric_limits<float>::max(); | ||
size_t n = poly2.size(); | ||
for (size_t i = 0; i < n; ++i) | ||
{ | ||
float d = distancePointToEdge(p, poly2[i], poly2[(i + 1) % n]); | ||
dist = std::min(dist, d); | ||
if (d < minDist) | ||
break; | ||
} | ||
minDist = std::min(minDist, dist); | ||
} | ||
for (const auto& p : poly2) | ||
{ | ||
float dist = std::numeric_limits<float>::max(); | ||
size_t n = poly1.size(); | ||
for (size_t i = 0; i < n; ++i) | ||
{ | ||
float d = distancePointToEdge(p, poly1[i], poly1[(i + 1) % n]); | ||
dist = std::min(dist, d); | ||
if (d < minDist) | ||
break; | ||
} | ||
minDist = std::min(minDist, dist); | ||
} | ||
return minDist; | ||
} | ||
|
||
inline bool doesVerticalLineIntersectPolygon(float x, const std::vector<Vec2f>& poly) | ||
{ | ||
for (size_t i = 0, n = poly.size(); i < n; ++i) | ||
{ | ||
const Vec2f& a = poly[i]; | ||
const Vec2f& b = poly[(i + 1) % n]; | ||
if ((x > std::min(a.x, b.x) && x < std::max(a.x, b.x))) | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
inline bool doesHorizontalLineIntersectPolygon(float y, const std::vector<Vec2f>& poly) | ||
{ | ||
for (size_t i = 0, n = poly.size(); i < n; ++i) | ||
{ | ||
const Vec2f& a = poly[i]; | ||
const Vec2f& b = poly[(i + 1) % n]; | ||
if ((y > std::min(a.y, b.y) && y < std::max(a.y, b.y))) | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
inline bool doesRayIntersectPolygon(const Vec2f& p1, const Vec2f& p2, const std::vector<Vec2f>& poly) | ||
{ | ||
Vec2f dir = p2 - p1; | ||
for (size_t i = 0, n = poly.size(); i < n; ++i) | ||
{ | ||
Vec2f A = poly[i]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it should be "const Vect2f&" or just "vect2f a=poly[i] - p1" |
||
Vec2f B = poly[(i + 1) % n]; | ||
Vec2f a = A - p1; | ||
Vec2f b = B - p1; | ||
Vec2f s = b - a; | ||
float den = Vec2f::cross(dir, s); | ||
if (std::fabs(den) <= +0.0f) | ||
continue; | ||
float t = Vec2f::cross(a, s) / den; | ||
float u = Vec2f::cross(a, dir) / den; | ||
if (t >= 0.f && u >= 0.f && u <= 1.f) | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
} // namespace indigo | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4775,6 +4775,40 @@ void BaseMolecule::getBoundingBox(float font_size, LABEL_MODE label_mode, Rect2f | |
bbox = Rect2f(a, b); | ||
} | ||
|
||
std::vector<Vec2f> BaseMolecule::getConvexHull(const Vec2f& min_box) const | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add comment that this is "Andrew's monotone chain convex hull algorithm" |
||
std::vector<Vec2f> vertices; | ||
std::transform(_xyz.ptr(), _xyz.ptr() + _xyz.size(), std::back_inserter(vertices), [](const Vec3f& v) -> Vec2f { return Vec2f(v.x, v.y); }); | ||
if (vertices.size() < 3) | ||
{ | ||
Rect2f bbox; | ||
getBoundingBox(bbox, min_box); | ||
vertices.clear(); | ||
vertices.emplace_back(bbox.leftTop()); | ||
vertices.emplace_back(bbox.rightTop()); | ||
vertices.emplace_back(bbox.rightBottom()); | ||
vertices.emplace_back(bbox.leftBottom()); | ||
return vertices; | ||
} | ||
std::sort(vertices.begin(), vertices.end()); | ||
std::vector<Vec2f> hull; | ||
for (const auto& p : vertices) | ||
{ | ||
while (hull.size() >= 2 && hull[hull.size() - 2].relativeCross(hull.back(), p) <= 0) | ||
hull.pop_back(); | ||
hull.push_back(p); | ||
} | ||
size_t lower_size = hull.size(); | ||
for (auto it = vertices.rbegin(); it != vertices.rend(); ++it) | ||
{ | ||
while (hull.size() > lower_size && hull[hull.size() - 2].relativeCross(hull.back(), *it) <= 0) | ||
hull.pop_back(); | ||
hull.push_back(*it); | ||
} | ||
hull.pop_back(); | ||
return hull; | ||
} | ||
|
||
void BaseMolecule::getBoundingBox(Vec2f& a, Vec2f& b) const | ||
{ | ||
for (int atom_idx = 0; atom_idx < vertexCount(); ++atom_idx) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need more comments.
Also no need to calculate x if both vertices has x > px
So I suggest something like