mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 13:07:02 +00:00
Previously when creating a CGPath with an arc, the path was wrong. It added a line when it wasn't necessary and when it was necessary it added a line to a wrong point. Now CGPath works like in cocoa (If the specified path already contains a subpath, Quartz implicitly adds a line connecting the subpath’s current point to the beginning of the arc. If the path is empty, Quartz creates a new subpath with a starting point set to the starting point of the arc.) Test app in Tests/Manual/CGPath/AppController.j
617 lines
19 KiB
Plaintext
617 lines
19 KiB
Plaintext
/*
|
||
* CGPath.j
|
||
* AppKit
|
||
*
|
||
* Created by Francisco Tolmasky.
|
||
* Copyright 2008, 280 North, Inc.
|
||
*
|
||
* This library is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU Lesser General Public
|
||
* License as published by the Free Software Foundation; either
|
||
* version 2.1 of the License, or (at your option) any later version.
|
||
*
|
||
* This library is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
* Lesser General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU Lesser General Public
|
||
* License along with this library; if not, write to the Free Software
|
||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||
*/
|
||
|
||
@import "CGAffineTransform.j"
|
||
@import "CGGeometry.j"
|
||
|
||
|
||
kCGPathElementMoveToPoint = 0;
|
||
kCGPathElementAddLineToPoint = 1;
|
||
kCGPathElementAddQuadCurveToPoint = 2;
|
||
kCGPathElementAddCurveToPoint = 3;
|
||
kCGPathElementCloseSubpath = 4;
|
||
|
||
kCGPathElementAddArc = 5;
|
||
kCGPathElementAddArcToPoint = 6;
|
||
|
||
/*!
|
||
@addtogroup coregraphics
|
||
@{
|
||
*/
|
||
|
||
/*!
|
||
Returns a new CGPath object.
|
||
*/
|
||
function CGPathCreateMutable()
|
||
{
|
||
return { count:0, start:CGPointMake(0, 0), current:CGPointMake(0, 0), elements:[] };
|
||
}
|
||
|
||
/*!
|
||
Returns a copy of the given path object.
|
||
*/
|
||
|
||
function CGPathCreateMutableCopy(aPath)
|
||
{
|
||
var path = CGPathCreateMutable();
|
||
|
||
CGPathAddPath(path, aPath);
|
||
|
||
return path;
|
||
}
|
||
|
||
/*!
|
||
Returns a copy of the given path object.
|
||
*/
|
||
|
||
function CGPathCreateCopy(aPath)
|
||
{
|
||
return CGPathCreateMutableCopy(aPath);
|
||
}
|
||
|
||
function CGPathRelease(aPath)
|
||
{
|
||
}
|
||
|
||
function CGPathRetain(aPath)
|
||
{
|
||
return aPath;
|
||
}
|
||
|
||
function CGPathAddArc(aPath, aTransform, x, y, aRadius, aStartAngle, anEndAngle, isClockwise)
|
||
{
|
||
if (aTransform && !CGAffineTransformIsIdentity(aTransform))
|
||
{
|
||
var center = CGPointMake(x, y),
|
||
end = CGPointMake(COS(anEndAngle), SIN(anEndAngle)),
|
||
start = CGPointMake(COS(aStartAngle), SIN(aStartAngle));
|
||
|
||
end = CGPointApplyAffineTransform(end, aTransform);
|
||
start = CGPointApplyAffineTransform(start, aTransform);
|
||
center = CGPointApplyAffineTransform(center, aTransform);
|
||
|
||
x = center.x;
|
||
y = center.y;
|
||
|
||
var oldEndAngle = anEndAngle,
|
||
oldStartAngle = aStartAngle;
|
||
|
||
anEndAngle = ATAN2(end.y - aTransform.ty, end.x - aTransform.tx);
|
||
aStartAngle = ATAN2(start.y - aTransform.ty, start.x - aTransform.tx);
|
||
|
||
// Angles that equal "modulo" 2 pi return as equal after transforming them,
|
||
// so we have to make sure to make them different again if they were different
|
||
// to start out with. It's the difference between no circle and a full circle.
|
||
if (anEndAngle === aStartAngle && oldEndAngle !== oldStartAngle)
|
||
if (oldStartAngle > oldEndAngle)
|
||
anEndAngle = anEndAngle - PI2;
|
||
else
|
||
aStartAngle = aStartAngle - PI2;
|
||
|
||
aRadius = CGSizeMake(aRadius, 0);
|
||
aRadius = CGSizeApplyAffineTransform(aRadius, aTransform);
|
||
aRadius = SQRT(aRadius.width * aRadius.width + aRadius.height * aRadius.height);
|
||
}
|
||
|
||
/*
|
||
From the Cocoa docs:
|
||
|
||
If the specified path already contains a subpath, Quartz implicitly adds a line connecting the subpath’s current point to the beginning of the arc. If the path is empty, Quartz creates a new subpath with a starting point set to the starting point of the arc.
|
||
|
||
The ending point of the arc becomes the new current point of the path.
|
||
*/
|
||
var arcEndX = x + aRadius * COS(anEndAngle),
|
||
arcEndY = y + aRadius * SIN(anEndAngle),
|
||
arcStartX = x + aRadius * COS(aStartAngle),
|
||
arcStartY = y + aRadius * SIN(aStartAngle);
|
||
|
||
if (aPath.count)
|
||
{
|
||
if (aPath.current.x !== x || aPath.current.y !== y)
|
||
CGPathAddLineToPoint(aPath, aTransform, arcStartX, arcStartY);
|
||
}
|
||
else
|
||
{
|
||
var arcStartX = x + aRadius * COS(aStartAngle),
|
||
arcStartY = y + aRadius * SIN(aStartAngle);
|
||
|
||
aPath.start = CGPointMake(arcStartX, arcStartY);
|
||
}
|
||
|
||
aPath.current = CGPointMake(arcEndX, arcEndY);
|
||
aPath.elements[aPath.count++] = { type:kCGPathElementAddArc, x:x, y:y, radius:aRadius, startAngle:aStartAngle, endAngle:anEndAngle, clockwise:isClockwise };
|
||
}
|
||
|
||
function CGPathAddArcToPoint(aPath, aTransform, x1, y1, x2, y2, aRadius)
|
||
{
|
||
var p1 = CGPointMake(x1, y1),
|
||
p2 = CGPointMake(x2, y2);
|
||
|
||
if (aTransform)
|
||
{
|
||
p1 = CGPointApplyAffineTransform(p1, aTransform);
|
||
p2 = CGPointApplyAffineTransform(p2, aTransform);
|
||
}
|
||
|
||
/*
|
||
From the Cocoa docs:
|
||
|
||
If the current point and the first tangent point of the arc (the starting point) are not equal, Quartz appends a straight line segment from the current point to the first tangent point.
|
||
|
||
The ending point of the arc becomes the new current point of the path.
|
||
*/
|
||
if (aPath.count)
|
||
{
|
||
if (aPath.current.x !== p1.x || aPath.current.y !== p1.y)
|
||
CGPathAddLineToPoint(aPath, aTransform, p1.x, p1.y);
|
||
}
|
||
else
|
||
aPath.start = p1;
|
||
|
||
aPath.current = p2;
|
||
aPath.elements[aPath.count++] = { type:kCGPathElementAddArcToPoint, p1x:p1.x, p1y:p1.y, p2x:p2.x, p2y:p2.y, radius:aRadius };
|
||
}
|
||
|
||
function CGPathAddCurveToPoint(aPath, aTransform, cp1x, cp1y, cp2x, cp2y, x, y)
|
||
{
|
||
var cp1 = CGPointMake(cp1x, cp1y),
|
||
cp2 = CGPointMake(cp2x, cp2y),
|
||
end = CGPointMake(x, y);
|
||
|
||
if (aTransform)
|
||
{
|
||
cp1 = CGPointApplyAffineTransform(cp1, aTransform);
|
||
cp2 = CGPointApplyAffineTransform(cp2, aTransform);
|
||
end = CGPointApplyAffineTransform(end, aTransform);
|
||
}
|
||
|
||
aPath.current = end;
|
||
aPath.elements[aPath.count++] = { type:kCGPathElementAddCurveToPoint, cp1x:cp1.x, cp1y:cp1.y, cp2x:cp2.x, cp2y:cp2.y, x:end.x, y:end.y };
|
||
}
|
||
|
||
function CGPathAddLines(aPath, aTransform, points, count)
|
||
{
|
||
if (count === null || count === undefined)
|
||
count = points.length;
|
||
|
||
if (!aPath || count < 1)
|
||
return;
|
||
|
||
CGPathMoveToPoint(aPath, aTransform, points[0].x, points[0].y);
|
||
|
||
for (var i = 1; i < count; ++i)
|
||
CGPathAddLineToPoint(aPath, aTransform, points[i].x, points[i].y);
|
||
}
|
||
|
||
function CGPathAddLineToPoint(aPath, aTransform, x, y)
|
||
{
|
||
var point = CGPointMake(x, y);
|
||
|
||
if (aTransform !== NULL)
|
||
point = CGPointApplyAffineTransform(point, aTransform);
|
||
|
||
aPath.elements[aPath.count++] = { type: kCGPathElementAddLineToPoint, x:point.x, y:point.y };
|
||
aPath.current = point;
|
||
}
|
||
|
||
function CGPathAddPath(aPath, aTransform, anotherPath)
|
||
{
|
||
for (var i = 0, count = anotherPath.count; i < count; ++i)
|
||
{
|
||
var element = anotherPath.elements[i];
|
||
|
||
switch (element.type)
|
||
{
|
||
case kCGPathElementAddLineToPoint:
|
||
CGPathAddLineToPoint(aPath, aTransform, element.x, element.y);
|
||
break;
|
||
|
||
case kCGPathElementAddCurveToPoint:
|
||
CGPathAddCurveToPoint(aPath, aTransform,
|
||
element.cp1x, element.cp1y,
|
||
element.cp2x, element.cp2y,
|
||
element.x, element.y);
|
||
break;
|
||
|
||
case kCGPathElementAddArc:
|
||
CGPathAddArc(aPath, aTransform, element.x, element.y,
|
||
element.radius, element.startAngle,
|
||
element.endAngle, element.clockwise);
|
||
break;
|
||
|
||
case kCGPathElementAddArcToPoint:
|
||
CGPathAddArcToPoint(aPath, aTransform,
|
||
element.p1x, element.p1y,
|
||
element.p2x, element.p2y,
|
||
element.radius);
|
||
break;
|
||
|
||
case kCGPathElementAddQuadCurveToPoint:
|
||
CGPathAddQuadCurveToPoint(aPath, aTransform,
|
||
element.cpx, element.cpy,
|
||
element.x, element.y);
|
||
break;
|
||
|
||
case kCGPathElementMoveToPoint:
|
||
CGPathMoveToPoint(aPath, aTransform, element.x, element.y);
|
||
break;
|
||
|
||
case kCGPathElementCloseSubpath:
|
||
CGPathCloseSubpath(aPath);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
function CGPathAddQuadCurveToPoint(aPath, aTransform, cpx, cpy, x, y)
|
||
{
|
||
var cp = CGPointMake(cpx, cpy),
|
||
end = CGPointMake(x, y);
|
||
|
||
if (aTransform)
|
||
{
|
||
cp = CGPointApplyAffineTransform(cp, aTransform);
|
||
end = CGPointApplyAffineTransform(end, aTransform);
|
||
}
|
||
|
||
aPath.elements[aPath.count++] = { type:kCGPathElementAddQuadCurveToPoint, cpx:cp.x, cpy:cp.y, x:end.x, y:end.y }
|
||
aPath.current = end;
|
||
}
|
||
|
||
function CGPathAddRect(aPath, aTransform, aRect)
|
||
{
|
||
CGPathAddRects(aPath, aTransform, [aRect], 1);
|
||
}
|
||
|
||
function CGPathAddRects(aPath, aTransform, rects, count)
|
||
{
|
||
var i = 0;
|
||
|
||
if (count === NULL)
|
||
var count = rects.length;
|
||
|
||
for (; i < count; ++i)
|
||
{
|
||
var rect = rects[i];
|
||
|
||
CGPathMoveToPoint(aPath, aTransform, CGRectGetMinX(rect), CGRectGetMinY(rect));
|
||
CGPathAddLineToPoint(aPath, aTransform, CGRectGetMaxX(rect), CGRectGetMinY(rect));
|
||
CGPathAddLineToPoint(aPath, aTransform, CGRectGetMaxX(rect), CGRectGetMaxY(rect));
|
||
CGPathAddLineToPoint(aPath, aTransform, CGRectGetMinX(rect), CGRectGetMaxY(rect));
|
||
|
||
CGPathCloseSubpath(aPath);
|
||
}
|
||
}
|
||
|
||
function CGPathMoveToPoint(aPath, aTransform, x, y)
|
||
{
|
||
var point = CGPointMake(x, y);
|
||
|
||
if (aTransform !== NULL)
|
||
point = CGPointApplyAffineTransform(point, aTransform);
|
||
|
||
aPath.start = aPath.current = point;
|
||
|
||
// If the previous op was a move, just update that point
|
||
if (aPath.count)
|
||
{
|
||
var previous = aPath.elements[aPath.count - 1];
|
||
|
||
if (previous.type === kCGPathElementMoveToPoint)
|
||
{
|
||
previous.x = point.x;
|
||
previous.y = point.y;
|
||
return;
|
||
}
|
||
}
|
||
|
||
aPath.elements[aPath.count++] = { type:kCGPathElementMoveToPoint, x:point.x, y:point.y };
|
||
}
|
||
|
||
var KAPPA = 4.0 * ((SQRT2 - 1.0) / 3.0);
|
||
|
||
function CGPathWithEllipseInRect(aRect)
|
||
{
|
||
var path = CGPathCreateMutable();
|
||
|
||
if (CGRectGetWidth(aRect) === CGRectGetHeight(aRect))
|
||
CGPathAddArc(path, nil, CGRectGetMidX(aRect), CGRectGetMidY(aRect), CGRectGetWidth(aRect) / 2.0, 0.0, 2 * PI, YES);
|
||
else
|
||
{
|
||
var axis = CGSizeMake(CGRectGetWidth(aRect) / 2.0, CGRectGetHeight(aRect) / 2.0),
|
||
center = CGPointMake(CGRectGetMinX(aRect) + axis.width, CGRectGetMinY(aRect) + axis.height);
|
||
|
||
CGPathMoveToPoint(path, nil, center.x, center.y - axis.height);
|
||
|
||
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);
|
||
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);
|
||
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);
|
||
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);
|
||
}
|
||
|
||
CGPathCloseSubpath(path);
|
||
|
||
return path;
|
||
}
|
||
|
||
function CGPathWithRoundedRectangleInRect(aRect, xRadius, yRadius/*not currently supported*/, ne, se, sw, nw)
|
||
{
|
||
var path = CGPathCreateMutable(),
|
||
xMin = CGRectGetMinX(aRect),
|
||
xMax = CGRectGetMaxX(aRect),
|
||
yMin = CGRectGetMinY(aRect),
|
||
yMax = CGRectGetMaxY(aRect);
|
||
|
||
CGPathMoveToPoint(path, nil, xMin + xRadius, yMin);
|
||
|
||
if (ne)
|
||
{
|
||
CGPathAddLineToPoint(path, nil, xMax - xRadius, yMin);
|
||
CGPathAddCurveToPoint(path, nil, xMax - xRadius, yMin, xMax, yMin, xMax, yMin + xRadius);
|
||
}
|
||
else
|
||
CGPathAddLineToPoint(path, nil, xMax, yMin);
|
||
|
||
if (se)
|
||
{
|
||
CGPathAddLineToPoint(path, nil, xMax, yMax - xRadius);
|
||
CGPathAddCurveToPoint(path, nil, xMax, yMax - xRadius, xMax, yMax, xMax - xRadius, yMax);
|
||
}
|
||
else
|
||
CGPathAddLineToPoint(path, nil, xMax, yMax);
|
||
|
||
if (sw)
|
||
{
|
||
CGPathAddLineToPoint(path, nil, xMin + xRadius, yMax);
|
||
CGPathAddCurveToPoint(path, nil, xMin + xRadius, yMax, xMin, yMax, xMin, yMax - xRadius);
|
||
}
|
||
else
|
||
CGPathAddLineToPoint(path, nil, xMin, yMax);
|
||
|
||
if (nw)
|
||
{
|
||
CGPathAddLineToPoint(path, nil, xMin, yMin + xRadius);
|
||
CGPathAddCurveToPoint(path, nil, xMin, yMin + xRadius, xMin, yMin, xMin + xRadius, yMin);
|
||
}
|
||
else
|
||
CGPathAddLineToPoint(path, nil, xMin, yMin);
|
||
|
||
CGPathCloseSubpath(path);
|
||
|
||
return path;
|
||
}
|
||
|
||
function CGPathCloseSubpath(aPath)
|
||
{
|
||
var count = aPath.count;
|
||
|
||
// Don't bother closing this subpath if there aren't any current elements, or the last element already closed the subpath.
|
||
if (count === 0 || aPath.elements[count - 1].type === kCGPathElementCloseSubpath)
|
||
return;
|
||
|
||
// After closing, the current point is the previous path's starting point
|
||
aPath.current = CGPointCreateCopy(aPath.start);
|
||
aPath.elements[aPath.count++] = { type:kCGPathElementCloseSubpath, start:aPath.start };
|
||
}
|
||
|
||
function CGPathEqualToPath(aPath, anotherPath)
|
||
{
|
||
if (aPath === anotherPath)
|
||
return YES;
|
||
|
||
if (aPath.count !== anotherPath.count || !CGPointEqualToPoint(aPath.start, anotherPath.start) || !CGPointEqualToPoint(aPath.current, anotherPath.current))
|
||
return NO;
|
||
|
||
var i = 0,
|
||
count = aPath.count;
|
||
|
||
for (; i < count; ++i)
|
||
{
|
||
var element = aPath[i],
|
||
anotherElement = anotherPath[i];
|
||
|
||
if (element.type !== anotherElement.type)
|
||
return NO;
|
||
|
||
switch (element.type)
|
||
{
|
||
case kCGPathElementAddArc:
|
||
if (element.x !== anotherElement.x ||
|
||
element.y !== anotherElement.y ||
|
||
element.radius !== anotherElement.radius ||
|
||
element.startAngle !== anotherElement.startAngle ||
|
||
element.endAngle !== anotherElement.endAngle ||
|
||
element.clockwise !== anotherElement.clockwise)
|
||
{
|
||
return NO;
|
||
}
|
||
break;
|
||
|
||
case kCGPathElementAddArcToPoint:
|
||
if (element.p1x !== anotherElement.p1x ||
|
||
element.p1y !== anotherElement.p1y ||
|
||
element.p2x !== anotherElement.p2x ||
|
||
element.p2y !== anotherElement.p2y ||
|
||
element.radius !== anotherElement.radius)
|
||
{
|
||
return NO;
|
||
}
|
||
break;
|
||
|
||
case kCGPathElementAddCurveToPoint:
|
||
if (element.cp1x !== anotherElement.cp1x ||
|
||
element.cp1y !== anotherElement.cp1y ||
|
||
element.cp2x !== anotherElement.cp2x ||
|
||
element.cp2y !== anotherElement.cp2y ||
|
||
element.x !== anotherElement.x ||
|
||
element.y !== anotherElement.y)
|
||
{
|
||
return NO;
|
||
}
|
||
break;
|
||
|
||
case kCGPathElementAddLineToPoint:
|
||
case kCGPathElementMoveToPoint:
|
||
if (element.x !== anotherElement.x ||
|
||
element.y !== anotherElement.y)
|
||
{
|
||
return NO;
|
||
}
|
||
break;
|
||
|
||
case kCGPathElementAddQuadCurveToPoint:
|
||
if (element.cpx !== anotherElement.cpx ||
|
||
element.cpy !== anotherElement.cpy ||
|
||
element.x !== anotherElement.x ||
|
||
element.y !== anotherElement.y)
|
||
{
|
||
return NO;
|
||
}
|
||
break;
|
||
|
||
case kCGPathElementCloseSubpath:
|
||
if (!CGPointEqualToPoint(element.start, anotherElement.start))
|
||
return NO;
|
||
break;
|
||
}
|
||
}
|
||
|
||
return YES;
|
||
}
|
||
|
||
function CGPathGetCurrentPoint(aPath)
|
||
{
|
||
return CGPointCreateCopy(aPath.current);
|
||
}
|
||
|
||
function CGPathIsEmpty(aPath)
|
||
{
|
||
return !aPath || aPath.count === 0;
|
||
}
|
||
|
||
/*!
|
||
Calculate the smallest rectangle to contain both the path of the receiver and all control points.
|
||
*/
|
||
function CGPathGetBoundingBox(aPath)
|
||
{
|
||
if (!aPath || !aPath.count)
|
||
return CGRectMakeZero();
|
||
|
||
var ox = 0,
|
||
oy = 0,
|
||
rx = 0,
|
||
ry = 0,
|
||
movePoint = nil;
|
||
|
||
function addPoint(x, y)
|
||
{
|
||
ox = MIN(ox, x);
|
||
oy = MIN(oy, y);
|
||
rx = MAX(rx, x);
|
||
ry = MAX(ry, y);
|
||
}
|
||
|
||
for (var i = 0, count = aPath.count; i < count; ++i)
|
||
{
|
||
var element = aPath.elements[i];
|
||
|
||
// Just enclose all the control points. The curves must be inside of the control points.
|
||
// This won't work for CGPathGetBoundingBox.
|
||
switch (element.type)
|
||
{
|
||
case kCGPathElementAddLineToPoint:
|
||
if (movePoint)
|
||
{
|
||
addPoint(movePoint.x, movePoint.y);
|
||
movePoint = nil;
|
||
}
|
||
|
||
addPoint(element.x, element.y);
|
||
break;
|
||
|
||
case kCGPathElementAddCurveToPoint:
|
||
if (movePoint)
|
||
{
|
||
addPoint(movePoint.x, movePoint.y);
|
||
movePoint = nil;
|
||
}
|
||
|
||
addPoint(element.cp1x, element.cp1y);
|
||
addPoint(element.cp2x, element.cp2y);
|
||
addPoint(element.x, element.y);
|
||
break;
|
||
|
||
case kCGPathElementAddArc:
|
||
if (movePoint)
|
||
{
|
||
addPoint(movePoint.x, movePoint.y);
|
||
movePoint = nil;
|
||
}
|
||
|
||
addPoint(element.x, element.y);
|
||
break;
|
||
|
||
case kCGPathElementAddArcToPoint:
|
||
if (movePoint)
|
||
{
|
||
addPoint(movePoint.x, movePoint.y);
|
||
movePoint = nil;
|
||
}
|
||
|
||
addPoint(element.p1x, element.p1y);
|
||
addPoint(element.p2x, element.p2y);
|
||
break;
|
||
|
||
case kCGPathElementAddQuadCurveToPoint:
|
||
if (movePoint)
|
||
{
|
||
addPoint(movePoint.x, movePoint.y);
|
||
movePoint = nil;
|
||
}
|
||
|
||
addPoint(element.cpx, element.cpy);
|
||
addPoint(element.x, element.y);
|
||
break;
|
||
|
||
case kCGPathElementMoveToPoint:
|
||
movePoint = CGPointMake(element.x, element.y);
|
||
break;
|
||
|
||
case kCGPathElementCloseSubpath:
|
||
if (movePoint)
|
||
{
|
||
addPoint(movePoint.x, movePoint.y);
|
||
movePoint = nil;
|
||
}
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
return CGRectMake(ox, oy, rx - ox, ry - oy);
|
||
}
|
||
|
||
/*!
|
||
@}
|
||
*/
|
||
|