blob: 1517a672f02c5fb7f69b116089f4ed98299cba2d [file] [log] [blame]
/*
* 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 <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
#endif