mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
227 lines
8.0 KiB
C++
227 lines
8.0 KiB
C++
#include <LottieMesh/LottieMesh.h>
|
|
|
|
#include "GenerateContours.h"
|
|
#include <LottieMesh/Point.h>
|
|
#include "Triangulation.h"
|
|
|
|
#include "tesselator.h"
|
|
#include "Polyline2D.h"
|
|
|
|
namespace {
|
|
};
|
|
|
|
namespace MeshGenerator {
|
|
|
|
std::unique_ptr<Mesh> generateMesh(std::vector<Path> const &paths, std::unique_ptr<Fill> fill, std::unique_ptr<Stroke> stroke) {
|
|
if (stroke) {
|
|
std::unique_ptr<Mesh> mesh = std::make_unique<Mesh>();
|
|
|
|
for (const auto &path : paths) {
|
|
crushedpixel::Polyline2D::JointStyle jointStyle = crushedpixel::Polyline2D::JointStyle::ROUND;
|
|
crushedpixel::Polyline2D::EndCapStyle endCapStyle = crushedpixel::Polyline2D::EndCapStyle::SQUARE;
|
|
switch (stroke->lineJoin) {
|
|
case Stroke::LineJoin::Miter:
|
|
jointStyle = crushedpixel::Polyline2D::JointStyle::MITER;
|
|
break;
|
|
case Stroke::LineJoin::Round:
|
|
jointStyle = crushedpixel::Polyline2D::JointStyle::ROUND;
|
|
break;
|
|
case Stroke::LineJoin::Bevel:
|
|
jointStyle = crushedpixel::Polyline2D::JointStyle::BEVEL;
|
|
break;
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
switch (stroke->lineCap) {
|
|
case Stroke::LineCap::Round: {
|
|
endCapStyle = crushedpixel::Polyline2D::EndCapStyle::ROUND;
|
|
break;
|
|
}
|
|
case Stroke::LineCap::Square: {
|
|
endCapStyle = crushedpixel::Polyline2D::EndCapStyle::SQUARE;
|
|
break;
|
|
}
|
|
case Stroke::LineCap::Butt: {
|
|
endCapStyle = crushedpixel::Polyline2D::EndCapStyle::BUTT;
|
|
break;
|
|
}
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
|
|
auto vertices = crushedpixel::Polyline2D::create(path.points, stroke->lineWidth, jointStyle, endCapStyle);
|
|
for (const auto &vertex : vertices) {
|
|
mesh->triangles.push_back((int)mesh->vertices.size());
|
|
mesh->vertices.push_back(vertex);
|
|
}
|
|
}
|
|
|
|
assert(mesh->triangles.size() % 3 == 0);
|
|
return mesh;
|
|
} else {
|
|
TESStesselator *tessellator = tessNewTess(NULL);
|
|
tessSetOption(tessellator, TESS_CONSTRAINED_DELAUNAY_TRIANGULATION, 1);
|
|
for (const auto &path : paths) {
|
|
tessAddContour(tessellator, 2, path.points.data(), sizeof(Point), (int)path.points.size());
|
|
}
|
|
tessTesselate(tessellator, TESS_WINDING_ODD, TESS_POLYGONS, 3, 2, NULL);
|
|
|
|
int vertexCount = tessGetVertexCount(tessellator);
|
|
const TESSreal *vertices = tessGetVertices(tessellator);
|
|
int indexCount = tessGetElementCount(tessellator) * 3;
|
|
const TESSindex *indices = tessGetElements(tessellator);
|
|
|
|
std::unique_ptr<Mesh> mesh = std::make_unique<Mesh>();
|
|
for (int i = 0; i < vertexCount; i++) {
|
|
mesh->vertices.push_back(Point(vertices[i * 2 + 0], vertices[i * 2 + 1]));
|
|
}
|
|
for (int i = 0; i < indexCount; i++) {
|
|
mesh->triangles.push_back(indices[i]);
|
|
}
|
|
return mesh;
|
|
}
|
|
|
|
std::unique_ptr<PlanarStraightLineGraph> graph;
|
|
std::unique_ptr<std::vector<Path>> updatedPaths;
|
|
|
|
bool isNonZero = false;
|
|
if (stroke) {
|
|
MeshGenerator::LineJoin mappedLineJoin = LineJoin::Round;
|
|
switch (stroke->lineJoin) {
|
|
case Stroke::LineJoin::Bevel:
|
|
mappedLineJoin = LineJoin::Bevel;
|
|
break;
|
|
case Stroke::LineJoin::Miter:
|
|
mappedLineJoin = LineJoin::Miter;
|
|
break;
|
|
case Stroke::LineJoin::Round:
|
|
mappedLineJoin = LineJoin::Round;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
LineCap mappedLineCap = LineCap::Round;
|
|
switch (stroke->lineCap) {
|
|
case Stroke::LineCap::Butt:
|
|
mappedLineCap = LineCap::Butt;
|
|
break;
|
|
case Stroke::LineCap::Round:
|
|
mappedLineCap = LineCap::Round;
|
|
break;
|
|
case Stroke::LineCap::Square:
|
|
mappedLineCap = LineCap::Square;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
auto strokePaths = generateStroke(paths, stroke->lineWidth, stroke->miterLimit, mappedLineJoin, mappedLineCap);
|
|
graph = makePlanarStraightLineGraph(strokePaths);
|
|
updatedPaths = std::make_unique<std::vector<Path>>(std::move(strokePaths));
|
|
isNonZero = true;
|
|
} else if (fill) {
|
|
graph = makePlanarStraightLineGraph(paths);
|
|
switch (fill->rule) {
|
|
case Fill::Rule::EvenOdd:
|
|
break;
|
|
case Fill::Rule::NonZero:
|
|
isNonZero = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
|
|
if (!graph) {
|
|
return nullptr;
|
|
}
|
|
|
|
std::unique_ptr<Mesh> resultMesh = std::make_unique<Mesh>();
|
|
for (const auto &vertex : graph->points) {
|
|
resultMesh->vertices.push_back(vertex);
|
|
}
|
|
|
|
auto faces = findFaces(*graph);
|
|
|
|
for (int iFace = (int)faces.size() - 1; iFace >= 0; iFace--) {
|
|
const auto &face = faces[iFace];
|
|
|
|
float edgeSum = 0.0f;
|
|
for (int i = 0; i < face.vertices.size(); i++) {
|
|
MeshGenerator::Point nextVertex(0.0f, 0.0f);
|
|
if (i == face.vertices.size() - 1) {
|
|
nextVertex = graph->points[face.vertices[0]];
|
|
} else {
|
|
nextVertex = graph->points[face.vertices[i + 1]];
|
|
}
|
|
MeshGenerator::Point vertex = graph->points[face.vertices[i]];
|
|
|
|
edgeSum += (nextVertex.x - vertex.x) * (nextVertex.y + vertex.y);
|
|
}
|
|
|
|
if (edgeSum < 0.0f) {
|
|
faces.erase(faces.begin() + iFace);
|
|
}
|
|
}
|
|
|
|
for (int iFace = 0; iFace < faces.size(); iFace++) {
|
|
const auto &face = faces[iFace];
|
|
|
|
std::vector<int> faceIndices;
|
|
for (const auto &point : face.vertices) {
|
|
faceIndices.push_back(point);
|
|
}
|
|
|
|
std::vector<std::vector<int>> holeIndices;
|
|
for (int iOtherFace = 0; iOtherFace < faces.size(); iOtherFace++) {
|
|
if (iFace == iOtherFace) {
|
|
continue;
|
|
}
|
|
if (faceInsideFace(*graph, face, faces[iOtherFace])) {
|
|
std::vector<int> otherFaceIndices;
|
|
for (const auto &point : faces[iOtherFace].vertices) {
|
|
otherFaceIndices.push_back(point);
|
|
}
|
|
holeIndices.push_back(std::move(otherFaceIndices));
|
|
}
|
|
}
|
|
|
|
auto triangleIndices = triangulatePolygon(graph->points, faceIndices, holeIndices);
|
|
if (triangleIndices.empty()) {
|
|
continue;
|
|
}
|
|
|
|
float largestTriangleArea = 0.0f;
|
|
int largestTriangleIndex = -1;
|
|
for (int i = 0; i < (int)(triangleIndices.size() / 3); i++) {
|
|
float area = MeshGenerator::triangleArea(graph->points[triangleIndices[i * 3 + 0]], graph->points[triangleIndices[i * 3 + 1]], graph->points[triangleIndices[i * 3 + 2]]);
|
|
if (largestTriangleIndex == -1 || largestTriangleArea < area) {
|
|
largestTriangleIndex = i / 3;
|
|
largestTriangleArea = area;
|
|
}
|
|
}
|
|
|
|
MeshGenerator::Point triangleCenter(0.0f, 0.0f);
|
|
for (int i = 0; i < 3; i++) {
|
|
triangleCenter.x += graph->points[triangleIndices[largestTriangleIndex * 3 + i]].x;
|
|
triangleCenter.y += graph->points[triangleIndices[largestTriangleIndex * 3 + i]].y;
|
|
}
|
|
triangleCenter.x /= 3.0f;
|
|
triangleCenter.y /= 3.0f;
|
|
if (!MeshGenerator::traceRay(updatedPaths == nullptr ? paths : *updatedPaths, triangleCenter, isNonZero)) {
|
|
continue;
|
|
}
|
|
|
|
for (auto index : triangleIndices) {
|
|
resultMesh->triangles.push_back(index);
|
|
}
|
|
}
|
|
|
|
return resultMesh;
|
|
}
|
|
|
|
}
|