Skip to content

Instantly share code, notes, and snippets.

@shyouhei
Last active December 17, 2015 23:58
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shyouhei/5692655 to your computer and use it in GitHub Desktop.
Save shyouhei/5692655 to your computer and use it in GitHub Desktop.
#! /your/favourite/path/to/ruby
# -*- coding: utf-8 -*-
# Copyright (c) 2013 Urabe, Shyouhei
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# Hilbert curve generator. Yields (x, y), where (x, y) are a series of points
# starts from (0, 0) towards (2**n-1, 2**n-1), in sequence of Hilbert curve.
# Algorithm translated from http://en.wikipedia.org/wiki/Hilbert_curve
def hilbert n
(0..Float::INFINITY).lazy.map do |d|
x = y = 0
t = d
s = 1
while s < n do
rx = 1 & (t / 2)
ry = 1 & (t ^ rx)
if ry == 0 then
if rx == 1 then
x = s - 1 - x
y = s - 1 - y
end
x, y = y, x
end
x += s * rx
y += s * ry
t /= 4
s *= 2
end
next x, y
end
end
# Nearest value to x, where it is grater than or equal to x, and 2**n.
# Alrothinm translated from the "Hacker's Delight".
def clp2 x
n = x.to_i - 1
n |= n >> 1
n |= n >> 2
n |= n >> 4
n |= n >> 8
n |= n >> 16
n |= n >> 32
n + 1
end
# Outputs PPM, assumes vector of 24bit pixmap.
def ppmout bmp, file
open file, 'w' do |fp|
x = bmp.size
y = bmp.max_by(&:size).size
fp.printf "P3\n%d %d\n255\n", y, x
x.times do |i|
y.times do |j|
k = bmp[i] ? (bmp[i][j] || 0) : 0 # can be nil
r, g, b = (k / 256 / 256) % 256, (k / 256) % 256, k % 256
fp.printf "%d %d %d ", r, g, b
end
fp.puts
end
end
end
def failed
abort "cd WORKING_DIRECTORY && ruby #$0 OUTFILE COMMIT"
end
# ARGV handling
filename = ARGV.shift or failed
treeish = case ARGV.shift
when /\A[0-9a-fA-F]+\Z/ then ARGV.first
when NilClass then 'HEAD'
else failed
end
# Check git
failed unless system "git", "show", treeish, out: IO::NULL, err: IO::NULL
# Get tree
who = {}
total = 0.0
order = []
IO.popen %W"git ls-tree -r --name-only #{treeish}", 'rb' do |fp|
fp.each_slice 5 do |a| #! <- FIXME: magic number
a.map do |i|
order << i
Thread.start(i) do |j|
IO.popen %W"git blame --line-porcelain --root -M -C #{treeish} -- #{j.chomp}", 'r:binary' do |fq|
a = who[j] = fq.each_line.grep(/^author /).map do |k|
/^author (.+)$/.match(k).captures.last
end
total += a.size
end
STDERR.write j
end
end.map(&:join)
end
end
# Generate bitmap, w/ regularization
require 'zlib'
dim = clp2 Math.sqrt(total)
col = {}
bmp = []
gen = hilbert(dim)
order.each do |i|
who[i].each do |w|
col[w] ||= Zlib.crc32(w)
x, y = gen.next
bmp[x] ||= []
bmp[x][y] = col[w]
end
end
ppmout bmp, filename
#
# Local Variables:
# mode: ruby
# coding: utf-8-unix
# indent-tabs-mode: t
# tab-width: 3
# ruby-indent-level: 3
# fill-column: 79
# default-justification: full
# End:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment