API 0.9.5
AppKit/CoreGraphics/CGPath.j
Go to the documentation of this file.
00001 /*
00002  * CGPath.j
00003  * AppKit
00004  *
00005  * Created by Francisco Tolmasky.
00006  * Copyright 2008, 280 North, Inc.
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Lesser General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2.1 of the License, or (at your option) any later version.
00012  *
00013  * This library is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00016  * Lesser General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Lesser General Public
00019  * License along with this library; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00021  */
00022 
00023 
00024 
00025 kCGPathElementMoveToPoint           = 0;
00026 kCGPathElementAddLineToPoint        = 1;
00027 kCGPathElementAddQuadCurveToPoint   = 2;
00028 kCGPathElementAddCurveToPoint       = 3;
00029 kCGPathElementCloseSubpath          = 4;
00030 
00031 kCGPathElementAddArc                = 5;
00032 kCGPathElementAddArcToPoint         = 6;
00033 
00042 function CGPathCreateMutable()
00043 {
00044     return { count:0, start:NULL, current:NULL, elements:[] };
00045 }
00046 
00051 function CGPathCreateMutableCopy(aPath)
00052 {
00053     var path = CGPathCreateMutable();
00054 
00055     CGPathAddPath(path, aPath);
00056 
00057     return path;
00058 }
00059 
00064 function CGPathCreateCopy(aPath)
00065 {
00066     return CGPathCreateMutableCopy(aPath);
00067 }
00068 
00069 function CGPathRelease(aPath)
00070 {
00071 }
00072 
00073 function CGPathRetain(aPath)
00074 {
00075     return aPath;
00076 }
00077 
00078 function CGPathAddArc(aPath, aTransform, x, y, aRadius, aStartAngle, anEndAngle, isClockwise)
00079 {
00080     if (aTransform && !_CGAffineTransformIsIdentity(aTransform))
00081     {
00082         var center = _CGPointMake(x, y),
00083             end = _CGPointMake(COS(anEndAngle), SIN(anEndAngle)),
00084             start = _CGPointMake(COS(aStartAngle), SIN(aStartAngle));
00085 
00086         end = _CGPointApplyAffineTransform(end, aTransform);
00087         start = _CGPointApplyAffineTransform(start, aTransform);
00088         center = _CGPointApplyAffineTransform(center, aTransform);
00089 
00090         x = center.x;
00091         y = center.y;
00092 
00093         var oldEndAngle = anEndAngle,
00094             oldStartAngle = aStartAngle;
00095 
00096         anEndAngle = ATAN2(end.y - aTransform.ty, end.x - aTransform.tx);
00097         aStartAngle = ATAN2(start.y - aTransform.ty, start.x - aTransform.tx);
00098 
00099         // Angles that equal "modulo" 2 pi return as equal after transforming them,
00100         // so we have to make sure to make them different again if they were different
00101         // to start out with.  It's the difference between no circle and a full circle.
00102         if (anEndAngle == aStartAngle && oldEndAngle != oldStartAngle)
00103             if (oldStartAngle > oldEndAngle)
00104                 anEndAngle = anEndAngle - PI2;
00105             else
00106                 aStartAngle = aStartAngle - PI2;
00107 
00108         aRadius = _CGSizeMake(aRadius, 0);
00109         aRadius = _CGSizeApplyAffineTransform(aRadius, aTransform);
00110         aRadius = SQRT(aRadius.width * aRadius.width + aRadius.height * aRadius.height);
00111     }
00112 
00113     aPath.current = _CGPointMake(x + aRadius * COS(anEndAngle), y + aRadius * SIN(anEndAngle));
00114     aPath.elements[aPath.count++] = { type:kCGPathElementAddArc, x:x, y:y, radius:aRadius, startAngle:aStartAngle, endAngle:anEndAngle };
00115 }
00116 
00117 function CGPathAddArcToPoint(aPath, aTransform, x1, y1, x2, y2, aRadius)
00118 {
00119 }
00120 
00121 function CGPathAddCurveToPoint(aPath, aTransform, cp1x, cp1y, cp2x, cp2y, x, y)
00122 {
00123     var cp1 = _CGPointMake(cp1x, cp1y),
00124         cp2 = _CGPointMake(cp2x, cp2y),
00125         end = _CGPointMake(x, y);
00126 
00127     if (aTransform)
00128     {
00129         cp1 = _CGPointApplyAffineTransform(cp1, aTransform);
00130         cp2 = _CGPointApplyAffineTransform(cp2, aTransform);
00131         end = _CGPointApplyAffineTransform(end, aTransform);
00132     }
00133 
00134    aPath.current = end;
00135    aPath.elements[aPath.count++] = { type:kCGPathElementAddCurveToPoint, cp1x:cp1.x, cp1y:cp1.y, cp2x:cp2.x, cp2y:cp2.y, x:end.x, y:end.y };
00136 }
00137 
00138 function CGPathAddLines(aPath, aTransform, points, count)
00139 {
00140     var i = 1;
00141 
00142     if (count === NULL)
00143         var count = points.length;
00144 
00145     if (!aPath || count < 2)
00146         return;
00147 
00148     CGPathMoveToPoint(aPath, aTransform, points[0].x, points[0].y);
00149 
00150     for (; i < count; ++i)
00151         CGPathAddLineToPoint(aPath, aTransform, points[i].x, points[i].y);
00152 }
00153 
00154 function CGPathAddLineToPoint(aPath, aTransform, x, y)
00155 {
00156     var point = _CGPointMake(x, y);
00157 
00158     if (aTransform != NULL)
00159         point = _CGPointApplyAffineTransform(point, aTransform);
00160 
00161     aPath.elements[aPath.count++] = { type: kCGPathElementAddLineToPoint, x:point.x, y:point.y };
00162     aPath.current = point;
00163 }
00164 
00165 function CGPathAddPath(aPath, aTransform, anotherPath)
00166 {
00167     for (var i = 0, count = anotherPath.count; i < count; ++i)
00168     {
00169         var element = anotherPath.elements[i];
00170 
00171         switch (element.type)
00172         {
00173             case kCGPathElementAddLineToPoint:      CGPathAddLineToPoint(aPath, aTransform, element.x, element.y);
00174                                                     break;
00175 
00176             case kCGPathElementAddCurveToPoint:     CGPathAddCurveToPoint(aPath, aTransform,
00177                                                                           element.cp1x, element.cp1y,
00178                                                                           element.cp2x, element.cp2y,
00179                                                                           element.x, element.y);
00180                                                     break;
00181 
00182             case kCGPathElementAddArc:              CGPathAddArc(aPath, aTransform, element.x, element.y,
00183                                                                  element.radius, element.startAngle,
00184                                                                  element.endAngle, element.isClockwise);
00185                                                     break;
00186 
00187             case kCGPathElementAddQuadCurveToPoint: CGPathAddQuadCurveToPoint(aPath, aTransform,
00188                                                                               element.cpx, element.cpy,
00189                                                                               element.x, element.y);
00190                                                     break;
00191 
00192             case kCGPathElementMoveToPoint:         CGPathMoveToPoint(aPath, aTransform, element.x, element.y);
00193                                                     break;
00194 
00195             case kCGPathElementCloseSubpath:        CGPathCloseSubpath(aPath);
00196                                                     break;
00197         }
00198     }
00199 }
00200 
00201 function CGPathAddQuadCurveToPoint(aPath, aTransform, cpx, cpy, x, y)
00202 {
00203     var cp = _CGPointMake(cpx, cpy),
00204         end = _CGPointMake(x, y);
00205 
00206     if (aTransform)
00207     {
00208         cp = _CGPointApplyAffineTransform(cp, aTransform);
00209         end = _CGPointApplyAffineTransform(end, aTransform);
00210     }
00211 
00212     aPath.elements[aPath.count++] = { type:kCGPathElementAddQuadCurveToPoint, cpx:cp.x, cpy:cp.y, x:end.x, y:end.y }
00213     aPath.current = end;
00214 }
00215 
00216 function CGPathAddRect(aPath, aTransform, aRect)
00217 {
00218     CGPathAddRects(aPath, aTransform, [aRect], 1);
00219 }
00220 
00221 function CGPathAddRects(aPath, aTransform, rects, count)
00222 {
00223     var i = 0;
00224 
00225     if (count === NULL)
00226         var count = rects.length;
00227 
00228     for (; i < count; ++i)
00229     {
00230         var rect = rects[i];
00231 
00232         CGPathMoveToPoint(aPath, aTransform, _CGRectGetMinX(rect), _CGRectGetMinY(rect));
00233         CGPathAddLineToPoint(aPath, aTransform, _CGRectGetMaxX(rect), _CGRectGetMinY(rect));
00234         CGPathAddLineToPoint(aPath, aTransform, _CGRectGetMaxX(rect), _CGRectGetMaxY(rect));
00235         CGPathAddLineToPoint(aPath, aTransform, _CGRectGetMinX(rect), _CGRectGetMaxY(rect));
00236 
00237         CGPathCloseSubpath(aPath);
00238     }
00239 }
00240 
00241 function CGPathMoveToPoint(aPath, aTransform, x, y)
00242 {
00243     var point = _CGPointMake(x, y),
00244         count = aPath.count;
00245 
00246     if (aTransform != NULL)
00247         point = _CGPointApplyAffineTransform(point, aTransform);
00248 
00249     aPath.start = point;
00250     aPath.current = point;
00251 
00252     var previous = aPath.elements[count - 1];
00253 
00254     if (count != 0 && previous.type == kCGPathElementMoveToPoint)
00255     {
00256         previous.x = point.x;
00257         previous.y = point.y;
00258     }
00259     else
00260         aPath.elements[aPath.count++] = { type:kCGPathElementMoveToPoint, x:point.x, y:point.y };
00261 }
00262 
00263 var KAPPA = 4.0 * ((SQRT2 - 1.0) / 3.0);
00264 
00265 function CGPathWithEllipseInRect(aRect)
00266 {
00267     var path = CGPathCreateMutable();
00268 
00269     if (_CGRectGetWidth(aRect) == _CGRectGetHeight(aRect))
00270         CGPathAddArc(path, nil, _CGRectGetMidX(aRect), _CGRectGetMidY(aRect), _CGRectGetWidth(aRect) / 2.0, 0.0, 2 * PI, YES);
00271     else
00272     {
00273         var axis = _CGSizeMake(_CGRectGetWidth(aRect) / 2.0, _CGRectGetHeight(aRect) / 2.0),
00274             center = _CGPointMake(_CGRectGetMinX(aRect) + axis.width, _CGRectGetMinY(aRect) + axis.height);
00275 
00276         CGPathMoveToPoint(path, nil, center.x, center.y - axis.height);
00277 
00278         CGPathAddCurveToPoint(path, nil, center.x + (KAPPA * axis.width), center.y - axis.height,  center.x + axis.width, center.y - (KAPPA * axis.height), center.x + axis.width, center.y);
00279         CGPathAddCurveToPoint(path, nil, center.x + axis.width, center.y + (KAPPA * axis.height), center.x + (KAPPA * axis.width), center.y + axis.height, center.x, center.y + axis.height);
00280         CGPathAddCurveToPoint(path, nil, center.x - (KAPPA * axis.width), center.y + axis.height, center.x - axis.width, center.y + (KAPPA * axis.height), center.x - axis.width, center.y);
00281         CGPathAddCurveToPoint(path, nil, center.x - axis.width, center.y - (KAPPA * axis.height), center.x - (KAPPA * axis.width), center.y - axis.height, center.x, center.y - axis.height);
00282     }
00283 
00284     CGPathCloseSubpath(path);
00285 
00286     return path;
00287 }
00288 
00289 function CGPathWithRoundedRectangleInRect(aRect, xRadius, yRadius/*not currently supported*/, ne, se, sw, nw)
00290 {
00291     var path = CGPathCreateMutable(),
00292         xMin = _CGRectGetMinX(aRect),
00293         xMax = _CGRectGetMaxX(aRect),
00294         yMin = _CGRectGetMinY(aRect),
00295         yMax = _CGRectGetMaxY(aRect);
00296 
00297     CGPathMoveToPoint(path, nil, xMin + xRadius, yMin);
00298 
00299     if (ne)
00300     {
00301         CGPathAddLineToPoint(path, nil, xMax - xRadius, yMin);
00302         CGPathAddCurveToPoint(path, nil, xMax - xRadius, yMin, xMax, yMin, xMax, yMin + xRadius);
00303     }
00304     else
00305         CGPathAddLineToPoint(path, nil, xMax, yMin);
00306 
00307     if (se)
00308     {
00309         CGPathAddLineToPoint(path, nil, xMax, yMax - xRadius);
00310         CGPathAddCurveToPoint(path, nil, xMax, yMax - xRadius, xMax, yMax, xMax - xRadius, yMax);
00311     }
00312     else
00313         CGPathAddLineToPoint(path, nil, xMax, yMax);
00314 
00315     if (sw)
00316     {
00317         CGPathAddLineToPoint(path, nil, xMin + xRadius, yMax);
00318         CGPathAddCurveToPoint(path, nil, xMin + xRadius, yMax, xMin, yMax, xMin, yMax - xRadius);
00319     }
00320     else
00321         CGPathAddLineToPoint(path, nil, xMin, yMax);
00322 
00323     if (nw)
00324     {
00325         CGPathAddLineToPoint(path, nil, xMin, yMin + xRadius);
00326         CGPathAddCurveToPoint(path, nil, xMin, yMin + xRadius, xMin, yMin, xMin + xRadius, yMin);
00327     }
00328     else
00329         CGPathAddLineToPoint(path, nil, xMin, yMin);
00330 
00331     CGPathCloseSubpath(path);
00332 
00333     return path;
00334 }
00335 
00336 function CGPathCloseSubpath(aPath)
00337 {
00338     var count = aPath.count;
00339 
00340     // Don't bother closing this subpath if there aren't any current elements, or the last element already closed the subpath.
00341     if (count == 0 || aPath.elements[count - 1].type == kCGPathElementCloseSubpath)
00342         return;
00343 
00344     aPath.elements[aPath.count++] = { type:kCGPathElementCloseSubpath, points:[aPath.start] };
00345 }
00346 
00347 function CGPathEqualToPath(aPath, anotherPath)
00348 {
00349     if (aPath == anotherPath)
00350         return YES;
00351 
00352     if (aPath.count != anotherPath.count || !_CGPointEqualToPoint(aPath.start, anotherPath.start) || !_CGPointEqualToPoint(aPath.current, anotherPath.current))
00353         return NO;
00354 
00355     var i = 0,
00356         count = aPath.count;
00357 
00358     for (; i < count; ++i)
00359     {
00360         var element = aPath[i],
00361             anotherElement = anotherPath[i];
00362 
00363         if (element.type != anotherElement.type)
00364             return NO;
00365 
00366         if ((element.type == kCGPathElementAddArc || element.type == kCGPathElementAddArcToPoint) &&
00367             element.radius != anotherElement.radius)
00368             return NO;
00369 
00370         var j = element.points.length;
00371 
00372         while (j--)
00373             if (!_CGPointEqualToPoint(element.points[j], anotherElement.points[j]))
00374                 return NO;
00375     }
00376 
00377     return YES;
00378 }
00379 
00380 function CGPathGetCurrentPoint(aPath)
00381 {
00382     return _CGPointCreateCopy(aPath.current);
00383 }
00384 
00385 function CGPathIsEmpty(aPath)
00386 {
00387     return !aPath || aPath.count == 0;
00388 }
00389 
 All Classes Files Functions Variables Defines