Coons patch meshing

This commit is contained in:
Jakub Melka 2019-09-22 13:18:42 +02:00
parent 2ca3f907c3
commit d87995f8b8
4 changed files with 268 additions and 6 deletions

View File

@ -296,6 +296,7 @@ PDFPatternPtr PDFPattern::createShadingPattern(const PDFDictionary* colorSpaceDi
case ShadingType::FreeFormGouradTriangle:
case ShadingType::LatticeFormGouradTriangle:
case ShadingType::CoonsPatchMesh:
case ShadingType::TensorProductPatchMesh:
{
PDFLatticeFormGouradTriangleShading* latticeFormGouradTriangleShading = nullptr;
@ -316,6 +317,12 @@ PDFPatternPtr PDFPattern::createShadingPattern(const PDFDictionary* colorSpaceDi
break;
}
case ShadingType::CoonsPatchMesh:
{
type4567Shading = new PDFCoonsPatchShading;
break;
}
case ShadingType::TensorProductPatchMesh:
{
type4567Shading = new PDFTensorProductPatchShading;
@ -1003,6 +1010,29 @@ void PDFMesh::transform(const QMatrix& matrix)
m_backgroundPath = matrix.map(m_backgroundPath);
}
void PDFMesh::addMesh(std::vector<QPointF>&& vertices, std::vector<PDFMesh::Triangle>&& triangles)
{
if (isEmpty())
{
m_vertices = qMove(vertices);
m_triangles = qMove(triangles);
}
else
{
size_t offset = m_vertices.size();
m_vertices.insert(m_vertices.cend(), vertices.cbegin(), vertices.cend());
for (Triangle& triangle : triangles)
{
triangle.v1 += static_cast<uint32_t>(offset);
triangle.v2 += static_cast<uint32_t>(offset);
triangle.v3 += static_cast<uint32_t>(offset);
}
m_triangles.insert(m_triangles.cend(), triangles.cbegin(), triangles.cend());
}
}
QPointF PDFMesh::getTriangleCenter(const PDFMesh::Triangle& triangle) const
{
return (m_vertices[triangle.v1] + m_vertices[triangle.v2] + m_vertices[triangle.v3]) / 3.0;
@ -1864,7 +1894,7 @@ PDFMesh PDFTensorProductPatchShading::createMesh(const PDFMeshQualitySettings& s
size_t remainder = (8 - (bitsPerPatch % 8)) % 8;
bitsPerPatch += remainder;
size_t bytesPerPatch = bitsPerPatch / 8;
size_t patchCount = m_data.size() / bytesPerPatch;
size_t patchCountEstimate = m_data.size() / bytesPerPatch;
const PDFReal vertexScaleRatio = 1.0 / double((static_cast<uint64_t>(1) << m_bitsPerCoordinate) - 1);
const PDFReal xScaleRatio = (m_xmax - m_xmin) * vertexScaleRatio;
@ -1872,7 +1902,7 @@ PDFMesh PDFTensorProductPatchShading::createMesh(const PDFMeshQualitySettings& s
const PDFReal colorScaleRatio = 1.0 / double((static_cast<uint64_t>(1) << m_bitsPerComponent) - 1);
PDFTensorPatches patches;
patches.reserve(patchCount);
patches.reserve(patchCountEstimate);
PDFBitReader reader(&m_data, 8);
@ -1903,7 +1933,7 @@ PDFMesh PDFTensorProductPatchShading::createMesh(const PDFMeshQualitySettings& s
return getColor(color);
};
for (size_t i = 0; i < patchCount; ++i)
while (!reader.isAtEnd())
{
const uint8_t flags = readFlags();
switch (flags)
@ -2291,8 +2321,7 @@ void PDFTensorProductPatchShadingBase::fillMesh(PDFMesh& mesh, const PDFMeshQual
triangles.push_back(meshTriangle);
}
mesh.setVertices(qMove(vertices));
mesh.setTriangles(qMove(triangles));
mesh.addMesh(qMove(vertices), qMove(triangles));
}
void PDFTensorProductPatchShadingBase::fillMesh(PDFMesh& mesh,
@ -2333,7 +2362,211 @@ void PDFTensorProductPatchShadingBase::addTriangle(std::vector<Triangle>& triang
triangles.push_back(triangle);
}
ShadingType PDFCoonsPatchShading::getShadingType() const
{
return ShadingType::CoonsPatchMesh;
}
PDFMesh PDFCoonsPatchShading::createMesh(const PDFMeshQualitySettings& settings) const
{
PDFMesh mesh;
QMatrix patternSpaceToDeviceSpaceMatrix = getPatternSpaceToDeviceSpaceMatrix(settings);
size_t bitsPerPatch = m_bitsPerFlag + 16 * 2 * m_bitsPerCoordinate + 4 * m_colorComponentCount * m_bitsPerComponent;
size_t remainder = (8 - (bitsPerPatch % 8)) % 8;
bitsPerPatch += remainder;
size_t bytesPerPatch = bitsPerPatch / 8;
size_t patchCountEstimate = m_data.size() / bytesPerPatch;
const PDFReal vertexScaleRatio = 1.0 / double((static_cast<uint64_t>(1) << m_bitsPerCoordinate) - 1);
const PDFReal xScaleRatio = (m_xmax - m_xmin) * vertexScaleRatio;
const PDFReal yScaleRatio = (m_ymax - m_ymin) * vertexScaleRatio;
const PDFReal colorScaleRatio = 1.0 / double((static_cast<uint64_t>(1) << m_bitsPerComponent) - 1);
PDFTensorPatches patches;
patches.reserve(patchCountEstimate);
PDFBitReader reader(&m_data, 8);
auto readFlags = [this, &reader]() -> uint8_t
{
return reader.read(m_bitsPerFlag);
};
auto readPoint = [this, &reader, &patternSpaceToDeviceSpaceMatrix, xScaleRatio, yScaleRatio]() -> QPointF
{
const PDFReal x = m_xmin + (reader.read(m_bitsPerCoordinate)) * xScaleRatio;
const PDFReal y = m_ymin + (reader.read(m_bitsPerCoordinate)) * yScaleRatio;
return patternSpaceToDeviceSpaceMatrix.map(QPointF(x, y));
};
auto readColor = [this, &reader, colorScaleRatio]() -> PDFColor
{
PDFColor color;
color.resize(m_colorComponentCount);
for (size_t i = 0; i < m_colorComponentCount; ++i)
{
const double cMin = m_limits[2 * i + 0];
const double cMax = m_limits[2 * i + 1];
color[i] = cMin + (reader.read(m_bitsPerComponent)) * (cMax - cMin) * colorScaleRatio;
}
return getColor(color);
};
std::array<QPointF, 12> vertices;
std::array<PDFColor, 4> colors;
auto createTensorPatch = [&]
{
// Jakub Melka: please see following pictures, in PDF 1.7 specification, figures 4.22 and 4.24.
// We copy the control points to the tensor patch in the appropriate order.
//
// P_13 P_23 V_5 V_6
// / \ / \
// P_03/ \ P_33 V_4/ \ V_7
// |-----------------------------| |-----------------------------|
// / | |\ / | C_2 C_3 |\
// / | | \ / | | \
// P_02 | | P_32 V_3 | | V_8
// | P_12 P_22 | | |
// | | | |
// | | | |
// | | | |
// | P_11 P_21 | | |
// | | | |
// P_01 | | P_31 V_2 | | V_9
// \ | | / \ | | /
// \ | |/ \ | C_1 C_4 |/
// |-----------------------------| |-----------------------------|
// P_00 \ / P_30 V_1 \ / V_10
// \ / \ /
// P_10 P_20 V_12 V_11
PDFTensorPatch::PointMatrix P;
PDFTensorPatch::Colors tensorColors;
P[0][0] = vertices[0];
P[0][1] = vertices[1];
P[0][2] = vertices[2];
P[0][3] = vertices[3];
P[1][3] = vertices[4];
P[2][3] = vertices[5];
P[3][3] = vertices[6];
P[3][2] = vertices[7];
P[3][1] = vertices[8];
P[3][0] = vertices[9];
P[2][0] = vertices[10];
P[1][0] = vertices[11];
auto computeTensorInterior = [](QPointF p1, QPointF p2, QPointF p3, QPointF p4, QPointF p5, QPointF p6, QPointF p7, QPointF p8)
{
return (-4.0 * p1 + 6.0 * (p2 + p3) - 2.0 * (p4 + p5) + 3.0 * (p6 + p7) - p8) / 9.0;
};
P[1][1] = computeTensorInterior(P[0][0], P[0][1], P[1][0], P[0][3], P[3][0], P[3][1], P[1][3], P[3][3]);
P[1][2] = computeTensorInterior(P[0][3], P[0][2], P[1][3], P[0][0], P[3][3], P[3][2], P[1][0], P[3][0]);
P[2][1] = computeTensorInterior(P[3][0], P[3][1], P[2][0], P[3][3], P[3][0], P[0][1], P[2][3], P[0][3]);
P[2][2] = computeTensorInterior(P[3][3], P[3][2], P[2][3], P[3][0], P[0][3], P[0][2], P[2][0], P[0][0]);
tensorColors[PDFTensorPatch::C_00] = colors[0];
tensorColors[PDFTensorPatch::C_03] = colors[1];
tensorColors[PDFTensorPatch::C_33] = colors[2];
tensorColors[PDFTensorPatch::C_30] = colors[3];
patches.emplace_back(P, tensorColors);
};
auto readPatchesFlag123 = [&]
{
for (size_t i = 4; i < vertices.size(); ++i)
{
vertices[i] = readPoint();
}
colors[2] = readColor();
colors[3] = readColor();
};
while (!reader.isAtEnd())
{
const uint8_t flags = readFlags();
switch (flags)
{
case 0:
{
// New Coons patch
for (size_t i = 0; i < vertices.size(); ++i)
{
vertices[i] = readPoint();
}
for (size_t i = 0; i < colors.size(); ++i)
{
colors[i] = readColor();
}
createTensorPatch();
break;
}
case 1:
{
vertices[0] = vertices[3];
vertices[1] = vertices[4];
vertices[2] = vertices[5];
vertices[3] = vertices[6];
colors[0] = colors[1];
colors[1] = colors[2];
readPatchesFlag123();
createTensorPatch();
break;
}
case 2:
{
vertices[0] = vertices[6];
vertices[1] = vertices[7];
vertices[2] = vertices[8];
vertices[3] = vertices[9];
colors[0] = colors[2];
colors[1] = colors[3];
readPatchesFlag123();
createTensorPatch();
break;
}
case 3:
{
vertices[3] = vertices[0];
vertices[0] = vertices[9];
vertices[1] = vertices[10];
vertices[2] = vertices[11];
colors[1] = colors[0];
colors[0] = colors[3];
readPatchesFlag123();
createTensorPatch();
break;
}
default:
throw PDFParserException(PDFTranslationContext::tr("Invalid data in coons patch shading (flags = %1).").arg(flags));
}
}
fillMesh(mesh, patternSpaceToDeviceSpaceMatrix, settings, patches);
return mesh;
}
// TODO: Apply graphic state of the pattern
// TODO: Implement settings of meshing in the settings dialog
// TODO: Zmenit PDFParserException na PDFException
} // namespace pdf

View File

@ -77,7 +77,7 @@ struct PDFMeshQualitySettings
PDFReal tolerance = 0.01;
/// Test points to determine maximal curvature of the tensor product patch meshes
PDFInteger patchTestPoints = 256;
PDFInteger patchTestPoints = 64;
/// Lower value of the surface curvature meshing resolution mapping. When ratio between
/// current curvature at the center of meshed triangle and maximal curvature is below
@ -149,6 +149,12 @@ public:
/// \param triangles New triangle array
void setTriangles(std::vector<Triangle>&& triangles) { m_triangles = qMove(triangles); }
/// Merges the vertices/triangles (renumbers triangle indices) to this mesh.
/// Algorithm assumes that vertices/triangles are numbered from zero.
/// \param vertices Added vertex array
/// \param triangles Added triangle array
void addMesh(std::vector<QPointF>&& vertices, std::vector<Triangle>&& triangles);
/// Returns vertex at given index
/// \param index Index of the vertex
const QPointF& getVertex(size_t index) const { return m_vertices[index]; }
@ -167,6 +173,9 @@ public:
/// \param backgroundColor Background color
void setBackgroundColor(QColor backgroundColor) { m_backgroundColor = backgroundColor; }
/// Returns true, if mesh is empty
bool isEmpty() const { return m_vertices.empty(); }
private:
std::vector<QPointF> m_vertices;
std::vector<Triangle> m_triangles;
@ -518,6 +527,18 @@ private:
friend class PDFPattern;
};
class PDFCoonsPatchShading : public PDFTensorProductPatchShadingBase
{
public:
explicit PDFCoonsPatchShading() = default;
virtual ShadingType getShadingType() const override;
virtual PDFMesh createMesh(const PDFMeshQualitySettings& settings) const override;
private:
friend class PDFPattern;
};
class PDFTensorProductPatchShading : public PDFTensorProductPatchShadingBase
{
public:

View File

@ -79,4 +79,9 @@ void PDFBitReader::alignToBytes()
}
}
bool PDFBitReader::isAtEnd() const
{
return (m_position >= m_stream->size());
}
} // namespace pdf

View File

@ -99,6 +99,9 @@ public:
/// Seeks data to the byte boundary (number of processed bits is divisible by 8)
void alignToBytes();
/// Returns true, if we are at the end of the data stream (no more data can be read)
bool isAtEnd() const;
private:
const QByteArray* m_stream;
int m_position;