remove old collision and rotation range code
This commit is contained in:
@@ -1,297 +0,0 @@
|
||||
#include <mbgl/text/collision.hpp>
|
||||
#include <mbgl/text/rotation_range.hpp>
|
||||
#include <mbgl/util/math.hpp>
|
||||
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
Box getBox(const CollisionAnchor &anchor, const CollisionRect &bbox, float minScale,
|
||||
float maxScale) {
|
||||
return Box{
|
||||
Point{
|
||||
anchor.x + util::min(bbox.tl.x / minScale, bbox.tl.x / maxScale),
|
||||
anchor.y + util::min(bbox.tl.y / minScale, bbox.tl.y / maxScale),
|
||||
},
|
||||
Point{
|
||||
anchor.x + util::max(bbox.br.x / minScale, bbox.br.x / maxScale),
|
||||
anchor.y + util::max(bbox.br.y / minScale, bbox.br.y / maxScale),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
Collision::Collision(float zoom_, float tileExtent, float tileSize, float placementDepth)
|
||||
// tile pixels per screen pixels at the tile's zoom level
|
||||
: tilePixelRatio(tileExtent / tileSize),
|
||||
|
||||
zoom(zoom_),
|
||||
|
||||
// Calculate the maximum scale we can go down in our fake-3d rtree so that
|
||||
// placement still makes sense. This is calculated so that the minimum
|
||||
// placement zoom can be at most 25.5 (we use an unsigned integer x10 to
|
||||
// store the minimum zoom).
|
||||
//
|
||||
// We don't want to place labels all the way to 25.5. This lets too many
|
||||
// glyphs be placed, slowing down collision checking. Only place labels if
|
||||
// they will show up within the intended zoom range of the tile.
|
||||
maxPlacementScale(std::exp(std::log(2) * util::min(3.0f, placementDepth, 25.5f - zoom_))) {
|
||||
const float m = 4096;
|
||||
const float edge = m * tilePixelRatio * 2;
|
||||
|
||||
|
||||
PlacementBox left;
|
||||
left.anchor = CollisionAnchor{ 0.0f, 0.0f };
|
||||
left.box = CollisionRect{CollisionPoint{-edge, -edge}, CollisionPoint{0, edge}};
|
||||
left.placementRange = {{ 2.0f * M_PI, 0.0f }};
|
||||
left.placementScale = 0.5f;
|
||||
left.maxScale = std::numeric_limits<float>::infinity();
|
||||
left.padding = 0.0f;
|
||||
leftEdge = PlacementValue{
|
||||
getBox(left.anchor, left.box, left.placementScale, left.maxScale),
|
||||
left};
|
||||
|
||||
PlacementBox top;
|
||||
top.anchor = CollisionAnchor{ 0.0f, 0.0f };
|
||||
top.box = CollisionRect{CollisionPoint{-edge, -edge}, CollisionPoint{edge, 0}};
|
||||
top.placementRange = {{ 2.0f * M_PI, 0.0f }};
|
||||
top.placementScale = 0.5f;
|
||||
top.maxScale = std::numeric_limits<float>::infinity();
|
||||
top.padding = 0.0f;
|
||||
topEdge = PlacementValue{
|
||||
getBox(top.anchor, top.box, top.placementScale, top.maxScale),
|
||||
top};
|
||||
|
||||
PlacementBox bottom;
|
||||
bottom.anchor = CollisionAnchor{ m, m };
|
||||
bottom.box = CollisionRect{CollisionPoint{-edge, 0}, CollisionPoint{edge, edge}};
|
||||
bottom.placementRange = {{ 2.0f * M_PI, 0.0f }};
|
||||
bottom.placementScale = 0.5f;
|
||||
bottom.maxScale = std::numeric_limits<float>::infinity();
|
||||
bottom.padding = 0.0f;
|
||||
bottomEdge = PlacementValue{
|
||||
getBox(bottom.anchor, bottom.box, bottom.placementScale, bottom.maxScale),
|
||||
bottom};
|
||||
|
||||
PlacementBox right;
|
||||
right.anchor = CollisionAnchor{ m, m };
|
||||
right.box = CollisionRect{CollisionPoint{0, -edge}, CollisionPoint{edge, edge}};
|
||||
right.placementRange = {{ 2.0f * M_PI, 0.0f }};
|
||||
right.placementScale = 0.5f;
|
||||
right.maxScale = std::numeric_limits<float>::infinity();
|
||||
right.padding = 0.0f;
|
||||
rightEdge = PlacementValue{
|
||||
getBox(right.anchor, right.box, right.placementScale, right.maxScale),
|
||||
right};
|
||||
|
||||
}
|
||||
|
||||
GlyphBox getMergedGlyphs(const GlyphBoxes &boxes, const CollisionAnchor &anchor) {
|
||||
GlyphBox mergedGlyphs;
|
||||
const float inf = std::numeric_limits<float>::infinity();
|
||||
mergedGlyphs.box = CollisionRect{{inf, inf}, {-inf, -inf}};
|
||||
mergedGlyphs.anchor = anchor;
|
||||
|
||||
CollisionRect &box = mergedGlyphs.box;
|
||||
for (const GlyphBox &glyph : boxes) {
|
||||
const CollisionRect &gbox = glyph.box;
|
||||
box.tl.x = util::min(box.tl.x, gbox.tl.x);
|
||||
box.tl.y = util::min(box.tl.y, gbox.tl.y);
|
||||
box.br.x = util::max(box.br.x, gbox.br.x);
|
||||
box.br.y = util::max(box.br.y, gbox.br.y);
|
||||
mergedGlyphs.minScale = util::max(mergedGlyphs.minScale, glyph.minScale);
|
||||
}
|
||||
|
||||
return mergedGlyphs;
|
||||
}
|
||||
|
||||
float Collision::getPlacementScale(const GlyphBoxes &glyphs, float minPlacementScale, bool avoidEdges) {
|
||||
|
||||
for (const GlyphBox &glyph : glyphs) {
|
||||
const CollisionRect &box = glyph.box;
|
||||
const CollisionRect &bbox = glyph.hBox ? glyph.hBox.get() : glyph.box;
|
||||
const CollisionAnchor &anchor = glyph.anchor;
|
||||
const float pad = glyph.padding;
|
||||
|
||||
|
||||
if (anchor.x < 0 || anchor.x > 4096 || anchor.y < 0 || anchor.y > 4096) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
float minScale = std::fmax(minPlacementScale, glyph.minScale);
|
||||
float maxScale = glyph.maxScale != 0 ? glyph.maxScale : std::numeric_limits<float>::infinity();
|
||||
|
||||
if (minScale >= maxScale) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compute the scaled bounding box of the unrotated glyph
|
||||
const Box searchBox = getBox(anchor, bbox, minScale, maxScale);
|
||||
|
||||
std::vector<PlacementValue> blocking;
|
||||
hTree.query(bgi::intersects(searchBox), std::back_inserter(blocking));
|
||||
cTree.query(bgi::intersects(searchBox), std::back_inserter(blocking));
|
||||
|
||||
if (avoidEdges) {
|
||||
if (searchBox.min_corner().get<0>() < 0) blocking.emplace_back(leftEdge);
|
||||
if (searchBox.min_corner().get<1>() < 0) blocking.emplace_back(topEdge);
|
||||
if (searchBox.max_corner().get<0>() >= 4096) blocking.emplace_back(rightEdge);
|
||||
if (searchBox.max_corner().get<1>() >= 4096) blocking.emplace_back(bottomEdge);
|
||||
}
|
||||
|
||||
if (blocking.size()) {
|
||||
const CollisionAnchor &na = anchor; // new anchor
|
||||
const CollisionRect &nb = box; // new box
|
||||
|
||||
for (const PlacementValue &value : blocking) {
|
||||
const PlacementBox &placement = std::get<1>(value);
|
||||
const CollisionAnchor &oa = placement.anchor; // old anchor
|
||||
const CollisionRect &ob = placement.box; // old box
|
||||
|
||||
// If anchors are identical, we're going to skip the label.
|
||||
// NOTE: this isn't right because there can be glyphs with
|
||||
// the same anchor but differing box offsets.
|
||||
if (na == oa) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// todo: unhardcode the 8 = tileExtent/tileSize
|
||||
float padding = std::fmax(pad, placement.padding) * 8.0f;
|
||||
|
||||
// Original algorithm:
|
||||
float s1 = (ob.tl.x - nb.br.x - padding) /
|
||||
(na.x - oa.x); // scale at which new box is to the left of old box
|
||||
float s2 = (ob.br.x - nb.tl.x + padding) /
|
||||
(na.x - oa.x); // scale at which new box is to the right of old box
|
||||
float s3 = (ob.tl.y - nb.br.y - padding) /
|
||||
(na.y - oa.y); // scale at which new box is to the top of old box
|
||||
float s4 = (ob.br.y - nb.tl.y + padding) /
|
||||
(na.y - oa.y); // scale at which new box is to the bottom of old box
|
||||
|
||||
if (std::isnan(s1) || std::isnan(s2)) {
|
||||
s1 = s2 = 1;
|
||||
}
|
||||
if (std::isnan(s3) || std::isnan(s4)) {
|
||||
s3 = s4 = 1;
|
||||
}
|
||||
|
||||
const float collisionFreeScale = std::fmin(std::fmax(s1, s2), std::fmax(s3, s4));
|
||||
|
||||
// Only update label's min scale if the glyph was
|
||||
// restricted by a collision
|
||||
if (collisionFreeScale > minPlacementScale &&
|
||||
collisionFreeScale > minScale &&
|
||||
collisionFreeScale < maxScale &&
|
||||
collisionFreeScale < placement.maxScale) {
|
||||
minPlacementScale = collisionFreeScale;
|
||||
}
|
||||
|
||||
if (minPlacementScale > maxPlacementScale) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return minPlacementScale;
|
||||
}
|
||||
|
||||
PlacementRange Collision::getPlacementRange(const GlyphBoxes &glyphs, float placementScale,
|
||||
bool horizontal) {
|
||||
PlacementRange placementRange = {{2.0f * M_PI, 0}};
|
||||
|
||||
for (const GlyphBox &glyph : glyphs) {
|
||||
const CollisionRect &bbox = glyph.hBox ? glyph.hBox.get() : glyph.box;
|
||||
const CollisionAnchor &anchor = glyph.anchor;
|
||||
|
||||
float minPlacedX = anchor.x + bbox.tl.x / placementScale;
|
||||
float minPlacedY = anchor.y + bbox.tl.y / placementScale;
|
||||
float maxPlacedX = anchor.x + bbox.br.x / placementScale;
|
||||
float maxPlacedY = anchor.y + bbox.br.y / placementScale;
|
||||
|
||||
Box query_box{Point{minPlacedX, minPlacedY}, Point{maxPlacedX, maxPlacedY}};
|
||||
|
||||
std::vector<PlacementValue> blocking;
|
||||
hTree.query(bgi::intersects(query_box), std::back_inserter(blocking));
|
||||
|
||||
if (horizontal) {
|
||||
cTree.query(bgi::intersects(query_box), std::back_inserter(blocking));
|
||||
}
|
||||
|
||||
for (const PlacementValue &value : blocking) {
|
||||
const Box &s = std::get<0>(value);
|
||||
const PlacementBox &b = std::get<1>(value);
|
||||
const CollisionRect &bbox2 = b.hBox ? b.hBox.get() : b.box;
|
||||
|
||||
float x1, x2, y1, y2, intersectX, intersectY;
|
||||
|
||||
// Adjust and compare bboxes to see if the glyphs might intersect
|
||||
if (placementScale > b.placementScale) {
|
||||
x1 = b.anchor.x + bbox2.tl.x / placementScale;
|
||||
y1 = b.anchor.y + bbox2.tl.y / placementScale;
|
||||
x2 = b.anchor.x + bbox2.br.x / placementScale;
|
||||
y2 = b.anchor.y + bbox2.br.y / placementScale;
|
||||
intersectX = x1 < maxPlacedX && x2 > minPlacedX;
|
||||
intersectY = y1 < maxPlacedY && y2 > minPlacedY;
|
||||
} else {
|
||||
x1 = anchor.x + bbox.tl.x / b.placementScale;
|
||||
y1 = anchor.y + bbox.tl.y / b.placementScale;
|
||||
x2 = anchor.x + bbox.br.x / b.placementScale;
|
||||
y2 = anchor.y + bbox.br.y / b.placementScale;
|
||||
|
||||
intersectX = x1 < s.max_corner().get<0>() && x2 > s.min_corner().get<0>();
|
||||
intersectY = y1 < s.max_corner().get<1>() && y2 > s.min_corner().get<1>();
|
||||
}
|
||||
|
||||
// If they can't intersect, skip more expensive rotation calculation
|
||||
if (!(intersectX && intersectY))
|
||||
continue;
|
||||
|
||||
float scale = std::fmax(placementScale, b.placementScale);
|
||||
// TODO? glyph.box or glyph.bbox?
|
||||
CollisionRange range = rotationRange(glyph, b, scale);
|
||||
|
||||
placementRange[0] = std::fmin(placementRange[0], range[0]);
|
||||
placementRange[1] = std::fmax(placementRange[1], range[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return placementRange;
|
||||
};
|
||||
|
||||
void Collision::insert(const GlyphBoxes &glyphs, const CollisionAnchor &anchor,
|
||||
float placementScale, const PlacementRange &placementRange,
|
||||
bool horizontal) {
|
||||
assert(placementScale != std::numeric_limits<float>::infinity());
|
||||
|
||||
std::vector<PlacementValue> allBounds;
|
||||
allBounds.reserve(glyphs.size());
|
||||
|
||||
for (const GlyphBox &glyph : glyphs) {
|
||||
const CollisionRect &box = glyph.box;
|
||||
const CollisionRect &bbox = glyph.hBox ? glyph.hBox.get() : glyph.box;
|
||||
|
||||
const float minScale = util::max(placementScale, glyph.minScale);
|
||||
const float maxScale = glyph.maxScale != 0 ? glyph.maxScale : std::numeric_limits<float>::infinity();
|
||||
|
||||
const Box bounds = getBox(anchor, bbox, minScale, maxScale);
|
||||
|
||||
PlacementBox placement;
|
||||
placement.anchor = anchor;
|
||||
placement.box = box;
|
||||
if (glyph.hBox) {
|
||||
placement.hBox = bbox;
|
||||
}
|
||||
placement.placementRange = placementRange;
|
||||
placement.placementScale = minScale;
|
||||
placement.maxScale = maxScale;
|
||||
placement.padding = glyph.padding;
|
||||
|
||||
allBounds.emplace_back(bounds, placement);
|
||||
}
|
||||
|
||||
// Bulk-insert all glyph boxes
|
||||
if (horizontal) {
|
||||
hTree.insert(allBounds.begin(), allBounds.end());
|
||||
} else {
|
||||
cTree.insert(allBounds.begin(), allBounds.end());
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
#ifndef MBGL_TEXT_COLLISION
|
||||
#define MBGL_TEXT_COLLISION
|
||||
|
||||
#include <mbgl/text/types.hpp>
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
#ifdef __clang__
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-register"
|
||||
#pragma GCC diagnostic ignored "-Wshorten-64-to-32"
|
||||
#else
|
||||
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
|
||||
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
#endif
|
||||
#include <boost/geometry.hpp>
|
||||
#include <boost/geometry/geometries/point.hpp>
|
||||
#include <boost/geometry/geometries/box.hpp>
|
||||
#include <boost/geometry/index/rtree.hpp>
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
namespace bg = boost::geometry;
|
||||
namespace bgm = bg::model;
|
||||
namespace bgi = bg::index;
|
||||
typedef bgm::point<float, 2, bg::cs::cartesian> Point;
|
||||
typedef bgm::box<Point> Box;
|
||||
typedef std::pair<Box, PlacementBox> PlacementValue;
|
||||
typedef bgi::rtree<PlacementValue, bgi::linear<16,4>> Tree;
|
||||
|
||||
class Collision {
|
||||
|
||||
public:
|
||||
Collision(float zoom, float tileExtent, float tileSize, float placementDepth = 1);
|
||||
|
||||
float getPlacementScale(const GlyphBoxes &glyphs, float minPlacementScale, bool avoidEdges);
|
||||
PlacementRange getPlacementRange(const GlyphBoxes &glyphs, float placementScale,
|
||||
bool horizontal);
|
||||
void insert(const GlyphBoxes &glyphs, const CollisionAnchor &anchor, float placementScale,
|
||||
const PlacementRange &placementRange, bool horizontal);
|
||||
|
||||
private:
|
||||
Tree hTree;
|
||||
Tree cTree;
|
||||
PlacementValue leftEdge;
|
||||
PlacementValue topEdge;
|
||||
PlacementValue rightEdge;
|
||||
PlacementValue bottomEdge;
|
||||
|
||||
public:
|
||||
const float tilePixelRatio;
|
||||
const float zoom;
|
||||
const float maxPlacementScale;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,263 +0,0 @@
|
||||
#include <mbgl/text/rotation_range.hpp>
|
||||
|
||||
#include <mbgl/util/interpolate.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
/*
|
||||
* Combine an array of collision ranges to form a continuous
|
||||
* range that includes 0. Collisions within the ignoreRange are ignored
|
||||
*/
|
||||
CollisionRange mergeCollisions(const CollisionList &collisions,
|
||||
PlacementRange ignoreRange) {
|
||||
// find continuous interval including 0 that doesn't have any collisions
|
||||
float min = 2.0f * M_PI;
|
||||
float max = 0.0f;
|
||||
|
||||
for (CollisionRange collision : collisions) {
|
||||
bool entryOutside =
|
||||
ignoreRange[0] <= collision[0] && collision[0] <= ignoreRange[1];
|
||||
bool exitOutside =
|
||||
ignoreRange[0] <= collision[1] && collision[1] <= ignoreRange[1];
|
||||
|
||||
if (entryOutside && exitOutside) {
|
||||
// no collision, since blocker is out of range
|
||||
} else if (entryOutside) {
|
||||
min = util::min(min, ignoreRange[1]);
|
||||
max = util::max(max, collision[1]);
|
||||
} else if (exitOutside) {
|
||||
min = util::min(min, collision[0]);
|
||||
max = util::max(max, ignoreRange[0]);
|
||||
} else {
|
||||
min = util::min(min, collision[0]);
|
||||
max = util::max(max, collision[1]);
|
||||
}
|
||||
}
|
||||
|
||||
return {{min, max}};
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate collision ranges for two rotating boxes.
|
||||
*/
|
||||
CollisionList
|
||||
rotatingRotatingCollisions(const CollisionRect &a, const CollisionRect &b,
|
||||
const CollisionAnchor &anchorToAnchor) {
|
||||
const float d = util::mag<float>(anchorToAnchor);
|
||||
const float d_sq = d * d;
|
||||
|
||||
const CollisionAnchor horizontal = {1, 0};
|
||||
const float angleBetweenAnchors =
|
||||
util::angle_between<float>(anchorToAnchor, horizontal);
|
||||
|
||||
// Calculate angles at which collisions may occur
|
||||
const std::array<float, 8> c = {{
|
||||
// top/bottom
|
||||
/*[0]*/ static_cast<float>(std::asin((float)(a.br.y - b.tl.y) / d)),
|
||||
/*[1]*/ static_cast<float>(std::asin((float)(a.br.y - b.tl.y) / d) + M_PI),
|
||||
/*[2]*/ static_cast<float>(2 * M_PI -
|
||||
std::asin((float)(-a.tl.y + b.br.y) / d)),
|
||||
/*[3]*/ static_cast<float>(M_PI - std::asin((float)(-a.tl.y + b.br.y) / d)),
|
||||
|
||||
// left/right
|
||||
/*[4]*/ static_cast<float>(2 * M_PI -
|
||||
std::acos((float)(a.br.x - b.tl.x) / d)),
|
||||
/*[5]*/ static_cast<float>(std::acos((float)(a.br.x - b.tl.x) / d)),
|
||||
/*[6]*/ static_cast<float>(M_PI - std::acos((float)(-a.tl.x + b.br.x) / d)),
|
||||
/*[7]*/ static_cast<float>(M_PI +
|
||||
std::acos((float)(-a.tl.x + b.br.x) / d))}};
|
||||
|
||||
const float rl = a.br.x - b.tl.x;
|
||||
const float lr = -a.tl.x + b.br.x;
|
||||
const float tb = a.br.y - b.tl.y;
|
||||
const float bt = -a.tl.y + b.br.y;
|
||||
|
||||
// Calculate the distance squared of the diagonal which will be used
|
||||
// to check if the boxes are close enough for collisions to occur at each
|
||||
// angle
|
||||
// todo, triple check these
|
||||
const std::array<float, 8> e = {{
|
||||
// top/bottom
|
||||
/*[0]*/ static_cast<float>(rl * rl + tb * tb),
|
||||
/*[1]*/ static_cast<float>(lr * lr + tb * tb),
|
||||
/*[2]*/ static_cast<float>(rl * rl + bt * bt),
|
||||
/*[3]*/ static_cast<float>(lr * lr + bt * bt),
|
||||
|
||||
// left/right
|
||||
/*[4]*/ static_cast<float>(rl * rl + tb * tb),
|
||||
/*[5]*/ static_cast<float>(rl * rl + bt * bt),
|
||||
/*[6]*/ static_cast<float>(lr * lr + bt * bt),
|
||||
/*[7]*/ static_cast<float>(lr * lr + tb * tb)}};
|
||||
|
||||
std::vector<float> f;
|
||||
for (size_t i = 0; i < c.size(); i++) {
|
||||
// Check if they are close enough to collide
|
||||
if (!std::isnan(c[i]) && d_sq <= e[i]) {
|
||||
// So far, angles have been calulated as relative to the vector
|
||||
// between anchors.
|
||||
// Convert the angles to angles from north.
|
||||
f.push_back(
|
||||
std::fmod((c[i] + angleBetweenAnchors + 2 * M_PI), (2 * M_PI)));
|
||||
}
|
||||
}
|
||||
|
||||
assert(f.size() % 2 == 0);
|
||||
|
||||
// Group the collision angles by two
|
||||
// each group represents a range where the two boxes collide
|
||||
CollisionList collisions;
|
||||
std::sort(f.begin(), f.end());
|
||||
for (size_t k = 0; k < f.size(); k += 2) {
|
||||
collisions.push_back({{f[k], f[k + 1]}});
|
||||
}
|
||||
|
||||
return collisions;
|
||||
}
|
||||
|
||||
double getAngle(const CollisionPoint &p1, const CollisionPoint &p2,
|
||||
CollisionAngle d, const CollisionPoint &corner) {
|
||||
return std::fmod(util::angle_between(util::interpolate(p1.x, p2.x, d),
|
||||
util::interpolate(p1.y, p2.y, d), corner.x,
|
||||
corner.y) +
|
||||
2 * M_PI,
|
||||
2 * M_PI);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the intersection points of a circle and a line segment;
|
||||
*/
|
||||
void circleEdgeCollisions(std::back_insert_iterator<CollisionAngles> angles,
|
||||
const CollisionPoint &corner, float radius,
|
||||
const CollisionPoint &p1, const CollisionPoint &p2) {
|
||||
const CollisionPoint::Type edgeX = p2.x - p1.x;
|
||||
const CollisionPoint::Type edgeY = p2.y - p1.y;
|
||||
|
||||
const CollisionAngle a = edgeX * edgeX + edgeY * edgeY;
|
||||
const CollisionAngle b = (edgeX * p1.x + edgeY * p1.y) * 2;
|
||||
const CollisionAngle c = p1.x * p1.x + p1.y * p1.y - radius * radius;
|
||||
|
||||
const CollisionAngle discriminant = b * b - 4 * a * c;
|
||||
|
||||
// a collision exists only if line intersects circle at two points
|
||||
if (discriminant > 0) {
|
||||
CollisionAngle x1 = (-b - std::sqrt(discriminant)) / (2 * a);
|
||||
CollisionAngle x2 = (-b + std::sqrt(discriminant)) / (2 * a);
|
||||
|
||||
// only add points if within line segment
|
||||
// hack to handle floating point representations of 0 and 1
|
||||
if (0 < x1 && x1 < 1) {
|
||||
angles = getAngle(p1, p2, x1, corner);
|
||||
}
|
||||
|
||||
if (0 < x2 && x2 < 1) {
|
||||
angles = getAngle(p1, p2, x2, corner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the ranges for which the corner,
|
||||
* rotatated around the anchor, is within the box;
|
||||
*/
|
||||
void cornerBoxCollisions(std::back_insert_iterator<CollisionList> collisions,
|
||||
const CollisionPoint &corner,
|
||||
const CollisionCorners &boxCorners, bool flip) {
|
||||
float radius = util::mag<float>(corner);
|
||||
|
||||
CollisionAngles angles;
|
||||
|
||||
// Calculate the points at which the corners intersect with the edges
|
||||
for (size_t i = 0, j = 3; i < 4; j = i++) {
|
||||
circleEdgeCollisions(std::back_inserter(angles), corner, radius,
|
||||
boxCorners[j], boxCorners[i]);
|
||||
}
|
||||
|
||||
if (angles.size() % 2 != 0) {
|
||||
// TODO fix
|
||||
// This could get hit when a point intersects very close to a corner
|
||||
// and floating point issues cause only one of the entry or exit to be
|
||||
// counted
|
||||
throw std::runtime_error("expecting an even number of intersections");
|
||||
}
|
||||
|
||||
std::sort(angles.begin(), angles.end());
|
||||
|
||||
// Group by pairs, where each represents a range where a collision occurs
|
||||
for (size_t k = 0; k < angles.size(); k += 2) {
|
||||
CollisionRange range = {{angles[k], angles[k + 1]}};
|
||||
if (flip) {
|
||||
range = util::flip(range);
|
||||
}
|
||||
collisions = range;
|
||||
}
|
||||
}
|
||||
|
||||
CollisionCorners getCorners(const CollisionRect &a) {
|
||||
return {{{a.tl.x, a.tl.y},
|
||||
{a.tl.x, a.br.y},
|
||||
{a.br.x, a.br.y},
|
||||
{a.br.x, a.tl.y}}};
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate collision ranges for a rotating box and a fixed box;
|
||||
*/
|
||||
CollisionList rotatingFixedCollisions(const CollisionRect &rotating,
|
||||
const CollisionRect &fixed) {
|
||||
const auto cornersR = getCorners(rotating);
|
||||
const auto cornersF = getCorners(fixed);
|
||||
|
||||
// A collision occurs when, and only at least one corner from one of the
|
||||
// boxes is within the other box. Calculate these ranges for each corner.
|
||||
|
||||
CollisionList collisions;
|
||||
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
cornerBoxCollisions(std::back_inserter(collisions), cornersR[i],
|
||||
cornersF);
|
||||
cornerBoxCollisions(std::back_inserter(collisions), cornersF[i],
|
||||
cornersR, true);
|
||||
}
|
||||
|
||||
return collisions;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the range a box conflicts with a second box
|
||||
*/
|
||||
CollisionRange rotationRange(const GlyphBox &inserting,
|
||||
const PlacementBox &blocker, float scale) {
|
||||
CollisionList collisions;
|
||||
|
||||
const GlyphBox &a = inserting;
|
||||
const PlacementBox &b = blocker;
|
||||
|
||||
// Instead of scaling the boxes, we move the anchors
|
||||
CollisionAnchor relativeAnchor{
|
||||
static_cast<float>((b.anchor.x - a.anchor.x) * scale),
|
||||
static_cast<float>((b.anchor.y - a.anchor.y) * scale)};
|
||||
|
||||
// Generate a list of collision interval
|
||||
if (a.hBox && b.hBox) {
|
||||
collisions = rotatingRotatingCollisions(a.box, b.box, relativeAnchor);
|
||||
} else if (a.hBox) {
|
||||
const CollisionRect box {
|
||||
b.box.tl.x + relativeAnchor.x, b.box.tl.y + relativeAnchor.y,
|
||||
b.box.br.x + relativeAnchor.x, b.box.br.y + relativeAnchor.y};
|
||||
collisions = rotatingFixedCollisions(a.box, box);
|
||||
} else if (b.hBox) {
|
||||
const CollisionRect box {
|
||||
a.box.tl.x - relativeAnchor.x, a.box.tl.y - relativeAnchor.y,
|
||||
a.box.br.x - relativeAnchor.x, a.box.br.y - relativeAnchor.y};
|
||||
collisions = rotatingFixedCollisions(b.box, box);
|
||||
} else {
|
||||
// collisions remains empty
|
||||
}
|
||||
|
||||
// Find and return the continous are around 0 where there are no collisions
|
||||
return mergeCollisions(collisions, blocker.placementRange);
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
#ifndef MBGL_TEXT_ROTATION_RANGE
|
||||
#define MBGL_TEXT_ROTATION_RANGE
|
||||
|
||||
#include <mbgl/util/math.hpp>
|
||||
#include <mbgl/text/types.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
/*
|
||||
* Combine an array of collision ranges to form a continuous
|
||||
* range that includes 0. Collisions within the ignoreRange are ignored
|
||||
*/
|
||||
CollisionRange mergeCollisions(const CollisionList &collisions,
|
||||
PlacementRange ignoreRange);
|
||||
|
||||
/*
|
||||
* Calculate collision ranges for two rotating boxes.e
|
||||
*/
|
||||
CollisionList rotatingRotatingCollisions(const CollisionRect &a,
|
||||
const CollisionRect &b,
|
||||
const CollisionAnchor &anchorToAnchor);
|
||||
|
||||
/*
|
||||
* Return the intersection points of a circle and a line segment;
|
||||
*/
|
||||
void circleEdgeCollisions(std::back_insert_iterator<CollisionAngles> angles,
|
||||
const CollisionPoint &corner, float radius,
|
||||
const CollisionPoint &p1, const CollisionPoint &p2);
|
||||
|
||||
/*
|
||||
* Calculate the ranges for which the corner,
|
||||
* rotatated around the anchor, is within the box;
|
||||
*/
|
||||
void cornerBoxCollisions(std::back_insert_iterator<CollisionList> collisions,
|
||||
const CollisionPoint &corner,
|
||||
const CollisionCorners &boxCorners, bool flip = false);
|
||||
|
||||
/*
|
||||
* Calculate collision ranges for a rotating box and a fixed box;
|
||||
*/
|
||||
CollisionList rotatingFixedCollisions(const CollisionRect &rotating,
|
||||
const CollisionRect &fixed);
|
||||
|
||||
/*
|
||||
* Calculate the range a box conflicts with a second box
|
||||
*/
|
||||
CollisionRange rotationRange(const GlyphBox &inserting,
|
||||
const PlacementBox &blocker, float scale);
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user