Skip to content

Instantly share code, notes, and snippets.

@tompng
Last active May 20, 2024 08:40
Show Gist options
  • Save tompng/f643ced454aac70ef96d171d1880c418 to your computer and use it in GitHub Desktop.
Save tompng/f643ced454aac70ef96d171d1880c418 to your computer and use it in GitHub Desktop.
sixel-minsa
require_relative'six' 'el';eval(%W`include~
Math;puts"Usage:~ruby ~#{__FILE__}~[line_he
ight]~[width]~[seed]" ;line_height=ARGV.fir
st&.to_i||14;width=AR GV[1]&.to_i||400;seed
=ARGV[2]&.to_i||rand( 100000);p(seed:);sran
d~seed;$maxw=240;$mpa t=[[1,1,0],[2,2,1],[1
,3,1]][rand**5*3];ab= [0,1].map{s=4*rand(2+
$mpat[2]..4);r=l=[2*r and(2),s,2*s].sample;
l,r=[l,4*s].shuffle~i f~rand<0.2;[l+r+3*s,l
,s,r,_1]};if(sum=ab.s um{_1[0]})<200*rand;i
=rand($maxw -sum);l=rand($maxw-su m-i);ab[1][
0]=l+ab[0 ][0]+i;ab[0][0]=l;els e~ab.pop;
ab[0][0 ]=rand($ma xw-ab[0][0 ])end;$
minsa =ab;def~noiz e(x)sin(x)+s in(1.
3*x) +sin(1.7*x)end;A,B,C, D=4.
time s.map{rand*100};E=1+r and(
2); $string_color=$maxw.t ime
s.m ap~do|ix|noize(0.05*i x+A
)>1? 4:noize(0.1*ix+B)>1?5 :noi
ze(0 .2*ix+C)>0?6+ix/E%2:n oize
(0.2 *ix+D)>0?3*(1+sin(ix* 0.1+
C))/ 2:4+rand(4)end;def~co lor_
at(x ,y)x-=10;x+=0.05*(sin (x-y
)+si n(2.7*x+y))+2*(sin(0. 013*
y)+s in(0.007*y));y+=2*(si n(0.
011* x+0.012*y)+sin(0.013* x-0.
017*y))+(noize(x/2+y/15)+noiz
e( x/3-y/13))/12;y+= no
ize(y/2.5)*(0.2+noize(0.0 2*x+y/5)*0.1);y2=y+sin(y)
;y3=y2+sin(y2);ci=((4 *x+y 3-PI )/4/PI);cj=((-4*x+y3-
PI)/4/PI);i=ci.floor; ci-= i;j= cj.floor;cj-=j;ck=(ci
+cj)/2;ix=i-j;color_i ndex =$st ring_color[ix];$minsa
.each~do|x,l,s,r,idx| if~x< =ix&&ix<x+l+3*s+r;xx=
ix-x-l;pattern=->(y){ y y=y-10*s*idx;0<=xx&&x
x<3*s&&(yy%(10*s)<6*s )&&(yy%(30*s)<6*s~?(s
..3*s/2).cover?([(y=y y%(6*s)-3*s).abs/1.5+
(xx-s*3/2).abs,-0.8*y +s/2].max):((yy/s/2).
floor%2+xx/s%2)%2==1) };offset=31*sin(31*ix
)%1*3;color_index=ix/ $mpat[0]%$mpat[1]<$mp
at[2]?3:12.times.coun t{!pattern[y+_1*0.5+o
ffset]}/4.0;break;end ;end;brightness=(ci*(
1-ci)*cj*(1-cj)*3+0.4 )*((4*ck*(1.0-ck))**3
6*0.5+0.5)*(0.9+0.1*s
in((3+sin(ix))*(ci-cj
)+ix+y/4).abs+0.1*sin
((5+cos(ix))*(ci-cj)+
7*ix+y/4).abs);if~ix<
0||ix>=$maxw;a=((y3-P
I)/4%PI-PI/2).abs-PI/
2;z=y-(x<1?0:2*PI);b=
sin((z/(PI*4)).floor)
*123%1+2;v=((z%(PI*4)
-2*PI)**2+ b*[x,($max
w-1)*PI/2-x] .min**2)/4/P
I/PI ;return~color(4,v<1?( 1-v)
*(0. 8+0.2*sin(11*v**0.5). abs)
*(0 .7+0.3*atan([a -x,x-$maxw*PI/ 2-a
] .max)*2/PI):0)en d;brightness*=1+ (
noize(0.004*x+0.01 7*y)+noize(0.007*x
-0.006*y))/9;color (color_index,brigh
t ness)end;def~col or(idx,brightnes s
)(i dx+rand).floor *32+(31*bright nes
s+ran d).floor.cla mp(0,31)end; color
s=[[1,1 ,1],[0.8,0 .8,1],[0.6 ,0.6,1]
,[0.4,0.4 ,1],*[[0 .5,0.7,1 ],[0.6,1,
1],[0.7,0.8 ,1],[0 .8,1,0 .4]].shuffl
e];colors.map !{|a |a.s ort.reverse}i
f~rand<0.5;tabl e= co lors.flat_map~d
o|r,g,b|32.times. map~do|i|cr,cg,cb
=[r,g,b].map{(255*_ 1* i/ 31+(i**3)/512).clam
p(0,255).roun
d} ;cr<<16|c g<
<8|c b;end ;end
;trap( ' INT'){
exit};y= 0;loop~d
o~sleep~0. 1;lines=li
ne_height.times.map~d
o~y+=1;width.times.ma
p{|x|color_at(x*400.0
/width,y*400.0/width)
}end;print~Sixel.buil
d~lines,table;end.for
RubyKaigi2024@Okinawa
`.join.tr(?~,32.chr))
# rubocop:disable all
require_relative 'sixel'
include Math
puts "Usage: ruby #{__FILE__} [line_height] [width] [seed]"
line_height = ARGV.first&.to_i || 14
width = ARGV[1]&.to_i || 400
seed = ARGV[2]&.to_i || rand(100000)
p(seed:)
srand seed
$maxw=240
$mpat=[[1,1,0],[2,2,1],[1,3,1]][rand**5*3]
ab=[0,1].map{
s=4*rand(2+$mpat[2]..4)
r=l=[2*rand(2),s,2*s].sample
l,r=[l,4*s].shuffle if rand<0.2
[l+r+3*s,l,s,r,_1]
}
if (sum=ab.sum{_1[0]})<200*rand
i=rand($maxw-sum)
l=rand($maxw-sum-i)
ab[1][0]=l+ab[0][0]+i
ab[0][0]=l
else
ab.pop
ab[0][0]=rand($maxw-ab[0][0])
end
$minsa=ab
def noize(x)
sin(x)+sin(1.3*x)+sin(1.7*x)
end
A,B,C,D=4.times.map{rand*100}
E=1+rand(2)
$string_color = $maxw.times.map do |ix|
noize(0.05*ix+A)>1 ? 4 : noize(0.1*ix+B)>1 ? 5 : noize(0.2*ix+C)>0 ? 6+ix/E%2 : noize(0.2*ix+D)>0 ? 3*(1+sin(ix*0.1+C))/2 : 4+rand(4)
end
def color_at(x, y)
x-=10
x+=0.05*(sin(x-y)+sin(2.7*x+y))+2*(sin(0.013*y)+sin(0.007*y))
y+=2*(sin(0.011*x+0.012*y)+sin(0.013*x-0.017*y))+(noize(x/2+y/15)+noize(x/3-y/13))/12
y+=noize(y/2.5)*(0.2+noize(0.02*x+y/5)*0.1)
y2 = y + sin(y)
y3 = y2 + sin(y2)
ci=((4*x+y3-PI)/4/PI)
cj=((-4*x+y3-PI)/4/PI)
i = ci.floor; ci -= i
j = cj.floor; cj -= j
ck = (ci+cj)/2
ix=i-j
color_index = $string_color[ix]
$minsa.each do |x,l,s,r,idx|
if x<=ix&&ix<x+l+3*s+r
xx=ix-x-l
pattern=->(y){
yy=y-10*s*idx
0<=xx&&xx<3*s&&(yy%(10*s)<6*s)&&(
yy%(30*s)<6*s ? (s..3*s/2).cover?([(y=yy%(6*s)-3*s).abs/1.5+(xx-s*3/2).abs, -0.8*y+s/2].max) :
((yy/s/2).floor%2+xx/s%2)%2==1
)
}
offset = 31*sin(31*ix)%1*3
color_index = ix/$mpat[0]%$mpat[1]<$mpat[2] ? 3 : 12.times.count{!pattern[y+_1*0.5+offset]}/4.0
break
end
end
brightness = (ci*(1-ci)*cj*(1-cj) * 3 + 0.4) * ((4*ck*(1.0-ck))**36*0.5+0.5) * (0.9+0.1*sin((3+sin(ix))*(ci-cj)+ix+y/4).abs+0.1*sin((5+cos(ix))*(ci-cj)+7*ix+y/4).abs)
if ix<0||ix>=$maxw
a=((y3-PI)/4%PI-PI/2).abs-PI/2
z=y-(x<1?0:2*PI)
b=sin((z/(PI*4)).floor)*123%1+2
v=((z%(PI*4)-2*PI)**2+b*[x,($maxw-1)*PI/2-x].min**2)/4/PI/PI
return color(4, v<1 ? (1-v)*(0.8+0.2*sin(11*v**0.5).abs)*(0.7+0.3*atan([a-x,x-$maxw*PI/2-a].max)*2/PI) : 0)
end
brightness *= 1 + (noize(0.004*x+0.017*y) + noize(0.007*x-0.006*y)) / 9
color(color_index, brightness)
end
def color(idx, brightness)
(idx+rand).floor*32+(31*brightness+rand).floor.clamp(0,31)
end
colors = [
[1, 1, 1],
[0.8, 0.8, 1],
[0.6, 0.6, 1],
[0.4, 0.4, 1],
*[[0.5, 0.7, 1],
[0.6, 1, 1],
[0.7, 0.8, 1],
[0.8, 1, 0.4]].shuffle
];
colors.map!{|a|a.sort.reverse} if rand<0.5
table = colors.flat_map do |r, g, b|
32.times.map do |i|
cr,cg,cb=[r,g,b].map{(255*_1*i/31+(i**3)/512).clamp(0,255).round}
cr<<16|cg<<8|cb
end
end
trap('INT') { exit }
y = 0
loop do
sleep 0.1
lines = line_height.times.map do
y += 1
width.times.map { |x|
color_at(x*400.0/width, y*400.0/width)
}
end
print Sixel.build lines, table
end
w=21
h=14
patterns = 2.times.map do |pattern|
lines = (3*h).times.map do |y|
(3*w).times.map do |x|
x=[3*w-1-x, x].min
my = (y-h*1.25+1)/1.2
mx=(x-w*1.5)/3.6
s=w*0.4
hoge = (s..s+1).cover?([my.abs/1.5+mx.abs, -my/2+h/2.2].max) ? 1 : 0
((x/w)+(y/h) + hoge) % 2 == pattern ? '#' : ' '
end.join
end
lines.join("\n")
end
puts patterns.join.count('#')
template = patterns.join("\n"*4)
code = File.read('minsa_base.rb').gsub(/^#.+/, '')
def replace(code, rep)
foo = nil
i=0
code2 = code.gsub(/ |\n/){i+=1; rep[i-1] || (foo||=_1; _1)}
[code2, foo]
end
replaces = []
require 'ripper'
def sexp_normalize(sexp)
case sexp
in [a, b, [Integer, Integer]]
[sexp_normalize(a), sexp_normalize(b)]
in Array
sexp.map{|x| sexp_normalize(x)}
else
sexp
end
end
sexp = sexp_normalize(Ripper.sexp(code))
while true
code2, sep = replace(code, replaces)
break unless sep
c1, = replace(code, replaces + [''])
c2, = replace(code, replaces + [';'])
c3, = replace(code, replaces + [sep])
if sexp_normalize(Ripper.sexp(c1))==sexp
replaces << ''
elsif sexp_normalize(Ripper.sexp(c2))==sexp
replaces << ';'
elsif sexp_normalize(Ripper.sexp(c3))==sexp
replaces << sep
else
binding.irb
end
end
chars = code2.gsub("require_relative'sixel';", '').tr(' ','~').chars
chars += '.forRubyKaigi2024@Okinawa'.chars
template.sub!(/\A[# ]{55}/, "require_relative'six' 'el';eval(%W`")
template.sub!(/\#{21}\s*\z/, '`.join.tr(?~,32.chr))')
template.gsub!('#'){chars.shift||';'}
File.write 'minsa.rb', template
puts chars.size
module Sixel
def self.build(image, palette = 256.times.map{_1*0x10101})
header = "\ePq"
footer = "\e\\"
color_table = palette.map.with_index do |color, idx|
r, g, b = 3.times.map { ((color >> (16 - 8 * _1) & 0xff) * 100 / 255.0).round }
"##{idx};2;#{r};#{g};#{b}"
end
h = image.size
w = image.first.size
sixel_lines = (h.fdiv(6).ceil).times.map { {} }
h.times do |y|
w.times do |x|
color = image[y][x]
(sixel_lines[y / 6][color] ||= Hash.new(0))[x] |= 1 << (y % 6)
end
end
output = [header, color_table]
sixel_lines.each do |line|
linedata = []
line.each do |color, bitmasks|
linedata << '$' unless linedata.empty?
linedata << "##{color}"
prev_x = -1
bitmasks.sort_by{_1}.each do |x, bitmask|
linedata << (prev_x + 2 == x ? '?' : "!#{x - prev_x - 1}?") if prev_x + 1 < x
linedata << (63 + bitmask).chr
prev_x = x
end
end
output << linedata << '-'
end
[output, footer].join
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment