| 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; |
no test coverage detected