xrvg 0.0.1

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/lib/color.rb ADDED
@@ -0,0 +1,295 @@
1
+ # Color functionalities file. See
2
+ # - +Color+
3
+ # - +Palette+
4
+ # - +Gradient+
5
+
6
+ require 'geometry2D'; # for vector extension
7
+ require 'interpolation'
8
+ require 'attributable'
9
+ require 'utils'
10
+ require 'shape'; # for gradient
11
+
12
+ #
13
+ # Color class
14
+ #
15
+ # = Basics
16
+ # Class Color derives from Vector, and consists in a 4D vector of (0.0..1.0) values, for red, blue, green, and opacity
17
+ # = Utilities
18
+ # Conversion from hsv and hsl color spaces available (see this link[http://en.wikipedia.org/wiki/HSV_color_space])
19
+ # = Future
20
+ # - Must use this library[https://rubyforge.org/projects/color/], to avoid effort duplication
21
+ # - Must add relative color operations as Nodebox wants to
22
+ # - Must optimize 4D vector operations (as C extension ?)
23
+ class Color < Vector
24
+
25
+ # Color builder
26
+ #
27
+ # only allows to build 4D vector, with composants between 0.0 and 1.0
28
+ def initialize( *args )
29
+ # TODO : check args number
30
+ super( *args )
31
+ end
32
+
33
+ # return the red composant
34
+ # Color[0.1,0.2,0.3,0.4].r => 0.1
35
+ def r
36
+ return self[0]
37
+ end
38
+
39
+ # return the green composant
40
+ # Color[0.1,0.2,0.3,0.4].r => 0.2
41
+ def g
42
+ return self[1]
43
+ end
44
+
45
+ # return the blue composant
46
+ # Color[0.1,0.2,0.3,0.4].r => 0.3
47
+ def b
48
+ return self[2]
49
+ end
50
+
51
+ # return the opacity composant
52
+ # Color[0.1,0.2,0.3,0.4].r => 0.4
53
+ def a
54
+ return self[3]
55
+ end
56
+
57
+ # set the red composant
58
+ # Color[0.1,0.2,0.3,0.4].r = 0.5 => Color[0.5,0.2,0.3,0.4]
59
+ def r=(n)
60
+ self[0]= n
61
+ end
62
+
63
+ # set the green composant
64
+ # Color[0.1,0.2,0.3,0.4].g = 0.5 => Color[0.1,0.5,0.3,0.4]
65
+ def g=(n)
66
+ self[1] = n
67
+ end
68
+
69
+ # set the blue composant
70
+ # Color[0.1,0.2,0.3,0.4].b = 0.5 => Color[0.1,0.2,0.5,0.4]
71
+ def b=(n)
72
+ self[2] = n
73
+ end
74
+
75
+ # set the opacity composant
76
+ # Color[0.1,0.2,0.3,0.4].a = 0.5 => Color[0.1,0.2,0.3,0.5]
77
+ def a=(n)
78
+ self[3] = n
79
+ end
80
+
81
+ # return an array containing colors on 255 integer format
82
+ # Color[0.0,1.0,0.0,1.0].format255 => [0,255,0,255]
83
+ def format255()
84
+ return self.map {|v| (v * 255.0).to_i}
85
+ end
86
+
87
+ # return a random color vector, with 1.0 opacity !!
88
+ # Color.rand => Color[0.2345, 0.987623, 0.4123, 1.0]
89
+ def Color.rand
90
+ return Color[Kernel::rand,Kernel::rand,Kernel::rand,1.0]
91
+ end
92
+
93
+ # return a black color vector
94
+ def Color.black(opacity=1.0)
95
+ return Color[0.0, 0.0, 0.0, opacity]
96
+ end
97
+
98
+ # return a blue color vector
99
+ def Color.blue(opacity=1.0)
100
+ return Color[0.0, 0.0, 1.0, opacity]
101
+ end
102
+
103
+ # return a red color vector
104
+ def Color.red(opacity=1.0)
105
+ return Color[1.0, 0.0, 0.0, opacity]
106
+ end
107
+
108
+ # return a yellow color vector
109
+ def Color.yellow(opacity=1.0)
110
+ return Color[1.0, 1.0, 0.0, opacity]
111
+ end
112
+
113
+ # return a orange color vector
114
+ def Color.orange(opacity=1.0)
115
+ return Color[1.0, 0.5, 0.0, opacity]
116
+ end
117
+
118
+ # return a green color vector
119
+ def Color.green(opacity=1.0)
120
+ return Color[0.0, 1.0, 0.0, opacity]
121
+ end
122
+
123
+ # return a white color vector
124
+ def Color.white(opacity=1.0)
125
+ return Color[1.0, 1.0, 1.0, opacity]
126
+ end
127
+
128
+ # build a color vector from hsv parametrization (convert from hsv to rgb) h, s, v being between 0.0 and 1.0
129
+ # taken from wikipedia[http://en.wikipedia.org/wiki/HSV_color_space]
130
+ def Color.hsv( h, s, v, a)
131
+ if s == 0.0
132
+ return Color[v, v, v, a]
133
+ end
134
+ h *= 360.0
135
+ hi = (h/60.0).floor
136
+ f = (h/60.0) - hi
137
+ p = v * ( 1 - s )
138
+ q = v * ( 1 - f * s )
139
+ t = v * ( 1 - ( 1 - f ) * s )
140
+ if hi == 0
141
+ return Color[ v, t, p, a]
142
+ end
143
+ if hi == 1
144
+ return Color[ q, v, p, a]
145
+ end
146
+ if hi == 2
147
+ return Color[ p, v, t, a]
148
+ end
149
+ if hi == 3
150
+ return Color[ p, q, v, a]
151
+ end
152
+ if hi == 4
153
+ return Color[ t, p, v, a]
154
+ end
155
+ if hi == 5
156
+ return Color[ v, p, q, a]
157
+ end
158
+ end
159
+
160
+ def Color.getHSLcomponent( tC, p, q ) #:nodoc:
161
+ while tC < 0.0
162
+ tC = tC + 1.0
163
+ end
164
+ while tC > 1.0
165
+ tC = tC - 1.0
166
+ end
167
+
168
+ if tC < (1.0 / 6.0)
169
+ tC = p + ( (q-p) * 6.0 * tC )
170
+ elsif tC >=(1.0 / 6.0) and tC < 0.5
171
+ tC = q
172
+ elsif tC >= 0.5 and tC < (2.0 / 3.0)
173
+ tC = p + ( (q-p) * 6.0 * ((2.0 / 3.0) - tC) )
174
+ else
175
+ tC = p
176
+ end
177
+ return tC
178
+ end
179
+
180
+ # build a color vector from hsl parametrization (convert from hsl to rgb) h, s, l being between 0.0 and 1.0
181
+ # taken from [[http://en.wikipedia.org/wiki/HSV_color_space]]
182
+ # h, s, l must be between 0.0 and 1.0
183
+ def Color.hsl( h, s, l, a)
184
+ h *= 360.0
185
+ if l < 0.5
186
+ q = l * (1.0 + s)
187
+ else
188
+ q = l+ s - (l * s)
189
+ end
190
+ p = 2 * l - q
191
+ hk = h / 360.0
192
+ tR = hk + 1.0 / 3.0
193
+ tG = hk
194
+ tB = hk - 1.0 / 3.0
195
+
196
+ tR = self.getHSLcomponent( tR, p, q )
197
+ tG = self.getHSLcomponent( tG, p, q )
198
+ tB = self.getHSLcomponent( tB, p, q )
199
+ return Color[tR, tG, tB, a]
200
+ end
201
+
202
+ # get svg description of a color
203
+ def svg
204
+ values = self[0..2].map {|v| (255.0 * v).to_i }
205
+ return "rgb(#{values.join(",")})"
206
+ end
207
+
208
+ end
209
+
210
+ # class Palette
211
+ # = Intro
212
+ # Palette defines color palettes, as interpolation between color points. As such, use Interpolation module, so uses for the moment only linear interpolation.
213
+ # But once built with interpolation, palette provides a continuous color "interval", and so is Samplable !
214
+ # = Use
215
+ # palette = Palette[ :colorlist, [ Color.blue, 0.0, Color.orange, 0.5, Color.yellow, 1.0 ] ]
216
+ # palette.rand( 10 ) # => return 10 random colors in palette
217
+ # palette.color( 0.5 ) # => Color.orange
218
+ class Palette
219
+ include Attributable
220
+ attribute :colorlist
221
+
222
+ # compute color given float pourcentage.
223
+ # Palette[ :colorlist, [ Color.black, 0.0, Color.white, 1.0 ] ].sample( 0.5 ) => Color[0.5,0.5,0.5,1.O]
224
+ # "sample" method as defined in Samplable module
225
+ def color(dindex)
226
+ result = self.interpolate(dindex)
227
+ return Color.elements(result[0..-1],false)
228
+ end
229
+
230
+ # return a new palette by reversing the current one
231
+ def reverse()
232
+ newcolorlist = []
233
+ self.colorlist.reverse.foreach do |index,color|
234
+ newcolorlist += [color, (0.0..1.0).complement( index )]
235
+ end
236
+ return Palette[ :colorlist, newcolorlist ]
237
+ end
238
+
239
+
240
+ include Samplable
241
+ include Interpolation
242
+
243
+ def apply_sample( abs ) #:nodoc:
244
+ # Trace("Palette#apply_sample abs #{abs}")
245
+ return self.color( abs )
246
+ end
247
+
248
+ # alias apply_sample color
249
+ # alias apply_split ? => TODO
250
+ alias samplelist colorlist
251
+ alias colors samples
252
+ end
253
+
254
+ class Gradient < Palette #:nodoc:
255
+ def defsvg()
256
+ Kernel::raise("Gradient::defsvg must be redefined in subclasses")
257
+ end
258
+ end
259
+
260
+
261
+ class LinearGradient < Gradient #:nodoc:
262
+
263
+ def svgdef
264
+ template = '<linearGradient id="%ID%" x1="0%" y1="0%" x2="0%" y2="100%">%stops%</linearGradient>'
265
+ stoptemplate = '<stop offset="%offset%" stop-color="%color%" stop-opacity="%opacity%"/>'
266
+
267
+ stops = "\n"
268
+ self.colorlist.foreach do |color, index|
269
+ stops += stoptemplate.subreplace( {"%offset%" => index, "%color%" => color.svg, "%opacity%" => color.a} )
270
+ stops += "\n"
271
+ end
272
+
273
+ return template.subreplace( {"%stops%" => stops} )
274
+ end
275
+ end
276
+
277
+ class CircularGradient < Gradient #:nodoc:
278
+ attribute :circle, nil, Circle
279
+
280
+ def svgdef
281
+ template = '<radialGradient id="%ID%" gradientUnits="userSpaceOnUse" cx="%cx%" cy="%cy%" r="%r%">%stops%</radialGradient>'
282
+ stoptemplate = '<stop offset="%offset%" stop-color="%color%" stop-opacity="%opacity%"/>'
283
+
284
+ stops = "\n"
285
+ self.colorlist.foreach do |color, index|
286
+ stops += stoptemplate.subreplace( {"%offset%" => index, "%color%" => color.svg, "%opacity%" => color.a} )
287
+ stops += "\n"
288
+ end
289
+
290
+ return template.subreplace( {"%stops%" => stops,
291
+ "%cx%" => circle.center.x,
292
+ "%cy%" => circle.center.y,
293
+ "%r%" => circle.radius} )
294
+ end
295
+ end
data/lib/frame.rb ADDED
@@ -0,0 +1,32 @@
1
+ # frame.rb file
2
+ #
3
+ # See +Frame+
4
+ require 'attributable'
5
+
6
+ #
7
+ # Frame class
8
+ # = Intro
9
+ # Defines a local geometry. Used by +Curve+ interface.
10
+ # = Attributes
11
+ # attribute :center
12
+ # attribute :vector
13
+ # attribute :rotation
14
+ # attribute :scale
15
+ class Frame
16
+ include Attributable
17
+ attribute :center
18
+ attribute :vector
19
+ attribute :rotation
20
+ attribute :scale
21
+
22
+ def ==(other)
23
+ if self.center == other.center and
24
+ self.vector == other.vector and
25
+ self.rotation == other.rotation and
26
+ self.scale == other.scale
27
+ return true
28
+ end
29
+ return false
30
+ end
31
+ end
32
+
data/lib/geometry2D.rb ADDED
@@ -0,0 +1,207 @@
1
+ #
2
+ # Contains Ruby Vector class extension.
3
+ # See :
4
+ # - +Vector+
5
+ # - +Point+
6
+
7
+ require 'matrix'
8
+
9
+ #
10
+ # Vector class extension, to provide several useful "geometrical" services
11
+ #
12
+ # For example :
13
+ # Vector[0.0,2.0].norm => Vector[0.0,1.0]
14
+ # Vector[0.0,2.0].angle => 0.0
15
+ # Vector[0.0,2.0].ortho => Vector[-2.0,0.0]
16
+ # Vector[0.0,2.0].rotate( Math::PI ) => Vector[0.0,-2.0]
17
+ #
18
+ # It could be efficient to define specific 2D vector, to speed up computation.
19
+ # It is however not compatible with Color definition, as Vector 4D, and prevent from using this lib in 3D or 4D (with time).
20
+ # Must be checked though.
21
+ #
22
+ # Another thing is to make this class C extension, to speed up computation.
23
+ class Vector
24
+ include Comparable
25
+
26
+ X = Vector[1.0, 0.0]
27
+ Y = Vector[0.0, 1.0]
28
+ O = Vector[0.0, 0.0]
29
+
30
+ # to optimize for 2D
31
+ if nil
32
+ def *( scalar )
33
+ return Vector[ self[0] * scalar, self[1] * scalar ]
34
+ end
35
+
36
+ def +(other)
37
+ return Vector[ self[0] + other[0], self[1] + other[1] ]
38
+ end
39
+ end
40
+
41
+
42
+ # method necessary to make Vector Ranges
43
+ #
44
+ # make <=> on x, then on y
45
+ def <=>( other )
46
+ first = self.x <=> other.x
47
+ if first != 0
48
+ return first
49
+ else
50
+ return self.y <=> other.y
51
+ end
52
+ end
53
+
54
+ # method necessary to make Vector Ranges
55
+ #
56
+ # simply call succ on each coord
57
+ def succ()
58
+ return Vector[ self.x.succ, self.y.succ ]
59
+ end
60
+
61
+ # compute the normalized vector given the current vector
62
+ # Vector[0.0,2.0].norm => Vector[0.0,1.0]
63
+ def norm
64
+ r = r()
65
+ if r != 0.0
66
+ return self / r
67
+ else
68
+ return Vector::O
69
+ end
70
+ end
71
+
72
+ # compute the angle of a vector considering x axis.
73
+ # Vector[0.0,2.0].angle => 0.0
74
+ # must be made in C extension, as expensive
75
+ def angle
76
+ r = self.r()
77
+ if r == 0
78
+ return 0
79
+ else
80
+ unitary = self/r
81
+ cos, sin = unitary[0], unitary[1]
82
+ angle = Math.acos( cos )
83
+ if sin < 0.0
84
+ angle = -angle
85
+ end
86
+ return angle
87
+ end
88
+ end
89
+
90
+ # compute the angle between two vectors
91
+ # Vector.angle( Vector[1.0,0.0], Vector[0.0,1.0] ) => Math::PI/2.0
92
+ def Vector.angle( v1, v2 )
93
+ return v1.angle - v2.angle
94
+ end
95
+
96
+ # scale a vector with another one
97
+ # Vector[1.0,2.0].scale( Vector[3.0,4.0] ) => Vector[3.0,8.0]
98
+ # v.scale( Vector[a,a] ) <=> v * a
99
+ def scale( scaler )
100
+ return Vector[self.x * scaler.x, self.y * scaler.y ]
101
+ end
102
+
103
+ # rotate a 2D vector
104
+ # Vector[1.0,0.0].rotate( Math::PI/2.0 ) => Vector[0.0,1.0]
105
+ def rotate( angle )
106
+ newx = self.x * Math.cos( angle ) - self.y * Math.sin( angle )
107
+ newy = self.x * Math.sin( angle ) + self.y * Math.cos( angle )
108
+ return Vector[ newx, newy ]
109
+ end
110
+
111
+ # compute the orthogonal vector. Equiv to .rotate( Math::PI/2.0 )
112
+ # Vector[1.0,0.0].ortho => Vector[0.0,1.0]
113
+ def ortho
114
+ return Vector[ -self[1], self[0] ]
115
+ end
116
+
117
+ # return first coord of a vector
118
+ # Vector[1.0,2.0].x => 1.0
119
+ def x
120
+ return self[0]
121
+ end
122
+
123
+ # return second coord of a vector
124
+ # Vector[1.0,2.0].y => 2.0
125
+ def y
126
+ return self[1]
127
+ end
128
+
129
+ # compute the symetric of vector considered as point, with center of symetrie being other considered as point
130
+ # Vector[1.0,2.0].sym( Vector[0.0,0.0] ) => Vector[-1.0,-2.0]
131
+ def sym( other )
132
+ return self * 2.0 - other
133
+ end
134
+
135
+ # shortcut method to do
136
+ # vector * (1.0 / scalar)
137
+ def /( scalar )
138
+ return self * (1.0 / scalar)
139
+ end
140
+
141
+ # more efficient that self * -1.0
142
+ def -@ ()
143
+ return Vector[ -self[0], -self[1] ]
144
+ end
145
+
146
+ alias reverse -@
147
+
148
+ # coords management between different coord systems (for the moment only euclidian and polar)
149
+ # Vector[0.0,1.0].coords => [0.0,1.0]
150
+ # Vector[0.0,1.0].coords(:polar) => [1.0,Math::PI/2.0]
151
+ def coords(type=:cartesien)
152
+ if type == :cartesien
153
+ return [self.x, self.y]
154
+ elsif type == :polar
155
+ if @polarcoords == nil
156
+ @polarcoords = [self.r, self.angle]
157
+ end
158
+ return @polarcoords
159
+ else
160
+ Kernel.raise( "Unknown coord type #{type}" )
161
+ end
162
+ end
163
+
164
+ # build a vector from polar coords
165
+ def Vector.polar( r, angle )
166
+ x = r * Math.cos( angle )
167
+ y = r * Math.sin( angle )
168
+ return Vector[x,y]
169
+ end
170
+
171
+ alias length r
172
+ alias translate +
173
+ end
174
+
175
+ #
176
+ # Point class just to have some more meaningful notation, as Point::O
177
+ #
178
+ class Point < Vector
179
+
180
+ O = Point[0.0, 0.0]
181
+
182
+ # compute the coord box enclosing every 2D elements considered as points
183
+ # Point.viewbox( [Vector[1.0,2.0], Vector[2.0,1.0]] ) => [1.0, 1.0, 2.0, 2.0]
184
+ def Point.viewbox (pointlist)
185
+ if pointlist.size == 0
186
+ return [0.0, 0.0, 0.0, 0.0]
187
+ end
188
+
189
+ xs = pointlist.map {|p| p.x}
190
+ ys = pointlist.map {|p| p.y}
191
+
192
+ return [xs.min, ys.min, xs.max, ys.max]
193
+ end
194
+
195
+ # compute dimension of the box enclosing 2D elemnts considered as points
196
+ # Point.viewbox( [Vector[1.0,2.0], Vector[10.0,1.0]] ) => [9.0, 1.0]
197
+ # use +viewvox+
198
+ def Point.size (pointlist)
199
+ xmin, ymin, xmax, ymax = viewbox( pointlist )
200
+ return [xmax - xmin, ymax - ymin]
201
+ end
202
+
203
+ end
204
+
205
+
206
+
207
+
@@ -0,0 +1,59 @@
1
+ # Interpolation file.
2
+ #
3
+ # See
4
+ # - +Interpolation+
5
+ # - +Interpolator+
6
+
7
+ require 'utils'
8
+ require 'attributable'
9
+
10
+ # Interpolation module
11
+ # = Intro
12
+ # Defines an interpolation service from a samplelist that must be a list [value1, index1, value2, index2, ..., valueN, indexN],
13
+ # with index between 0.0 and 1.0 and in increasing order.
14
+ # value must be an object with + and * scalar operators defined
15
+ # = Uses
16
+ # Used for example by Palette
17
+ # = Future
18
+ # Must be extended for all kinds of interpolation (bezier, ...)
19
+ module Interpolation
20
+
21
+ # must be the overloaded method to adapt Interpolation
22
+ #
23
+ # for example, Palette redefines samplelist as
24
+ # alias samplelist colorlist
25
+ def samplelist()
26
+ return [0.0, 0.0, 1.0, 1.0]
27
+ end
28
+
29
+ # computing method
30
+ #
31
+ # from an input between 0.0 and 1.0, returns linear interpolated value
32
+ def interpolate( dindex )
33
+ result = nil
34
+ pvalue, pindex = self.samplelist[0..1]
35
+ self.samplelist.foreach do |value, index|
36
+ if dindex <= index
37
+ if dindex == index
38
+ return value
39
+ end
40
+ result = pvalue + ((value - pvalue ) * ((dindex - pindex) / (index - pindex )))
41
+ break
42
+ end
43
+ pvalue, pindex = value, index
44
+ end
45
+ if not result
46
+ result = self.samplelist[-2]
47
+ end
48
+ return result
49
+ end
50
+ end
51
+
52
+ # Buildable interpolator
53
+ #
54
+ # Simply instanciated module in a class
55
+ class Interpolator
56
+ include Attributable
57
+ attribute :samplelist
58
+ include Interpolation
59
+ end