Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/msdfgen/core/Shape.cpp
20849 views
1
2
#include "Shape.h"
3
4
#include <cstdlib>
5
#include "arithmetics.hpp"
6
#include "convergent-curve-ordering.h"
7
8
#define DECONVERGE_OVERSHOOT 1.11111111111111111 // moves control points slightly more than necessary to account for floating-point errors
9
10
namespace msdfgen {
11
12
Shape::Shape() : inverseYAxis(false) { }
13
14
void Shape::addContour(const Contour &contour) {
15
contours.push_back(contour);
16
}
17
18
#ifdef MSDFGEN_USE_CPP11
19
void Shape::addContour(Contour &&contour) {
20
contours.push_back((Contour &&) contour);
21
}
22
#endif
23
24
Contour &Shape::addContour() {
25
contours.resize(contours.size()+1);
26
return contours.back();
27
}
28
29
bool Shape::validate() const {
30
for (std::vector<Contour>::const_iterator contour = contours.begin(); contour != contours.end(); ++contour) {
31
if (!contour->edges.empty()) {
32
Point2 corner = contour->edges.back()->point(1);
33
for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
34
if (!*edge)
35
return false;
36
if ((*edge)->point(0) != corner)
37
return false;
38
corner = (*edge)->point(1);
39
}
40
}
41
}
42
return true;
43
}
44
45
static void deconvergeEdge(EdgeHolder &edgeHolder, int param, Vector2 vector) {
46
switch (edgeHolder->type()) {
47
case (int) QuadraticSegment::EDGE_TYPE:
48
edgeHolder = static_cast<const QuadraticSegment *>(&*edgeHolder)->convertToCubic();
49
// fallthrough
50
case (int) CubicSegment::EDGE_TYPE:
51
{
52
Point2 *p = static_cast<CubicSegment *>(&*edgeHolder)->p;
53
switch (param) {
54
case 0:
55
p[1] += (p[1]-p[0]).length()*vector;
56
break;
57
case 1:
58
p[2] += (p[2]-p[3]).length()*vector;
59
break;
60
}
61
}
62
}
63
}
64
65
void Shape::normalize() {
66
for (std::vector<Contour>::iterator contour = contours.begin(); contour != contours.end(); ++contour) {
67
if (contour->edges.size() == 1) {
68
EdgeSegment *parts[3] = { };
69
contour->edges[0]->splitInThirds(parts[0], parts[1], parts[2]);
70
contour->edges.clear();
71
contour->edges.push_back(EdgeHolder(parts[0]));
72
contour->edges.push_back(EdgeHolder(parts[1]));
73
contour->edges.push_back(EdgeHolder(parts[2]));
74
} else if (!contour->edges.empty()) {
75
// Push apart convergent edge segments
76
EdgeHolder *prevEdge = &contour->edges.back();
77
for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
78
Vector2 prevDir = (*prevEdge)->direction(1).normalize();
79
Vector2 curDir = (*edge)->direction(0).normalize();
80
if (dotProduct(prevDir, curDir) < MSDFGEN_CORNER_DOT_EPSILON-1) {
81
double factor = DECONVERGE_OVERSHOOT*sqrt(1-(MSDFGEN_CORNER_DOT_EPSILON-1)*(MSDFGEN_CORNER_DOT_EPSILON-1))/(MSDFGEN_CORNER_DOT_EPSILON-1);
82
Vector2 axis = factor*(curDir-prevDir).normalize();
83
if (convergentCurveOrdering(*prevEdge, *edge) < 0)
84
axis = -axis;
85
deconvergeEdge(*prevEdge, 1, axis.getOrthogonal(true));
86
deconvergeEdge(*edge, 0, axis.getOrthogonal(false));
87
}
88
prevEdge = &*edge;
89
}
90
}
91
}
92
}
93
94
void Shape::bound(double &xMin, double &yMin, double &xMax, double &yMax) const {
95
for (std::vector<Contour>::const_iterator contour = contours.begin(); contour != contours.end(); ++contour)
96
contour->bound(xMin, yMin, xMax, yMax);
97
}
98
99
void Shape::boundMiters(double &xMin, double &yMin, double &xMax, double &yMax, double border, double miterLimit, int polarity) const {
100
for (std::vector<Contour>::const_iterator contour = contours.begin(); contour != contours.end(); ++contour)
101
contour->boundMiters(xMin, yMin, xMax, yMax, border, miterLimit, polarity);
102
}
103
104
Shape::Bounds Shape::getBounds(double border, double miterLimit, int polarity) const {
105
static const double LARGE_VALUE = 1e240;
106
Shape::Bounds bounds = { +LARGE_VALUE, +LARGE_VALUE, -LARGE_VALUE, -LARGE_VALUE };
107
bound(bounds.l, bounds.b, bounds.r, bounds.t);
108
if (border > 0) {
109
bounds.l -= border, bounds.b -= border;
110
bounds.r += border, bounds.t += border;
111
if (miterLimit > 0)
112
boundMiters(bounds.l, bounds.b, bounds.r, bounds.t, border, miterLimit, polarity);
113
}
114
return bounds;
115
}
116
117
void Shape::scanline(Scanline &line, double y) const {
118
std::vector<Scanline::Intersection> intersections;
119
double x[3];
120
int dy[3];
121
for (std::vector<Contour>::const_iterator contour = contours.begin(); contour != contours.end(); ++contour) {
122
for (std::vector<EdgeHolder>::const_iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
123
int n = (*edge)->scanlineIntersections(x, dy, y);
124
for (int i = 0; i < n; ++i) {
125
Scanline::Intersection intersection = { x[i], dy[i] };
126
intersections.push_back(intersection);
127
}
128
}
129
}
130
#ifdef MSDFGEN_USE_CPP11
131
line.setIntersections((std::vector<Scanline::Intersection> &&) intersections);
132
#else
133
line.setIntersections(intersections);
134
#endif
135
}
136
137
int Shape::edgeCount() const {
138
int total = 0;
139
for (std::vector<Contour>::const_iterator contour = contours.begin(); contour != contours.end(); ++contour)
140
total += (int) contour->edges.size();
141
return total;
142
}
143
144
void Shape::orientContours() {
145
struct Intersection {
146
double x;
147
int direction;
148
int contourIndex;
149
150
static int compare(const void *a, const void *b) {
151
return sign(reinterpret_cast<const Intersection *>(a)->x-reinterpret_cast<const Intersection *>(b)->x);
152
}
153
};
154
155
const double ratio = .5*(sqrt(5)-1); // an irrational number to minimize chance of intersecting a corner or other point of interest
156
std::vector<int> orientations(contours.size());
157
std::vector<Intersection> intersections;
158
for (int i = 0; i < (int) contours.size(); ++i) {
159
if (!orientations[i] && !contours[i].edges.empty()) {
160
// Find an Y that crosses the contour
161
double y0 = contours[i].edges.front()->point(0).y;
162
double y1 = y0;
163
for (std::vector<EdgeHolder>::const_iterator edge = contours[i].edges.begin(); edge != contours[i].edges.end() && y0 == y1; ++edge)
164
y1 = (*edge)->point(1).y;
165
for (std::vector<EdgeHolder>::const_iterator edge = contours[i].edges.begin(); edge != contours[i].edges.end() && y0 == y1; ++edge)
166
y1 = (*edge)->point(ratio).y; // in case all endpoints are in a horizontal line
167
double y = mix(y0, y1, ratio);
168
// Scanline through whole shape at Y
169
double x[3];
170
int dy[3];
171
for (int j = 0; j < (int) contours.size(); ++j) {
172
for (std::vector<EdgeHolder>::const_iterator edge = contours[j].edges.begin(); edge != contours[j].edges.end(); ++edge) {
173
int n = (*edge)->scanlineIntersections(x, dy, y);
174
for (int k = 0; k < n; ++k) {
175
Intersection intersection = { x[k], dy[k], j };
176
intersections.push_back(intersection);
177
}
178
}
179
}
180
if (!intersections.empty()) {
181
qsort(&intersections[0], intersections.size(), sizeof(Intersection), &Intersection::compare);
182
// Disqualify multiple intersections
183
for (int j = 1; j < (int) intersections.size(); ++j)
184
if (intersections[j].x == intersections[j-1].x)
185
intersections[j].direction = intersections[j-1].direction = 0;
186
// Inspect scanline and deduce orientations of intersected contours
187
for (int j = 0; j < (int) intersections.size(); ++j)
188
if (intersections[j].direction)
189
orientations[intersections[j].contourIndex] += 2*((j&1)^(intersections[j].direction > 0))-1;
190
intersections.clear();
191
}
192
}
193
}
194
// Reverse contours that have the opposite orientation
195
for (int i = 0; i < (int) contours.size(); ++i)
196
if (orientations[i] < 0)
197
contours[i].reverse();
198
}
199
200
YAxisOrientation Shape::getYAxisOrientation() const {
201
return inverseYAxis ? MSDFGEN_Y_AXIS_NONDEFAULT_ORIENTATION : MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION;
202
}
203
204
void Shape::setYAxisOrientation(YAxisOrientation yAxisOrientation) {
205
inverseYAxis = yAxisOrientation != MSDFGEN_Y_AXIS_DEFAULT_ORIENTATION;
206
}
207
208
}
209
210