API  0.9.6
 All Classes Files Functions Variables Macros Groups Pages
CPGradient.j
Go to the documentation of this file.
1 /*
2  * CPGradient.j
3  * AppKit
4  *
5  * Created by Alexander Ljungberg.
6  * Copyright 2012, SlevenBits Ltd.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
25 
29 @implementation CPGradient : CPObject
30 {
31  CGGradient _gradient;
32 }
33 
34 - (id)initWithColors:(CPArray)someColors
35 {
36  var count = [someColors count];
37 
38  if (count < 2)
39  [CPException raise:CPInvalidArgumentException reason:@"at least 2 colors required"];
40 
41  var distance = 1.0 / (count - 1),
42  locations = [CPMutableArray array],
43  location = 0.0;
44 
45  for (var i = 0; i < count; i++)
46  {
47  [locations addObject:location];
48  location += distance;
49  }
50 
51  return [self initWithColors:someColors atLocations:locations colorSpace:nil];
52 }
53 
54 - (id)initWithColors:(CPArray)someColors atLocations:(CPArray)someLocations colorSpace:(CPColorSpace)aColorSpace
55 {
56  if (self = [super init])
57  {
58  var cgColors = [],
59  count = [someColors count],
60  colorSpace = [aColorSpace CGColorSpace] || CGColorSpaceCreateDeviceRGB;
61  for (var i = 0; i < count; i++)
62  cgColors.push(CGColorCreate(colorSpace, [someColors[i] components]));
63  _gradient = CGGradientCreateWithColors(colorSpace, cgColors, someLocations);
64  }
65 
66  return self;
67 }
68 
69 - (void)drawInRect:(CGRect)rect angle:(float)angle
70 {
72 
74  CGContextClipToRect(ctx, rect);
75  CGContextAddRect(ctx, rect);
76 
77  var startPoint,
78  endPoint;
79 
80  // Modulo of negative values doesn't work as expected in JS.
81  angle = ((angle % 360.0) + 360.0) % 360.0;
82 
83  if (angle < 90.0)
84  startPoint = _CGPointMake(_CGRectGetMinX(rect), _CGRectGetMinY(rect));
85  else if (angle < 180.0)
86  startPoint = _CGPointMake(_CGRectGetMaxX(rect), _CGRectGetMinY(rect));
87  else if (angle < 270.0)
88  startPoint = _CGPointMake(_CGRectGetMaxX(rect), _CGRectGetMaxY(rect));
89  else
90  startPoint = _CGPointMake(_CGRectGetMinX(rect), _CGRectGetMaxY(rect));
91 
92  // A line segment comes out of the starting point at the given angle, with the first colour
93  // at the starting point and the last at the end. To do what drawInRect: is supposed to do
94  // we want the opposite corner of the starting corner to just reach the final colour stop.
95  // So when the angle isn't a right angle, the segment has to extend beyond the edge of the
96  // rectangle just far enough. This is hard to describe without a picture but if we place
97  // another line through the opposite corner at -90 degrees, it'll help form some triangles
98  // from which we can derive this formula:
99  //
100  // length = cos(angle) * (rectWidth + rectHeight * tan(angle))
101  //
102  // This simplifies down to (in the first quadrant) rectWidth * cos(a) + rectHeight * sin(a).
103 
104  var radians = PI * angle / 180.0,
105  length = ABS(_CGRectGetWidth(rect) * COS(radians)) + ABS(_CGRectGetHeight(rect) * SIN(radians));
106 
107  endPoint = _CGPointMake(startPoint.x + length * COS(radians),
108  startPoint.y + length * SIN(radians));
109 
110  [self drawFromPoint:startPoint toPoint:endPoint options:CPGradientDrawsBeforeStartingLocation | CPGradientDrawsAfterEndingLocation];
112 }
113 
114 - (void)drawFromPoint:(NSPoint)startingPoint toPoint:(NSPoint)endingPoint options:(NSGradientDrawingOptions)options
115 {
117 
118  // TODO kCGGradientDrawsBeforeStartLocation and kCGGradientDrawsAfterEndLocation are not actually supported
119  // by CGContextDrawLinearGradient yet.
120  CGContextDrawLinearGradient(ctx, _gradient, startingPoint, endingPoint, options);
121 }
122 
123 @end