summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/graphics/gpu/LoopBlinnLocalTriangulator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/platform/graphics/gpu/LoopBlinnLocalTriangulator.cpp')
-rw-r--r--WebCore/platform/graphics/gpu/LoopBlinnLocalTriangulator.cpp275
1 files changed, 275 insertions, 0 deletions
diff --git a/WebCore/platform/graphics/gpu/LoopBlinnLocalTriangulator.cpp b/WebCore/platform/graphics/gpu/LoopBlinnLocalTriangulator.cpp
new file mode 100644
index 0000000..3b73ff6
--- /dev/null
+++ b/WebCore/platform/graphics/gpu/LoopBlinnLocalTriangulator.cpp
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "LoopBlinnLocalTriangulator.h"
+
+#include "LoopBlinnMathUtils.h"
+#include <algorithm>
+
+namespace WebCore {
+
+using LoopBlinnMathUtils::approxEqual;
+using LoopBlinnMathUtils::linesIntersect;
+using LoopBlinnMathUtils::pointInTriangle;
+
+bool LoopBlinnLocalTriangulator::Triangle::contains(LoopBlinnLocalTriangulator::Vertex* v)
+{
+ return indexForVertex(v) >= 0;
+}
+
+LoopBlinnLocalTriangulator::Vertex* LoopBlinnLocalTriangulator::Triangle::nextVertex(LoopBlinnLocalTriangulator::Vertex* current, bool traverseCounterClockwise)
+{
+ int index = indexForVertex(current);
+ ASSERT(index >= 0);
+ if (traverseCounterClockwise)
+ ++index;
+ else
+ --index;
+ if (index < 0)
+ index += 3;
+ else
+ index = index % 3;
+ return m_vertices[index];
+}
+
+int LoopBlinnLocalTriangulator::Triangle::indexForVertex(LoopBlinnLocalTriangulator::Vertex* vertex)
+{
+ for (int i = 0; i < 3; ++i)
+ if (m_vertices[i] == vertex)
+ return i;
+ return -1;
+}
+
+void LoopBlinnLocalTriangulator::Triangle::makeCounterClockwise()
+{
+ // Possibly swaps two vertices so that the triangle's vertices are
+ // always specified in counterclockwise order. This orders the
+ // vertices canonically when walking the interior edges from the
+ // start to the end vertex.
+ FloatPoint3D point0(m_vertices[0]->xyCoordinates());
+ FloatPoint3D point1(m_vertices[1]->xyCoordinates());
+ FloatPoint3D point2(m_vertices[2]->xyCoordinates());
+ FloatPoint3D crossProduct = (point1 - point0).cross(point2 - point0);
+ if (crossProduct.z() < 0)
+ std::swap(m_vertices[1], m_vertices[2]);
+}
+
+LoopBlinnLocalTriangulator::LoopBlinnLocalTriangulator()
+{
+ reset();
+}
+
+void LoopBlinnLocalTriangulator::reset()
+{
+ m_numberOfTriangles = 0;
+ m_numberOfInteriorVertices = 0;
+ for (int i = 0; i < 4; ++i) {
+ m_interiorVertices[i] = 0;
+ m_vertices[i].resetFlags();
+ }
+}
+
+void LoopBlinnLocalTriangulator::triangulate(InsideEdgeComputation computeInsideEdges, LoopBlinnConstants::FillSide sideToFill)
+{
+ triangulateHelper(sideToFill);
+
+ if (computeInsideEdges == ComputeInsideEdges) {
+ // We need to compute which vertices describe the path along the
+ // interior portion of the shape, to feed these vertices to the
+ // more general tessellation algorithm. It is possible that we
+ // could determine this directly while producing triangles above.
+ // Here we try to do it generally just by examining the triangles
+ // that have already been produced. We walk around them in a
+ // specific direction determined by which side of the curve is
+ // being filled. We ignore the interior vertex unless it is also
+ // the ending vertex, and skip the edges shared between two
+ // triangles.
+ Vertex* v = &m_vertices[0];
+ addInteriorVertex(v);
+ int numSteps = 0;
+ while (!v->end() && numSteps < 4) {
+ // Find the next vertex according to the above rules
+ bool gotNext = false;
+ for (int i = 0; i < numberOfTriangles() && !gotNext; ++i) {
+ Triangle* tri = getTriangle(i);
+ if (tri->contains(v)) {
+ Vertex* next = tri->nextVertex(v, sideToFill == LoopBlinnConstants::RightSide);
+ if (!next->marked() && !isSharedEdge(v, next) && (!next->interior() || next->end())) {
+ addInteriorVertex(next);
+ v = next;
+ // Break out of for loop
+ gotNext = true;
+ }
+ }
+ }
+ ++numSteps;
+ }
+ if (!v->end()) {
+ // Something went wrong with the above algorithm; add the last
+ // vertex to the interior vertices anyway. (FIXME: should we
+ // add an assert here and do more extensive testing?)
+ addInteriorVertex(&m_vertices[3]);
+ }
+ }
+}
+
+void LoopBlinnLocalTriangulator::triangulateHelper(LoopBlinnConstants::FillSide sideToFill)
+{
+ reset();
+
+ m_vertices[3].setEnd(true);
+
+ // First test for degenerate cases.
+ for (int i = 0; i < 4; ++i) {
+ for (int j = i + 1; j < 4; ++j) {
+ if (approxEqual(m_vertices[i].xyCoordinates(), m_vertices[j].xyCoordinates())) {
+ // Two of the vertices are coincident, so we can eliminate at
+ // least one triangle. We might be able to eliminate the other
+ // as well, but this seems sufficient to avoid degenerate
+ // triangulations.
+ int indices[3] = { 0 };
+ int index = 0;
+ for (int k = 0; k < 4; ++k)
+ if (k != j)
+ indices[index++] = k;
+ addTriangle(&m_vertices[indices[0]],
+ &m_vertices[indices[1]],
+ &m_vertices[indices[2]]);
+ return;
+ }
+ }
+ }
+
+ // See whether any of the points are fully contained in the
+ // triangle defined by the other three.
+ for (int i = 0; i < 4; ++i) {
+ int indices[3] = { 0 };
+ int index = 0;
+ for (int j = 0; j < 4; ++j)
+ if (i != j)
+ indices[index++] = j;
+ if (pointInTriangle(m_vertices[i].xyCoordinates(),
+ m_vertices[indices[0]].xyCoordinates(),
+ m_vertices[indices[1]].xyCoordinates(),
+ m_vertices[indices[2]].xyCoordinates())) {
+ // Produce three triangles surrounding this interior vertex.
+ for (int j = 0; j < 3; ++j)
+ addTriangle(&m_vertices[indices[j % 3]],
+ &m_vertices[indices[(j + 1) % 3]],
+ &m_vertices[i]);
+ // Mark the interior vertex so we ignore it if trying to trace
+ // the interior edge.
+ m_vertices[i].setInterior(true);
+ return;
+ }
+ }
+
+ // There are only a few permutations of the vertices, ignoring
+ // rotations, which are irrelevant:
+ //
+ // 0--3 0--2 0--3 0--1 0--2 0--1
+ // | | | | | | | | | | | |
+ // | | | | | | | | | | | |
+ // 1--2 1--3 2--1 2--3 3--1 3--2
+ //
+ // Note that three of these are reflections of each other.
+ // Therefore there are only three possible triangulations:
+ //
+ // 0--3 0--2 0--3
+ // |\ | |\ | |\ |
+ // | \| | \| | \|
+ // 1--2 1--3 2--1
+ //
+ // From which we can choose by seeing which of the potential
+ // diagonals intersect. Note that we choose the shortest diagonal
+ // to split the quad.
+ if (linesIntersect(m_vertices[0].xyCoordinates(),
+ m_vertices[2].xyCoordinates(),
+ m_vertices[1].xyCoordinates(),
+ m_vertices[3].xyCoordinates())) {
+ if ((m_vertices[2].xyCoordinates() - m_vertices[0].xyCoordinates()).diagonalLengthSquared() <
+ (m_vertices[3].xyCoordinates() - m_vertices[1].xyCoordinates()).diagonalLengthSquared()) {
+ addTriangle(&m_vertices[0], &m_vertices[1], &m_vertices[2]);
+ addTriangle(&m_vertices[0], &m_vertices[2], &m_vertices[3]);
+ } else {
+ addTriangle(&m_vertices[0], &m_vertices[1], &m_vertices[3]);
+ addTriangle(&m_vertices[1], &m_vertices[2], &m_vertices[3]);
+ }
+ } else if (linesIntersect(m_vertices[0].xyCoordinates(),
+ m_vertices[3].xyCoordinates(),
+ m_vertices[1].xyCoordinates(),
+ m_vertices[2].xyCoordinates())) {
+ if ((m_vertices[3].xyCoordinates() - m_vertices[0].xyCoordinates()).diagonalLengthSquared() <
+ (m_vertices[2].xyCoordinates() - m_vertices[1].xyCoordinates()).diagonalLengthSquared()) {
+ addTriangle(&m_vertices[0], &m_vertices[1], &m_vertices[3]);
+ addTriangle(&m_vertices[0], &m_vertices[3], &m_vertices[2]);
+ } else {
+ addTriangle(&m_vertices[0], &m_vertices[1], &m_vertices[2]);
+ addTriangle(&m_vertices[2], &m_vertices[1], &m_vertices[3]);
+ }
+ } else {
+ // Lines (0->1), (2->3) intersect -- or should, modulo numerical
+ // precision issues
+ if ((m_vertices[1].xyCoordinates() - m_vertices[0].xyCoordinates()).diagonalLengthSquared() <
+ (m_vertices[3].xyCoordinates() - m_vertices[2].xyCoordinates()).diagonalLengthSquared()) {
+ addTriangle(&m_vertices[0], &m_vertices[2], &m_vertices[1]);
+ addTriangle(&m_vertices[0], &m_vertices[1], &m_vertices[3]);
+ } else {
+ addTriangle(&m_vertices[0], &m_vertices[2], &m_vertices[3]);
+ addTriangle(&m_vertices[3], &m_vertices[2], &m_vertices[1]);
+ }
+ }
+}
+
+void LoopBlinnLocalTriangulator::addTriangle(Vertex* v0, Vertex* v1, Vertex* v2)
+{
+ ASSERT(m_numberOfTriangles < 3);
+ m_triangles[m_numberOfTriangles++].setVertices(v0, v1, v2);
+}
+
+void LoopBlinnLocalTriangulator::addInteriorVertex(Vertex* v)
+{
+ ASSERT(m_numberOfInteriorVertices < 4);
+ m_interiorVertices[m_numberOfInteriorVertices++] = v;
+ v->setMarked(true);
+}
+
+bool LoopBlinnLocalTriangulator::isSharedEdge(Vertex* v0, Vertex* v1)
+{
+ bool haveEdge01 = false;
+ bool haveEdge10 = false;
+ for (int i = 0; i < numberOfTriangles(); ++i) {
+ Triangle* tri = getTriangle(i);
+ if (tri->contains(v0) && tri->nextVertex(v0, true) == v1)
+ haveEdge01 = true;
+ if (tri->contains(v1) && tri->nextVertex(v1, true) == v0)
+ haveEdge10 = true;
+ }
+ return haveEdge01 && haveEdge10;
+}
+
+} // namespace WebCore