00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
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;
00040
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
00129
00130 if (dx > width * 0.5f)
00131 dx = width * 0.5f;
00132
00133
00134
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
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 }