snowy 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.
- checksums.yaml +7 -0
- data/LICENSE +24 -0
- data/README.md +41 -0
- data/Rakefile +212 -0
- data/gemstub.rb +25 -0
- data/lib/snowy.rb +529 -0
- data/lib/snowy/cairo.rb +50 -0
- data/lib/snowy/common.rb +154 -0
- data/snowy-demo.png +0 -0
- data/snowy-demo.rb +50 -0
- metadata +77 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 88efa9809a87f18c9a1a0cac09ac97cad8b79517
|
4
|
+
data.tar.gz: 297a08a7dd949d0da1bc7082c4da98c697de0f56
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 87702bc961ce392404cad01d2ce482c1fe6c30ecad2cbe8c3739fd695c785d84288c078da64c3d699ce607c12438ffe0460900238f54db4303f2650ca330543c
|
7
|
+
data.tar.gz: 72e89848974cb49e0e750a9d8bff96986428ee2954fde7c0bff10133d96d6f57fa2f6230dab63311fd0abbcef1df51c6965bab8c68682a108771b9b8ea779de2
|
data/LICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Copyright (c) 2016, dearblue. All rights reserved.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or
|
4
|
+
without modification, are permitted provided that the following
|
5
|
+
conditions are met:
|
6
|
+
|
7
|
+
1. Redistributions of source code must retain the above copyright
|
8
|
+
notice, this list of conditions and the following disclaimer.
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright
|
10
|
+
notice, this list of conditions and the following disclaimer in
|
11
|
+
the documentation and/or other materials provided with the
|
12
|
+
distribution.
|
13
|
+
|
14
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
15
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
16
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
17
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
18
|
+
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
19
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
20
|
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
21
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
22
|
+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
23
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
24
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# snowy
|
2
|
+
|
3
|
+
"snowy" is an identicon implements with the snow crystal motif.
|
4
|
+
|
5
|
+
* package name: snowy
|
6
|
+
* version: 0.1
|
7
|
+
* software quality: EXPERIMENTAL
|
8
|
+
* license: BSD-2-clause License
|
9
|
+
* author: dearblue <dearblue@users.noreply.github.com>
|
10
|
+
* report issue to: <https://github.com/dearblue/ruby-snowy/issues/>
|
11
|
+
* dependency ruby: ruby-2.1+
|
12
|
+
* dependency ruby gems: (none)
|
13
|
+
* dependency library: (none)
|
14
|
+
* bundled external C library: (none)
|
15
|
+
|
16
|
+

|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
## How to usage
|
21
|
+
|
22
|
+
``` ruby:ruby
|
23
|
+
require "snowy"
|
24
|
+
require "zlib"
|
25
|
+
|
26
|
+
str = "abcdefg"
|
27
|
+
salt = 0
|
28
|
+
id = Zlib.crc32(str, salt) # transcode to 32-bits integer
|
29
|
+
pngdata = Snowy.generate_to_png(id, size: 256)
|
30
|
+
File.binwrite("snowy.png", pngdata)
|
31
|
+
```
|
32
|
+
|
33
|
+
|
34
|
+
## Demonstration with web browser
|
35
|
+
|
36
|
+
``` shell
|
37
|
+
$ gem install sinatra haml
|
38
|
+
$ ruby snowy-demo.rb
|
39
|
+
```
|
40
|
+
|
41
|
+
And, access to http://localhost:4567/ on web browser.
|
data/Rakefile
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
|
2
|
+
require "pathname"
|
3
|
+
require "rake/clean"
|
4
|
+
|
5
|
+
docnames = "{README,LICENSE,CHANGELOG,Changelog,HISTORY}"
|
6
|
+
doctypes = "{,.txt,.rd,.rdoc,.md,.markdown}"
|
7
|
+
cexttypes = "{c,C,cc,cxx,cpp,h,H,hh}"
|
8
|
+
|
9
|
+
DOC = FileList["#{docnames}{,.ja}#{doctypes}"] +
|
10
|
+
FileList["{contrib,ext}/**/#{docnames}{,.ja}#{doctypes}"] +
|
11
|
+
FileList["ext/**/*.#{cexttypes}"]
|
12
|
+
EXT = FileList["ext/**/*"]
|
13
|
+
BIN = FileList["bin/*"]
|
14
|
+
LIB = FileList["lib/**/*.rb"]
|
15
|
+
SPEC = FileList["spec/**/*"]
|
16
|
+
TEST = FileList["test/**/*"]
|
17
|
+
EXAMPLE = FileList["examples/**/*"]
|
18
|
+
GEMSTUB_SRC = "gemstub.rb"
|
19
|
+
RAKEFILE = [File.basename(__FILE__), GEMSTUB_SRC]
|
20
|
+
EXTRA = []
|
21
|
+
EXTCONF = FileList["ext/**/extconf.rb"]
|
22
|
+
EXTCONF.reject! { |n| !File.file?(n) }
|
23
|
+
EXTMAP = {}
|
24
|
+
|
25
|
+
load GEMSTUB_SRC
|
26
|
+
|
27
|
+
EXTMAP.dup.each_pair do |dir, name|
|
28
|
+
EXTMAP[Pathname.new(dir).cleanpath.to_s] = Pathname.new(name).cleanpath.to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
GEMSTUB.extensions += EXTCONF
|
32
|
+
GEMSTUB.executables += FileList["bin/*"].map { |n| File.basename n }
|
33
|
+
GEMSTUB.executables.sort!
|
34
|
+
|
35
|
+
PACKAGENAME = "#{GEMSTUB.name}-#{GEMSTUB.version}"
|
36
|
+
GEMFILE = "#{PACKAGENAME}.gem"
|
37
|
+
GEMSPEC = "#{PACKAGENAME}.gemspec"
|
38
|
+
|
39
|
+
GEMSTUB.files += DOC + EXT + EXTCONF + BIN + LIB + SPEC + TEST + EXAMPLE + RAKEFILE + EXTRA
|
40
|
+
GEMSTUB.files.sort!
|
41
|
+
if GEMSTUB.rdoc_options.nil? || GEMSTUB.rdoc_options.empty?
|
42
|
+
readme = %W(.md .markdown .rd .rdoc .txt #{""}).map { |ext| "README#{ext}" }.find { |m| DOC.find { |n| n == m } }
|
43
|
+
GEMSTUB.rdoc_options = %w(--charset UTF-8) + (readme ? %W(-m #{readme}) : [])
|
44
|
+
end
|
45
|
+
GEMSTUB.extra_rdoc_files += DOC + LIB + EXT.reject { |n| n.include?("/externals/") || !%w(.h .hh .c .cc .cpp .cxx).include?(File.extname(n)) }
|
46
|
+
GEMSTUB.extra_rdoc_files.sort!
|
47
|
+
|
48
|
+
GEMSTUB_TRYOUT = GEMSTUB.dup
|
49
|
+
GEMSTUB_TRYOUT.version = "#{GEMSTUB.version}#{Time.now.strftime(".TRYOUT.%Y%m%d.%H%M%S")}"
|
50
|
+
PACKAGENAME_TRYOUT = "#{GEMSTUB.name}-#{GEMSTUB_TRYOUT.version}"
|
51
|
+
GEMFILE_TRYOUT = "#{PACKAGENAME_TRYOUT}.gem"
|
52
|
+
GEMSPEC_TRYOUT = "#{PACKAGENAME_TRYOUT}.gemspec"
|
53
|
+
|
54
|
+
CLEAN << GEMSPEC << GEMSPEC_TRYOUT
|
55
|
+
CLOBBER << GEMFILE
|
56
|
+
|
57
|
+
task :default => :tryout do
|
58
|
+
$stderr.puts <<-EOS
|
59
|
+
#{__FILE__}:#{__LINE__}:
|
60
|
+
\ttype ``rake release'' to build release package.
|
61
|
+
EOS
|
62
|
+
end
|
63
|
+
|
64
|
+
desc "build tryout package"
|
65
|
+
task :tryout
|
66
|
+
|
67
|
+
desc "build release package"
|
68
|
+
task :release => :all
|
69
|
+
|
70
|
+
unless EXTCONF.empty?
|
71
|
+
RUBYSET ||= (ENV["RUBYSET"] || "").split(",")
|
72
|
+
|
73
|
+
if RUBYSET.nil? || RUBYSET.empty?
|
74
|
+
$stderr.puts <<-EOS
|
75
|
+
#{__FILE__}:
|
76
|
+
|
|
77
|
+
| If you want binary gem package, launch rake with ``RUBYSET`` enviroment
|
78
|
+
| variable for set ruby interpreters by comma separated.
|
79
|
+
|
|
80
|
+
| e.g.) $ rake RUBYSET=ruby
|
81
|
+
| or) $ rake RUBYSET=ruby21,ruby22,ruby23
|
82
|
+
|
|
83
|
+
EOS
|
84
|
+
else
|
85
|
+
platforms = RUBYSET.map { |ruby| `#{ruby} --disable-gems -e "puts RUBY_PLATFORM"`.chomp }
|
86
|
+
platforms1 = platforms.uniq
|
87
|
+
unless platforms1.size == 1 && !platforms1[0].empty?
|
88
|
+
abort <<-EOS
|
89
|
+
#{__FILE__}:#{__LINE__}: different platforms:
|
90
|
+
#{RUBYSET.zip(platforms).map { |ruby, platform| "%24s => %s" % [ruby, platform] }.join("\n")}
|
91
|
+
ABORTED.
|
92
|
+
EOS
|
93
|
+
end
|
94
|
+
PLATFORM = platforms1[0]
|
95
|
+
|
96
|
+
RUBY_VERSIONS = RUBYSET.map do |ruby|
|
97
|
+
ver = `#{ruby} --disable-gems -e "puts RUBY_VERSION"`.slice(/\d+\.\d+/)
|
98
|
+
raise "failed ruby checking - ``#{ruby}''" unless $?.success?
|
99
|
+
[ver, ruby]
|
100
|
+
end
|
101
|
+
|
102
|
+
SOFILES_SET = RUBY_VERSIONS.map { |(ver, ruby)|
|
103
|
+
EXTCONF.map { |extconf|
|
104
|
+
extdir = Pathname.new(extconf).cleanpath.dirname.to_s
|
105
|
+
case
|
106
|
+
when soname = EXTMAP[extdir.sub(/^ext\//i, "")]
|
107
|
+
soname = soname.sub(/\.so$/i, "")
|
108
|
+
when extdir == "ext" || extdir == "."
|
109
|
+
soname = GEMSTUB.name
|
110
|
+
else
|
111
|
+
soname = File.basename(extdir)
|
112
|
+
end
|
113
|
+
|
114
|
+
[ruby, File.join("lib", "#{soname.sub(/(?<=\/)|^(?!.*\/)/, "#{ver}/")}.so"), extconf]
|
115
|
+
}
|
116
|
+
}.flatten(1)
|
117
|
+
SOFILES = SOFILES_SET.map { |(ruby, sopath, extconf)| sopath }
|
118
|
+
|
119
|
+
GEMSTUB_NATIVE = GEMSTUB.dup
|
120
|
+
GEMSTUB_NATIVE.files += SOFILES
|
121
|
+
GEMSTUB_NATIVE.platform = Gem::Platform.new(PLATFORM).to_s
|
122
|
+
GEMSTUB_NATIVE.extensions.clear
|
123
|
+
GEMFILE_NATIVE = "#{GEMSTUB_NATIVE.name}-#{GEMSTUB_NATIVE.version}-#{GEMSTUB_NATIVE.platform}.gem"
|
124
|
+
GEMSPEC_NATIVE = "#{GEMSTUB_NATIVE.name}-#{GEMSTUB_NATIVE.platform}.gemspec"
|
125
|
+
|
126
|
+
task :all => ["native-gem", GEMFILE]
|
127
|
+
|
128
|
+
desc "build binary gem package"
|
129
|
+
task "native-gem" => GEMFILE_NATIVE
|
130
|
+
|
131
|
+
desc "generate binary gemspec"
|
132
|
+
task "native-gemspec" => GEMSPEC_NATIVE
|
133
|
+
|
134
|
+
file GEMFILE_NATIVE => DOC + EXT + EXTCONF + BIN + LIB + SPEC + TEST + EXAMPLE + SOFILES + RAKEFILE + [GEMSPEC_NATIVE] do
|
135
|
+
sh "gem build #{GEMSPEC_NATIVE}"
|
136
|
+
end
|
137
|
+
|
138
|
+
file GEMSPEC_NATIVE => RAKEFILE do
|
139
|
+
File.write(GEMSPEC_NATIVE, GEMSTUB_NATIVE.to_ruby, mode: "wb")
|
140
|
+
end
|
141
|
+
|
142
|
+
desc "build c-extension libraries"
|
143
|
+
task "sofiles" => SOFILES
|
144
|
+
|
145
|
+
SOFILES_SET.each do |(ruby, soname, extconf)|
|
146
|
+
sodir = File.dirname(soname)
|
147
|
+
makefile = File.join(sodir, "Makefile")
|
148
|
+
|
149
|
+
CLEAN << GEMSPEC_NATIVE << sodir
|
150
|
+
CLOBBER << GEMFILE_NATIVE
|
151
|
+
|
152
|
+
directory sodir
|
153
|
+
|
154
|
+
desc "generate Makefile for binary extension library"
|
155
|
+
file makefile => [sodir, extconf] do
|
156
|
+
rel_extconf = Pathname.new(extconf).relative_path_from(Pathname.new(sodir)).to_s
|
157
|
+
cd sodir do
|
158
|
+
sh *%W"#{ruby} #{rel_extconf} --ruby=#{ruby} #{ENV["EXTCONF"]}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
desc "build binary extension library"
|
163
|
+
file soname => [makefile] + EXT do
|
164
|
+
cd sodir do
|
165
|
+
sh "make"
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
task :all => GEMFILE
|
174
|
+
task :tryout => GEMFILE_TRYOUT
|
175
|
+
|
176
|
+
desc "generate local rdoc"
|
177
|
+
task :rdoc => DOC + LIB do
|
178
|
+
sh *(%w(rdoc) + GEMSTUB.rdoc_options + DOC + LIB)
|
179
|
+
end
|
180
|
+
|
181
|
+
desc "launch rspec"
|
182
|
+
task rspec: :all do
|
183
|
+
sh "rspec"
|
184
|
+
end
|
185
|
+
|
186
|
+
desc "build gem package"
|
187
|
+
task gem: GEMFILE
|
188
|
+
|
189
|
+
desc "generate gemspec"
|
190
|
+
task gemspec: GEMSPEC
|
191
|
+
|
192
|
+
desc "print package name"
|
193
|
+
task "package-name" do
|
194
|
+
puts PACKAGENAME
|
195
|
+
end
|
196
|
+
|
197
|
+
file GEMFILE => DOC + EXT + EXTCONF + BIN + LIB + SPEC + TEST + EXAMPLE + RAKEFILE + [GEMSPEC] do
|
198
|
+
sh "gem build #{GEMSPEC}"
|
199
|
+
end
|
200
|
+
|
201
|
+
file GEMFILE_TRYOUT => DOC + EXT + EXTCONF + BIN + LIB + SPEC + TEST + EXAMPLE + RAKEFILE + [GEMSPEC_TRYOUT] do
|
202
|
+
#file GEMFILE_TRYOUT do
|
203
|
+
sh "gem build #{GEMSPEC_TRYOUT}"
|
204
|
+
end
|
205
|
+
|
206
|
+
file GEMSPEC => RAKEFILE do
|
207
|
+
File.write(GEMSPEC, GEMSTUB.to_ruby, mode: "wb")
|
208
|
+
end
|
209
|
+
|
210
|
+
file GEMSPEC_TRYOUT => RAKEFILE do
|
211
|
+
File.write(GEMSPEC_TRYOUT, GEMSTUB_TRYOUT.to_ruby, mode: "wb")
|
212
|
+
end
|
data/gemstub.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
verreg = /^\s*\*\s+version:\s*(\d+(?:\.\w+)+)\s*$/i
|
2
|
+
unless File.read("README.md", mode: "rt") =~ verreg
|
3
|
+
raise "``version'' is not defined or bad syntax in ``README.md''"
|
4
|
+
end
|
5
|
+
|
6
|
+
version = String($1)
|
7
|
+
|
8
|
+
GEMSTUB = Gem::Specification.new do |s|
|
9
|
+
s.name = "snowy"
|
10
|
+
s.version = version
|
11
|
+
s.summary = "an identicon implements"
|
12
|
+
s.description = <<EOS
|
13
|
+
Pure ruby identicon implement with the snow crystal motif
|
14
|
+
EOS
|
15
|
+
s.homepage = "https://github.com/dearblue/ruby-snowy/"
|
16
|
+
s.license = "BSD-2-Clause"
|
17
|
+
s.author = "dearblue"
|
18
|
+
s.email = "dearblue@users.noreply.github.com"
|
19
|
+
|
20
|
+
#s.required_ruby_version = ">= 2.1"
|
21
|
+
s.add_development_dependency "rake", "~> 11"
|
22
|
+
end
|
23
|
+
|
24
|
+
EXTRA << "snowy-demo.png"
|
25
|
+
EXTRA << "snowy-demo.rb"
|
data/lib/snowy.rb
ADDED
@@ -0,0 +1,529 @@
|
|
1
|
+
#!ruby
|
2
|
+
|
3
|
+
require_relative "snowy/common"
|
4
|
+
|
5
|
+
module Snowy
|
6
|
+
using Extentions
|
7
|
+
|
8
|
+
module DefaultDriver
|
9
|
+
def self.render(size, triangles, background, color, outline, angle)
|
10
|
+
img = Canvas.new(size, size, 0, 1, plt = [background, color])
|
11
|
+
plt << outline if outline
|
12
|
+
|
13
|
+
img.instance_exec do
|
14
|
+
translate(width / 2.0, height / 2.0)
|
15
|
+
scale(width / 32.0, height / 32.0)
|
16
|
+
rotate_deg(angle)
|
17
|
+
sqrt3 = Math.sqrt(3)
|
18
|
+
[30, 90, 150, 210, 270, 330].each do |deg|
|
19
|
+
push_matrix do
|
20
|
+
rotate_deg(deg)
|
21
|
+
scale(1, sqrt3)
|
22
|
+
triangles.each do |t|
|
23
|
+
triangle(*t)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# plot outline
|
29
|
+
if outline
|
30
|
+
w = width
|
31
|
+
h = height
|
32
|
+
pix = pixels
|
33
|
+
(1 ... (h - 1)).step(1) do |py|
|
34
|
+
py0 = py * w
|
35
|
+
py1 = (py + 1) * w
|
36
|
+
py2 = (py - 1) * w
|
37
|
+
(1 ... (w - 1)).step(1) do |px|
|
38
|
+
px1 = px + 1
|
39
|
+
px2 = px - 1
|
40
|
+
if pix.getbyte(py0 + px) == 0
|
41
|
+
if pix.getbyte(py0 + px1) == 1 ||
|
42
|
+
pix.getbyte(py0 + px2) == 1 ||
|
43
|
+
pix.getbyte(py1 + px ) == 1 ||
|
44
|
+
pix.getbyte(py2 + px ) == 1
|
45
|
+
pix.setbyte(py0 + px, 2)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
export_to_png
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
@@driver = DefaultDriver
|
57
|
+
|
58
|
+
|
59
|
+
BLACK = rgba(0, 0, 0)
|
60
|
+
WHITE = rgba(255, 255, 255)
|
61
|
+
|
62
|
+
#
|
63
|
+
# 256 indexed canvas
|
64
|
+
#
|
65
|
+
class Canvas
|
66
|
+
attr_reader :width, :height, :pixels, :matrix, :palette, :color
|
67
|
+
|
68
|
+
MIN_PIXEL = 1
|
69
|
+
MAX_PIXEL = 4096
|
70
|
+
|
71
|
+
def initialize(width, height, background = 0, color = 1, palette = [WHITE, BLACK])
|
72
|
+
@width = width.to_i
|
73
|
+
@height = height.to_i
|
74
|
+
if @width < MIN_PIXEL || @width > MAX_PIXEL ||
|
75
|
+
@height < MIN_PIXEL || @height > MAX_PIXEL
|
76
|
+
raise ArgumentError,
|
77
|
+
"width and height are too small or large (given %d x %d, expect %d..%d)" %
|
78
|
+
[@width, @height, MIN_PIXEL, MAX_PIXEL]
|
79
|
+
end
|
80
|
+
|
81
|
+
@pixels = [background].pack("C") * (@width * @height)
|
82
|
+
@matrix = Matrix.new
|
83
|
+
@palette = palette
|
84
|
+
@color = color
|
85
|
+
end
|
86
|
+
|
87
|
+
## primitive operators
|
88
|
+
;
|
89
|
+
|
90
|
+
def getpixel(x, y)
|
91
|
+
x = x.to_i
|
92
|
+
y = y.to_i
|
93
|
+
if validate_point(x, y)
|
94
|
+
getpixel!(x, y)
|
95
|
+
else
|
96
|
+
nil
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
alias [] getpixel
|
101
|
+
|
102
|
+
def setpixel(x, y, level)
|
103
|
+
x = x.to_i
|
104
|
+
y = y.to_i
|
105
|
+
validate_point(x, y) and setpixel!(x, y, level)
|
106
|
+
self
|
107
|
+
end
|
108
|
+
|
109
|
+
alias []= setpixel
|
110
|
+
|
111
|
+
## lowlevel operations
|
112
|
+
;
|
113
|
+
|
114
|
+
def fill(color = @color)
|
115
|
+
@pixels.bytesize.times do |i|
|
116
|
+
@pixels.setbyte(i, color)
|
117
|
+
end
|
118
|
+
self
|
119
|
+
end
|
120
|
+
|
121
|
+
def validate_point(x, y)
|
122
|
+
if x < 0 || x >= @width || y < 0 || y >= @height
|
123
|
+
false
|
124
|
+
else
|
125
|
+
true
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_point(x, y)
|
130
|
+
unless validate_point(x, y)
|
131
|
+
raise ArgumentError,
|
132
|
+
"x and y are out of canvas (given (%d, %d), expect (0, 0) .. (%d, %d))" %
|
133
|
+
[x, y, @width - 1, @height - 1]
|
134
|
+
end
|
135
|
+
|
136
|
+
nil
|
137
|
+
end
|
138
|
+
|
139
|
+
def getpixel!(x, y)
|
140
|
+
@pixels.getbyte(x + y * @width)
|
141
|
+
end
|
142
|
+
|
143
|
+
def setpixel!(x, y, color)
|
144
|
+
@pixels.setbyte(x + y * @width, color)
|
145
|
+
end
|
146
|
+
|
147
|
+
def plotscanline!(y, x0, x1)
|
148
|
+
w = width
|
149
|
+
px = pixels
|
150
|
+
i = color
|
151
|
+
x0.upto(x1 - 1) { |x| px.setbyte(x + y * w, i) }
|
152
|
+
self
|
153
|
+
end
|
154
|
+
|
155
|
+
## primitive figures
|
156
|
+
;
|
157
|
+
|
158
|
+
def plotscanline(y, x0, x1)
|
159
|
+
y = y.round
|
160
|
+
x0 = x0.round
|
161
|
+
x1 = x1.round
|
162
|
+
|
163
|
+
if y >= 0 && y < height
|
164
|
+
(x0, x1) = x1, x0 if x0 > x1
|
165
|
+
if x0 < width && x1 >= 0
|
166
|
+
x0 = [0, x0].max
|
167
|
+
x1 = [x1, width].min
|
168
|
+
plotscanline! y, x0, x1
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
self
|
173
|
+
end
|
174
|
+
|
175
|
+
## additional operators
|
176
|
+
;
|
177
|
+
|
178
|
+
#
|
179
|
+
# plot dot by char
|
180
|
+
#
|
181
|
+
# example:
|
182
|
+
# # plot dot "Error(TIMEDOUT)"
|
183
|
+
# canv.dot_by_char(5, 5, <<-CODE, { "*" => 3 })
|
184
|
+
# *** * *** *** * * *** ** *** * * *** *
|
185
|
+
# * * * * ** ** * * * * * * * * *
|
186
|
+
# *** *** *** *** *** * * * * * * *** * * * * * * * *
|
187
|
+
# * * * * * * * * * * * * * * * * * * * * *
|
188
|
+
# *** * * *** * * * *** * * *** ** *** *** * *
|
189
|
+
# CODE
|
190
|
+
#
|
191
|
+
def dot_by_char(x, y, seq, colormap = { "*" => 2 })
|
192
|
+
x0 = x
|
193
|
+
seq.each_char do |ch|
|
194
|
+
case ch
|
195
|
+
when "\n"
|
196
|
+
x = x0
|
197
|
+
y += 1
|
198
|
+
when " "
|
199
|
+
x += 1
|
200
|
+
else
|
201
|
+
if color = colormap[ch]
|
202
|
+
setpixel(x, y, color)
|
203
|
+
end
|
204
|
+
x += 1
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
self
|
209
|
+
end
|
210
|
+
|
211
|
+
## transform operators
|
212
|
+
;
|
213
|
+
|
214
|
+
def init_matrix
|
215
|
+
matrix.init
|
216
|
+
self
|
217
|
+
end
|
218
|
+
|
219
|
+
def push_matrix
|
220
|
+
save = matrix.dup
|
221
|
+
return save unless block_given?
|
222
|
+
|
223
|
+
begin
|
224
|
+
yield
|
225
|
+
ensure
|
226
|
+
matrix.load(save)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def pop_matrix(saved_matrix)
|
231
|
+
matrix.load(saved_matrix)
|
232
|
+
self
|
233
|
+
end
|
234
|
+
|
235
|
+
def mult_matrix(matrix1)
|
236
|
+
matrix.mult(matrix1)
|
237
|
+
self
|
238
|
+
end
|
239
|
+
|
240
|
+
def transform(x, y, w = 1)
|
241
|
+
matrix.transform(x, y, w)
|
242
|
+
end
|
243
|
+
|
244
|
+
def translate(dx, dy, dw = 1)
|
245
|
+
matrix.translate(dx, dy, dw)
|
246
|
+
self
|
247
|
+
end
|
248
|
+
|
249
|
+
def scale(ax, ay, aw = 1)
|
250
|
+
matrix.scale(ax, ay, aw)
|
251
|
+
self
|
252
|
+
end
|
253
|
+
|
254
|
+
def rotate(rad)
|
255
|
+
matrix.rotate(rad)
|
256
|
+
self
|
257
|
+
end
|
258
|
+
|
259
|
+
def rotate_deg(deg)
|
260
|
+
matrix.rotate(Math::PI * deg / 180)
|
261
|
+
self
|
262
|
+
end
|
263
|
+
|
264
|
+
## transformed figures
|
265
|
+
;
|
266
|
+
|
267
|
+
def triangle(x0, y0, x1, y1, x2, y2)
|
268
|
+
(x0, y0) = transform(x0, y0)
|
269
|
+
(x1, y1) = transform(x1, y1)
|
270
|
+
(x2, y2) = transform(x2, y2)
|
271
|
+
|
272
|
+
x0 = x0.round
|
273
|
+
x1 = x1.round
|
274
|
+
x2 = x2.round
|
275
|
+
y0 = y0.round
|
276
|
+
y1 = y1.round
|
277
|
+
y2 = y2.round
|
278
|
+
|
279
|
+
(x0, y0, x1, y1) = x1, y1, x0, y0 if y0 > y1
|
280
|
+
(x1, y1, x2, y2) = x2, y2, x1, y1 if y1 > y2
|
281
|
+
(x0, y0, x1, y1) = x1, y1, x0, y0 if y0 > y1
|
282
|
+
|
283
|
+
if y0 == y2
|
284
|
+
(a, *, b) = [x0, x1, x2].sort
|
285
|
+
plotscanline(y0, a, b)
|
286
|
+
else
|
287
|
+
d1 = (x1 - x0) / (y1 - y0).to_f
|
288
|
+
d2 = (x2 - x0) / (y2 - y0).to_f
|
289
|
+
(y0 ... y1).step(1) do |py|
|
290
|
+
dy = py - y0
|
291
|
+
plotscanline(py, x0 + dy * d1, x0 + dy * d2)
|
292
|
+
end
|
293
|
+
|
294
|
+
if y1 == y2
|
295
|
+
plotscanline(y1, x1, x2)
|
296
|
+
else
|
297
|
+
d0 = (x0 - x2) / (y0 - y2).to_f
|
298
|
+
d1 = (x1 - x2) / (y1 - y2).to_f
|
299
|
+
(y1 .. y2).step(1) do |py|
|
300
|
+
dy = (py - y2)
|
301
|
+
plotscanline(py, x2 + dy * d1, x2 + dy * d0)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
self
|
307
|
+
end
|
308
|
+
|
309
|
+
## extra
|
310
|
+
;
|
311
|
+
|
312
|
+
def halfdown!
|
313
|
+
pixels = @pixels
|
314
|
+
height = @height
|
315
|
+
width2 = width >> 1
|
316
|
+
height2 = height >> 1
|
317
|
+
height2.times do |y|
|
318
|
+
width2.times do |x|
|
319
|
+
xx = x * 2
|
320
|
+
yy = y * 2
|
321
|
+
xx1 = xx + 1
|
322
|
+
yy1 = (yy + 1) * height
|
323
|
+
yy = yy * height
|
324
|
+
pixels.setbyte(x + y * width2,
|
325
|
+
(pixels.getbyte(xx + yy) +
|
326
|
+
pixels.getbyte(xx1 + yy) +
|
327
|
+
pixels.getbyte(xx + yy1) +
|
328
|
+
pixels.getbyte(xx1 + yy1)) / 4)
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
@width = width2
|
333
|
+
@height = height2
|
334
|
+
@pixels[(width2 * height2) .. -1] = ""
|
335
|
+
|
336
|
+
self
|
337
|
+
end
|
338
|
+
|
339
|
+
## export to png
|
340
|
+
;
|
341
|
+
|
342
|
+
def export_to_png(io = "".b, level: Zlib::DEFAULT_COMPRESSION)
|
343
|
+
io << [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A].pack("C*")
|
344
|
+
storechunk = ->(code, chunk) {
|
345
|
+
crc = Zlib.crc32(chunk, Zlib.crc32(code))
|
346
|
+
io << [chunk.bytesize].pack("N")
|
347
|
+
io << code << chunk
|
348
|
+
io << [crc].pack("N")
|
349
|
+
}
|
350
|
+
|
351
|
+
## IDAT
|
352
|
+
## color
|
353
|
+
## type bit depth
|
354
|
+
## 0 1,2,4,8,16 immediate grayscale sample
|
355
|
+
## 2 8,16 immediate RGB sample
|
356
|
+
## 3 1,2,4,8 palette sample (need PLTE chunk)
|
357
|
+
## 4 8,16 immediate grayscale sample with alpha
|
358
|
+
## 6 8,16 immediate RGB sample with alpha
|
359
|
+
colorbits = 8
|
360
|
+
colortype = 3
|
361
|
+
storechunk["IHDR", [width, height, colorbits, colortype, 0, 0, 0].pack("NNCCCCC")]
|
362
|
+
|
363
|
+
## PLTE
|
364
|
+
plte = palette.reduce("".b) { |a, e|
|
365
|
+
a << e.pack_rgb
|
366
|
+
}
|
367
|
+
storechunk["PLTE", plte]
|
368
|
+
|
369
|
+
## tRNS
|
370
|
+
## Alpha for palette index 0: 1 byte
|
371
|
+
## Alpha for palette index 1: 1 byte
|
372
|
+
## ...etc...
|
373
|
+
trns = palette.reduce([]) { |a, e| a << e.get_alpha }
|
374
|
+
storechunk["tRNS", trns.pack("C*")]
|
375
|
+
|
376
|
+
## iTXt
|
377
|
+
## Keyword: 1-79 bytes (character string)
|
378
|
+
## Null separator: 1 byte
|
379
|
+
## Compression flag: 1 byte
|
380
|
+
## Compression method: 1 byte
|
381
|
+
## Language tag: 0 or more bytes (character string)
|
382
|
+
## Null separator: 1 byte
|
383
|
+
## Translated keyword: 0 or more bytes
|
384
|
+
## Null separator: 1 byte
|
385
|
+
## Text: 0 or more bytes
|
386
|
+
storechunk["iTXt", ["snowy", 0, 0, <<-'SNOWY'].pack("a*xCCxxa*")]
|
387
|
+
This image is generated by snowy <https://rubygems.org/gems/snowy>
|
388
|
+
SNOWY
|
389
|
+
storechunk["iTXt", ["LICENSING", 0, 0, <<-'LICENSING'].pack("a*xCCxxa*")]
|
390
|
+
Creative Commons License Zero (CC0 / Public Domain)
|
391
|
+
See <https://creativecommons.org/publicdomain/zero/1.0/>
|
392
|
+
LICENSING
|
393
|
+
|
394
|
+
## IDAT
|
395
|
+
scanline = ->(lines, line) {
|
396
|
+
lines << "\0" << line
|
397
|
+
}
|
398
|
+
scanline ||= ->(lines, line) {
|
399
|
+
v = 0
|
400
|
+
line.each_byte.with_index { |vv, i|
|
401
|
+
line.setbyte(i, vv - v)
|
402
|
+
v = vv
|
403
|
+
}
|
404
|
+
lines << "\1" << line
|
405
|
+
}
|
406
|
+
lines = height.times.reduce("".b) { |a, h|
|
407
|
+
line = pixels.byteslice(h * width, width)
|
408
|
+
scanline[a, line]
|
409
|
+
}
|
410
|
+
storechunk["IDAT", Zlib.deflate(lines, level)]
|
411
|
+
|
412
|
+
storechunk["IEND", ""]
|
413
|
+
|
414
|
+
io
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
class Matrix
|
419
|
+
attr_reader :matrix
|
420
|
+
|
421
|
+
def self.[](matrix)
|
422
|
+
case matrix
|
423
|
+
when self
|
424
|
+
matrix
|
425
|
+
else
|
426
|
+
new matrix
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
def initialize(mat = nil)
|
431
|
+
@matrix = [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]
|
432
|
+
|
433
|
+
if mat
|
434
|
+
load mat
|
435
|
+
else
|
436
|
+
reset
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
def initialize_copy(mat)
|
441
|
+
@matrix = [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]
|
442
|
+
load mat
|
443
|
+
end
|
444
|
+
|
445
|
+
def reset
|
446
|
+
load [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
|
447
|
+
end
|
448
|
+
|
449
|
+
def load(mat)
|
450
|
+
case mat
|
451
|
+
when Matrix
|
452
|
+
matrix[0][0, 3] = mat.matrix[0]
|
453
|
+
matrix[1][0, 3] = mat.matrix[1]
|
454
|
+
matrix[2][0, 3] = mat.matrix[2]
|
455
|
+
when Array
|
456
|
+
if mat.size == 3 &&
|
457
|
+
mat[0].kind_of?(Array) && mat[0].size == 3 &&
|
458
|
+
mat[1].kind_of?(Array) && mat[1].size == 3 &&
|
459
|
+
mat[2].kind_of?(Array) && mat[2].size == 3
|
460
|
+
matrix[0][0, 3] = mat[0]
|
461
|
+
matrix[1][0, 3] = mat[1]
|
462
|
+
matrix[2][0, 3] = mat[2]
|
463
|
+
else
|
464
|
+
if mat.size == 9
|
465
|
+
matrix[0][0, 3] = mat[0, 3]
|
466
|
+
matrix[1][0, 3] = mat[3, 3]
|
467
|
+
matrix[2][0, 3] = mat[6, 3]
|
468
|
+
else
|
469
|
+
raise ArgumentError, "wrong element number (given #{mat.size} elements, expect 9 elements)"
|
470
|
+
end
|
471
|
+
end
|
472
|
+
else
|
473
|
+
raise ArgumentError, "wrong argument type (expect Snowy::Matrix or Array)"
|
474
|
+
end
|
475
|
+
|
476
|
+
self
|
477
|
+
end
|
478
|
+
|
479
|
+
def mult(mat)
|
480
|
+
mult! self.class[mat].matrix
|
481
|
+
end
|
482
|
+
|
483
|
+
def mult!(mat)
|
484
|
+
3.times do |i|
|
485
|
+
m0 = matrix[i]
|
486
|
+
mm = m0.dup
|
487
|
+
3.times do |j|
|
488
|
+
m0[j] = mm[0] * mat[0][j] +
|
489
|
+
mm[1] * mat[1][j] +
|
490
|
+
mm[2] * mat[2][j]
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
self
|
495
|
+
end
|
496
|
+
|
497
|
+
def transform2(x, y, w = 1)
|
498
|
+
mx = matrix[0]
|
499
|
+
my = matrix[1]
|
500
|
+
[x * mx[0] + y * mx[1] + w * mx[2],
|
501
|
+
x * my[0] + y * my[1] + w * my[2]]
|
502
|
+
end
|
503
|
+
|
504
|
+
alias transform transform2
|
505
|
+
|
506
|
+
def transform3(x, y, w = 1)
|
507
|
+
mx = matrix[0]
|
508
|
+
my = matrix[1]
|
509
|
+
mw = matrix[2]
|
510
|
+
[x * mx[0] + y * mx[1] + w * mx[2],
|
511
|
+
x * my[0] + y * my[1] + w * my[2],
|
512
|
+
x * mw[0] + y * mw[1] + w * mw[2]]
|
513
|
+
end
|
514
|
+
|
515
|
+
def translate(dx, dy, dw = 1)
|
516
|
+
mult!([[1, 0, dx], [0, 1, dy], [0, 0, dw]])
|
517
|
+
end
|
518
|
+
|
519
|
+
def scale(ax, ay, aw = 1)
|
520
|
+
mult!([[ax, 0, 0], [0, ay, 0], [0, 0, aw]])
|
521
|
+
end
|
522
|
+
|
523
|
+
def rotate(rad)
|
524
|
+
cos = Math.cos(rad)
|
525
|
+
sin = Math.sin(rad)
|
526
|
+
mult!([[cos, -sin, 0], [sin, cos, 0], [0, 0, 1]])
|
527
|
+
end
|
528
|
+
end
|
529
|
+
end
|
data/lib/snowy/cairo.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require_relative "common"
|
2
|
+
require "cairo"
|
3
|
+
|
4
|
+
module Snowy
|
5
|
+
using Extentions
|
6
|
+
|
7
|
+
module CairoDriver
|
8
|
+
def self.render(size, triangles, background, color, outline, angle)
|
9
|
+
deg2rad = Math::PI / 180
|
10
|
+
surface = Cairo::ImageSurface.new(Cairo::Format::ARGB32, size, size)
|
11
|
+
Cairo::Context.new(surface) do |context|
|
12
|
+
context.instance_eval do
|
13
|
+
set_line_width 0.5
|
14
|
+
set_source_color [background.get_red / 255.0, background.get_green / 255.0, background.get_blue / 255.0, background.get_alpha / 255.0]
|
15
|
+
paint
|
16
|
+
translate(size / 2.0, size / 2.0)
|
17
|
+
scale(size / 32.0, size / 32.0)
|
18
|
+
rotate(angle * deg2rad) unless deg2rad == 0
|
19
|
+
sqrt3 = Math.sqrt(3)
|
20
|
+
[30, 90, 150, 210, 270, 330].each do |deg|
|
21
|
+
save do
|
22
|
+
rotate(-deg * deg2rad)
|
23
|
+
scale(1, sqrt3)
|
24
|
+
triangles.each do |(x1, y1, x2, y2, x3, y3)|
|
25
|
+
move_to(x1, y1)
|
26
|
+
line_to(x2, y2)
|
27
|
+
line_to(x3, y3)
|
28
|
+
close_path
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
if outline
|
33
|
+
set_source_rgba outline.get_red / 255.0, outline.get_green / 255.0, outline.get_blue / 255.0, 255 / 255.0
|
34
|
+
stroke true
|
35
|
+
end
|
36
|
+
set_source_rgba color.get_red / 255.0, color.get_green / 255.0, color.get_blue / 255.0, 255 / 255.0
|
37
|
+
fill
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
buffer = "".b
|
42
|
+
outport = Object.new
|
43
|
+
outport.define_singleton_method(:write, ->(d) { buffer << d; d.bytesize })
|
44
|
+
surface.write_to_png outport
|
45
|
+
buffer
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
@@driver = CairoDriver
|
50
|
+
end
|
data/lib/snowy/common.rb
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
require "zlib"
|
2
|
+
|
3
|
+
module Snowy
|
4
|
+
module Extentions
|
5
|
+
refine Numeric do
|
6
|
+
unless 0.respond_to?(:clamp)
|
7
|
+
def clamp(min, max)
|
8
|
+
case
|
9
|
+
when self < min
|
10
|
+
return min
|
11
|
+
when self > max
|
12
|
+
return max
|
13
|
+
else
|
14
|
+
return self
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
refine Integer do
|
21
|
+
def get_red
|
22
|
+
0xff & (self >> 24)
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_green
|
26
|
+
0xff & (self >> 16)
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_blue
|
30
|
+
0xff & (self >> 8)
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_alpha
|
34
|
+
0xff & self
|
35
|
+
end
|
36
|
+
|
37
|
+
def pack_rgb
|
38
|
+
[self >> 24, self >> 16, self >> 8].pack("C3")
|
39
|
+
end
|
40
|
+
|
41
|
+
def pack_rgba
|
42
|
+
[self].pack("N")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
using Extentions
|
48
|
+
|
49
|
+
|
50
|
+
def self.rgba(r, g, b, a = 255)
|
51
|
+
return (r.to_i.clamp(0, 255) << 24) |
|
52
|
+
(g.to_i.clamp(0, 255) << 16) |
|
53
|
+
(b.to_i.clamp(0, 255) << 8) |
|
54
|
+
(a.to_i.clamp(0, 255) )
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
#
|
59
|
+
# call-seq:
|
60
|
+
# generate_to_png(code, size = 128)
|
61
|
+
#
|
62
|
+
# @return
|
63
|
+
# string object
|
64
|
+
#
|
65
|
+
# @param [Integer] code
|
66
|
+
# 32 bits integer
|
67
|
+
#
|
68
|
+
# @param [Integer] size
|
69
|
+
# output png image size
|
70
|
+
#
|
71
|
+
def self.generate_to_png(code, size: 128, cap: true, extendcap: true, angle: 0, color: nil, outline: nil)
|
72
|
+
if code.kind_of?(String)
|
73
|
+
code = Zlib.crc32(code)
|
74
|
+
end
|
75
|
+
|
76
|
+
if color
|
77
|
+
if outline.nil?
|
78
|
+
r = color.get_red
|
79
|
+
g = color.get_green
|
80
|
+
b = color.get_blue
|
81
|
+
a = color.get_alpha
|
82
|
+
end
|
83
|
+
else
|
84
|
+
r = (code >> 28) & 0x0f
|
85
|
+
g = (code >> 24) & 0x0f
|
86
|
+
b = (code >> 20) & 0x0f
|
87
|
+
r = (r << 3) | 0x80
|
88
|
+
g = (g << 3) | 0x80
|
89
|
+
b = (b << 3) | 0x80
|
90
|
+
color = rgba(r, g, b)
|
91
|
+
end
|
92
|
+
|
93
|
+
if outline.nil?
|
94
|
+
outline = rgba(r * 7 / 8, g * 7 / 8, b * 7 / 8, a || 0xff)
|
95
|
+
end
|
96
|
+
|
97
|
+
code = code ^ (code >> 16) ^ ((code & 0xffff) << 16) if extendcap
|
98
|
+
|
99
|
+
depth = extendcap ? 7 : 6
|
100
|
+
triangles = [] # [[x1, y1, x2, y2, x3, y3], ...]
|
101
|
+
depth.times do |level|
|
102
|
+
# level # 現在の階層
|
103
|
+
# total # 現在の階層までの総要素数
|
104
|
+
# layer # 現在の階層の要素数
|
105
|
+
level_1 = level + 1
|
106
|
+
total = level_1 ** 2
|
107
|
+
layer = level * 2 + 1
|
108
|
+
offbase = (level * level_1) / 2
|
109
|
+
offpivot = (layer + 1) / 2 - 1
|
110
|
+
layer.times do |i|
|
111
|
+
if !extendcap && level_1 == depth
|
112
|
+
i += 1
|
113
|
+
break if (i + 1) == layer
|
114
|
+
#break if i > layer
|
115
|
+
end
|
116
|
+
|
117
|
+
#if (i + 1) > (layer + 1) / 2
|
118
|
+
if i > offpivot
|
119
|
+
# mirror
|
120
|
+
off = offbase + (layer - i - 1)
|
121
|
+
else
|
122
|
+
off = offbase + i
|
123
|
+
end
|
124
|
+
|
125
|
+
off -= 1 if !extendcap && level_1 == depth
|
126
|
+
next if code[off] == 0
|
127
|
+
|
128
|
+
m_level_i = -level + i
|
129
|
+
if i.even?
|
130
|
+
triangles << [m_level_i, level, m_level_i + 1, level_1, m_level_i - 1, level_1]
|
131
|
+
else
|
132
|
+
triangles << [m_level_i, level_1, m_level_i + 1, level, m_level_i - 1, level]
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# 一番外側に三角形を配置する
|
138
|
+
if cap
|
139
|
+
if extendcap
|
140
|
+
triangles << [-5, 7, -3, 7, -4, 8]
|
141
|
+
triangles << [5, 7, 4, 8, 3, 7]
|
142
|
+
else
|
143
|
+
triangles << [-4, 6, -2, 6, -3, 7]
|
144
|
+
triangles << [4, 6, 3, 7, 2, 6]
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
driver.render(size, triangles, rgba(255, 255, 255, 0), color, outline, angle)
|
149
|
+
end
|
150
|
+
|
151
|
+
def self.driver
|
152
|
+
@@driver
|
153
|
+
end
|
154
|
+
end
|
data/snowy-demo.png
ADDED
Binary file
|
data/snowy-demo.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
#!ruby
|
2
|
+
|
3
|
+
require "sinatra"
|
4
|
+
require "haml"
|
5
|
+
require_relative "lib/snowy.rb"
|
6
|
+
#require_relative "lib/snowy/cairo.rb"
|
7
|
+
|
8
|
+
if $-d
|
9
|
+
require "sinatra/reloader"
|
10
|
+
also_reload "lib/snowy/common.rb"
|
11
|
+
also_reload "lib/snowy.rb"
|
12
|
+
#also_reload "lib/snowy/cairo.rb"
|
13
|
+
end
|
14
|
+
|
15
|
+
get "/" do
|
16
|
+
haml <<-HAML
|
17
|
+
!!! 5
|
18
|
+
%title Demonstration for snowy
|
19
|
+
:css
|
20
|
+
body
|
21
|
+
{
|
22
|
+
background: url("snowy/#{"%08X" % (0xeef00000 | rand(0x100000))}.png?size=256&angle=-10&extendcap=true");
|
23
|
+
}
|
24
|
+
|
25
|
+
%div{style: "text-align: center"}
|
26
|
+
%div{style: "padding: 1em; font-size: 200%"}
|
27
|
+
"snowy" is an identicon implements with the snow crystal motif.
|
28
|
+
%div
|
29
|
+
#{20.times.map { %(<img src="snowy/%08X.png?size=131&angle=5&extendcap=true" alt="">) % [rand(0xffffffff)] }.join}
|
30
|
+
%div
|
31
|
+
#{20.times.map { %(<img src="snowy/%08X.png?size=131&angle=0&extendcap=true" alt="">) % [rand(0x00100000) | 0x69f00000] }.join}
|
32
|
+
HAML
|
33
|
+
end
|
34
|
+
|
35
|
+
get "/snowy/*.png" do |id|
|
36
|
+
id = id.hex
|
37
|
+
size = (params["size"] || 128).to_i
|
38
|
+
size = [32, size, 4096].sort[1]
|
39
|
+
cap = (params["nocap"]) ? false : true
|
40
|
+
angle = (params["angle"] || 0).to_i
|
41
|
+
if params["monotone"]
|
42
|
+
id = (id & 0x000fffff) | 0x9cf00000
|
43
|
+
end
|
44
|
+
extendcap = (params["extendcap"] || "false") == "false" ? false : true
|
45
|
+
bin = Snowy.generate_to_png(id, size: size, cap: cap, extendcap: extendcap, angle: -angle)
|
46
|
+
|
47
|
+
status 200
|
48
|
+
headers "Content-Type" => "image/png"
|
49
|
+
body bin
|
50
|
+
end
|
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: snowy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- dearblue
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-12-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '11'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '11'
|
27
|
+
description: 'Pure ruby identicon implement with the snow crystal motif
|
28
|
+
|
29
|
+
'
|
30
|
+
email: dearblue@users.noreply.github.com
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files:
|
34
|
+
- LICENSE
|
35
|
+
- README.md
|
36
|
+
- lib/snowy.rb
|
37
|
+
- lib/snowy/cairo.rb
|
38
|
+
- lib/snowy/common.rb
|
39
|
+
files:
|
40
|
+
- LICENSE
|
41
|
+
- README.md
|
42
|
+
- Rakefile
|
43
|
+
- gemstub.rb
|
44
|
+
- lib/snowy.rb
|
45
|
+
- lib/snowy/cairo.rb
|
46
|
+
- lib/snowy/common.rb
|
47
|
+
- snowy-demo.png
|
48
|
+
- snowy-demo.rb
|
49
|
+
homepage: https://github.com/dearblue/ruby-snowy/
|
50
|
+
licenses:
|
51
|
+
- BSD-2-Clause
|
52
|
+
metadata: {}
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options:
|
55
|
+
- "--charset"
|
56
|
+
- UTF-8
|
57
|
+
- "-m"
|
58
|
+
- README.md
|
59
|
+
require_paths:
|
60
|
+
- lib
|
61
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
requirements: []
|
72
|
+
rubyforge_project:
|
73
|
+
rubygems_version: 2.6.4
|
74
|
+
signing_key:
|
75
|
+
specification_version: 4
|
76
|
+
summary: an identicon implements
|
77
|
+
test_files: []
|