/* * 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" #if ENABLE(ACCELERATED_2D_CANVAS) #include "LoopBlinnLocalTriangulator.h" #include "LoopBlinnMathUtils.h" #include 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 #endif