improve round line joins for semi-transparent lines
https://github.com/mapbox/mapbox-gl-js/pull/1359 Round line joins used to be drawn by adding a semicircle cap to the end of each segment. This looked fine for opaque lines not for semi-transparent lines. This changes the triangulation so that round line joins don't overlap with segments. The gap between segments is filled with small triangles that look like pie slices. The edge of the round linejoin is made up of many short straight lines that look round at the sizes we draw lines. Since sharp angles are infrequent, this does not significantly affect the total number of triangles created. Joins for angles that are really sharp are still drawn with overlap.
This commit is contained in:
@@ -92,6 +92,8 @@ enum class JoinType : uint8_t {
|
||||
Miter,
|
||||
Bevel,
|
||||
Round,
|
||||
// the following two types are for internal use only
|
||||
FakeRound,
|
||||
FlipBevel
|
||||
};
|
||||
|
||||
@@ -99,6 +101,7 @@ MBGL_DEFINE_ENUM_CLASS(JoinTypeClass, JoinType, {
|
||||
{ JoinType::Miter, "miter" },
|
||||
{ JoinType::Bevel, "bevel" },
|
||||
{ JoinType::Round, "round" },
|
||||
{ JoinType::FakeRound, "fakeround" },
|
||||
{ JoinType::FlipBevel, "flipbevel" },
|
||||
});
|
||||
|
||||
|
||||
@@ -146,8 +146,12 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
|
||||
const CapType currentCap = nextVertex ? beginCap : endCap;
|
||||
|
||||
if (middleVertex) {
|
||||
if (currentJoin == JoinType::Round && miterLength < layout.round_limit) {
|
||||
currentJoin = JoinType::Miter;
|
||||
if (currentJoin == JoinType::Round) {
|
||||
if (miterLength < layout.round_limit) {
|
||||
currentJoin = JoinType::Miter;
|
||||
} else if (miterLength <= 2) {
|
||||
currentJoin = JoinType::FakeRound;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentJoin == JoinType::Miter && miterLength > miterLimit) {
|
||||
@@ -191,13 +195,13 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
|
||||
triangleStore);
|
||||
flip = -flip;
|
||||
|
||||
} else if (middleVertex && currentJoin == JoinType::Bevel) {
|
||||
const float dir = prevNormal.x * nextNormal.y - prevNormal.y * nextNormal.x;
|
||||
} else if (middleVertex && (currentJoin == JoinType::Bevel || currentJoin == JoinType::FakeRound)) {
|
||||
const bool lineTurnsLeft = flip * (prevNormal.x * nextNormal.y - prevNormal.y * nextNormal.x) > 0;
|
||||
const float offset = -std::sqrt(miterLength * miterLength - 1);
|
||||
float offsetA;
|
||||
float offsetB;
|
||||
|
||||
if (flip * dir > 0) {
|
||||
if (lineTurnsLeft) {
|
||||
offsetB = 0;
|
||||
offsetA = offset;
|
||||
} else {
|
||||
@@ -211,6 +215,29 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
|
||||
startVertex, triangleStore);
|
||||
}
|
||||
|
||||
if (currentJoin == JoinType::FakeRound) {
|
||||
// The join angle is sharp enough that a round join would be visible.
|
||||
// Bevel joins fill the gap between segments with a single pie slice triangle.
|
||||
// Create a round join by adding multiple pie slices. The join isn't actually round, but
|
||||
// it looks like it is at the sizes we render lines at.
|
||||
|
||||
// Add more triangles for sharper angles.
|
||||
// This math is just a good enough approximation. It isn't "correct".
|
||||
const int n = std::floor((0.5 - (cosHalfAngle - 0.5)) * 8);
|
||||
|
||||
for (int m = 0; m < n; m++) {
|
||||
auto approxFractionalJoinNormal = util::unit(nextNormal * ((m + 1.0f) / (n + 1.0f)) + prevNormal);
|
||||
addPieSliceVertex(currentVertex, flip, distance, approxFractionalJoinNormal, lineTurnsLeft, startVertex, triangleStore);
|
||||
}
|
||||
|
||||
addPieSliceVertex(currentVertex, flip, distance, joinNormal, lineTurnsLeft, startVertex, triangleStore);
|
||||
|
||||
for (int k = n - 1; k >= 0; k--) {
|
||||
auto approxFractionalJoinNormal = util::unit(prevNormal * ((k + 1.0f) / (n + 1.0f)) + nextNormal);
|
||||
addPieSliceVertex(currentVertex, flip, distance, approxFractionalJoinNormal, lineTurnsLeft, startVertex, triangleStore);
|
||||
}
|
||||
}
|
||||
|
||||
// Start next segment
|
||||
if (nextVertex) {
|
||||
addCurrentVertex(currentVertex, flip, distance, nextNormal, -offsetA, -offsetB,
|
||||
@@ -260,15 +287,14 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
|
||||
// The segment is done. Unset vertices to disconnect segments.
|
||||
e1 = e2 = -1;
|
||||
flip = 1;
|
||||
|
||||
} else if (beginCap == CapType::Round) {
|
||||
// Add round cap before first segment
|
||||
addCurrentVertex(currentVertex, flip, distance, nextNormal, -1, -1, true,
|
||||
startVertex, triangleStore);
|
||||
}
|
||||
|
||||
// Start next segment with a butt
|
||||
if (nextVertex) {
|
||||
// Add round cap before first segment
|
||||
addCurrentVertex(currentVertex, flip, distance, nextNormal, -1, -1, true,
|
||||
startVertex, triangleStore);
|
||||
|
||||
addCurrentVertex(currentVertex, flip, distance, nextNormal, 0, 0, false,
|
||||
startVertex, triangleStore);
|
||||
}
|
||||
@@ -337,6 +363,29 @@ void LineBucket::addCurrentVertex(const Coordinate& currentVertex,
|
||||
e2 = e3;
|
||||
}
|
||||
|
||||
void LineBucket::addPieSliceVertex(const Coordinate& currentVertex,
|
||||
float flip,
|
||||
double distance,
|
||||
const vec2<double>& extrude,
|
||||
bool lineTurnsLeft,
|
||||
int32_t startVertex,
|
||||
std::vector<TriangleElement>& triangleStore) {
|
||||
int8_t ty = lineTurnsLeft;
|
||||
|
||||
auto flippedExtrude = extrude * (flip * (lineTurnsLeft ? -1 : 1));
|
||||
e3 = (int32_t)vertexBuffer.add(currentVertex.x, currentVertex.y, flippedExtrude.x, flippedExtrude.y, 0, ty,
|
||||
distance) - startVertex;
|
||||
if (e1 >= 0 && e2 >= 0) {
|
||||
triangleStore.emplace_back(e1, e2, e3);
|
||||
}
|
||||
|
||||
if (lineTurnsLeft) {
|
||||
e2 = e3;
|
||||
} else {
|
||||
e1 = e3;
|
||||
}
|
||||
}
|
||||
|
||||
void LineBucket::upload() {
|
||||
vertexBuffer.upload();
|
||||
triangleElementsBuffer.upload();
|
||||
|
||||
@@ -47,6 +47,9 @@ private:
|
||||
void addCurrentVertex(const Coordinate& currentVertex, float flip, double distance,
|
||||
const vec2<double>& normal, float endLeft, float endRight, bool round,
|
||||
int32_t startVertex, std::vector<LineBucket::TriangleElement>& triangleStore);
|
||||
void addPieSliceVertex(const Coordinate& currentVertex, float flip, double distance,
|
||||
const vec2<double>& extrude, bool lineTurnsLeft, int32_t startVertex,
|
||||
std::vector<TriangleElement>& triangleStore);
|
||||
|
||||
public:
|
||||
StyleLayoutLine layout;
|
||||
|
||||
Reference in New Issue
Block a user