KHTML
Path.cpp
Go to the documentation of this file.
00001 /* 00002 * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. 00003 * 2006 Rob Buis <buis@kde.org> 00004 * Copyright (C) 2007 Eric Seidel <eric@webkit.org> 00005 * 00006 * Redistribution and use in source and binary forms, with or without 00007 * modification, are permitted provided that the following conditions 00008 * are met: 00009 * 1. Redistributions of source code must retain the above copyright 00010 * notice, this list of conditions and the following disclaimer. 00011 * 2. Redistributions in binary form must reproduce the above copyright 00012 * notice, this list of conditions and the following disclaimer in the 00013 * documentation and/or other materials provided with the distribution. 00014 * 00015 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 00016 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00017 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 00018 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 00019 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 00020 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 00021 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 00022 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 00023 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00024 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 00025 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00026 */ 00027 00028 00029 #include "config.h" 00030 #include "wtf/Platform.h" 00031 #include "Path.h" 00032 00033 #include "FloatPoint.h" 00034 #include "FloatRect.h" 00035 #include "PathTraversalState.h" 00036 #include <math.h> 00037 #include <wtf/MathExtras.h> 00038 00039 const float QUARTER = 0.552f; // approximation of control point positions on a bezier 00040 // to simulate a quarter of a circle. 00041 00042 using namespace WebCore; 00043 00044 namespace khtml { 00045 00046 static void pathLengthApplierFunction(void* info, const PathElement* element) 00047 { 00048 PathTraversalState& traversalState = *static_cast<PathTraversalState*>(info); 00049 if (traversalState.m_success) 00050 return; 00051 traversalState.m_previous = traversalState.m_current; 00052 FloatPoint* points = element->points; 00053 float segmentLength = 0.0f; 00054 switch (element->type) { 00055 case PathElementMoveToPoint: 00056 segmentLength = traversalState.moveTo(points[0]); 00057 break; 00058 case PathElementAddLineToPoint: 00059 segmentLength = traversalState.lineTo(points[0]); 00060 break; 00061 case PathElementAddQuadCurveToPoint: 00062 segmentLength = traversalState.quadraticBezierTo(points[0], points[1]); 00063 break; 00064 case PathElementAddCurveToPoint: 00065 segmentLength = traversalState.cubicBezierTo(points[0], points[1], points[2]); 00066 break; 00067 case PathElementCloseSubpath: 00068 segmentLength = traversalState.closeSubpath(); 00069 break; 00070 } 00071 traversalState.m_totalLength += segmentLength; 00072 if ((traversalState.m_action == PathTraversalState::TraversalPointAtLength || 00073 traversalState.m_action == PathTraversalState::TraversalNormalAngleAtLength) && 00074 (traversalState.m_totalLength >= traversalState.m_desiredLength)) { 00075 FloatSize change = traversalState.m_current - traversalState.m_previous; 00076 float slope = atan2f(change.height(), change.width()); 00077 00078 if (traversalState.m_action == PathTraversalState::TraversalPointAtLength) { 00079 float offset = traversalState.m_desiredLength - traversalState.m_totalLength; 00080 traversalState.m_current.move(offset * cosf(slope), offset * sinf(slope)); 00081 } else { 00082 static const float rad2deg = 180.0f / piFloat; 00083 traversalState.m_normalAngle = slope * rad2deg; 00084 } 00085 00086 traversalState.m_success = true; 00087 } 00088 } 00089 00090 float Path::length() 00091 { 00092 PathTraversalState traversalState(PathTraversalState::TraversalTotalLength); 00093 apply(&traversalState, pathLengthApplierFunction); 00094 return traversalState.m_totalLength; 00095 } 00096 00097 FloatPoint Path::pointAtLength(float length, bool& ok) 00098 { 00099 PathTraversalState traversalState(PathTraversalState::TraversalPointAtLength); 00100 traversalState.m_desiredLength = length; 00101 apply(&traversalState, pathLengthApplierFunction); 00102 ok = traversalState.m_success; 00103 return traversalState.m_current; 00104 } 00105 00106 float Path::normalAngleAtLength(float length, bool& ok) 00107 { 00108 PathTraversalState traversalState(PathTraversalState::TraversalNormalAngleAtLength); 00109 traversalState.m_desiredLength = length; 00110 apply(&traversalState, pathLengthApplierFunction); 00111 ok = traversalState.m_success; 00112 return traversalState.m_normalAngle; 00113 } 00114 00115 Path Path::createRoundedRectangle(const FloatRect& rectangle, const FloatSize& roundingRadii) 00116 { 00117 Path path; 00118 float x = rectangle.x(); 00119 float y = rectangle.y(); 00120 float width = rectangle.width(); 00121 float height = rectangle.height(); 00122 float rx = roundingRadii.width(); 00123 float ry = roundingRadii.height(); 00124 if (width <= 0.0f || height <= 0.0f) 00125 return path; 00126 00127 float dx = rx, dy = ry; 00128 // If rx is greater than half of the width of the rectangle 00129 // then set rx to half of the width (required in SVG spec) 00130 if (dx > width * 0.5f) 00131 dx = width * 0.5f; 00132 00133 // If ry is greater than half of the height of the rectangle 00134 // then set ry to half of the height (required in SVG spec) 00135 if (dy > height * 0.5f) 00136 dy = height * 0.5f; 00137 00138 path.moveTo(FloatPoint(x + dx, y)); 00139 00140 if (dx < width * 0.5f) 00141 path.addLineTo(FloatPoint(x + width - rx, y)); 00142 00143 path.addBezierCurveTo(FloatPoint(x + width - dx * (1 - QUARTER), y), FloatPoint(x + width, y + dy * (1 - QUARTER)), FloatPoint(x + width, y + dy)); 00144 00145 if (dy < height * 0.5) 00146 path.addLineTo(FloatPoint(x + width, y + height - dy)); 00147 00148 path.addBezierCurveTo(FloatPoint(x + width, y + height - dy * (1 - QUARTER)), FloatPoint(x + width - dx * (1 - QUARTER), y + height), FloatPoint(x + width - dx, y + height)); 00149 00150 if (dx < width * 0.5) 00151 path.addLineTo(FloatPoint(x + dx, y + height)); 00152 00153 path.addBezierCurveTo(FloatPoint(x + dx * (1 - QUARTER), y + height), FloatPoint(x, y + height - dy * (1 - QUARTER)), FloatPoint(x, y + height - dy)); 00154 00155 if (dy < height * 0.5) 00156 path.addLineTo(FloatPoint(x, y + dy)); 00157 00158 path.addBezierCurveTo(FloatPoint(x, y + dy * (1 - QUARTER)), FloatPoint(x + dx * (1 - QUARTER), y), FloatPoint(x + dx, y)); 00159 00160 path.closeSubpath(); 00161 00162 return path; 00163 } 00164 00165 Path Path::createRoundedRectangle(const FloatRect& rectangle, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius) 00166 { 00167 Path path; 00168 00169 float width = rectangle.width(); 00170 float height = rectangle.height(); 00171 if (width <= 0.0 || height <= 0.0) 00172 return path; 00173 00174 if (width < topLeftRadius.width() + topRightRadius.width() 00175 || width < bottomLeftRadius.width() + bottomRightRadius.width() 00176 || height < topLeftRadius.height() + bottomLeftRadius.height() 00177 || height < topRightRadius.height() + bottomRightRadius.height()) 00178 // If all the radii cannot be accommodated, return a rect. 00179 return createRectangle(rectangle); 00180 00181 float x = rectangle.x(); 00182 float y = rectangle.y(); 00183 00184 path.moveTo(FloatPoint(x + topLeftRadius.width(), y)); 00185 00186 path.addLineTo(FloatPoint(x + width - topRightRadius.width(), y)); 00187 00188 path.addBezierCurveTo(FloatPoint(x + width - topRightRadius.width() * (1 - QUARTER), y), FloatPoint(x + width, y + topRightRadius.height() * (1 - QUARTER)), FloatPoint(x + width, y + topRightRadius.height())); 00189 00190 path.addLineTo(FloatPoint(x + width, y + height - bottomRightRadius.height())); 00191 00192 path.addBezierCurveTo(FloatPoint(x + width, y + height - bottomRightRadius.height() * (1 - QUARTER)), FloatPoint(x + width - bottomRightRadius.width() * (1 - QUARTER), y + height), FloatPoint(x + width - bottomRightRadius.width(), y + height)); 00193 00194 path.addLineTo(FloatPoint(x + bottomLeftRadius.width(), y + height)); 00195 00196 path.addBezierCurveTo(FloatPoint(x + bottomLeftRadius.width() * (1 - QUARTER), y + height), FloatPoint(x, y + height - bottomLeftRadius.height() * (1 - QUARTER)), FloatPoint(x, y + height - bottomLeftRadius.height())); 00197 00198 path.addLineTo(FloatPoint(x, y + topLeftRadius.height())); 00199 00200 path.addBezierCurveTo(FloatPoint(x, y + topLeftRadius.height() * (1 - QUARTER)), FloatPoint(x + topLeftRadius.width() * (1 - QUARTER), y), FloatPoint(x + topLeftRadius.width(), y)); 00201 00202 path.closeSubpath(); 00203 00204 return path; 00205 } 00206 00207 Path Path::createRectangle(const FloatRect& rectangle) 00208 { 00209 Path path; 00210 float x = rectangle.x(); 00211 float y = rectangle.y(); 00212 float width = rectangle.width(); 00213 float height = rectangle.height(); 00214 if (width <= 0.0f || height <= 0.0f) 00215 return path; 00216 00217 path.moveTo(FloatPoint(x, y)); 00218 path.addLineTo(FloatPoint(x + width, y)); 00219 path.addLineTo(FloatPoint(x + width, y + height)); 00220 path.addLineTo(FloatPoint(x, y + height)); 00221 path.closeSubpath(); 00222 00223 return path; 00224 } 00225 00226 Path Path::createEllipse(const FloatPoint& center, float rx, float ry) 00227 { 00228 float cx = center.x(); 00229 float cy = center.y(); 00230 Path path; 00231 if (rx <= 0.0f || ry <= 0.0f) 00232 return path; 00233 00234 float x = cx; 00235 float y = cy; 00236 00237 unsigned step = 0, num = 100; 00238 bool running = true; 00239 while (running) 00240 { 00241 if (step == num) 00242 { 00243 running = false; 00244 break; 00245 } 00246 00247 float angle = static_cast<float>(step) / static_cast<float>(num) * 2.0f * piFloat; 00248 x = cx + cosf(angle) * rx; 00249 y = cy + sinf(angle) * ry; 00250 00251 step++; 00252 if (step == 1) 00253 path.moveTo(FloatPoint(x, y)); 00254 else 00255 path.addLineTo(FloatPoint(x, y)); 00256 } 00257 00258 path.closeSubpath(); 00259 00260 return path; 00261 } 00262 00263 Path Path::createCircle(const FloatPoint& center, float r) 00264 { 00265 return createEllipse(center, r, r); 00266 } 00267 00268 Path Path::createLine(const FloatPoint& start, const FloatPoint& end) 00269 { 00270 Path path; 00271 if (start.x() == end.x() && start.y() == end.y()) 00272 return path; 00273 00274 path.moveTo(start); 00275 path.addLineTo(end); 00276 00277 return path; 00278 } 00279 00280 }
KDE 4.6 API Reference