1 /* 2 * Copyright (C) 2010 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 28 #if ENABLE(ACCELERATED_2D_CANVAS) 29 30 #include "LoopBlinnLocalTriangulator.h" 31 32 #include "LoopBlinnMathUtils.h" 33 #include <algorithm> 34 35 namespace WebCore { 36 37 using LoopBlinnMathUtils::approxEqual; 38 using LoopBlinnMathUtils::linesIntersect; 39 using LoopBlinnMathUtils::pointInTriangle; 40 41 bool LoopBlinnLocalTriangulator::Triangle::contains(LoopBlinnLocalTriangulator::Vertex* v) 42 { 43 return indexForVertex(v) >= 0; 44 } 45 46 LoopBlinnLocalTriangulator::Vertex* LoopBlinnLocalTriangulator::Triangle::nextVertex(LoopBlinnLocalTriangulator::Vertex* current, bool traverseCounterClockwise) 47 { 48 int index = indexForVertex(current); 49 ASSERT(index >= 0); 50 if (traverseCounterClockwise) 51 ++index; 52 else 53 --index; 54 if (index < 0) 55 index += 3; 56 else 57 index = index % 3; 58 return m_vertices[index]; 59 } 60 61 int LoopBlinnLocalTriangulator::Triangle::indexForVertex(LoopBlinnLocalTriangulator::Vertex* vertex) 62 { 63 for (int i = 0; i < 3; ++i) 64 if (m_vertices[i] == vertex) 65 return i; 66 return -1; 67 } 68 69 void LoopBlinnLocalTriangulator::Triangle::makeCounterClockwise() 70 { 71 // Possibly swaps two vertices so that the triangle's vertices are 72 // always specified in counterclockwise order. This orders the 73 // vertices canonically when walking the interior edges from the 74 // start to the end vertex. 75 FloatPoint3D point0(m_vertices[0]->xyCoordinates()); 76 FloatPoint3D point1(m_vertices[1]->xyCoordinates()); 77 FloatPoint3D point2(m_vertices[2]->xyCoordinates()); 78 FloatPoint3D crossProduct = (point1 - point0).cross(point2 - point0); 79 if (crossProduct.z() < 0) 80 std::swap(m_vertices[1], m_vertices[2]); 81 } 82 83 LoopBlinnLocalTriangulator::LoopBlinnLocalTriangulator() 84 { 85 reset(); 86 } 87 88 void LoopBlinnLocalTriangulator::reset() 89 { 90 m_numberOfTriangles = 0; 91 m_numberOfInteriorVertices = 0; 92 for (int i = 0; i < 4; ++i) { 93 m_interiorVertices[i] = 0; 94 m_vertices[i].resetFlags(); 95 } 96 } 97 98 void LoopBlinnLocalTriangulator::triangulate(InsideEdgeComputation computeInsideEdges, LoopBlinnConstants::FillSide sideToFill) 99 { 100 triangulateHelper(sideToFill); 101 102 if (computeInsideEdges == ComputeInsideEdges) { 103 // We need to compute which vertices describe the path along the 104 // interior portion of the shape, to feed these vertices to the 105 // more general tessellation algorithm. It is possible that we 106 // could determine this directly while producing triangles above. 107 // Here we try to do it generally just by examining the triangles 108 // that have already been produced. We walk around them in a 109 // specific direction determined by which side of the curve is 110 // being filled. We ignore the interior vertex unless it is also 111 // the ending vertex, and skip the edges shared between two 112 // triangles. 113 Vertex* v = &m_vertices[0]; 114 addInteriorVertex(v); 115 int numSteps = 0; 116 while (!v->end() && numSteps < 4) { 117 // Find the next vertex according to the above rules 118 bool gotNext = false; 119 for (int i = 0; i < numberOfTriangles() && !gotNext; ++i) { 120 Triangle* tri = getTriangle(i); 121 if (tri->contains(v)) { 122 Vertex* next = tri->nextVertex(v, sideToFill == LoopBlinnConstants::RightSide); 123 if (!next->marked() && !isSharedEdge(v, next) && (!next->interior() || next->end())) { 124 addInteriorVertex(next); 125 v = next; 126 // Break out of for loop 127 gotNext = true; 128 } 129 } 130 } 131 ++numSteps; 132 } 133 if (!v->end()) { 134 // Something went wrong with the above algorithm; add the last 135 // vertex to the interior vertices anyway. (FIXME: should we 136 // add an assert here and do more extensive testing?) 137 addInteriorVertex(&m_vertices[3]); 138 } 139 } 140 } 141 142 void LoopBlinnLocalTriangulator::triangulateHelper(LoopBlinnConstants::FillSide sideToFill) 143 { 144 reset(); 145 146 m_vertices[3].setEnd(true); 147 148 // First test for degenerate cases. 149 for (int i = 0; i < 4; ++i) { 150 for (int j = i + 1; j < 4; ++j) { 151 if (approxEqual(m_vertices[i].xyCoordinates(), m_vertices[j].xyCoordinates())) { 152 // Two of the vertices are coincident, so we can eliminate at 153 // least one triangle. We might be able to eliminate the other 154 // as well, but this seems sufficient to avoid degenerate 155 // triangulations. 156 int indices[3] = { 0 }; 157 int index = 0; 158 for (int k = 0; k < 4; ++k) 159 if (k != j) 160 indices[index++] = k; 161 addTriangle(&m_vertices[indices[0]], 162 &m_vertices[indices[1]], 163 &m_vertices[indices[2]]); 164 return; 165 } 166 } 167 } 168 169 // See whether any of the points are fully contained in the 170 // triangle defined by the other three. 171 for (int i = 0; i < 4; ++i) { 172 int indices[3] = { 0 }; 173 int index = 0; 174 for (int j = 0; j < 4; ++j) 175 if (i != j) 176 indices[index++] = j; 177 if (pointInTriangle(m_vertices[i].xyCoordinates(), 178 m_vertices[indices[0]].xyCoordinates(), 179 m_vertices[indices[1]].xyCoordinates(), 180 m_vertices[indices[2]].xyCoordinates())) { 181 // Produce three triangles surrounding this interior vertex. 182 for (int j = 0; j < 3; ++j) 183 addTriangle(&m_vertices[indices[j % 3]], 184 &m_vertices[indices[(j + 1) % 3]], 185 &m_vertices[i]); 186 // Mark the interior vertex so we ignore it if trying to trace 187 // the interior edge. 188 m_vertices[i].setInterior(true); 189 return; 190 } 191 } 192 193 // There are only a few permutations of the vertices, ignoring 194 // rotations, which are irrelevant: 195 // 196 // 0--3 0--2 0--3 0--1 0--2 0--1 197 // | | | | | | | | | | | | 198 // | | | | | | | | | | | | 199 // 1--2 1--3 2--1 2--3 3--1 3--2 200 // 201 // Note that three of these are reflections of each other. 202 // Therefore there are only three possible triangulations: 203 // 204 // 0--3 0--2 0--3 205 // |\ | |\ | |\ | 206 // | \| | \| | \| 207 // 1--2 1--3 2--1 208 // 209 // From which we can choose by seeing which of the potential 210 // diagonals intersect. Note that we choose the shortest diagonal 211 // to split the quad. 212 if (linesIntersect(m_vertices[0].xyCoordinates(), 213 m_vertices[2].xyCoordinates(), 214 m_vertices[1].xyCoordinates(), 215 m_vertices[3].xyCoordinates())) { 216 if ((m_vertices[2].xyCoordinates() - m_vertices[0].xyCoordinates()).diagonalLengthSquared() < 217 (m_vertices[3].xyCoordinates() - m_vertices[1].xyCoordinates()).diagonalLengthSquared()) { 218 addTriangle(&m_vertices[0], &m_vertices[1], &m_vertices[2]); 219 addTriangle(&m_vertices[0], &m_vertices[2], &m_vertices[3]); 220 } else { 221 addTriangle(&m_vertices[0], &m_vertices[1], &m_vertices[3]); 222 addTriangle(&m_vertices[1], &m_vertices[2], &m_vertices[3]); 223 } 224 } else if (linesIntersect(m_vertices[0].xyCoordinates(), 225 m_vertices[3].xyCoordinates(), 226 m_vertices[1].xyCoordinates(), 227 m_vertices[2].xyCoordinates())) { 228 if ((m_vertices[3].xyCoordinates() - m_vertices[0].xyCoordinates()).diagonalLengthSquared() < 229 (m_vertices[2].xyCoordinates() - m_vertices[1].xyCoordinates()).diagonalLengthSquared()) { 230 addTriangle(&m_vertices[0], &m_vertices[1], &m_vertices[3]); 231 addTriangle(&m_vertices[0], &m_vertices[3], &m_vertices[2]); 232 } else { 233 addTriangle(&m_vertices[0], &m_vertices[1], &m_vertices[2]); 234 addTriangle(&m_vertices[2], &m_vertices[1], &m_vertices[3]); 235 } 236 } else { 237 // Lines (0->1), (2->3) intersect -- or should, modulo numerical 238 // precision issues 239 if ((m_vertices[1].xyCoordinates() - m_vertices[0].xyCoordinates()).diagonalLengthSquared() < 240 (m_vertices[3].xyCoordinates() - m_vertices[2].xyCoordinates()).diagonalLengthSquared()) { 241 addTriangle(&m_vertices[0], &m_vertices[2], &m_vertices[1]); 242 addTriangle(&m_vertices[0], &m_vertices[1], &m_vertices[3]); 243 } else { 244 addTriangle(&m_vertices[0], &m_vertices[2], &m_vertices[3]); 245 addTriangle(&m_vertices[3], &m_vertices[2], &m_vertices[1]); 246 } 247 } 248 } 249 250 void LoopBlinnLocalTriangulator::addTriangle(Vertex* v0, Vertex* v1, Vertex* v2) 251 { 252 ASSERT(m_numberOfTriangles < 3); 253 m_triangles[m_numberOfTriangles++].setVertices(v0, v1, v2); 254 } 255 256 void LoopBlinnLocalTriangulator::addInteriorVertex(Vertex* v) 257 { 258 ASSERT(m_numberOfInteriorVertices < 4); 259 m_interiorVertices[m_numberOfInteriorVertices++] = v; 260 v->setMarked(true); 261 } 262 263 bool LoopBlinnLocalTriangulator::isSharedEdge(Vertex* v0, Vertex* v1) 264 { 265 bool haveEdge01 = false; 266 bool haveEdge10 = false; 267 for (int i = 0; i < numberOfTriangles(); ++i) { 268 Triangle* tri = getTriangle(i); 269 if (tri->contains(v0) && tri->nextVertex(v0, true) == v1) 270 haveEdge01 = true; 271 if (tri->contains(v1) && tri->nextVertex(v1, true) == v0) 272 haveEdge10 = true; 273 } 274 return haveEdge01 && haveEdge10; 275 } 276 277 } // namespace WebCore 278 279 #endif 280