-
Notifications
You must be signed in to change notification settings - Fork 106
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
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; | ||
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. 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. |
||
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) | ||
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. Looks like this function computation is more complex tnan isPointInPolygon. |
||
{ | ||
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) | ||
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. Could you please add comment that this is "Shoelace formula - triangle form" |
||
{ | ||
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