jax-fractals 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.md +124 -0
- data/Rakefile +1 -0
- data/app/assets/jax/models/heightmap.js.coffee +160 -0
- data/app/assets/jax/resources/heightmaps/default.resource +4 -0
- data/app/controllers/fractal/fractals_controller.rb +95 -0
- data/jax-fractals.gemspec +23 -0
- data/lib/core_ext/math.rb +23 -0
- data/lib/fractal.rb +22 -0
- data/lib/fractal/engine.rb +10 -0
- data/lib/fractal/generator.rb +146 -0
- data/lib/fractal/island_generator.rb +32 -0
- data/lib/fractal/map.rb +49 -0
- data/lib/fractal/version.rb +10 -0
- data/lib/generators/fractal/USAGE +8 -0
- data/lib/generators/fractal/fractal_generator.rb +18 -0
- data/lib/jax-fractals.rb +1 -0
- data/screenshots/1.png +0 -0
- data/screenshots/3.png +0 -0
- data/screenshots/4.png +0 -0
- data/screenshots/hm.png +0 -0
- data/spec/fixtures/fractals/1 +0 -0
- data/spec/fixtures/fractals/1?alpha=1 +0 -0
- data/spec/fixtures/fractals/1?high_color=ff0000 +0 -0
- data/spec/fixtures/fractals/1?island=1 +0 -0
- data/spec/fixtures/fractals/1?low_color=0000ff +0 -0
- data/spec/fixtures/fractals/1?low_color=0000ff&high_color=ff0000 +0 -0
- data/spec/fixtures/fractals/1?smoothness=1.2 +0 -0
- data/spec/fixtures/fractals/1?width=126&height=130 +0 -0
- data/spec/javascripts/jax/models/heightmap_spec.js.coffee +67 -0
- data/spec/lib/fractal/generator_spec.rb +11 -0
- data/spec/lib/fractal/images_spec.rb +39 -0
- data/spec/lib/fractal/map_spec.rb +33 -0
- data/spec/spec_helper.rb +12 -0
- metadata +122 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
# jax-fractals
|
2
|
+
|
3
|
+
Fractal textures for [Jax](http://blog.jaxgl.com/what-is-jax)!
|
4
|
+
|
5
|
+
[<img src="https://raw.github.com/sinisterchipmunk/jax-fractals/master/screenshots/hm.png" width="128" height="128">](https://github.com/sinisterchipmunk/jax-fractals/blob/master/screenshots/hm.png)
|
6
|
+
[<img src="https://raw.github.com/sinisterchipmunk/jax-fractals/master/screenshots/1.png" width="128" height="128">](https://github.com/sinisterchipmunk/jax-fractals/blob/master/screenshots/1.png)
|
7
|
+
[<img src="https://raw.github.com/sinisterchipmunk/jax-fractals/master/screenshots/3.png" width="128" height="128">](https://github.com/sinisterchipmunk/jax-fractals/blob/master/screenshots/3.png)
|
8
|
+
[<img src="https://raw.github.com/sinisterchipmunk/jax-fractals/master/screenshots/4.png" width="128" height="128">](https://github.com/sinisterchipmunk/jax-fractals/blob/master/screenshots/4.png)
|
9
|
+
|
10
|
+
## Why?
|
11
|
+
|
12
|
+
Because I can! Also, fractals are oh-so-useful for terrain generation, cloud simulation, lava, and various other natural-looking effects.
|
13
|
+
|
14
|
+
## Features
|
15
|
+
|
16
|
+
* Adds a command-line generator for creating static fractal images
|
17
|
+
* Customizable options include random seed, image width, image height, and smoothness factor
|
18
|
+
* Defaults to a grayscale image, but can generate fractals using any color for low and high values
|
19
|
+
* Fractals which have power-of-two dimensions can be cleanly tiled
|
20
|
+
* Fractals can be generated with an alpha channel, which is helpful for rendering clouds
|
21
|
+
* Can generate "islands" -- that is, there are no high points on the borders
|
22
|
+
* Adds a Heightmap model to Jax for processing fractal images into terrain
|
23
|
+
* Vertical scale can be set independently from width and depth scale
|
24
|
+
* Quickly calculates normals for the height map, for efficient lighting
|
25
|
+
* Returns interpolated height values for fractional coordinates
|
26
|
+
* Height maps whose width and depth are powers of two can be cleanly tiled for endless landscapes
|
27
|
+
* For Rails projects, adds a controller for generating fractal images on-the-fly and caching them as appropriate
|
28
|
+
|
29
|
+
## Dependencies
|
30
|
+
|
31
|
+
This gem requires Jax and RMagick 2.
|
32
|
+
|
33
|
+
## Installation
|
34
|
+
|
35
|
+
Add the following to your Gemfile:
|
36
|
+
|
37
|
+
gem 'jax-fractals'
|
38
|
+
|
39
|
+
Or, for the latest development version, the following:
|
40
|
+
|
41
|
+
gem 'jax-fractals', :git => "http://github.com/sinisterchipmunk/jax-fractals"
|
42
|
+
|
43
|
+
Then type:
|
44
|
+
|
45
|
+
bundle install
|
46
|
+
|
47
|
+
## Usage
|
48
|
+
|
49
|
+
### The Generator
|
50
|
+
|
51
|
+
The quickest way to get a fractal image into your app is to generate it from the command line:
|
52
|
+
|
53
|
+
rails generate fractal name-of-fractal
|
54
|
+
|
55
|
+
By default, it will have pixel dimensions 128x128, and will be generated with a smoothness factor of 2. You can customize all of these options:
|
56
|
+
|
57
|
+
rails generate fractal name-of-fractal --width=256 --height=256 --smoothness=1.25
|
58
|
+
|
59
|
+
A different fractal image will be generated each time the generator is run unless a random seed is specified:
|
60
|
+
|
61
|
+
rails generate fractal name-of-fractal --seed=100
|
62
|
+
|
63
|
+
You can also change the colors for the low and high values, which default to black and white, respectively. The following example will replace black with red, and white with blue:
|
64
|
+
|
65
|
+
rails generate fractal name-of-fractal --low-color=ff0000 --high-color=0000ff
|
66
|
+
|
67
|
+
Sometimes you need the fractal to include an alpha (transparency) channel, so that the lower values are more transparent and the higher ones are more opaque. Simple!
|
68
|
+
|
69
|
+
rails generate fractal name-of-fractal --alpha
|
70
|
+
|
71
|
+
Occasionally, you'll want to generate a fractal that is guaranteed to have intensity values equal to 0 along its borders. This is useful if you're generating a single cloud in the sky (as opposed to a tileable texture) or an island in the sea. To do this with the generator:
|
72
|
+
|
73
|
+
rails generate fractal name-of-fractal --island
|
74
|
+
|
75
|
+
|
76
|
+
### The Controller
|
77
|
+
|
78
|
+
To mount the fractal controller, add the following to your routes.rb file:
|
79
|
+
|
80
|
+
mount Fractal::Engine => "/fractals"
|
81
|
+
|
82
|
+
Restart the Rails server, and you can generate a fractal by visiting its URL. There is a single required parameter, its ID, which is used as a random seed. You can also pass width, height, smoothness and color parameters.
|
83
|
+
|
84
|
+
Experiment with the following examples:
|
85
|
+
|
86
|
+
* [http://localhost:3000/fractals/1](http://localhost:3000/fractals/1)
|
87
|
+
* [http://localhost:3000/fractals/2](http://localhost:3000/fractals/2)
|
88
|
+
* [http://localhost:3000/fractals/2?island=1](http://localhost:3000/fractals/2?island=1)
|
89
|
+
* [http://localhost:3000/fractals/2?low_color=ff0000&high_color=0000ff](http://localhost:3000/fractals/2?low_color=ff0000&high_color=0000ff)
|
90
|
+
* [http://localhost:3000/fractals/2?low_color=ff0000&high_color=0000ff&alpha=true](http://localhost:3000/fractals/2?low_color=ff0000&high_color=0000ff&alpha=true)
|
91
|
+
* [http://localhost:3000/fractals/1?width=256&height=256](http://localhost:3000/fractals/1?width=256&height=256)
|
92
|
+
* [http://localhost:3000/fractals/1?smoothness=1.25](http://localhost:3000/fractals/1?smoothness=1.25)
|
93
|
+
* [http://localhost:3000/fractals/1?width=256&height=256&smoothness=1.25](http://localhost:3000/fractals/1?width=256&height=256&smoothness=1.25)
|
94
|
+
|
95
|
+
Once generated, fractals will be stored in the Rails cache, so that they only need to be generated one time. After generation, the cached copy will be returned instead.
|
96
|
+
|
97
|
+
|
98
|
+
### The Heightmap
|
99
|
+
|
100
|
+
Height maps will work with any image, not just fractals, but they play so nicely together that I couldn't resist adding the Heightmap model to this library.
|
101
|
+
|
102
|
+
The easiest way to create a height map is to create a resource file. In your Rails or Jax project, create the file `app/assets/jax/resources/heightmaps/test.resource` and add the following information to it:
|
103
|
+
|
104
|
+
path: "/fractals/5"
|
105
|
+
xz_scale: 0.75
|
106
|
+
y_scale: 8.0
|
107
|
+
|
108
|
+
This will load the fractal dynamically from the Fractals controller (note: you have to change the path to reference a static image if you're not using Rails), scale its width and depth by 3/4, and then scale its height by 8 to produce a hilly (but not too mountainous!) terrain.
|
109
|
+
|
110
|
+
If you want to texture it (and who wouldn't?), create a material like you'd create any other Jax material:
|
111
|
+
|
112
|
+
$ jax g material ground texture
|
113
|
+
|
114
|
+
Then, set it in the resource file:
|
115
|
+
|
116
|
+
material: "ground"
|
117
|
+
|
118
|
+
#### Actually Using It
|
119
|
+
|
120
|
+
To add the "test" heightmap to the world, add it like you would any other Jax model instance. Do this in your Jax controller:
|
121
|
+
|
122
|
+
@world.addObject Heightmap.find "test"
|
123
|
+
|
124
|
+
Done!
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# Height map can be created in one of two ways. The most common is by referencing
|
2
|
+
# a height image by its path:
|
3
|
+
#
|
4
|
+
# hm = new HeightMap path: "/path/to/image.png"
|
5
|
+
#
|
6
|
+
# When loaded form an image, the height value for any given vertex is calculated
|
7
|
+
# with the following formula, clamping the value between 0 and 1:
|
8
|
+
#
|
9
|
+
# (red * 65536 + green * 256 + blue) / 16777215
|
10
|
+
#
|
11
|
+
# The height is then scaled by @y_scale, if given. Alpha values are ignored.
|
12
|
+
# You can ignore this formula entirely if the image is an 8bpp grayscale.
|
13
|
+
#
|
14
|
+
# You can also set the @xz_scale to control the horizontal dimensions of the
|
15
|
+
# height map.
|
16
|
+
#
|
17
|
+
#
|
18
|
+
# The second method of instantiation is to specify height values explicitly
|
19
|
+
# along with a width and depth. You can use nested arrays for this, for
|
20
|
+
# organization, but a single flat array is also supported:
|
21
|
+
#
|
22
|
+
# hm = new HeightMap
|
23
|
+
# width: 4
|
24
|
+
# depth: 2
|
25
|
+
# heights: [
|
26
|
+
# [ 0, 64, 128, 255 ],
|
27
|
+
# [ 255, 64, 128, 0 ]
|
28
|
+
# ]
|
29
|
+
#
|
30
|
+
# hm = new HeightMap
|
31
|
+
# width: 4
|
32
|
+
# depth: 2
|
33
|
+
# heights: [
|
34
|
+
# 0, 64, 128, 255,
|
35
|
+
# 255, 64, 128, 0
|
36
|
+
# ]
|
37
|
+
#
|
38
|
+
# Note that each height value must be a number between 0 and 255, and each
|
39
|
+
# height value corresponds to a separate vertex (so, no worrying about RGBA).
|
40
|
+
# The height values will be automatically scaled to a value between 0 and 1,
|
41
|
+
# and then scaled again according to @y_scale, as with loading from an image.
|
42
|
+
#
|
43
|
+
RGB_SCALE = 1.0 / 16777215
|
44
|
+
Jax.getGlobal()['Heightmap'] = Jax.Model.create
|
45
|
+
# Returns the height map value at the given X or Z offset. Values may be
|
46
|
+
# fractional. If the values are negative or if they exceed the width or
|
47
|
+
# depth of this height map, they will be wrapped. If they are fractional,
|
48
|
+
# then a suitable height value between the floor and ceiling of the values
|
49
|
+
# will be calculated.
|
50
|
+
#
|
51
|
+
# Examples:
|
52
|
+
# hm = new Heightmap(width:2,depth:2,heights:[[0,1],[2,0]])
|
53
|
+
# hm.height( 0 , 0 ) #=> 0
|
54
|
+
# hm.height( 1 , 0 ) #=> 1
|
55
|
+
# hm.height( 0 , 1 ) #=> 2
|
56
|
+
# hm.height(-1 , 0 ) #=> 1
|
57
|
+
# hm.height( 3 , 0 ) #=> 1
|
58
|
+
# hm.height( 0.5, 0 ) #=> 0.5
|
59
|
+
# hm.height( 0 , 0.75) #=> 1.5
|
60
|
+
#
|
61
|
+
height: (x, z) ->
|
62
|
+
if @heights == undefined then return 0 # heightmap hasn't loaded yet
|
63
|
+
|
64
|
+
x %= @width
|
65
|
+
z %= @depth
|
66
|
+
x = @width + x if x < 0
|
67
|
+
z = @depth + z if z < 0
|
68
|
+
|
69
|
+
[fracX, fracZ] = [x % 1, z % 1]
|
70
|
+
x = Math.floor x if fracX
|
71
|
+
z = Math.floor z if fracZ
|
72
|
+
|
73
|
+
p = (x+z*@width)*4
|
74
|
+
# read RGB, ignoring alpha
|
75
|
+
y = (@heights[p] * 65536 + @heights[p+1] * 256 + @heights[p+2]) * RGB_SCALE * @y_scale
|
76
|
+
return y unless fracX || fracZ
|
77
|
+
|
78
|
+
[h, h2, h3, h4] = [y, @height(x+1, z)-y, @height(x, z+1)-y, @height(x+1,z+1)-y]
|
79
|
+
y = h2 * fracX + h3 * fracZ
|
80
|
+
if fracX && fracZ
|
81
|
+
y += (h4 + h2 - h3) * 0.5 * fracX
|
82
|
+
y += (h4 + h3 - h2) * 0.5 * fracZ
|
83
|
+
y /= 3
|
84
|
+
h + y
|
85
|
+
|
86
|
+
after_initialize: ->
|
87
|
+
if @path
|
88
|
+
@img = new Image
|
89
|
+
@img.onload = =>
|
90
|
+
@loaded = true
|
91
|
+
c = document.createElement 'canvas'
|
92
|
+
[c.width, c.height] = [@img.width, @img.height]
|
93
|
+
c2d = c.getContext '2d'
|
94
|
+
c2d.drawImage @img, 0, 0
|
95
|
+
img = c2d.getImageData 0, 0, @img.width, @img.height
|
96
|
+
@width = img.width
|
97
|
+
@depth = img.height
|
98
|
+
@heights = img.data
|
99
|
+
@mesh.rebuild()
|
100
|
+
@img.src = @path
|
101
|
+
else if @width && @depth && @heights
|
102
|
+
heights = @heights
|
103
|
+
@heights = []
|
104
|
+
# modify data for RGBA compatibility
|
105
|
+
#
|
106
|
+
# this is a tradeoff -- slightly more memory for
|
107
|
+
# much faster image loading, since images will
|
108
|
+
# be much bigger and much more common. Note the only
|
109
|
+
# alternative is to iterate through each image pixel
|
110
|
+
# and convert it to a straight value, much like below,
|
111
|
+
# but way slower since images are bigger and more common.
|
112
|
+
for h in heights
|
113
|
+
if h instanceof Array
|
114
|
+
@heights.push v, v, v, v for v in h
|
115
|
+
else
|
116
|
+
@heights.push h, h, h, h
|
117
|
+
@loaded = true
|
118
|
+
else
|
119
|
+
throw new Error "Heightmap requires a path!"
|
120
|
+
|
121
|
+
@mesh = new Jax.Mesh
|
122
|
+
default_material: @material
|
123
|
+
draw_mode: GL_TRIANGLE_STRIP
|
124
|
+
init: (vertices, colors, texcoords, normals, indices) =>
|
125
|
+
if @loaded
|
126
|
+
# since we know some details about the map, this is much
|
127
|
+
# faster than the default Jax normal calcs, which must find
|
128
|
+
# and then average all adjacent face normals for any given
|
129
|
+
# vertex.
|
130
|
+
[r, l, t, b, h, v, n] = [vec3.create(),vec3.create(),vec3.create(),
|
131
|
+
vec3.create(),vec3.create(),vec3.create(),
|
132
|
+
vec3.create()]
|
133
|
+
calculateNormal = (x,z) =>
|
134
|
+
[r[0],r[1],r[2]] = [(x+1)*@xz_scale, @height(x+1,z), z*@xz_scale]
|
135
|
+
[l[0],l[1],l[2]] = [(x-1)*@xz_scale, @height(x-1,z), z*@xz_scale]
|
136
|
+
[t[0],t[1],t[2]] = [x*@xz_scale, @height(x,z+1), (z+1)*@xz_scale]
|
137
|
+
[b[0],b[1],b[2]] = [x*@xz_scale, @height(x,z-1), (z-1)*@xz_scale]
|
138
|
+
|
139
|
+
vec3.normalize vec3.subtract r, l, h
|
140
|
+
vec3.normalize vec3.subtract t, b, v
|
141
|
+
vec3.normalize vec3.cross v, h, n
|
142
|
+
normals.push n[0], n[1], n[2]
|
143
|
+
|
144
|
+
for x in [0..(@width-2)] by 2
|
145
|
+
index_base = vertices.length / 3
|
146
|
+
for z in [0..@depth]
|
147
|
+
indices.push index_base+z*3+1, index_base+z*3
|
148
|
+
vertices.push x *@xz_scale, @height(x , z), z*@xz_scale
|
149
|
+
vertices.push (x+1)*@xz_scale, @height(x+1, z), z*@xz_scale
|
150
|
+
vertices.push (x+2)*@xz_scale, @height(x+2, z), z*@xz_scale
|
151
|
+
texcoords.push x / @width, z / @depth
|
152
|
+
texcoords.push (x+1) / @width, z / @depth
|
153
|
+
texcoords.push (x+2) / @width, z / @depth
|
154
|
+
calculateNormal x, z
|
155
|
+
calculateNormal x+1, z
|
156
|
+
calculateNormal x+2, z
|
157
|
+
|
158
|
+
# reverse direction of z every other x, so triangle strip renders properly
|
159
|
+
for z in [(@depth)..0]
|
160
|
+
indices.push index_base+z*3+1, index_base+z*3+2
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require_dependency 'fractal'
|
2
|
+
|
3
|
+
class Fractal::FractalsController < ApplicationController
|
4
|
+
before_filter :set_default_parameters
|
5
|
+
before_filter :validate_dimensions
|
6
|
+
|
7
|
+
# Fields:
|
8
|
+
#
|
9
|
+
# id - the random seed for this fractal. Required.
|
10
|
+
# width - the width of this fractal. Default: 128
|
11
|
+
# height - the height of this fractal. Default: 128
|
12
|
+
# smoothness - the smoothness of this fractal. Lower values produce more jagged / turbulent
|
13
|
+
# results, higher values produce smoother results. Default: 2
|
14
|
+
# high_color - the hex color code to use for high intensity values. Default: "ffffff"
|
15
|
+
# low_color - the hex color code to use for low intensity values. Default: "000000"
|
16
|
+
# alpha - if true, an alpha channel will be added. Lower intensity values will be more
|
17
|
+
# transparent. Default: false
|
18
|
+
# island - if true, an "island" fractal will be generated, such that its borders are
|
19
|
+
# guaranteed to have intensity values equal to 0. Default: false
|
20
|
+
#
|
21
|
+
def show
|
22
|
+
cache_key = File.join(params[:id], params[:width].to_s, params[:height].to_s,
|
23
|
+
params[:smoothness].to_s, params[:alpha].to_s,
|
24
|
+
params[:high_color].to_s, params[:low_color].to_s,
|
25
|
+
params[:island].to_s)
|
26
|
+
|
27
|
+
unless data = Rails.cache.read(cache_key)
|
28
|
+
# The proc ensures that the image leaves scope prior to garbage collection,
|
29
|
+
# thus ensuring that it will actually be collected. This is all to prevent
|
30
|
+
# a memory leak, detailed here:
|
31
|
+
# http://rubyforge.org/forum/forum.php?thread_id=1374&forum_id=1618
|
32
|
+
proc {
|
33
|
+
image = generate_image
|
34
|
+
data = image.to_blob { self.format = 'PNG' }
|
35
|
+
image.destroy!
|
36
|
+
}.call
|
37
|
+
GC.start
|
38
|
+
|
39
|
+
Rails.cache.write cache_key, data
|
40
|
+
end
|
41
|
+
|
42
|
+
send_data data, :filename => "#{params[:id]}.png", :type => "image/png",
|
43
|
+
:disposition => 'inline'
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
def generate_image
|
48
|
+
Fractal::Generator.image(params.merge(:seed => params[:id].to_i))
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_default_parameters
|
52
|
+
params[:width] = params[:width].blank? ? default_dimensions[:width] : params[:width].to_i
|
53
|
+
params[:height] = params[:height].blank? ? default_dimensions[:height] : params[:height].to_i
|
54
|
+
params[:smoothness] = params[:smoothness].blank? ? 2 : params[:smoothness].to_f
|
55
|
+
params[:smoothness] = 2 if params[:smoothness] <= 0
|
56
|
+
params[:alpha] = false unless params.key?(:alpha)
|
57
|
+
params[:island] = false unless params.key?(:island)
|
58
|
+
end
|
59
|
+
|
60
|
+
def default_dimensions
|
61
|
+
{ :width => 128, :height => 128 }
|
62
|
+
end
|
63
|
+
|
64
|
+
def validate_dimensions
|
65
|
+
if (requested_width = params[:width]) > Fractal.max_size
|
66
|
+
params[:width] = Fractal.max_size
|
67
|
+
log = true
|
68
|
+
end
|
69
|
+
if (requested_height = params[:height]) > Fractal.max_size
|
70
|
+
params[:height] = Fractal.max_size
|
71
|
+
log = true
|
72
|
+
end
|
73
|
+
if log
|
74
|
+
logger.warn <<-end_warning
|
75
|
+
Requested dimensions #{requested_width}x#{requested_height} exceed maximum size
|
76
|
+
#{Fractal.max_size}x#{Fractal.max_size}. If you need a larger fractal,
|
77
|
+
create a file called config/initializers/fractals.rb and set
|
78
|
+
|
79
|
+
Fractal.max_size = [some acceptable number]
|
80
|
+
|
81
|
+
Or, set
|
82
|
+
|
83
|
+
Fractal.max_size = nil
|
84
|
+
|
85
|
+
...to remove limits altogether (not recommended, as large fractals
|
86
|
+
can take a long time to generate!)
|
87
|
+
|
88
|
+
The safest way to use very large fractals is just to preprocess them:
|
89
|
+
|
90
|
+
rails g fractal
|
91
|
+
|
92
|
+
end_warning
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "fractal/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "jax-fractals"
|
7
|
+
s.version = Fractal::VERSION
|
8
|
+
s.authors = ["Colin MacKenzie IV"]
|
9
|
+
s.email = ["sinisterchipmunk@gmail.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Adds a fractal generator, and corresponding controller for Jax projects}
|
12
|
+
s.description = %q{Adds a fractal generator, and corresponding controller for Jax projects. Also adds a Heightmap model for Jax.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "jax-fractals"
|
15
|
+
|
16
|
+
s.add_dependency 'jax', '~> 2.0.6'
|
17
|
+
s.add_dependency 'rmagick', '~> 2.13.1'
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Fixnum
|
2
|
+
def pot?
|
3
|
+
(self & (self - 1)) == 0
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
module Math
|
8
|
+
module_function
|
9
|
+
|
10
|
+
def max(a, b)
|
11
|
+
a > b ? a : b
|
12
|
+
end
|
13
|
+
|
14
|
+
def min(a, b)
|
15
|
+
a > b ? b : a
|
16
|
+
end
|
17
|
+
|
18
|
+
def pot(x)
|
19
|
+
pot = 1
|
20
|
+
pot *= 2 until pot >= x
|
21
|
+
pot
|
22
|
+
end
|
23
|
+
end
|
data/lib/fractal.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
begin
|
2
|
+
require 'rmagick'
|
3
|
+
rescue LoadError
|
4
|
+
raise "RMagick not found! Make sure `gem 'rmagick', '~> 2.13.1'` is in your Gemfile"
|
5
|
+
end
|
6
|
+
|
7
|
+
require File.expand_path('core_ext/math', File.dirname(__FILE__))
|
8
|
+
|
9
|
+
module Fractal
|
10
|
+
require File.expand_path("fractal/engine", File.dirname(__FILE__))
|
11
|
+
|
12
|
+
autoload :Map, File.expand_path("fractal/map", File.dirname(__FILE__))
|
13
|
+
autoload :Generator, File.expand_path("fractal/generator", File.dirname(__FILE__))
|
14
|
+
autoload :Version, File.expand_path("fractal/version", File.dirname(__FILE__))
|
15
|
+
autoload :IslandGenerator, File.expand_path('fractal/island_generator', File.dirname(__FILE__))
|
16
|
+
|
17
|
+
class << self
|
18
|
+
attr_accessor :max_size
|
19
|
+
end
|
20
|
+
|
21
|
+
self.max_size = 1024
|
22
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
class Fractal::Generator
|
2
|
+
class << self
|
3
|
+
def image(options = {})
|
4
|
+
options.reverse_merge! default_image_options
|
5
|
+
klass = options[:island] ? Fractal::IslandGenerator : self
|
6
|
+
|
7
|
+
fractal = klass.new options
|
8
|
+
|
9
|
+
if options[:alpha]
|
10
|
+
image = Magick::Image.new(fractal.width, fractal.height) { self.background_color = 'transparent' }
|
11
|
+
image.import_pixels 0, 0, fractal.width, fractal.height, 'IA', fractal.bytes.collect { |b| [b,b] }.flatten.pack('C*')
|
12
|
+
else
|
13
|
+
image = Magick::Image.new(fractal.width, fractal.height)
|
14
|
+
image.import_pixels 0, 0, fractal.width, fractal.height, 'I', fractal.bytes.pack('C*')
|
15
|
+
end
|
16
|
+
|
17
|
+
options[:low_color] = '000000' if options[:low_color].blank?
|
18
|
+
options[:high_color] = 'ffffff' if options[:high_color].blank?
|
19
|
+
image = image.level_colors("##{options[:low_color]}", "##{options[:high_color]}", true)
|
20
|
+
image
|
21
|
+
end
|
22
|
+
|
23
|
+
def default_image_options
|
24
|
+
{ :alpha => false, :island => false, :high_color => 'ffffff', :low_color => '000000' }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
INITIAL_RANGE = 400
|
29
|
+
include Math
|
30
|
+
attr_reader :map, :random, :smoothness, :seed
|
31
|
+
|
32
|
+
def width; map.width; end
|
33
|
+
def height; map.height; end
|
34
|
+
def bytes; map.bytes; end
|
35
|
+
|
36
|
+
def initialize(options = {})
|
37
|
+
options.reverse_merge! default_options
|
38
|
+
width, height = options[:width], options[:height]
|
39
|
+
@map = Fractal::Map.new(pot(max(width, height)) + 1)
|
40
|
+
@random = options[:seed] ? Random.new(options[:seed]) : Random.new
|
41
|
+
@seed = @random.seed
|
42
|
+
@smoothness = options[:smoothness] || 2
|
43
|
+
generate
|
44
|
+
@map.truncate(width, height)
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_s
|
48
|
+
"".tap do |result|
|
49
|
+
for x in 0...width
|
50
|
+
for y in 0...height
|
51
|
+
result.concat map[x, y].to_s[0..6].rjust(7)
|
52
|
+
result.concat " "
|
53
|
+
end
|
54
|
+
result.concat "\n"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
def default_options
|
61
|
+
{
|
62
|
+
:width => 128,
|
63
|
+
:height => 128,
|
64
|
+
:smoothness => 2
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
# Seed initial values, then return [step, range]
|
69
|
+
def sow_seeds
|
70
|
+
map[ 0, 0] ||= 128
|
71
|
+
map[ 0, height-1] ||= 128
|
72
|
+
map[width-1, 0] ||= 128
|
73
|
+
map[width-1, height-1] ||= 128
|
74
|
+
|
75
|
+
[width - 1, INITIAL_RANGE]
|
76
|
+
end
|
77
|
+
|
78
|
+
def compute(x, y, points, range)
|
79
|
+
c = map[x, y] || 0
|
80
|
+
4.times do |i|
|
81
|
+
if points[i][0] < 0 then points[i][0] += (width - 1)
|
82
|
+
elsif points[i][0] > width then points[i][0] -= (width - 1)
|
83
|
+
elsif points[i][1] < 0 then points[i][1] += (height - 1)
|
84
|
+
elsif points[i][1] > height then points[i][1] -= (height - 1)
|
85
|
+
end
|
86
|
+
c += map[points[i][0], points[i][1]] * 0.25
|
87
|
+
end
|
88
|
+
|
89
|
+
c += random.rand() * range - range / 2.0
|
90
|
+
if c < 0 then c = 0
|
91
|
+
elsif c > 255 then c = 255
|
92
|
+
end
|
93
|
+
|
94
|
+
c = c.to_i
|
95
|
+
map[x, y] = c
|
96
|
+
if x == 0 then map[width-1, y] = c
|
97
|
+
elsif x == width-1 then map[0, y] = c
|
98
|
+
elsif y == 0 then map[x, height-1] = c
|
99
|
+
elsif y == height-1 then map[x, 0] = c
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
def half(step)
|
105
|
+
step >> 1
|
106
|
+
end
|
107
|
+
|
108
|
+
def diamond(step, range)
|
109
|
+
halfstep = half step
|
110
|
+
(0...(width-1)).step step do |x|
|
111
|
+
(0...(height-1)).step step do |y|
|
112
|
+
sx = x + halfstep
|
113
|
+
sy = y + halfstep
|
114
|
+
points = [ [x, y], [x+step, y], [x, y+step], [x+step, y+step] ]
|
115
|
+
compute sx, sy, points, range
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def square(step, range)
|
121
|
+
halfstep = half step
|
122
|
+
(0...(width-1)).step step do |x|
|
123
|
+
(0...(height-1)).step step do |y|
|
124
|
+
x1 = x + halfstep
|
125
|
+
y1 = y
|
126
|
+
x2 = x
|
127
|
+
y2 = y + halfstep
|
128
|
+
points1 = [ [x1 - halfstep, y1], [x1, y1 - halfstep], [x1 + halfstep, y1], [x1, y1 + halfstep] ]
|
129
|
+
points2 = [ [x2 - halfstep, y2], [x2, y2 - halfstep], [x2 + halfstep, y2], [x2, y2 + halfstep] ]
|
130
|
+
compute x1, y1, points1, range
|
131
|
+
compute x2, y2, points2, range
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def generate
|
137
|
+
step, range = *sow_seeds
|
138
|
+
|
139
|
+
while step > 1
|
140
|
+
diamond step, range
|
141
|
+
square step, range
|
142
|
+
range /= smoothness
|
143
|
+
step >>= 1
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Generates fractals which are guaranteed to have border pixels set to 0
|
2
|
+
class Fractal::IslandGenerator < Fractal::Generator
|
3
|
+
protected
|
4
|
+
def sow_seeds
|
5
|
+
step, range = *super
|
6
|
+
map[ 0, 0] = 0
|
7
|
+
map[ 0, height-1] = 0
|
8
|
+
map[width-1, 0] = 0
|
9
|
+
map[width-1, height-1] = 0
|
10
|
+
|
11
|
+
# we can multiply range by 2 to maintain brightness
|
12
|
+
# because we are effectively cutting area in half by
|
13
|
+
# setting borders to 0 -- we must do this if we want
|
14
|
+
# max brightness == 255, instead of 128
|
15
|
+
[step, range*2]
|
16
|
+
end
|
17
|
+
|
18
|
+
def compute(x, y, points, range)
|
19
|
+
# set borders to 0, but center to 128
|
20
|
+
if x == (width-1)/2 && y == (width-1/2)
|
21
|
+
map[x, y] = 128
|
22
|
+
elsif x == 0 || x == width-1
|
23
|
+
map[0, y] = 0
|
24
|
+
map[width-1, y] = 0
|
25
|
+
elsif y == 0 || y == height-1
|
26
|
+
map[x, 0] = 0
|
27
|
+
map[x, height-1] = 0
|
28
|
+
else
|
29
|
+
super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/fractal/map.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
class Fractal::Map < Array
|
2
|
+
attr_accessor :width, :height
|
3
|
+
alias :row :[]
|
4
|
+
|
5
|
+
class Line < Array
|
6
|
+
def initialize(count)
|
7
|
+
super count, nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def [](a)
|
11
|
+
if a.kind_of?(Numeric)
|
12
|
+
raise "Out of bounds: #{a} / #{length}" if a < 0 || a >= length
|
13
|
+
end
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def []=(a, *)
|
18
|
+
raise "Out of bounds: #{a} / #{length}" if a < 0 || a >= length
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(size)
|
24
|
+
@width = @height = size
|
25
|
+
super(size) { Fractal::Map::Line.new(size) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def truncate(width, height)
|
29
|
+
pop while length > height
|
30
|
+
collect! { |line| line[0...width] }
|
31
|
+
@width, @height = width, height
|
32
|
+
end
|
33
|
+
|
34
|
+
def [](x, y)
|
35
|
+
raise "Out of bounds: #{y} / #{@height}" if y < 0 || y >= @height
|
36
|
+
super(y)[x]
|
37
|
+
end
|
38
|
+
|
39
|
+
def []=(x, y, value)
|
40
|
+
row(y)[x] = value
|
41
|
+
end
|
42
|
+
|
43
|
+
# Encodes the map as a grayscale bitmap, with a color depth of 8 bits per pixel.
|
44
|
+
#
|
45
|
+
# Returns the bitmap as an array of bytes.
|
46
|
+
def bytes
|
47
|
+
flatten.pack("C*").bytes.to_a
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'fractal'
|
2
|
+
|
3
|
+
class FractalGenerator < Rails::Generators::NamedBase
|
4
|
+
class_option :seed, :default => nil, :desc => "Random seed for the fractal", :type => :numeric
|
5
|
+
class_option :width, :default => 128, :desc => "Width of the fractal image in pixels", :type => :numeric
|
6
|
+
class_option :height, :default => 128, :desc => "Height of the fractal image in pixels", :type => :numeric
|
7
|
+
class_option :smoothness, :default => 2, :desc => "Smoothness factor (higher is smoother)", :type => :numeric
|
8
|
+
class_option :dest, :default => "app/assets", :desc => "Where to place generated fractal", :type => :string
|
9
|
+
class_option :high_color, :default => 'ffffff', :desc => "Color to use for high intensity values", :type => :string
|
10
|
+
class_option :low_color, :default => '000000', :desc => "Color to use for low intensity values", :type => :string
|
11
|
+
class_option :alpha, :default => false, :desc => "Whether to save transparency data", :type => :boolean
|
12
|
+
class_option :island, :default => false, :desc => "Whether the borders should be guaranteed to have 0 intensity", :type => :boolean
|
13
|
+
|
14
|
+
def generate_fractal
|
15
|
+
create_file File.join(options[:dest], 'images/fractals', "#{name}.png"),
|
16
|
+
Fractal::Generator.image(options).to_blob { self.format = 'PNG' }
|
17
|
+
end
|
18
|
+
end
|
data/lib/jax-fractals.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.expand_path('fractal', File.dirname(__FILE__))
|
data/screenshots/1.png
ADDED
Binary file
|
data/screenshots/3.png
ADDED
Binary file
|
data/screenshots/4.png
ADDED
Binary file
|
data/screenshots/hm.png
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,67 @@
|
|
1
|
+
describe "Heightmap", ->
|
2
|
+
model = null
|
3
|
+
|
4
|
+
describe "while still loading", ->
|
5
|
+
beforeEach ->
|
6
|
+
model = new Heightmap(path: "/fractals/1")
|
7
|
+
|
8
|
+
it "should return height 0", ->
|
9
|
+
expect(model.heights(5, 5)).toEqual 0
|
10
|
+
|
11
|
+
describe "loaded from a path", ->
|
12
|
+
beforeEach ->
|
13
|
+
model = new Heightmap(path: "/fractals/1")
|
14
|
+
|
15
|
+
it "should load the model", ->
|
16
|
+
waitsFor -> model.loaded
|
17
|
+
|
18
|
+
describe "with RGB, not grayscale", ->
|
19
|
+
beforeEach ->
|
20
|
+
model = new Heightmap(width: 1, depth: 1, heights: [[1]], y_scale: 16777215)
|
21
|
+
# HACK no good way to do this, but really it's not meant to be done
|
22
|
+
model.heights[0] = 1 # R
|
23
|
+
model.heights[1] = 2 # G
|
24
|
+
model.heights[2] = 3 # B
|
25
|
+
model.heights[3] = 4 # A
|
26
|
+
|
27
|
+
it "should sample RGB and ignore A", ->
|
28
|
+
expected_height = 1 * 65536 + 2 * 256 + 3 # RGB, ignoring A
|
29
|
+
expect(model.height(0, 0)).toEqual expected_height
|
30
|
+
|
31
|
+
describe "defined in-line", ->
|
32
|
+
beforeEach ->
|
33
|
+
model = new Heightmap(width: 4, depth: 2, heights: [ [ 0, 4, 0, 0 ], [2, 3, 0, 0] ], y_scale: 255)
|
34
|
+
|
35
|
+
it "should return the expected height data", ->
|
36
|
+
expect(model.height(0, 0)).toEqual 0
|
37
|
+
expect(model.height(1, 0)).toEqual 4
|
38
|
+
expect(model.height(2, 0)).toEqual 0
|
39
|
+
expect(model.height(3, 0)).toEqual 0
|
40
|
+
expect(model.height(0, 1)).toEqual 2
|
41
|
+
expect(model.height(1, 1)).toEqual 3
|
42
|
+
expect(model.height(2, 1)).toEqual 0
|
43
|
+
expect(model.height(3, 1)).toEqual 0
|
44
|
+
|
45
|
+
it "should wrap height indices out of bounds", ->
|
46
|
+
# X
|
47
|
+
expect(model.height(-3, 0)).toEqual 4
|
48
|
+
expect(model.height(-7, 0)).toEqual 4
|
49
|
+
expect(model.height( 5, 0)).toEqual 4
|
50
|
+
expect(model.height( 9, 0)).toEqual 4
|
51
|
+
# Z
|
52
|
+
expect(model.height(0, -1)).toEqual 2
|
53
|
+
expect(model.height(0, -3)).toEqual 2
|
54
|
+
expect(model.height(0, 2)).toEqual 0
|
55
|
+
expect(model.height(0, 3)).toEqual 2
|
56
|
+
expect(model.height(0, 4)).toEqual 0
|
57
|
+
|
58
|
+
it "should compensate for fractional X,Z components", ->
|
59
|
+
expect(model.height(0.5, 0)).toEqual 2
|
60
|
+
expect(model.height(0, 0.5)).toEqual 1
|
61
|
+
expect(model.height(0.5, 0.5)).toEqual 1.5
|
62
|
+
|
63
|
+
# Proximity -- should return approximately the same as x+1/z+1
|
64
|
+
expect(model.height(0.99999999, 0)).toBeGreaterThan 4-Math.EPSILON
|
65
|
+
expect(model.height(0, 0.99999999)).toBeGreaterThan 2-Math.EPSILON
|
66
|
+
expect(model.height(0.99999999, 0.99999999)).toBeGreaterThan 3-Math.EPSILON
|
67
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Fractal::Generator do
|
4
|
+
it "should truncate map by default" do
|
5
|
+
subject = Fractal::Generator.new(:width => 10, :height => 10)
|
6
|
+
subject.width.should == 10
|
7
|
+
subject.height.should == 10
|
8
|
+
subject.map.length.should == 10
|
9
|
+
subject.map.row(0).length.should == 10
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Fractal images" do
|
4
|
+
def subject(options = {})
|
5
|
+
Fractal::Generator.image(options.reverse_merge(:seed => 1)).to_blob { self.format = 'PNG' }
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should generate default image" do
|
9
|
+
subject.should == fractal('1')
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should generate with alpha" do
|
13
|
+
subject(:alpha => true).should == fractal('1?alpha=1')
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should truncate size" do
|
17
|
+
subject(:width => 126, :height => 130).should == fractal('1?width=126&height=130')
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should generate an island" do
|
21
|
+
subject(:island => true).should == fractal('1?island=1')
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should generate more jagged fractals" do
|
25
|
+
subject(:smoothness => 1.2).should == fractal('1?smoothness=1.2')
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should set high color" do
|
29
|
+
subject(:high_color => 'ff0000').should == fractal('1?high_color=ff0000')
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should set low color" do
|
33
|
+
subject(:low_color => '0000ff').should == fractal('1?low_color=0000ff')
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should set high and low color" do
|
37
|
+
subject(:high_color => 'ff0000', :low_color => '0000ff').should == fractal('1?low_color=0000ff&high_color=ff0000')
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Fractal::Map do
|
4
|
+
subject { Fractal::Map.new(32) }
|
5
|
+
|
6
|
+
it "should have size 32x32" do
|
7
|
+
subject.width.should == 32
|
8
|
+
subject.height.should == 32
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should have 32x32 elements" do
|
12
|
+
subject.flatten.length.should == 32*32
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should restrict bounds to 32x32" do
|
16
|
+
proc { subject[32, 0] }.should raise_error
|
17
|
+
proc { subject[32, 0] = 1 }.should raise_error
|
18
|
+
proc { subject[0, 32] }.should raise_error
|
19
|
+
proc { subject[0, 32] = 1 }.should raise_error
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should not allow assignment to first dimension" do
|
23
|
+
proc { subject[0] = [] }.should raise_error
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should truncate size" do
|
27
|
+
subject.truncate 10, 15
|
28
|
+
subject.width.should == 10
|
29
|
+
subject.height.should == 15
|
30
|
+
subject.length.should == 15
|
31
|
+
subject.row(0).length.should == 10
|
32
|
+
end
|
33
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$:.unshift File.expand_path('../lib', File.dirname(__FILE__))
|
2
|
+
require 'jax-fractals'
|
3
|
+
|
4
|
+
module Fixtures
|
5
|
+
def fractal(name)
|
6
|
+
File.read(File.expand_path(File.join("fixtures/fractals", name), File.dirname(__FILE__))).force_encoding('BINARY')
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
RSpec.configure do |c|
|
11
|
+
c.include Fixtures
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jax-fractals
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 1.0.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Colin MacKenzie IV
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2012-01-08 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: jax
|
17
|
+
prerelease: false
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ~>
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 2.0.6
|
24
|
+
type: :runtime
|
25
|
+
version_requirements: *id001
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rmagick
|
28
|
+
prerelease: false
|
29
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
30
|
+
none: false
|
31
|
+
requirements:
|
32
|
+
- - ~>
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 2.13.1
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id002
|
37
|
+
description: Adds a fractal generator, and corresponding controller for Jax projects. Also adds a Heightmap model for Jax.
|
38
|
+
email:
|
39
|
+
- sinisterchipmunk@gmail.com
|
40
|
+
executables: []
|
41
|
+
|
42
|
+
extensions: []
|
43
|
+
|
44
|
+
extra_rdoc_files: []
|
45
|
+
|
46
|
+
files:
|
47
|
+
- .gitignore
|
48
|
+
- Gemfile
|
49
|
+
- README.md
|
50
|
+
- Rakefile
|
51
|
+
- app/assets/jax/models/heightmap.js.coffee
|
52
|
+
- app/assets/jax/resources/heightmaps/default.resource
|
53
|
+
- app/controllers/fractal/fractals_controller.rb
|
54
|
+
- jax-fractals.gemspec
|
55
|
+
- lib/core_ext/math.rb
|
56
|
+
- lib/fractal.rb
|
57
|
+
- lib/fractal/engine.rb
|
58
|
+
- lib/fractal/generator.rb
|
59
|
+
- lib/fractal/island_generator.rb
|
60
|
+
- lib/fractal/map.rb
|
61
|
+
- lib/fractal/version.rb
|
62
|
+
- lib/generators/fractal/USAGE
|
63
|
+
- lib/generators/fractal/fractal_generator.rb
|
64
|
+
- lib/jax-fractals.rb
|
65
|
+
- screenshots/1.png
|
66
|
+
- screenshots/3.png
|
67
|
+
- screenshots/4.png
|
68
|
+
- screenshots/hm.png
|
69
|
+
- spec/fixtures/fractals/1
|
70
|
+
- spec/fixtures/fractals/1?alpha=1
|
71
|
+
- spec/fixtures/fractals/1?high_color=ff0000
|
72
|
+
- spec/fixtures/fractals/1?island=1
|
73
|
+
- spec/fixtures/fractals/1?low_color=0000ff
|
74
|
+
- spec/fixtures/fractals/1?low_color=0000ff&high_color=ff0000
|
75
|
+
- spec/fixtures/fractals/1?smoothness=1.2
|
76
|
+
- spec/fixtures/fractals/1?width=126&height=130
|
77
|
+
- spec/javascripts/jax/models/heightmap_spec.js.coffee
|
78
|
+
- spec/lib/fractal/generator_spec.rb
|
79
|
+
- spec/lib/fractal/images_spec.rb
|
80
|
+
- spec/lib/fractal/map_spec.rb
|
81
|
+
- spec/spec_helper.rb
|
82
|
+
homepage: ""
|
83
|
+
licenses: []
|
84
|
+
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
|
88
|
+
require_paths:
|
89
|
+
- lib
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: "0"
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: "0"
|
102
|
+
requirements: []
|
103
|
+
|
104
|
+
rubyforge_project: jax-fractals
|
105
|
+
rubygems_version: 1.8.10
|
106
|
+
signing_key:
|
107
|
+
specification_version: 3
|
108
|
+
summary: Adds a fractal generator, and corresponding controller for Jax projects
|
109
|
+
test_files:
|
110
|
+
- spec/fixtures/fractals/1
|
111
|
+
- spec/fixtures/fractals/1?alpha=1
|
112
|
+
- spec/fixtures/fractals/1?high_color=ff0000
|
113
|
+
- spec/fixtures/fractals/1?island=1
|
114
|
+
- spec/fixtures/fractals/1?low_color=0000ff
|
115
|
+
- spec/fixtures/fractals/1?low_color=0000ff&high_color=ff0000
|
116
|
+
- spec/fixtures/fractals/1?smoothness=1.2
|
117
|
+
- spec/fixtures/fractals/1?width=126&height=130
|
118
|
+
- spec/javascripts/jax/models/heightmap_spec.js.coffee
|
119
|
+
- spec/lib/fractal/generator_spec.rb
|
120
|
+
- spec/lib/fractal/images_spec.rb
|
121
|
+
- spec/lib/fractal/map_spec.rb
|
122
|
+
- spec/spec_helper.rb
|