hotcocoa 0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +4 -0
- data/bin/hotcocoa +31 -0
- data/lib/hotcocoa/application_builder.rb +320 -0
- data/lib/hotcocoa/attributed_string.rb +143 -0
- data/lib/hotcocoa/behaviors.rb +7 -0
- data/lib/hotcocoa/data_sources/combo_box_data_source.rb +44 -0
- data/lib/hotcocoa/data_sources/table_data_source.rb +18 -0
- data/lib/hotcocoa/delegate_builder.rb +85 -0
- data/lib/hotcocoa/graphics/canvas.rb +836 -0
- data/lib/hotcocoa/graphics/color.rb +781 -0
- data/lib/hotcocoa/graphics/elements/particle.rb +75 -0
- data/lib/hotcocoa/graphics/elements/rope.rb +99 -0
- data/lib/hotcocoa/graphics/elements/sandpainter.rb +71 -0
- data/lib/hotcocoa/graphics/gradient.rb +63 -0
- data/lib/hotcocoa/graphics/image.rb +488 -0
- data/lib/hotcocoa/graphics/path.rb +325 -0
- data/lib/hotcocoa/graphics/pdf.rb +71 -0
- data/lib/hotcocoa/graphics.rb +161 -0
- data/lib/hotcocoa/kernel_ext.rb +14 -0
- data/lib/hotcocoa/kvo_accessors.rb +48 -0
- data/lib/hotcocoa/layout_view.rb +448 -0
- data/lib/hotcocoa/mapper.rb +227 -0
- data/lib/hotcocoa/mapping_methods.rb +40 -0
- data/lib/hotcocoa/mappings/alert.rb +25 -0
- data/lib/hotcocoa/mappings/application.rb +112 -0
- data/lib/hotcocoa/mappings/array_controller.rb +87 -0
- data/lib/hotcocoa/mappings/box.rb +39 -0
- data/lib/hotcocoa/mappings/button.rb +92 -0
- data/lib/hotcocoa/mappings/collection_view.rb +44 -0
- data/lib/hotcocoa/mappings/color.rb +28 -0
- data/lib/hotcocoa/mappings/column.rb +21 -0
- data/lib/hotcocoa/mappings/combo_box.rb +24 -0
- data/lib/hotcocoa/mappings/control.rb +33 -0
- data/lib/hotcocoa/mappings/font.rb +44 -0
- data/lib/hotcocoa/mappings/gradient.rb +15 -0
- data/lib/hotcocoa/mappings/image.rb +15 -0
- data/lib/hotcocoa/mappings/image_view.rb +43 -0
- data/lib/hotcocoa/mappings/label.rb +25 -0
- data/lib/hotcocoa/mappings/layout_view.rb +9 -0
- data/lib/hotcocoa/mappings/menu.rb +71 -0
- data/lib/hotcocoa/mappings/menu_item.rb +47 -0
- data/lib/hotcocoa/mappings/movie.rb +13 -0
- data/lib/hotcocoa/mappings/movie_view.rb +27 -0
- data/lib/hotcocoa/mappings/notification.rb +17 -0
- data/lib/hotcocoa/mappings/popup.rb +110 -0
- data/lib/hotcocoa/mappings/progress_indicator.rb +68 -0
- data/lib/hotcocoa/mappings/scroll_view.rb +29 -0
- data/lib/hotcocoa/mappings/search_field.rb +9 -0
- data/lib/hotcocoa/mappings/secure_text_field.rb +17 -0
- data/lib/hotcocoa/mappings/segmented_control.rb +97 -0
- data/lib/hotcocoa/mappings/slider.rb +25 -0
- data/lib/hotcocoa/mappings/sort_descriptor.rb +13 -0
- data/lib/hotcocoa/mappings/sound.rb +9 -0
- data/lib/hotcocoa/mappings/speech_synthesizer.rb +25 -0
- data/lib/hotcocoa/mappings/split_view.rb +21 -0
- data/lib/hotcocoa/mappings/status_bar.rb +7 -0
- data/lib/hotcocoa/mappings/status_item.rb +9 -0
- data/lib/hotcocoa/mappings/table_view.rb +110 -0
- data/lib/hotcocoa/mappings/text_field.rb +41 -0
- data/lib/hotcocoa/mappings/text_view.rb +13 -0
- data/lib/hotcocoa/mappings/timer.rb +25 -0
- data/lib/hotcocoa/mappings/toolbar.rb +97 -0
- data/lib/hotcocoa/mappings/toolbar_item.rb +36 -0
- data/lib/hotcocoa/mappings/view.rb +67 -0
- data/lib/hotcocoa/mappings/web_view.rb +22 -0
- data/lib/hotcocoa/mappings/window.rb +118 -0
- data/lib/hotcocoa/mappings/xml_parser.rb +41 -0
- data/lib/hotcocoa/mappings.rb +109 -0
- data/lib/hotcocoa/mvc.rb +175 -0
- data/lib/hotcocoa/notification_listener.rb +62 -0
- data/lib/hotcocoa/object_ext.rb +22 -0
- data/lib/hotcocoa/plist.rb +45 -0
- data/lib/hotcocoa/standard_rake_tasks.rb +17 -0
- data/lib/hotcocoa/template.rb +27 -0
- data/lib/hotcocoa/virtual_file_system.rb +172 -0
- data/lib/hotcocoa.rb +26 -0
- data/template/Rakefile +5 -0
- data/template/config/build.yml +8 -0
- data/template/lib/application.rb +45 -0
- data/template/lib/menu.rb +32 -0
- data/template/resources/HotCocoa.icns +0 -0
- data/test/test_helper.rb +3 -0
- data/test/test_hotcocoa.rb +11 -0
- metadata +137 -0
@@ -0,0 +1,325 @@
|
|
1
|
+
# Ruby Cocoa Graphics is a graphics library providing a simple object-oriented
|
2
|
+
# interface into the power of Mac OS X's Core Graphics and Core Image drawing libraries.
|
3
|
+
# With a few lines of easy-to-read code, you can write scripts to draw simple or complex
|
4
|
+
# shapes, lines, and patterns, process and filter images, create abstract art or visualize
|
5
|
+
# scientific data, and much more.
|
6
|
+
#
|
7
|
+
# Inspiration for this project was derived from Processing and NodeBox. These excellent
|
8
|
+
# graphics programming environments are more full-featured than RCG, but they are implemented
|
9
|
+
# in Java and Python, respectively. RCG was created to offer similar functionality using
|
10
|
+
# the Ruby programming language.
|
11
|
+
#
|
12
|
+
# Author:: James Reynolds (mailto:drtoast@drtoast.com)
|
13
|
+
# Copyright:: Copyright (c) 2008 James Reynolds
|
14
|
+
# License:: Distributes under the same terms as Ruby
|
15
|
+
|
16
|
+
module HotCocoa::Graphics
|
17
|
+
|
18
|
+
# Make a reusable path. Draw it using canvas.draw(path)
|
19
|
+
class Path
|
20
|
+
|
21
|
+
attr_accessor :path, :rand, :inc, :fill, :stroke, :scale, :strokewidth, :x, :y, :image
|
22
|
+
|
23
|
+
# create a new path, starting at optional x,y
|
24
|
+
def initialize(x=0, y=0, &block)
|
25
|
+
@path = CGPathCreateMutable()
|
26
|
+
@transform = CGAffineTransformMakeTranslation(0,0)
|
27
|
+
moveto(x, y)
|
28
|
+
|
29
|
+
# set randomized rendering parameters
|
30
|
+
@rand = {}
|
31
|
+
randomize(:x, 0.0)
|
32
|
+
randomize(:y, 0.0)
|
33
|
+
randomize(:scale, 1.0)
|
34
|
+
randomize(:scalex, 1.0)
|
35
|
+
randomize(:scaley, 1.0)
|
36
|
+
randomize(:rotation, 0.0)
|
37
|
+
randomize(:strokewidth, 1.0)
|
38
|
+
|
39
|
+
# set incremental rendering parameters
|
40
|
+
@inc = {}
|
41
|
+
increment(:rotation, 0.0)
|
42
|
+
increment(:x, 0.0)
|
43
|
+
increment(:y, 0.0)
|
44
|
+
increment(:scale, 1.0)
|
45
|
+
increment(:scalex, 1.0)
|
46
|
+
increment(:scaley, 1.0)
|
47
|
+
|
48
|
+
@strokewidth = 1.0
|
49
|
+
@x = 0.0
|
50
|
+
@y = 0.0
|
51
|
+
|
52
|
+
if block
|
53
|
+
case block.arity
|
54
|
+
when 0
|
55
|
+
self.send(:instance_eval, &block)
|
56
|
+
else
|
57
|
+
block.call(self)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
def fill(colors=nil)
|
64
|
+
if colors
|
65
|
+
rand[:fill] = colors
|
66
|
+
else
|
67
|
+
@fill
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# randomize the specified parameter within the specified range
|
72
|
+
def randomize(parameter, value)
|
73
|
+
rand[parameter] = value
|
74
|
+
end
|
75
|
+
|
76
|
+
# increment the specified parameter within the specified range
|
77
|
+
def increment(parameter, value)
|
78
|
+
inc[parameter] = value
|
79
|
+
end
|
80
|
+
|
81
|
+
# return a mutable clone of this path
|
82
|
+
def clone
|
83
|
+
newpath = self.dup
|
84
|
+
newpath.path = CGPathCreateMutableCopy(@path)
|
85
|
+
newpath
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
# SET PARAMETERS
|
90
|
+
|
91
|
+
# set registration mode to :center or :corner
|
92
|
+
def registration(mode=:center)
|
93
|
+
@registration = mode
|
94
|
+
end
|
95
|
+
|
96
|
+
# print drawing operations if verbose is true
|
97
|
+
def verbose(tf=true)
|
98
|
+
@verbose = tf
|
99
|
+
end
|
100
|
+
|
101
|
+
# draw without stroke
|
102
|
+
def nostroke
|
103
|
+
@stroke = nil
|
104
|
+
end
|
105
|
+
|
106
|
+
# GET PATH INFO
|
107
|
+
|
108
|
+
# print origin and dimensions
|
109
|
+
def to_s
|
110
|
+
"path.to_s: bounding box at [#{originx},#{originy}] with #{width}x#{height}, current point [#{currentpoint[0]},#{currentpoint[1]}]"
|
111
|
+
end
|
112
|
+
|
113
|
+
# return the x coordinate of the path's origin
|
114
|
+
def originx
|
115
|
+
CGPathGetBoundingBox(@path).origin.x
|
116
|
+
end
|
117
|
+
|
118
|
+
# return the y coordinate of the path's origin
|
119
|
+
def originy
|
120
|
+
CGPathGetBoundingBox(@path).origin.y
|
121
|
+
end
|
122
|
+
|
123
|
+
# return the width of the path's bounding box
|
124
|
+
def width
|
125
|
+
CGPathGetBoundingBox(@path).size.width
|
126
|
+
end
|
127
|
+
|
128
|
+
# return the height of the path's bounding box
|
129
|
+
def height
|
130
|
+
CGPathGetBoundingBox(@path).size.height
|
131
|
+
end
|
132
|
+
|
133
|
+
# return the current point
|
134
|
+
def currentpoint
|
135
|
+
CGPathGetCurrentPoint(@path)
|
136
|
+
end
|
137
|
+
|
138
|
+
# true if the path contains the current point # doesn't work?
|
139
|
+
def contains(x,y)
|
140
|
+
eorule = true
|
141
|
+
CGPathContainsPoint(@path, @transform, CGPointMake(x, y), eorule)
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
# ADD SHAPES TO PATH
|
146
|
+
|
147
|
+
# add another path to this path
|
148
|
+
def addpath(p)
|
149
|
+
CGPathAddPath(@path, @transform, p.path)
|
150
|
+
end
|
151
|
+
|
152
|
+
# add a rectangle starting at [x,y] with dimensions w x h
|
153
|
+
def rect(x=0, y=0, w=20, h=20, reg=@registration)
|
154
|
+
if reg == :center
|
155
|
+
x = x - w / 2
|
156
|
+
y = y - h / 2
|
157
|
+
end
|
158
|
+
puts "path.rect at [#{x},#{y}] with #{w}x#{h}" if @verbose
|
159
|
+
CGPathAddRect(@path, @transform, CGRectMake(x,y,w,h))
|
160
|
+
self
|
161
|
+
end
|
162
|
+
|
163
|
+
# draw a rounded rectangle using quadratic curved corners (FIXME)
|
164
|
+
def roundrect(x=0, y=0, width=20, height=20, roundness=0, reg=@registration)
|
165
|
+
if roundness == 0
|
166
|
+
p.rect(x, y, width, height, reg)
|
167
|
+
else
|
168
|
+
if reg == :center
|
169
|
+
x = x - self.width / 2
|
170
|
+
y = y - self.height / 2
|
171
|
+
end
|
172
|
+
curve = min(width * roundness, height * roundness)
|
173
|
+
p = Path.new
|
174
|
+
p.moveto(x, y+curve)
|
175
|
+
p.curveto(x, y, x, y, x+curve, y)
|
176
|
+
p.lineto(x+width-curve, y)
|
177
|
+
p.curveto(x+width, y, x+width, y, x+width, y+curve)
|
178
|
+
p.lineto(x+width, y+height-curve)
|
179
|
+
p.curveto(x+width, y+height, x+width, y+height, x+width-curve, y+height)
|
180
|
+
p.lineto(x+curve, y+height)
|
181
|
+
p.curveto(x, y+height, x, y+height, x, y+height-curve)
|
182
|
+
p.endpath
|
183
|
+
end
|
184
|
+
addpath(p)
|
185
|
+
self
|
186
|
+
end
|
187
|
+
|
188
|
+
# create an oval starting at x,y with dimensions w x h, optionally registered at :center
|
189
|
+
def oval(x=0, y=0, w=20, h=20, reg=@registration)
|
190
|
+
if (reg == :center)
|
191
|
+
x = x - w / 2
|
192
|
+
y = y - h / 2
|
193
|
+
end
|
194
|
+
puts "path.oval at [#{x},#{y}] with #{w}x#{h}" if @verbose
|
195
|
+
CGPathAddEllipseInRect(@path, @transform, CGRectMake(x, y, w, h))
|
196
|
+
self
|
197
|
+
end
|
198
|
+
|
199
|
+
# draw a circle with center at x,y with width and (optional) height
|
200
|
+
# def circle(x,y,w,h=w)
|
201
|
+
# oval(x - w/2, y - h/2, w, h)
|
202
|
+
# end
|
203
|
+
|
204
|
+
# ADD LINES TO PATH
|
205
|
+
|
206
|
+
# draw a line from x1,x2 to x2,y2
|
207
|
+
def line(x1, y1, x2, y2)
|
208
|
+
CGPathAddLines(@path, @transform, [NSMakePoint(x1, y1), NSMakePoint(x2, y2)])
|
209
|
+
self
|
210
|
+
end
|
211
|
+
|
212
|
+
# draw the arc of a circle with center point x,y, radius, start angle (0 deg = 12 o'clock) and end angle
|
213
|
+
def arc(x, y, radius, start_angle, end_angle)
|
214
|
+
start_angle = radians(90 - start_angle)
|
215
|
+
end_angle = radians(90 - end_angle)
|
216
|
+
clockwise = 1 # 1 = clockwise, 0 = counterclockwise
|
217
|
+
CGPathAddArc(@path, @transform, x, y, radius, start_angle, end_angle, clockwise)
|
218
|
+
self
|
219
|
+
end
|
220
|
+
|
221
|
+
# draw lines connecting the array of points
|
222
|
+
def lines(points)
|
223
|
+
CGPathAddLines(@path, @transform, points)
|
224
|
+
self
|
225
|
+
end
|
226
|
+
|
227
|
+
|
228
|
+
# CONSTRUCT PATHS IN PATH OBJECT
|
229
|
+
|
230
|
+
# move the "pen" to x,y
|
231
|
+
def moveto(x, y)
|
232
|
+
CGPathMoveToPoint(@path, @transform,x,y)
|
233
|
+
self
|
234
|
+
end
|
235
|
+
|
236
|
+
# draw a line from the current point to x,y
|
237
|
+
def lineto(x,y)
|
238
|
+
CGPathAddLineToPoint(@path, @transform, x, y)
|
239
|
+
self
|
240
|
+
end
|
241
|
+
|
242
|
+
# draw a bezier curve from the current point, given the coordinates of two handle control points and an end point
|
243
|
+
def curveto(cp1x, cp1y, cp2x, cp2y, x, y)
|
244
|
+
CGPathAddCurveToPoint(@path, @transform, cp1x, cp1y, cp2x, cp2y, x, y)
|
245
|
+
self
|
246
|
+
end
|
247
|
+
|
248
|
+
# draw a quadratic curve given a single control point and an end point
|
249
|
+
def qcurveto(cpx, cpy, x, y)
|
250
|
+
CGPathAddQuadCurveToPoint(@path, @transform, cpx, cpy, x, y)
|
251
|
+
self
|
252
|
+
end
|
253
|
+
|
254
|
+
# draw an arc given the endpoints of two tangent lines and a radius
|
255
|
+
def arcto(x1, y1, x2, y2, radius)
|
256
|
+
CGPathAddArcToPoint(@path, @transform, x1, y1, x2, y2, radius)
|
257
|
+
self
|
258
|
+
end
|
259
|
+
|
260
|
+
# end the current path
|
261
|
+
def endpath
|
262
|
+
CGPathCloseSubpath(@path)
|
263
|
+
end
|
264
|
+
|
265
|
+
|
266
|
+
# TRANSFORMATIONS
|
267
|
+
|
268
|
+
# specify rotation for subsequent operations
|
269
|
+
def rotate(deg)
|
270
|
+
puts "path.rotate #{deg}" if @verbose
|
271
|
+
@transform = CGAffineTransformRotate(@transform, radians(deg))
|
272
|
+
end
|
273
|
+
|
274
|
+
# scale by horizontal/vertical scaling factors sx,sy for subsequent drawing operations
|
275
|
+
def scale(sx=nil, sy=nil)
|
276
|
+
if sx == nil && sy == nil
|
277
|
+
@scale
|
278
|
+
else
|
279
|
+
sy = sx unless sy
|
280
|
+
puts "path.scale #{sx}x#{sy}" if @verbose
|
281
|
+
@transform = CGAffineTransformScale(@transform, sx, sy)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
# specify translation by x,y for subsequent drawing operations
|
286
|
+
def translate(x,y)
|
287
|
+
puts "path.translate #{x}x#{y}" if @verbose
|
288
|
+
@transform = CGAffineTransformTranslate(@transform, x, y)
|
289
|
+
end
|
290
|
+
|
291
|
+
|
292
|
+
# BUILD PRIMITIVES
|
293
|
+
|
294
|
+
# draw a petal starting at x,y with w x h and center bulge height using quadratic curves
|
295
|
+
def petal(x=0, y=0, w=10, h=50, bulge=h/2)
|
296
|
+
moveto(x,y)
|
297
|
+
qcurveto(x - w, y + bulge, x, y + h)
|
298
|
+
qcurveto(x + w, y + bulge, x, y)
|
299
|
+
endpath
|
300
|
+
self
|
301
|
+
end
|
302
|
+
|
303
|
+
# duplicate and rotate the Path object the specified number of times
|
304
|
+
def kaleidoscope(path,qty)
|
305
|
+
deg = 360 / qty
|
306
|
+
qty.times do
|
307
|
+
addpath(path)
|
308
|
+
rotate(deg)
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
# duplicate and rotate the Path object the specified number of times
|
313
|
+
#path, rotation, scale, translation, iterations
|
314
|
+
def spiral(path=nil, rotation=20, scalex=0.95, scaley=0.95, tx=10, ty=10, iterations=30)
|
315
|
+
iterations.times do
|
316
|
+
addpath(path)
|
317
|
+
rotate(rotation)
|
318
|
+
scale(scalex, scaley)
|
319
|
+
translate(tx, ty)
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
|
324
|
+
end
|
325
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# Ruby Cocoa Graphics is a graphics library providing a simple object-oriented
|
2
|
+
# interface into the power of Mac OS X's Core Graphics and Core Image drawing libraries.
|
3
|
+
# With a few lines of easy-to-read code, you can write scripts to draw simple or complex
|
4
|
+
# shapes, lines, and patterns, process and filter images, create abstract art or visualize
|
5
|
+
# scientific data, and much more.
|
6
|
+
#
|
7
|
+
# Inspiration for this project was derived from Processing and NodeBox. These excellent
|
8
|
+
# graphics programming environments are more full-featured than RCG, but they are implemented
|
9
|
+
# in Java and Python, respectively. RCG was created to offer similar functionality using
|
10
|
+
# the Ruby programming language.
|
11
|
+
#
|
12
|
+
# Author:: James Reynolds (mailto:drtoast@drtoast.com)
|
13
|
+
# Copyright:: Copyright (c) 2008 James Reynolds
|
14
|
+
# License:: Distributes under the same terms as Ruby
|
15
|
+
|
16
|
+
module HotCocoa::Graphics
|
17
|
+
|
18
|
+
# parse a PDF file to determine pages, width, height
|
19
|
+
class Pdf
|
20
|
+
|
21
|
+
attr_reader :pages, :pdf
|
22
|
+
|
23
|
+
# create a new Pdf object given the original pathname, and password if needed
|
24
|
+
def initialize(original, password = nil)
|
25
|
+
# http://developer.apple.com/documentation/GraphicsImaging/Reference/CGPDFDocument/Reference/reference.html
|
26
|
+
# http://developer.apple.com/documentation/GraphicsImaging/Reference/CGPDFPage/Reference/reference.html
|
27
|
+
@pdf = CGPDFDocumentCreateWithURL(NSURL.fileURLWithPath(original)) # => CGPDFDocumentRef
|
28
|
+
result = CGPDFDocumentUnlockWithPassword(@pdf, password) if password # unlock if necessary
|
29
|
+
@pages = CGPDFDocumentGetNumberOfPages(@pdf) # => 4
|
30
|
+
puts "pdf.new #{original} (#{@pages} pages)" if @verbose
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
# print drawing functions to console if verbose is true
|
35
|
+
def verbose(tf)
|
36
|
+
@verbose = tf
|
37
|
+
end
|
38
|
+
|
39
|
+
# get the width of the specified pagenum
|
40
|
+
def width(pagenum=1)
|
41
|
+
cgpdfpage = page(pagenum)
|
42
|
+
mediabox = CGPDFPageGetBoxRect(cgpdfpage, KCGPDFMediaBox) # => CGRect
|
43
|
+
width = mediabox.size.width # CGRectGetWidth(mediabox)
|
44
|
+
width
|
45
|
+
end
|
46
|
+
|
47
|
+
# get the height of the specified pagenum
|
48
|
+
def height(pagenum=1)
|
49
|
+
cgpdfpage = page(pagenum)
|
50
|
+
mediabox = CGPDFPageGetBoxRect(cgpdfpage, KCGPDFMediaBox) # => CGRect
|
51
|
+
height = mediabox.size.height # CGRectGetHeight(mediabox)
|
52
|
+
height
|
53
|
+
end
|
54
|
+
|
55
|
+
# draw pagenum of the pdf document into a rectangle at x,y with dimensions w,h of drawing context ctx
|
56
|
+
def draw(ctx, x=0, y=0, w=width(pagenum), h=height(pagenum), pagenum=1)
|
57
|
+
rect = CGRectMake(x,y,w,h)
|
58
|
+
puts "pdf.draw page #{pagenum} at [#{x},#{y}] with #{w}x#{h}" if @verbose
|
59
|
+
CGContextDrawPDFDocument(ctx, rect, @pdf, pagenum)
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
# return a CGPDFPageRef for this pagenum
|
66
|
+
def page(pagenum)
|
67
|
+
CGPDFDocumentGetPage(@pdf, pagenum) # => CGPDFPageRef
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# Ruby Cocoa Graphics is a graphics library providing a simple object-oriented
|
2
|
+
# interface into the power of Mac OS X's Core Graphics and Core Image drawing libraries.
|
3
|
+
# With a few lines of easy-to-read code, you can write scripts to draw simple or complex
|
4
|
+
# shapes, lines, and patterns, process and filter images, create abstract art or visualize
|
5
|
+
# scientific data, and much more.
|
6
|
+
#
|
7
|
+
# Inspiration for this project was derived from Processing and NodeBox. These excellent
|
8
|
+
# graphics programming environments are more full-featured than RCG, but they are implemented
|
9
|
+
# in Java and Python, respectively. RCG was created to offer similar functionality using
|
10
|
+
# the Ruby programming language.
|
11
|
+
#
|
12
|
+
# Author:: James Reynolds (mailto:drtoast@drtoast.com)
|
13
|
+
# Copyright:: Copyright (c) 2008 James Reynolds
|
14
|
+
# License:: Distributes under the same terms as Ruby
|
15
|
+
|
16
|
+
# More information about Quartz 2D is available on the Apple's website:
|
17
|
+
# http://developer.apple.com/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_overview/dq_overview.html#//apple_ref/doc/uid/TP30001066-CH202-TPXREF101
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
module HotCocoa;end # needed in case this is required without hotcocoa
|
22
|
+
|
23
|
+
framework 'Cocoa'
|
24
|
+
|
25
|
+
module HotCocoa::Graphics
|
26
|
+
|
27
|
+
# UTILITY FUNCTIONS (math/geometry)
|
28
|
+
TEST = 'OK'
|
29
|
+
|
30
|
+
# convert degrees to radians
|
31
|
+
def radians(deg)
|
32
|
+
deg * (Math::PI / 180.0)
|
33
|
+
end
|
34
|
+
|
35
|
+
# convert radians to degrees
|
36
|
+
def degrees(rad)
|
37
|
+
rad * (180 / Math::PI)
|
38
|
+
end
|
39
|
+
|
40
|
+
# return the angle of the line joining the two points
|
41
|
+
def angle(x0, y0, x1, y1)
|
42
|
+
degrees(Math.atan2(y1-y0, x1-x0))
|
43
|
+
end
|
44
|
+
|
45
|
+
# return the distance between two points
|
46
|
+
def distance(x0, y0, x1, y1)
|
47
|
+
Math.sqrt((x1-x0)**2 + (y1-y0)**2)
|
48
|
+
end
|
49
|
+
|
50
|
+
# return the coordinates of a new point at the given distance and angle from a starting point
|
51
|
+
def coordinates(x0, y0, distance, angle)
|
52
|
+
x1 = x0 + Math.cos(radians(angle)) * distance
|
53
|
+
y1 = y0 + Math.sin(radians(angle)) * distance
|
54
|
+
[x1,y1]
|
55
|
+
end
|
56
|
+
|
57
|
+
# return the lesser of a,b
|
58
|
+
def min(a, b)
|
59
|
+
a < b ? a : b
|
60
|
+
end
|
61
|
+
|
62
|
+
# return the greater of a,b
|
63
|
+
def max(a, b)
|
64
|
+
a > b ? a : b
|
65
|
+
end
|
66
|
+
|
67
|
+
# restrict the value to stay within the range
|
68
|
+
def inrange(value, min, max)
|
69
|
+
if value < min
|
70
|
+
min
|
71
|
+
elsif value > max
|
72
|
+
max
|
73
|
+
else
|
74
|
+
value
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# return a random number within the range, or a float from 0 to the number
|
79
|
+
def random(left=nil, right=nil)
|
80
|
+
if right
|
81
|
+
rand * (right - left) + left
|
82
|
+
elsif left
|
83
|
+
rand * left
|
84
|
+
else
|
85
|
+
rand
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def reflect(x0, y0, x1, y1, d=1.0, a=180)
|
90
|
+
d *= distance(x0, y0, x1, y1)
|
91
|
+
a += angle(x0, y0, x1, y1)
|
92
|
+
x, y = coordinates(x0, y0, d, a)
|
93
|
+
[x,y]
|
94
|
+
end
|
95
|
+
|
96
|
+
def choose(object)
|
97
|
+
case object
|
98
|
+
when Range
|
99
|
+
case object.first
|
100
|
+
when Float
|
101
|
+
rand * (object.last - object.first) + object.first
|
102
|
+
when Integer
|
103
|
+
rand(object.last - object.first + 1) + object.first
|
104
|
+
end
|
105
|
+
when Array
|
106
|
+
object.sample
|
107
|
+
else
|
108
|
+
object
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# given an object's x,y coordinates and dimensions, return the distance
|
113
|
+
# needed to move in order to orient the object at the given location (:center, :bottomleft, etc)
|
114
|
+
def reorient(x, y, w, h, location)
|
115
|
+
case location
|
116
|
+
when :bottomleft
|
117
|
+
movex = -x
|
118
|
+
movey = -y
|
119
|
+
when :centerleft
|
120
|
+
movex = -x
|
121
|
+
movey = -y - h / 2
|
122
|
+
when :topleft
|
123
|
+
movex = -x
|
124
|
+
movey = -x - h
|
125
|
+
when :bottomright
|
126
|
+
movex = -x - w
|
127
|
+
movey = -y
|
128
|
+
when :centerright
|
129
|
+
movex = -x - w
|
130
|
+
movey = -y - h / 2
|
131
|
+
when :topright
|
132
|
+
movex = -x - w
|
133
|
+
movey = -y - h
|
134
|
+
when :bottomcenter
|
135
|
+
movex = -x - w / 2
|
136
|
+
movey = -y
|
137
|
+
when :center
|
138
|
+
movex = -x - w / 2
|
139
|
+
movey = -y - h / 2
|
140
|
+
when :topcenter
|
141
|
+
movex = -x - w / 2
|
142
|
+
movey = -y - h
|
143
|
+
else
|
144
|
+
raise "ERROR: image origin locator not recognized: #{location}"
|
145
|
+
end
|
146
|
+
#newx = oldx + movex
|
147
|
+
#newy = oldy + movey
|
148
|
+
[movex,movey]
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
require 'hotcocoa/graphics/canvas'
|
154
|
+
require 'hotcocoa/graphics/color'
|
155
|
+
require 'hotcocoa/graphics/gradient'
|
156
|
+
require 'hotcocoa/graphics/image'
|
157
|
+
require 'hotcocoa/graphics/path'
|
158
|
+
require 'hotcocoa/graphics/pdf'
|
159
|
+
require 'hotcocoa/graphics/elements/particle'
|
160
|
+
require 'hotcocoa/graphics/elements/rope'
|
161
|
+
require 'hotcocoa/graphics/elements/sandpainter'
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class Object
|
2
|
+
|
3
|
+
def self.kvo_array(key, &b)
|
4
|
+
key = key.to_s
|
5
|
+
capitalized_key = key[0].capitalize + key[1..-1]
|
6
|
+
signatures = { :size => { selector: :"countOf#{capitalized_key}", type_signature: "i@:", flip: false },
|
7
|
+
:[] => { selector: :"objectIn#{capitalized_key}AtIndex:", type_signature: "@@:i", flip: false },
|
8
|
+
:insert => { selector: :"insertObject:in#{capitalized_key}AtIndex:", type_signature: "v@:@i", flip: true },
|
9
|
+
:delete_at => { selector: :"removeObjectFrom#{capitalized_key}AtIndex:", type_signature: "v@:i", flip: false }
|
10
|
+
}
|
11
|
+
define_methods_with_signatures(signatures, &b)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.kvo_set(key, &b)
|
15
|
+
key = key.to_s
|
16
|
+
capitalized_key = key[0].capitalize + key[1..-1]
|
17
|
+
signatures = { :add => { selector: :"add#{capitalized_key}Object:", type_signature: "v@:@", flip: false },
|
18
|
+
:delete => { selector: :"remove#{capitalized_key}Object:", type_signature: "v@:@", flip: false},
|
19
|
+
:merge => { selector: :"add#{capitalized_key}:", type_signature: "v@:@", flip: false },
|
20
|
+
:subtract => { selector: :"remove#{capitalized_key}:", type_signature: "v@:@", flip: false },
|
21
|
+
:set => { selector: :"#{key}", type_signature: "@@:", flip: false }
|
22
|
+
}
|
23
|
+
define_methods_with_signatures(signatures, &b)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def self.define_methods_with_signatures(signatures, &b)
|
29
|
+
c = Module.new
|
30
|
+
c.module_eval &b
|
31
|
+
c.instance_methods.each do |m|
|
32
|
+
signature = signatures[m]
|
33
|
+
if signature
|
34
|
+
method = c.instance_method(m)
|
35
|
+
if signature[:flip]
|
36
|
+
method = Proc.new { |a, b| method.bind(self).call(b, a)}
|
37
|
+
end
|
38
|
+
c.send(:define_method, signature[:selector], method)
|
39
|
+
c.send(:remove_method, m)
|
40
|
+
c.send(:method_signature, signature[:selector], signature[:type_signature])
|
41
|
+
elsif not Module.instance_methods.include?(m)
|
42
|
+
raise ArgumentError, "Method `#{m}' isn't a KVO accessor"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
include c
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|