Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[QUIZ] Text Image (#50)

2 views
Skip to first unread message

Ruby Quiz

unread,
Oct 7, 2005, 9:01:55 AM10/7/05
to
The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

This week's Ruby Quiz is to create a tool that will allow you to preview an
image file at the command-line in pure text.

Your program will need to read in an image, in whatever format you want to
support, and respond with a text representation of the image that fits in the
terminal.

For example, given the image:

http://rubyquiz.com/images/Ducky.png

Your program might respond with something like:

........--**####**::::::::::::........
......--##oooooo\\**::::::::..........
......**oo==oooo\\\\::::..............
....--\\oooooooo\\$$**::..............
....**&&$$oooo\\$$$$%%................
....**\\oooo\\<<<<$$##................
....!!<<<<<<<<<<\\\\##..........::::::
....::%%<<<<<<<<\\$$^^....::**++**....
....^^%%<<<<<<<<$$$$##**""==++++==****
....**oo<<<<<<$$<<<<\\oo==++++++==""""
..!!==oo<<<<<<<<$$$$oo====++++==oo####
^^====oo\\<<$$\\oooooo==++======oo****
**++++====oooo====++++======oooo\\^^^^
++====++++========++======oooooo**....
""====++++==========oooooooooo##--....
""==================oooooooooo^^......
!!oo==oo======oooooooooo\\##^^::......
..""oooooooooooooooo\\oo""^^..........
..::**====oooooooooo==**::............
....::^^**""""""""!!..--::............
....::^^**""""""""!!..--::............

Go for as much accuracy as you can possibly squeeze out of it.


Jim Freeze

unread,
Oct 7, 2005, 9:52:39 AM10/7/05
to
Ahh, a half-toning algorithm.

Hal Fulton

unread,
Oct 7, 2005, 7:24:35 PM10/7/05
to
Ruby Quiz wrote:
> This week's Ruby Quiz is to create a tool that will allow you to preview an
> image file at the command-line in pure text.
>
> Your program will need to read in an image, in whatever format you want to
> support, and respond with a text representation of the image that fits in the
> terminal.

I never participate in these, but this one is tempting.

What about ANSI color? ;)


Hal

James Edward Gray II

unread,
Oct 7, 2005, 8:35:29 PM10/7/05
to
On Oct 7, 2005, at 6:24 PM, Hal Fulton wrote:

> I never participate in these, but this one is tempting.

Ah, why not? :) We do try to have a little fun.

Perhaps that means you need to send in a problem *you* can't resist! ;)

> What about ANSI color? ;)

Very clever. I didn't think of that...

James Edward Gray II


James Britt

unread,
Oct 8, 2005, 8:06:44 PM10/8/05
to

Might even make a few bucks, no?

http://www.artima.com/forums/flat.jsp?forum=123&thread=130735

James

--

http://www.ruby-doc.org - The Ruby Documentation Site
http://www.rubyxml.com - News, Articles, and Listings for Ruby & XML
http://www.rubystuff.com - The Ruby Store for Ruby Stuff
http://www.jamesbritt.com - Playing with Better Toys


Kero

unread,
Oct 9, 2005, 8:24:34 AM10/9/05
to
>> I never participate in these, but this one is tempting.
>
> Might even make a few bucks, no?
>
> http://www.artima.com/forums/flat.jsp?forum=123&thread=130735

do all contributors get a free sample?
*grin*

+--- Kero ------------------------- kero@chello@nl ---+
| all the meaningless and empty words I spoke |
| Promises -- The Cranberries |
+--- M38c --- http://members.chello.nl/k.vangelder ---+

Christian Neukirchen

unread,
Oct 9, 2005, 11:54:00 AM10/9/05
to
Kero <ke...@chello.single-dot.nl> writes:

>>> I never participate in these, but this one is tempting.
>>
>> Might even make a few bucks, no?
>>
>> http://www.artima.com/forums/flat.jsp?forum=123&thread=130735
>
> do all contributors get a free sample?
> *grin*

+1 :D

> +--- Kero ------------------------- kero@chello@nl ---+

--
Christian Neukirchen <chneuk...@gmail.com> http://chneukirchen.org


James Edward Gray II

unread,
Oct 9, 2005, 12:31:10 PM10/9/05
to
On Oct 9, 2005, at 7:26 AM, Kero wrote:

>>> I never participate in these, but this one is tempting.
>>>
>>
>> Might even make a few bucks, no?
>>
>> http://www.artima.com/forums/flat.jsp?forum=123&thread=130735
>>
>
> do all contributors get a free sample?

Contributors of quizzes and code used in the book will be contacted
very soon now...

James Edward Gray II

Simon Kröger

unread,
Oct 9, 2005, 5:56:38 PM10/9/05
to
Hi,

here is my attempt of solving this weeks quiz:

This solution uses the RMagick lib to read the image and
to find edges because trying to find apropiate letters for
halftoning is hard on all the different terminals.

I had another version without the generator/enumerator
stuff and it was a lot faster. But speed isn't an issue this
week and it's so much smoother than fiddling with indexes.

cheers

Simon

----------------------------------------------------------------
require 'RMagick'
require 'generator'
require 'enumerator'

puts "Usage: #{$0} <img> [size]" or exit if !ARGV[0]

img, size = Magick::ImageList.new(ARGV[0]), (ARGV[1]||40).to_f
factor = [size*1.5/img.rows, size/img.columns].min

img.resize!(img.columns*factor, 2*(img.rows*factor*0.75).round)
img = img.edge.despeckle.despeckle.normalize.threshold(50)

pixels = img.get_pixels(0, 0, img.columns, img.rows).map{|c| c.red.zero?}

pixels.to_enum(:each_slice, img.columns).each_slice(2) do |l|
puts SyncEnumerator.new(*l).map{|p1, p2|
[' ', "'", ".", ":"] [(p1 ? 0 : 1) + (p2 ? 0 : 2)]}.join('')
end
----------------------------------------------------------------

sample output:

.:'''''''.
.: :.
.: :.
: :
:' ':
.: :
: .':. ::
:'.' : .. ':
: :..' :' ': :
:. ' : .: :
': '::'':.. ':' . :
:: ''.. :' .:'':.
:: . ': .: ..' . '.
': ''''' .: .:::::''' :.
:: .:': :
.: : .:' :
.' '' '' :
:' :
: :
' :
: ' :'
: .:
' : .:
' :
: .:
: .:
: .'
'. :'
:. .'
'. .:'
':. .:'
':. .:'
''..........''


James Edward Gray II

unread,
Oct 9, 2005, 6:06:49 PM10/9/05
to
On Oct 9, 2005, at 4:56 PM, Simon Kröger wrote:

> Hi,
>
> here is my attempt of solving this weeks quiz:

[snip]

> ':. .:'
> ''..........''

Wow, I really like that! Too cool.

Here's the code used in the quiz:

#!/usr/local/bin/ruby -w

unless ARGV.size == 1 and File.exists? ARGV.first
puts "Usage: #{File.basename($0)} IMAGE_FILE"
exit
end

require "RMagick"

text = %w{. : - ^ ! * + " = % o # \\ $ < &}

image = Magick::Image.read(ARGV.shift).first

image = image.quantize(text.size)
image.scale!([39.0 / image.columns, 20.0 / image.rows].min)
image = image.quantize(text.size)

pixels = Array.new
0.upto(image.rows) do |y|
0.upto(image.columns) do |x|
pixel = image.pixel_color(x, y).intensity
pixels << pixel unless pixels.include? pixel
end
end
pixels.sort! { |a, b| b <=> a }

0.upto(image.rows) do |y|
0.upto(image.columns) do |x|
2.times { print text[pixels.index(image.pixel_color(x,
y).intensity)] }
end
puts
end

__END__

James Edward Gray II

Timothy Hunter

unread,
Oct 9, 2005, 6:18:40 PM10/9/05
to
My solution relies on having RMagick do all the work. Here's the output
for the test image:

+-------------------------------------------------------------------------+
| .::+====+:: |
| .:==o======ooo*oo+. |
| +oo=====++++===oo***+ |
| :oo==++++===++==ooooo*^*. |
| :oo====++++====oooooooo*^* |
| .oo=====++++========ooo**^^o |
| .ooo=====++++++======ooo**^^%: |
| :*oo*o==============oooo****^^^. |
| :ooo$WW^===========oooo****^^^^^= |
| ooo*WWW%=========oooo^^*^**^^^^^^ |
| oooo=o::o=====oooo*%WWW$^^*^^^^*^: |
| =ooo=:+====ooooo**o^WWW$***^****^: |
| .*o*o*ooooo==ooo*^*+====o*^*****^. |
| :o%$$$$$WW$$$%%^^^^**********^^= |
| .oWWWWWWWWWWWWWW$%%^**o****^^= :::::: |
| %$$$$$$$W$$W$WWWW$^****^^o. ...::::::::==: |
| =WWWW$$$W$$$$WW$$%^^****+. .........:::::::::::::::::=o+ |
| :$WWWWWWWWWWWW%^****^%%%%%^^**oo====++:::::::::::::::+=o+ |
| :+oo*$$WWWWWWW$%^*^^^^^%$$$%%%^**o===+:::::::::::::::::++==o: |
| .:====o*^%$$$$$%^^%%^^^^%%%%%%%^^**o===+::::::::::::::::::===oo |
| .:+++++==o*^%%%%%%$%%%%^^^^%%%^^^**oo===++:::::::::::::::::+==ooo |
| .:+::::+++=oo*^%%%$%%%%%^^^^^*****ooo===+++::::::::::::::::++===oo= |
| :++::::::+++=oo*^%%%%%%^^****ooooo=====++:::::::::::::++:::++====o*. |
| :+::::::::::+===o**^^^^**ooo=====+=+++++::::::::::::+::==++++===ooo+ |
| :+::::::::::::+++==oooooo====+::::+:::::+::::+::++::+::+==++=====oo= |
| .+::::::::::::::::++=+=====+::::::+:::::+::::+:::+:::+++========ooo= |
| :+::::::::::::::::::+:++++::::::++::::::::::+:::++:++==========ooo+ |
|.++++:::::::::::::::::::::::::::+:::::+::::+:::++++===========oooo: |
|.++++::::::::::::::::::::::::::::::++:+::+=+++==============oooo=. |
| +++++++::::::::::::::::::::::++::++::+==+=+===========o===ooo=: |
| :==++++::::+:::::::::+:+:::::::::+++===++=============ooooo=: |
| .===++++++++++::+++::+++:+++++::+===++====o==========o=o==:. |
| :=======+++++++++++++++++:++===============oo====oo=o==:. |
| :====================++++++==+=========o=====ooo====: |
| .==================================ooooooo=oo===+: |
| :+=============================oooo=o==oo==::. |
| ::==========================o====oo===::. |
| ::++==============+===========+::. |
| ..:::++++==++=+++++++++::.. |
| ....:.::::::... |
+-------------------------------------------------------------------------+


require 'RMagick'

CHARS = ['W', 'M', '$', '@', '#', '%', '^', 'x', '*', 'o', '=', '+',
':', '~', '.', ' ']
FONT_ROWS = 8
FONT_COLS = 4

img = Magick::Image.read(ARGV[0] || "Flower_Hat.jpg").first

# Resize too-large images. The resulting image is going to be
# about twice the size of the input, so if the original image is too
# large we need to make it smaller so the ASCII version won't be too
# big. The `change_geometry' method computes new dimensions for an
# image based on the geometry argument. The '320x320>' argument says
# "If the image is too big to fit in a 320x320 square, compute the
# dimensions of an image that will fit, but retain the original aspect
# ratio. If the image is already smaller than 320x320, keep the same
# dimensions."
img.change_geometry('320x320>') do |cols, rows|
img.resize!(cols, rows) if cols != img.columns || rows != img.rows
end

# Compute the image size in ASCII "pixels" and resize the image to have
# those dimensions. The resulting image does not have the same aspect
# ratio as the original, but since our "pixels" are twice as tall as
# they are wide we'll get our proportions back (roughly) when we render.
pr = img.rows / FONT_ROWS
pc = img.columns / FONT_COLS
img.resize!(pc, pr)

img = img.quantize(16, Magick::GRAYColorspace)
img = img.normalize

# Draw the image surrounded by a border. The `view' method is slow but
# it makes it easy to address individual pixels. In grayscale images,
# all three RGB channels have the same value so the red channel is as
# good as any for choosing which character to represent the intensity of
# this particular pixel.
border = '+' + ('-' * pc) + '+'
puts border
img.view(0, 0, pc, pr) do |view|
pr.times do |i|
putc '|'
pc.times { |j| putc CHARS[view[i][j].red/16] }
puts '|'
end
end
puts border

Ryan Leavengood

unread,
Oct 9, 2005, 7:57:01 PM10/9/05
to
I'm surprised how easy this Quiz was because of ImageMagick and
RMagick (based on the other submissions, I didn't try it.) Kudos to
the developers of those projects (though I imagine only the RMagick
developer will be reading this.)

Of course you quiz developers deserve kudos for your clever solutions
using RMagick.

I wonder how long the code would be for a solution that did NOT use
RMagick or any other image library? Maybe future submissions will
answer this question for me...

Ryan


James Edward Gray II

unread,
Oct 9, 2005, 8:45:07 PM10/9/05
to
On Oct 9, 2005, at 6:57 PM, Ryan Leavengood wrote:

> I'm surprised how easy this Quiz was because of ImageMagick and
> RMagick (based on the other submissions, I didn't try it.)

Indeed. I only found RMagick two weeks ago, but it impressed me
enough to make the quiz. It's a bit of a bear to get installed
(because of ImageMagick, not the Ruby bindings), but worth the effort.

I second the thanks to the developer for giving us such great tools
to work with!

James Edward Gray II

Dave Burt

unread,
Oct 10, 2005, 4:41:48 AM10/10/05
to
Hi,

I have a solution written five years ago by Brian Fundakowski Feldman. Is
that allowed?

This code, which is not mine, uses the GD library.

This is not my code:

http://www.dave.burt.id.au/ruby/img2ascii.rb

Cheers,
Dave


Simon Kröger

unread,
Oct 10, 2005, 5:02:45 PM10/10/05
to
James Edward Gray II wrote:

> Wow, I really like that! Too cool.

Thanks, yes it turned out better than expected.


.
. ' ::
.. : ':
.. :: ' '.
: '': :. . ::
'' :: :' . ':

:: :: :: :... . :.
': :: . . :: ':':: :':. :
: ::: :' : . ::' : .:: ::
:' .': ':: : : : '' :: ':
: :: : '' ' ': .
' :. :: : : : . ... :: . :
..:': : :: ' ':...: ' :. . :
:' ' :: :: '''' : .: ': ' :.
: ::'.' :: :: : ': ::::::: '
' :' :: .:' .::'' ' ::
: : : '..: ' .:' ....... :. :


:: .:' . ' .'' ' '. '.:
:::''' .:' .' . . '. ::

. : .:' .:' .. . .: ::
. '' .:' :' ' :' ::
' .::': .' :' .: ::
::..': : .' .:' . :
': :.:. : .:' ..:'' : :.


:. :::. .:' .:'' : ':
. ': ''.:. ' .:' .:' . :.
. :. :::. .:' .:' . ::
: : ': .:' : :::.
. ': '.'' .::' : :: ::

. ': . ..:' : :' .:
' ::' ..:' : .: .: .:
' :.:::'' : ' : :. :


'. ' :: .::' ':' :
: ' :' '.. .:
' ': :. .: :'
. : .' :

'. : .: .... . :.'
' ' :: : ::

Unfortunately my console has another aspect ratio than most mail readers
(more like 1.5/1 than 2/1) so i had to tweak it a bit for the image above.

the original is here:
http://perso.wanadoo.fr/psylo-vision/_images/wallpapers/_first/Tomb-Raider-cot%E9.jpg
(i made no other optimisation than choosing a suitable region)

cheers

Simon


Rob Rypka

unread,
Oct 10, 2005, 7:01:11 PM10/10/05
to
I tried this one out. I'm not sure if I was successful or not...

My first instinct was to rank characters for intensity levels
programmaticly. I then came to the realization that not all
characters have the same distribution of intensity, so I thought I
would try splitting them up, and matching them with sets of pixel
intensities (instead of just one).

ruby create_tables.rb num [extended]
does the first part - pass it a number (number of pixels sqare that
will be replaced by one character). uses extended ASCII if the second
argument is applied.

ruby i2a.rb src_img outfile num [normalized]
does the second part. takes num x num clumps of pixels from src_image
and finds the closest character from the appropriate table (by
Euclidean distance), puts it out to outfile. If normalized is
supplied, the image is normalized for maximum contrast in the source
image.

You can find the code here:
http://www.rabble-rooster.org/rubyquiz/50/

Results are in the results folder.

The underscore also rendered to pure white, so I had to remove it
manully from the YAML tables. Those tables are available also.

I know this code needs some refactoring/reengineering, and I didn't
take the time to look into how *Magick did things like scaling. Feel
free to chide me on any points that are inefficient/bad/wrong/stupid.

--
Rob


Brian Schröder

unread,
Oct 10, 2005, 9:35:53 PM10/10/05
to
Hello Everyone,

I went into this quiz using the following approach. I split the image
into pieces and find for each piece of the image the letter most
similar to this piece. I did it all with image magick. There is room
for improvement in the distance function, but it works ok. I'm sure it
is the slowest solution so far, but it works.

You can find the sourcecode and examples at

http://ruby.brian-schroeder.de/quiz/asciiview/

best regards,

Brian

_,,uuuuu,,_
_u:EEEEEEEEEE{{[p,_
_:EEEEEEEEEEEEEEE{{[Ou
:EEEEEEEEEEEEEEEE{{{{[O3_
UEEEEEEEEEEEEEEEE{{E{{{[O3_
UEEEEEEEEEEEEEEEEEEEEE{{[S3B
;{EEEEEEEEEEEEEEEEEEEE{{[SO3B$
;{{{EEEEEEEEEEEEEEEEEE{{[[O3333u
{[[SQQ{EEEEEEEEEEEEEEE{{[[OO3333B
E{{[###${EEEEEEEEEEEE{{[SSSS333333q
E{{{###PEEEEEEEEEEE{SSQQ3333O333333
{{{{ " LEEEEEEEE{{[S$####$333333333
{{{{{:cEEEEEE{{{[[SZZ####Z3O3333OS3
F{{{{{{EEjjEEE{[SO35EEEFEEOO3OOOO33
F{]###########QQ3993SSSOSOO[[OS33~
"f################$Q3O[[[[OSS33~ L-F"FL
I#IIII#############Q[SSOO33P" L--" FE.
P######$##IIII####T3SS333P' LL---F -EEU
##############T3OOO3OOQ$$$9SSUUUUEEEEEEF- -EEEE
.;{[I##########TS3333S3$IIII$$B3O[{EEEEEFF -EEEE{
LEEEE{O#$I#####@BBB33333$$$II]$$B3O[{EEEEEF- -EEEE{L
LEEEEEEE[O3$$$$$$$$$$$$9BB$$$$$$@B3O[{EEEEEE-- --EEEEE{{
LEEEEEEEEEE[3B$$]I]]$$$$$BBBBBBB333O[[{EEEEEEF-- ---EEEEEEE{{
LEFFFFFFEEEEE{[3B$$]]$$$$B333OOO[[[{{{EEEEEEEEFF .EE--EEEEEEEE{{"
-FFF-- --FEEEEE{[3B@@@@@B33O[[{{EEEEEEEEEEEFFF L E .LEEEEEEEEEEEE{{
-FFFF- -FFEEEEE{[O22OOO[[{EEEEEEEEEFFFEEF --- LE -F .EEEEEEEEEEEE{{
LEF--- --FFEEEEEEEEEEEEEEEFFF-FEF EE LE LE" -E EEEEEEEEEEEE{{"
EEE--- --FFFEEEEEEEEEEEFF LEF LE LE -EE LEEEEEEEEEEEEEE{{"
EEEEEF- ---FFEEEFEEFF -EE" -EF LEFL-EEEEEEEEEEEEEEEEEE{E"
EEEE-F--- --F-FFFFFF -EF LLE LLEFEEEEEEEEEEEEEEEEEEEE{{E
EEEEEE--------- --- --- LE LLEFFLEEEEEEEEEEEEEEEEEEEEEEE{{EF
EEEEEEEEE--F------ - -L--LLLLLEEELLEEE-LEEEEEEEEEEEEEEEEEEEEEEEE{EE"
FEEEEEEEEEEEE-----L-LLEEEEFF" .""FF"LEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"
.EEEEEEEEEEEEEEEEEEEEEEEEEELLLEELLLEEEEEEEEEEEEEEEEEEEEEEEEEEEEF
.FEEEEEEEEEEEEEEEEEEEEEEEEEEEFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEF
FEEEEEEEEEEEEEEEEEEEEEEEELLEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEF
FEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"
"EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE{{E{EEEEEEEEEF"
"FEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE{EEEEEEF"
FFEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEFF"
"FFEEEEEEEEEEEEEEEEEEEEEEEEEEEFFF"
"FFFFFFEEEEEEEEEEEEEEFF"
FF"""""


_yv*/^^^^^^^/#Yy_
y#^ '^#y
y# ^#,
_#^ Ty
_# #y
# ,- #_
#^ #
_#' ^g
y# _Y/Xy #
# # Tg ^$
# #,^ y^ y**v_ #
$ ###^ # _. # #
# ^ _$_'_,# #
#y _yyyyyyv*gyy_ ^###IIP #
#y#$ ^ ^P*yy_ '' yf ,yvy_
^#$ ^P*y_ y# _y#^^ ,^#y
'# ^P/*+ ,_ _T* _#^ __yv*P^ ' ^#_
Ty~/**^^M ^P**#^v/^y# _g#yyyyyyyyvY*#P^^ #y
y# '^rr**~'y#^^ _vP^' #_
y#P^ #P_ y#^ T Y #
_#^ ^#C/xvv-_yYP' ^$
y#^ ^^Pr/~^' #
_#^ #
g^ yT
#^ # #
# F ^ $^
#' _P @ # g # y^
y^ ,^ / g ^ y^
# v y^
# _/ y _ #^
# Y^ _. , ,#'
# r^ _^ g^
# r/ Yr *^ _x ' y#
^y _ *' _- y#^
# __ r _#^
# '^ ' ' y#^
^# - y#^
#y _ _YP'
^#, r _y#^
^#y r _v#^
^#y_ -x `~ -r _y*P^
^/*y_ ~/***r__yY/^^
^^P**YvyyyyyyyyyvvY*/P^^
--
http://ruby.brian-schroeder.de/

Stringed instrument chords: http://chordlist.brian-schroeder.de/


Rob Rypka

unread,
Oct 11, 2005, 10:40:08 AM10/11/05
to
On 10/10/05, Brian Schröder <ruby....@gmail.com> wrote:
> I went into this quiz using the following approach. I split the image
> into pieces and find for each piece of the image the letter most
> similar to this piece. I did it all with image magick. There is room
> for improvement in the distance function, but it works ok. I'm sure it
> is the slowest solution so far, but it works.
>
> You can find the sourcecode and examples at
>
> http://ruby.brian-schroeder.de/quiz/asciiview/

I do believe I got pWn3d!!1!... Nice work, Brian.

I think my solution is probably slower, though :-). I'm too used to
doing this kind of thing in C/++.

--
Rob


Brian Schröder

unread,
Oct 11, 2005, 2:03:18 PM10/11/05
to
> [snip]

> >
> > You can find the sourcecode and examples at
> >
> > http://ruby.brian-schroeder.de/quiz/asciiview/
>
> I do believe I got pWn3d!!1!... Nice work, Brian.
>

Thank you, rob. Though I can't decipher sms-code, though I hope it's
good for me ;-)

greetings,

Brian

Simon Kröger

unread,
Oct 11, 2005, 5:48:51 PM10/11/05
to
Hi,

i'm completely new to ncurses, so could someone with more exprerience
(or another OS) try this and tell me whats happening?
On windows all the colors are very pale (are there consts for lighter
colors?)
-----------------------------------------------------------------------
require 'RMagick'
require "ncurses"

Ncurses.initscr


puts "Usage: #{$0} <img> [size]" or exit if !ARGV[0]

puts "Sorry, no colors!" or exit unless Ncurses.has_colors?

img, size = Magick::ImageList.new(ARGV[0]), (ARGV[1]||40).to_f

factor = [size*2/img.rows, size/img.columns].min
img.resize!(img.columns*factor, (img.rows*factor*0.5).round)

COLORS =
[[Ncurses::COLOR_BLACK, [0, 0, 0]], [Ncurses::COLOR_RED, [1, 0, 0]],
[Ncurses::COLOR_GREEN, [0, 1, 0]], [Ncurses::COLOR_BLUE, [0, 0, 1]],
[Ncurses::COLOR_YELLOW, [1, 1, 0]], [Ncurses::COLOR_MAGENTA, [1, 0, 1]],
[Ncurses::COLOR_CYAN, [0, 1, 1]], [Ncurses::COLOR_WHITE, [1, 1, 1]]]

GRADIENT = [[0, ' '], [50, ':'], [100, '|'], [150, 'I'], [200, '#']]
COLORMAP = {}

COLORS.size.times do |bg|
COLORS.size.times do |fg|
next if fg == bg
i = (bg*COLORS.size) + fg
Ncurses.init_pair(i, COLORS[fg][0], COLORS[bg][0])
GRADIENT.each do |gr, c|
r = COLORS[fg][1][0] * gr + COLORS[bg][1][0] * (255-gr)
g = COLORS[fg][1][1] * gr + COLORS[bg][1][1] * (255-gr)
b = COLORS[fg][1][2] * gr + COLORS[bg][1][2] * (255-gr)
COLORMAP[[r, g, b]] = [i, c]
end
end
end

#(16*16*4).times do |i|
# Ncurses.stdscr.attrset(Ncurses.COLOR_PAIR(i))
# Ncurses.stdscr.mvaddstr(i/16, (i%16)*4, 'TEST')
#end
#Ncurses.refresh

pixels = img.get_pixels(0, 0, img.columns, img.rows)

img.rows.times do |y|
img.columns.times do |x|
p = pixels[y*img.columns + x]
r, g, b, best, dist = p.red, p.green, p.blue, -1, 255
COLORMAP.each do |k, v|
d = Math.sqrt((r-k[0])**2 + (g-k[1])**2 + (b-k[2])**2)
(best, dist = v, d) if d < dist
end
Ncurses.stdscr.attrset(Ncurses.COLOR_PAIR(best[0]))
Ncurses.stdscr.mvaddstr(y, x, best[1])
end
end

Ncurses.refresh
-----------------------------------------------------------------------

maybe someone can help..

cheers

Simon


Harold Hausman

unread,
Oct 11, 2005, 9:23:09 PM10/11/05
to
Hi,

Best quiz yet. Er, well, quiz which held my attention longest anyway. :)

My solution is um, less... elegant, than some of the others, but in a lot of
ways, like a parent who mistakenly thinks their ugly child is cute I kinda
like it. I don't really share my code a lot (maybe for obvious reasons) but
I'm just so pleased with how easily ruby lets me hack things to high heaven
and this one in particular makes me smile. Take special note that it only
works on 24 bit .bmp files and put your hard hat on if you think you're
going to feed anything but that into it. ;) Okay, enough blather, without
further ado, the first code I've shared with you guys:

#It only supports 24bit bmp files, and it even chokes on most of them ;)
#It creates one character per pixel (which has obvious implications)
#But it's 40 lines of pure ruby no lib use binary file up-hackery...
#And quite frankly, thats what I do.

MyPixel = Struct.new( 'MyPixel', :r, :g, :b )

the_gradient = %w|D Y 8 S 6 5 J j t c + i ! ; : .|
###############PUT YOUR FILENAME
HERE#########################################
the_file = File.new('ducky.bmp', 'rb')
the_file.read(2) #BM
the_file.read(4).unpack('V')[0] #filesize
the_file.read(4) #unused
the_file.read(4).unpack('V')[0] #offset from beginning to bitmap data
the_file.read(4).unpack('V')[0] #size of bitmap header
image_x = the_file.read(4).unpack('V')[0] #width x in pixels
image_y = the_file.read(4).unpack('V')[0] #height y in pixels
the_file.read(2).unpack('v')[0] #planes?
the_file.read(2).unpack('v')[0] #bits per pixel
the_file.read(24) #unused

the_bitmap = []
puts "CRRRRUNCHHHH --- please wait, reading file..."
image_y.times do |row|
the_bitmap[row] = []
image_x.times do |col|
the_bitmap[row][col] = MyPixel.new( 0, 0, 0 )
the_bitmap[row][col].b = the_file.read(1).unpack('c')[0]
the_bitmap[row][col].g = the_file.read(1).unpack('c')[0]
the_bitmap[row][col].r = the_file.read(1).unpack('c')[0]
end
end

puts "output coming:"
the_output = File.new('output.asciiart', 'w')
(image_y-1).downto(0) do |row|
image_x.times do |col|
the_avg =
(the_bitmap[row][col].b+the_bitmap[row][col].g+the_bitmap[row][col].r)/3
the_output.write(the_gradient[the_avg>>4])
end
the_output.write("\n")
end

Dave Burt

unread,
Oct 11, 2005, 9:59:01 PM10/11/05
to
Harold Hausman:

#It only supports 24bit bmp files, and it even chokes on most of them ;)

Thanks for submitting this - well done, Harold.

(If I was going to solve this quiz, I'd have done a BMP-reader.)

Cheers,
Dave


daz

unread,
Oct 12, 2005, 8:03:58 AM10/12/05
to

Harold Hausman wrote:
> Hi,
>
> [...] the first code I've shared with you guys:

Hi Harold - and thanks for doing that.

> #It creates one character per pixel (which has obvious implications)

Not obvious to me until I tried it.
That's a fair-sized hunk of duck ;-))

> #But it's 40 lines of pure ruby no lib use binary file up-hackery...

Excellent.
I refactored a bit:

#------------------------------------------------------------------------
Gradient = %w{ D Y 8 S 6 5 J j t c + i ! ; : . }

# http://www.d10.karoo.net/ruby/quiz/50/duck.bmp (NOTE: 800KB BMP)
bmp = File.open('duck.bmp', 'rb') { |fi| fi.read }
bmo = bmp[10, 4].unpack('V')[0] # offset to bitmap data
image_x, image_y = bmp[18, 8].unpack('VV') # width x / height y (pixels)
by_start = bmo + ((image_y - 1) * (image_x * 3))

File.open('output.txt', 'w') do |fo|
by_start.step(bmo, -(image_x * 3)) do |by_ptr|
image_x.times do |x|
t = 0; 3.times {|n| t += bmp[by_ptr + (x * 3) + n] }
fo.putc( Gradient[ (t / 3 ) >> 4 ] )
end
fo.puts
end
end
#------------------------------------------------------------------------

There's one change in effect from your original and I suspect
you may have intended it differently ...

the_file.read(1).unpack('c')[0]

... gives a signed byte so, when you take the average by adding and
dividing by 3, you can be adding negatives. Replacing those with ('C')
gives you unsigned bytes and the overall result matches the output from
the code above.

Maybe we'll see a smaller fowl soon :-)


daz

Robbie Carlton

unread,
Oct 12, 2005, 8:39:21 AM10/12/05
to
thank you very very much. I've been struggling with a problem in the
graphics library I'm using for lisp (lispworks capi library) which requires
me to generate a bitmap file from an array of pixels. I couldn't decipher
any of the specs I've found for the format because they go into loads of
extraneous detail about color tables and compression which I don't need to
know about. Your code is the clearest definition of the spec I need
(assuming it's correct). So thanks

Dominik Bathon

unread,
Oct 12, 2005, 10:18:10 AM10/12/05
to
On Wed, 12 Oct 2005 14:07:08 +0200, daz <do...@d10.karoo.co.uk> wrote:

>
> Harold Hausman wrote:
>> Hi,
>>
>> [...] the first code I've shared with you guys:
>
> Hi Harold - and thanks for doing that.

Yes, a really nice solution.

> ...

> Maybe we'll see a smaller fowl soon :-)

I am not sure if you meant golfing by "a smaller fowl", but anyway here
are my attempts:

The first version version is just a refactoring like yours:

GRADIENT = %w|D Y 8 S 6 5 J j t c + i ! ; : .|
file = File.new(ARGV.shift || "ducky.bmp", "rb")
file.read(2+4+4+4+4) # headers
image_x, image_y = file.read(8).unpack("VV") # width / height
file.read(2+2+24) # headers

puts((0...image_y).collect do |row|
(0...image_x).collect do |col|
GRADIENT[(file.read(3).unpack("CCC").inject { |a,b| a+b } / 3) >> 4]
end.join
end.reverse)

It writes to stdout and optionally uses ARGV.shift as input.

And here are two different short version, they need ARGV[0] and also write
to stdout:

f=open$*[0],"rb";w,h=f.read(54)[18,8].unpack"VV"
puts((1..h).collect{(1..w).collect{"DY8S65Jjtc+i!;:."[f.read(3).unpack(
"CCC").inject{|a,b|a+b}/48,1]}.join}.reverse)


d=IO.read$*[0];puts
d[54..-1].scan(/.../m).map{|s|"DY8S65Jjtc+i!;:."[s.unpack(
"CCC").inject{|a,b|a+b}/48,1]}.join.scan(/.{#{d[18,4].unpack"V"}}/).reverse


The second one might be problematic on Windows (I am not sure if IO.read
reads binary or not).

Dominik


Harold Hausman

unread,
Oct 12, 2005, 10:26:28 AM10/12/05
to
On 10/12/05, daz <do...@d10.karoo.co.uk> wrote:
>
>
> Harold Hausman wrote:
> > Hi,
> >
> > [...] the first code I've shared with you guys:
>
> Hi Harold - and thanks for doing that.

Hey, np, I'm glad I did now! (:


> > #But it's 40 lines of pure ruby no lib use binary file up-hackery...
>
> Excellent.
> I refactored a bit:

...snip your most excellent code...


> There's one change in effect from your original and I suspect
> you may have intended it differently ...
>
> the_file.read(1).unpack('c')[0]
>

> .... gives a signed byte so, when you take the average by adding and


> dividing by 3, you can be adding negatives. Replacing those with ('C')
> gives you unsigned bytes and the overall result matches the output from
> the code above.

Ah, great point. I was vaguely wondering where the solarization effect was
coming from.
Unfortunately my attention span is painfully short. ;)

Maybe we'll see a smaller fowl soon :-)

But what of the majesty of the whole thing?! (:

daz
>

Anthony Moralez

unread,
Oct 12, 2005, 4:40:09 PM10/12/05
to
I'm happy to submit my first ruby quiz solution. I really liked the RMagick
library. It made this pretty easy.

the code:
require 'RMagick'

class ImageToAscii
@@ascii_pixel = [ Array.new(10, '#'),
Array.new(35, '.'),
Array.new(5, '\\'),
Array.new(15, '-'),
Array.new(20, '*'),
Array.new(15, '+'),
Array.new(20, ':'),
Array.new(20, '/'),
Array.new(30, '='),
Array.new(30, '|'),
Array.new(30,'@'),
Array.new(30, ' ')].flatten!
def initialize( image )
@image = image
end
def convert
prepare_image
translate_pixels
end
def translate_pixels
pixels = @image.get_pixels(0, 0, @image.columns, @image.rows)
new_pixels = pixels.map { |pix| to_ascii pix.intensity}
0.upto(@image.rows) do |row|
new_pixels.insert( (row*@image.columns+row), "\n")
end
new_pixels
end
def prepare_image
@image = @image.blur_image.blur_image.scale(40,40)
end
def to_ascii( index )
@@ascii_pixel[index]
end
end

img = Magick::ImageList.new(ARGV[0])
converter = ImageToAscii.new(img)
puts converter.convert.to_s


the output:
from ducky.png

@@|||@
@||||||==@
@||||||||==|
@|||||||||===@
|||||||||||===
@|||||||||||==/@
|||||||||||===/=
|==|||||||||===//
==\/|||||||====//@
@=/.+||||||=====//|
@=|||||||==+.+==/==
@==|||||===*\:=====
==========|||====|
|:**-**://=======@
=-------*:=====| @@@@
@*-**-----+==== @@@@@|@
*--*-**-*/===@ @@@@@@@@@||
+\\\\\-:===:://==|||@@@@@@@@|||
@|/*-\\-:/=//++:/==||@@@@@@@@@|||
@||=/+*+:////::::/=|||@@@@@@@@@|||@
@||||=/::::://////==|||@@@@@@@@@|||@
@@@|||=/:++:///=====|||@@@@@@@@@|||=@
@@@@@|||=/:://====|||||@@@@@@@||||||=
|@@@@@||===/===|||||||@@@@@@@||||||||
@@@@@@@@|||===|||@|@@@@@@@@@@@||||||=
|@@@@@@@@||||||@@@|@@|@@|@|@|||||||||
@|@@@@@@@@@@|||@@@|@@@@@@@@||||||||||
@||@@@@@@@@@@@@@@@@@@@@|@|||||||||||@
@||@@@@@@@@@@@@@@@@@|||||||||||||||@
@||||@@@@@@@@@@||||||||||||||||||||
|||||@|@@@@||@@@@@|||||||||||||||
||||||||||||||||||||||||||||||||
@|||||||||||||||||||||||||||||@
||||||||||||||||||||||||||||@
@|||||||||||||||||||=||||||@
@|||||||||||||||||=|||||@
@||||||||||||||||||||@
@||||||||||||||||@
@@||||||||||@@
@@@@@@

and from tomb-raider.png:

.......................................
.......................................
...............................*.......
...................:.............*.....
.........--......../-+-..........:.....
........\--.......*|+=*-.\.......\*....
........**|:...#..-|:=/+-..--.....*....
........**@=.......|/=||=-*=|:....*....
........**@=.......://|@|=|||=\...*....
........*-@=......./==||||@||/....+....
...#..-#.-@=.......==|||||||=/-...-....
...#.--***@|........==||||===/:*..+....
....*:+-|*@|........+==||=/====/...-...
...====:|-@|.........==/+===||=|..-\...
.-*==|||=-@|-#.......\-../|=||==:..*...
.**||@@@=-||*....\.......-|==+**+..-...
.-+|@@@|=-/*............*|/\.#.....*...
..-|@@||*..............+:.-://::-*.*...
\..||||=.............\-../=====//-**...
...+==*......#......\-.-===|||==/+-*...
\..................\../@|||@||===:-*...
\\....-.\........-...*@|||@@||===*-*...
...#.:|||...........\|||||||||==/.--...
...../|@|/.........*||||@@@||==/..--...
.....+=@@=......../|||@@@@||=/*...\+...
......||@|/.....\=|||@@@|||=-.....\\*#.
......==|@=+...*=|||@@@|||*.........:..
......*=|||=*.:=|||@@|||/.#.....\..\/-.
......./=|||=/==|@@||||+........:..-/+.
.......-==|||=|||||||/.........\=-.\=/.
....\.../=|||||||||=-........\\=/\..=..
....\...\=|||||||/-..........**=*-.:*..
....\..../|@@@|=:...........--=+:+\/...
.....\...*==/*.++...........-+/*+:*+...
.....\.........:*...........+/--++.-...
.....\........./*...........++-**......
..............-/*...........//-*.......
...#.........\.-.............|-........
...#.........-.*.............:+\.......
...............+.......-......=:.......


Thanks for letting me play too.

Anthony Moralez

Wilson Bilkovich

unread,
Oct 12, 2005, 5:39:24 PM10/12/05
to
On 10/11/05, Harold Hausman <hhau...@gmail.com> wrote:
> Hi,
>
> Best quiz yet. Er, well, quiz which held my attention longest anyway. :)
>
> My solution is um, less... elegant, than some of the others, but in a lot of
> ways, like a parent who mistakenly thinks their ugly child is cute I kinda
> like it. I don't really share my code a lot (maybe for obvious reasons) but
> I'm just so pleased with how easily ruby lets me hack things to high heaven
> and this one in particular makes me smile. Take special note that it only
> works on 24 bit .bmp files and put your hard hat on if you think you're
> going to feed anything but that into it. ;) Okay, enough blather, without
> further ado, the first code I've shared with you guys:
>
Man, the output of this is very cool with large files and tiny font
sizes. Excellent.


Adam Shelly

unread,
Oct 12, 2005, 7:30:55 PM10/12/05
to
On 10/11/05, Dave Burt <da...@burt.id.au> wrote:
> Harold Hausman:
> #It only supports 24bit bmp files, and it even chokes on most of them ;)
> (If I was going to solve this quiz, I'd have done a BMP-reader.)
>
I had trouble installing the RMagick gem, so I went ahead and did a bmp reader.
In retrospect, it probably would have a better use of my time to
figure out what was wrong with the install.
My biggest gotcha in writing the BMP reader was forgetting about the
padding bytes in the raw data. I made the mistake of doing my early
tests with an 80x80 square file, which worked fine without handling
padding. Oh, and unpack is weird. Why can't I extract bits and
nibbles as integers instead of strings?

---
# iview.rb
# Adam Shelly
# for ruby quiz #50
#usage iview file [screenwidth] [threshold] [disable_optimization]
# Displays BMP files as ascii art. resizes to screenwidth (default=80)
# threshold controls edge sensitivity. Lower for more detail, raise
for less. (default 500)
# File must be BMP.
# supports bit depths of 2,4,8,24
# maybe 16, but I didn't test any
# converts image to greyscale, resizes to screenwidth, does sobel edge
detection, displays edges as 'x'
# unless disable_optimization is non-nil, re-runs the edge detector
several times to improve pixel density
require 'Matrix'

class BMFile
def initialize filename
@f = File.open(filename, "rb")
end
def read size, spec
@f.sysread(size).unpack(spec)
end
def parse spec
rslt = spec.inject({}){|h,v| h[v.first]=read(v.last,
v.last==2?'S':'L').first; h}
rslt.each {|tag,v| puts "Read #{tag} = #{v}"} if $DEBUG
rslt
end
end


class BmpView
attr_reader :show
FileHeaderSpec = %w( type size reserved offset).zip [2,4,4,4]
InfoHeaderSpec = %w( size width height planes bpp compression
imgSize xppm yppm colorsUsed clrimp).zip [4,4,4,2,2,4,4,4,4,4,4]

def initialize filename, screenwidth = 80
@file = BMFile.new filename
file_h = @file.parse FileHeaderSpec
info_h = @file.parse InfoHeaderSpec
throw "#{filename} is Not a BMP file: #{file_h['type']}" if
file_h['type'] != 19778 #=='BM'
@width,@height = info_h['width'], info_h['height']
@bpp = info_h['bpp']
@size = (@width*@height*@bpp/8.0).ceil
@image = resize( load_data , screenwidth)
@show = edgefind @image
end

def greyscale a
(a[0]+a[1]+a[2])/3
end

def load_ctab #load color table, convert to greyscale right away.
@ctable = (0...2**@bpp).inject([]){|a,v|
a<<greyscale(@file.read(4,"C4"))}
end

def calc_padding
databits = @width*@bpp
paddingbits = (32-(databits%32)) %32
width, sparebytes = (databits+paddingbits)/8,paddingbits/8
end

def load_data
puts "loading data..."
pattern = {1=>"B8",4=>"H2",8=>"C1" ,16=>"L1",24=>"C3"}
throw "#{@file} is not a Valid BMP file" if !pattern[@bpp]
rowdata =[]
width,sparebytes = calc_padding
if @bpp < 24
load_ctab
@height.times do
line = @file.read(width, pattern[@bpp]*width)
line = line.map{|s| s.split(//)}.flatten if @bpp < 8
#separate all the values
sparebytes.times { line.pop }
#reject the padding
rowdata << line.map {|s| @ctable[s.to_i]}
end
else
@height.times do
rowdata << (0...@width).inject([]){|a,v|
a<< greyscale(@file.read(3, pattern[@bpp]))
}
sparebytes.times {@file.read(1,"C")} #slurp padding
end
end
Matrix.rows(rowdata.reverse,false)
end

def resize data, screenwidth
factor = (@width / screenwidth.to_f).ceil
@width /= factor; @height /= factor
newdata = Array.new(@height){ Array.new(@width) }
puts "Resizing by #{factor} to #{@width}x#{@height}... "
@height.times { |y|
@width.times { |x| sum = 0;
grid = data.minor(y*factor,factor,x*factor,factor)
grid.to_a.flatten.each{|e|sum+=e} #take average over square
newdata[y][x]=sum/factor
}
}
Matrix.rows(newdata,false)
end

def edgefind data
puts "Finding Edges..."
@output = ""
gx =Matrix.rows [[-1,0,1],[-2,0,2],[-1,0,1]] #sobel convolution kernels
gy = Matrix.rows [[1,2,1],[0,0,0],[-1,-2,-1]]

1.upto(@height-2) {|y|
1.upto(@width-2) {|x|
sumX,sumY = 0,0
v = data.minor((y-1)..(y+1),(x-1)..(x+1)).row_vectors
3.times do |i|
gx.row(i).each2(v[i]) {|a,b| sumX += a*b}
gy.row(i).each2(v[i]) {|a,b| sumY += a*b}
end
@output += ((sumX.abs+sumY.abs) > $threshold) ? 'x' : ' '
}
@output+="\n"
}
@output
end

def optimize
8.times do #if it doesn't get better in 8 tries, give up...
density = @show.count('x') / (@width*@height).to_f
break if (0.12 .. 0.33) === density #rough heuristic
puts show,density,$threshold if $DEBUG
@show = edgefind @image
$threshold *= density / 0.2
end
end
end

$threshold = (ARGV[2]|| 500).to_i
b = BmpView.new(ARGV[0]||"ducky.bmp", ARGV[1]||80)
b.optimize unless ARGV[3]
puts b.show

---
$ ruby iview.rb ducky.bmp
loading data...
Resizing by 7 to 71x78...
Finding Edges...
xxxxxxxxxxxxxxxxx
xxxxxx xxxxx xxxxxx
xxxxx xx xxxxx
xxxx xxxxx
xxxx xxx
xxx xxxx
xxxx xx xxx
xxx x xxxx
xxx xxx
xxx xxx
xxx xxx
xxx xxx
xxxx xxx
xxx xxxx xxx
xxxxxxxxx xxx
xxxxxxxxxx xxx
xxxxxxx xxx xxx xxx
xxx xxxxxxx xxxxx xxx
xxx xxxxxx xxxxxxx xxx
xxx xxxxx xxxxxxxxx xxx
xxx xxxxx x xx xxx xxx
xx xxxxx xxxxxxxxx xxx
xxx xx xxxxxxxx xxx
xxx xxxxxxxxxx xxxxxxxx xxx
xxxxxxxxxxxxxxxxx xxxxxxx xxx
xxxxxxxxxxxxxxxxxxxxxxx xxx xxxxx
xxxxx xxxxxxxx xxxx xxxxxxxx
xxxx xxxxxx xxx xxxxxx xxxx
xxx xx xxxxx xxxx xxxxxx xxx
xxxx xxx xxx xxxx xxxxxxxx x xxx
xxx xxxxx xxxx xxxxxxxxxxxxxxxxxxxx xxxxx
xxx xxxx xxxx xxxxxxxxxxxxxxxxx x xxx
xxx x xxxx xxxxxxxxxxxxxxx xxxx
xxxxxx xxx xxxxxxxxx x xx
xxxxxxxxx xxxx xxx xxx xx
xxxxxxxxxxxxxxxxxx xxx xxxxxx x
xxxx xxxxxxxxxxxx xxx xxxxxx x
xxxx xxxxxx x x xx xxxxxxx x
xxxx xxxxxx xxxxxxxxxx x
xxxx xxxxx xxxxxxxxxx x
xxxx xxxxxx xxxxxxxxxx x
xxxx xxxxxx xxxxxxxxxxxxxxxx x
xxx xxxxxxx xxxxxxxxxxxxxxx x
xxx xxxxxxxxxxxxxxxxxxxx x x xx
xxx xxxxxxxxxxxxxxxxxx xx x xx
xxx xxxxxxxxxxxxxxxxx x x xxx
xxx xxxxxxxxxxxxxx x xxx
xx xxxx x xxx x xx xxxx
xx xx x xxx
xx xxxx
x x xxx
x x xxxx
x xxxx
x xxx
x xxxx
x xxxx
x xxxx
x xxxx
xx xxxxx
xx x xxxxx
xx xxxxx
xxx xxxxx
xxx xxxxx
xxxx xxxxx
xxx xxxxx
xxxx xxxxxxx
xxxx xxxxxx
xxxxx xxxxxx
xxxxx xxxxxxx
xxxxxx xxxxxxx
xxxxxxx xxxxxxxx
xxxxxxxx xxxxxxxx
xxxxxxxxx xxxxxxxx
xxxxxxxxxxxx xx xxxxxxxxx
xxxxxxxxxxx xxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxx

$ ruby iview.rb toumb.bmp
loading data...
Resizing by 7 to 77x108...
Finding Edges...
Finding Edges...
Finding Edges...
xx
xxx
xxx xx xx
xx xxx
xxxx
x x xx xx
xxxx xxxxx
xxxx xx xx
xxxx xxxx xxxxx
xxxxxxx xxxx x xxxx
xx x xx xxxxxxxx xxxx
x x xxxxxxxxx xx xx
x xx xxxxxxxxxx xxxxx
xx xx xx xxxxxxx xxxx
xx xx xx xxxxxxx x xx x
xxx xxxxxx xx xxx xxx xx x
xxx xxxxxxx xxxxxxxxxxxx xxxx xx xx
xxx xx xxxx xxxxxxxxxxxxxx xxxxxx xxxxx
xxx xx xxx xxxxxxx xxxxx xxxxxxxxx xxxx
xxx xx xxx xxxxx x xxxx xxx xxxx x xx
xx xx xxx xxxxxxxxx xxxxxx xxx xxxx
xx xx xxx xxxx xxx xxxxxx xxx xxxx
xx xxx xxx xxx xx xxxxx xxx xxxx
xx xxx xxx xxx xx xx xxxx xxxx
xxx xxxxxxx xxxxxxx xxxxx xxxx
x x xxxxxx xxxxxxx x xxx xxxx
xxx xxxxxx xxxxxxx x xxxx xxx
x xxxxxxxx xxxx x xxxx x x
x xx xxxxxxxx xxxx x xxxxxx xxxx
x xxxxxxxxxx xxx x x xxxxx xxxx
x xxxxxxxxxx xx x x xxxxx xxxx
xx x xxxxxxxxxxxx xxx x xx xxxxx xxxx
xxxxxx xxxx xxxxxx xxx x xxxx xx x x
xxxxxxxxxx xxxxxxxx xxxxx xxxx xx xxx
xxxx xxxxxx xxxxxx xxxx xxx xx xxx xxxx
xxxxx xxxxxx xxxxxx xxx xxxxxxxx xxx xxxx
xxx xxxxx xxxxxx xxxxxxxxxxxx xxxx xxxx
xxxxx x xx xxxxxx xxxxxxx xx x x x xx x x
xxxxxx xx xxxxxxxxx x xxxx x x xxxx xx x
xxxxxxxxx xx xxxxxxx xxx x xxxx x x
xxxxxx xxx xxxxxxx xxxx xxxxxxxxxx x x
xx xx xxx xxxxxxx xxxxxxxxxxxxxxxxx xxx
xxxxx xx xxxxxxx x xxx xxxxxx xxxx x x
x xxx x xxxxxxxxxx xxxxxxxxx x x
x xxx xxxxxxxx xxxxxxx xxxx xxxx x xx
x xxx xxxxxxx xxxxxx xxxxxxxxxx xxxxxxx
xxx xxx x xxxxxx xxxxxxxxxxxx x x xxx
xxx xxx x xxxxxxxxx xx xxxxxx
xxx xxx xxxxxxxx xxx xxxx
xx xx xxx xx x xx
xxx xxxxxx xxx xxx xx
xxxxxxxxxxxx x x xxx xx xx
xxxxxxxxxxx xx x xxxx xx x xx
xxxxxxx xx xxxxx x x xx
x xxxx xxx xxxxxx x xx
x xx x xxxxx xxx xx
x xx xxx xxx xx xx x xx
xxxxxx xx xxx xxxxx xx x xx
x xxxxxxxxx xx xxx xxx x xxxxx xx
xxxxxxxxxx x xx xxxxxx xxxx xx
xxxxx xxx xx xxxxx xxx x xx
xxxx xxx x xxx x xxx x xx
xxxxx xxx x xxx x xxx x x
xxxx xxx x xxx x xxxx x x
xxxxx x xxx xxx xxxxx x xxx
xxxxxx xxxxx xxx xxxxxx x x xx
xxx xx xxxxx xxx xxxxxxx x xxxx
xx xxx xxxxx xxx xxxxxx x xxxxx
xxx x xxxxx xxx xxxxx x xxxxx
xxxx x xxxxx xxx xxxxx xxx xxxx
xxxx xx xxxxxx xxx xxxx x xxxx
xxx x xxxxx xxx xxxx x xxxxxxx
xxx x xxxxx xxx xxxx x xxxxxx
xxx xxxx xxxx xxxxx xx xxxxxxxx
xx xxxxxxxx xxxxx xxx x xxx xx
xxx xx xxxxxx xxxxx xxxx xxx xx
xxx x xxxxx xxxxx xxxxx xxxxxx
xx x xx xx xxxxx xxxxx xxxxxx
xxx x xxxxx xxxxx xxxxxx xxxxxx
xx xxxx xxxx xxxxxxxxx xxxxxx
xxx xxxxx xxxxxxxxx xxxxx
xxx xxxxx xx xxxxxx xxxxx
xxx xxxxxx x xxx xxx xxxxx
xxx xxxxx xx xxxxxxx xxxxx
x xx xxxxxx xxx xxxxx xx xxxx
xxx xxxxx xxxxxxx xxxx xx
xxx xxxxxx xx xxxxx x xxxx
xx xxxxxx xxx xxxx xxxxxx
xxx xxxxxxxxx xxxxxxx xxxxxx
xxx xxxxxxxxxxxxx xxxxxxx xx xxx
xxxxxxxxxx xxxxx xxxx xx xx x x
xxxxxxxx xxxxxx xxxxxx x xxxx x
xxx xx xxx xx xxxx xxxxxx
xx xxx xx xxx xxx xxx
xx xxx xx x x xxx
xxxxxx xxx xx xx xx
xxxxxxx xxxxx x xxx
xxxxxx xxxxx xx
x xxxxx xxx xx xx
xx xxxxx xxxxxxxxxx
x xxx xxx xx x
x xxx x xxxxx x
x x xxxx xxx xxxxxxx
xxxxxxxx xxxx xxxxxxx
xxxxx xx xx xxx xxxx
xxxxx x xxxxxxx


-Adam


daz

unread,
Oct 12, 2005, 11:18:35 PM10/12/05
to

Harold Hausman wrote:
> On 10/12/05, daz wrote:
>
> [...]

> > Maybe we'll see a smaller fowl soon :-)
>
> But what of the majesty of the whole thing?! (:
>

I can't really judge when just two eyes or the beak
fill the screen in 1280x1024 resolution :p

Turning my monitor towards the window and viewing
from the end of my garden helps ;))

( Wilson's idea of using a small font was good. )

Certainly majestic !

daz

daz

unread,
Oct 12, 2005, 11:36:22 PM10/12/05
to

Dominik Bathon wrote:

> On Wed, 12 Oct 2005 14:07:08 +0200, daz wrote:
>
> > Maybe we'll see a smaller fowl soon :-)
>
> I am not sure if you meant golfing by "a smaller fowl", [...]


I'd just like to be able to see the whole image without needing
to project it onto the side of a tower block, first ;) ...
... but golfing's OK ...

>
> [...]


> GRADIENT[(file.read(3).unpack("CCC").inject { |a,b| a+b } / 3) >> 4]


I abandoned a similar construct after finding that String#[] is
quicker than short String#unpack sequences. Unpacking the whole
string is very quick but then Array#[] is needed later.
Mine is still messy, though.


> [...]
> end.reverse)

Yes, I should have had the courage to reverse instead of maintaining
a pointer.


> And here are two different short version, they need ARGV[0] and
> also write to stdout:

> ['snip']*2
>
> Dominik


I won't try to compete :)

Cheers,

daz

Ruby Quiz

unread,
Oct 13, 2005, 8:31:14 AM10/13/05
to
I just love it when a totally crazy idea of mine blossoms into a popular quiz.
Who would a thunk it?

As you've probably seen from the solutions, this quiz turns out to be fairly
easy, thanks to great tools like RMagick and GD. Those libraries can do the
heavy lifting of loading an image, resizing it, and dropping it to a smaller
color map. With that, you're code just needs to replace colors with some
symbols.

Here's some RMagick code from Mr. RMagick himself, Timothy Hunter:

require 'RMagick'

That is wonderfully commented code, of course, so I'm not going to repeat what
it does here.

I will mention a trick I found while playing with my own similar solution
though. I couldn't decide how many symbols to use, so I played with different
amounts. After about the third time of changing the Array and the argument to
quantize(), I realized that I could save myself a step. The same can be done
with the above code by changing two lines:

# ...

img = img.quantize(CHARS.size, Magick::GRAYColorspace)

# ...

pc.times { |j| putc CHARS[view[i][j].red/CHARS.size] }

# ...

With that, you can add and remove CHARS to play around and the code will just do
the right thing.

Here's how our mascot looks, when hit with the above code:

For an interesting different approach, Simon Kroeger wrote some code to outline
the primary subject. That makes our duck look like this:

.:'''''''.
.: :.
.: :.
: :
:' ':
.: :
: .':. ::

:'.' : .. ':
: :..' :' ': :


:. ' : .: :
': '::'':.. ':' . :
:: ''.. :' .:'':.

:: . ': .: ..' . '.


': ''''' .: .:::::''' :.
:: .:': :
.: : .:' :
.' '' '' :
:' :
: :
.' :
: ' :'
: .:
' : .:
' :
: .:
: .:
: .'
'. :'
:. .'
'. .:'
':. .:'

':. .:'
''..........''

I thought that was a surprising variation that got a great amount of detail
across. The bill is probably the easiest to make out here, compared with all
the solutions.

Let's see the code for that:

require 'RMagick'
require 'generator'
require 'enumerator'

puts "Usage: #{$0} <img> [size]" or exit if !ARGV[0]

img, size = Magick::ImageList.new(ARGV[0]), (ARGV[1]||40).to_f


factor = [size*1.5/img.rows, size/img.columns].min

img.resize!(img.columns*factor, 2*(img.rows*factor*0.75).round)
img = img.edge.despeckle.despeckle.normalize.threshold(50)

pixels = img.get_pixels(0, 0, img.columns, img.rows).map{|c| c.red.zero?}

pixels.to_enum(:each_slice, img.columns).each_slice(2) do |l|
puts SyncEnumerator.new(*l).map{|p1, p2|

[' ', "'", ".", ":"] [(p1 ? 0 : 1) + (p2 ? 0 : 2)]}.join('')
end

Here we see RMagick used again to read in the picture and resize it to something
closer to terminal dimensions. Instead of dropping the color map here though,
we get an interesting chain of filters designed to draw out the edges of the
primary subject. (Obviously, this works better on some images than others.)

What's after that? I honestly had no clue. to_enum(), each_slice(),
SyncEnumerator? Did I switch languages and nobody told me? Obviously those two
harmless looking requires at the beginning of the program change some of the
rules and we're going to need to learn a little bit about "generator" and
"enumerator".

So, what's the first step? I tried http://www.ruby-doc.org/ because I'm a wimp.
Yep, there's "generator" but we're in trouble with "enumerator". No
documentation! I clicked the link anyway, to see what I could learn.

Seems each_slice() is added to Enumerable by the library and it expects one
argument. Well, that's something. It's a little hard to tell how it's being
used in Simon's code (complicated by to_enum()), so I figure, just put something
in an Array and try to call it. That should tell us something. irb to the
rescue!

>> require "enumerator"
=> true
>> pixels = (1..10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>> pixels.each_slice(2) { |slice| p slice }
[1, 2]
[3, 4]
[5, 6]
[7, 8]
[9, 10]
=> nil

Perfect! That was all we need to see. It allows you to take off chunks of an
Array, it seems. Dang that's cool! Why didn't any of you tell me that was
there?!

Anybody see the Tic-Tac-Toe code posted to Ruby Talk on Tuesday? Look how
simple drawing the board can be:

>> board = Array.new(9) { rand > 0.5 ? "X" : "O" }
=> ["X", "O", "O", "O", "O", "O", "O", "X", "O"]
>> board.each_slice(3) { |row| puts row.join }
XOO
OOO
OXO
=> nil

Okay, we've got each_slice() figured out. Let's do some more detective work.
Let's see if we can figure out to_enum(). When it is called in Simon's code it
seems to get passed a method name (the one we just learned!) and a number.
Well, we know each_slice() requires a number, so maybe that's the argument to
it? Again, let's just see if we can call it:

>> pixels
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>> enum = pixels.to_enum(:each_slice, 2)
=> #<Enumerable::Enumerator:0x342b58>

Well, we got... something. Hmm, I wonder what it can do?

>> enum.methods
=> ["reject", "method", "send", "object_id", "enum_for", "singleton_methods",
"member?", "__send__", "equal?", "taint", "find", "frozen?",
"instance_variable_get", "each_with_index", "enum_cons", "kind_of?", "to_a",
"instance_eval", "collect", "all?", "entries", "type", "enum_with_index",
"protected_methods", "extend", "detect", "eql?", "display", "zip",
"instance_variable_set", "hash", "is_a?", "map", "to_s", "any?", "sort",
"class", "each_slice", "min", "tainted?", "private_methods", "find_all",
"untaint", "each", "id", "inspect", "inject", "==", "===", "sort_by", "clone",
"public_methods", "enum_slice", "max", "respond_to?", "select", "freeze",
"__id__", "to_enum", "partition", "=~", "methods", "grep", "nil?", "dup",
"each_cons", "instance_variables", "include?", "instance_of?"]

Okay, it seems to be Enumerable. Let's just see what each entry is:

>> enum.each { |e| p e }
[1, 2]
[3, 4]
[5, 6]
[7, 8]
[9, 10]
=> nil

Now I get it. We turned an each_slice(2) call into an each() call. That's
interesting.

It always bugs me that Strings iterate over lines instead of characters, by
default, and now I have the tool to fix it:

>> char_str = "team".to_enum(:each_byte)
=> #<Enumerable::Enumerator:0x322d30>
>> if char_str.any? { |c| c == ?i }
>> puts "Huh?!"
>> else
?> puts "There's no I in T-E-A-M!"
>> end
There's no I in T-E-A-M!
=> nil
>> char_str.to_a
=> [116, 101, 97, 109]

Notice how I was able to use any?() and to_a() there, because we switched
each_byte() to each() and all other Enumerable methods use each().

One more mysterious piece of the puzzle, but this one is documented, which
almost takes all the fun out of it. Here's the example right out of the
documentation, minus some irb noise:

>> require "generator"
=> true
>> s = SyncEnumerator.new([1, 2, 3], %w{a b c})
=> #<SyncEnumerator:0x1b339c ...>
>> s.each { |row| puts row.join(", ") }
1, a
2, b
3, c
=> #<SyncEnumerator:0x1b339c ...>

Obviously, that just let's you traverse two Enumerable objects at once. First
you get the first entry of both, then the second, etc. Nothing too tricky
there.

Still remember the code that started all this?

# ...



pixels.to_enum(:each_slice, img.columns).each_slice(2) do |l|
puts SyncEnumerator.new(*l).map{|p1, p2|

[' ', "'", ".", ":"] [(p1 ? 0 : 1) + (p2 ? 0 : 2)]}.join('')
end

This code iterates over rows of pixels (pixels.to_enum(:each_slice, img.columns)
..), two at-a-time (... .each_slice(2) ...). It then traverses those two rows
pixel-by-pixel in tandem (SyncEnumerator.new(*l).map{|p1, p2| ... }), averaging
the two on/off values (... [' ', "'", ".", ":"] [(p1 ? 0 : 1) + (p2 ? 0 : 2)]
..), and printing the results (puts ... .join('')). Work through that slowly,
until it sinks in. I know it took me a couple of tries.

I better wrap this up, since it's already quite lengthy, but don't forget to
take a peek at the other solutions. Harold Hausman rolled his own code for
analyzing bitmap images and others are now golfing that solution on Ruby Talk.
Rob Rypka and Brian Schroeder also did some sensational work mapping colors to
characters, producing some nice gradients.

A big thank you to all the enlightening solutions to this week's quiz. The
combined intelligence of the Ruby Quiz community is beyond measure.

Tomorrow, I have a new game for you. I figure it's The RubyConf Collective
verses the rest of us in the tournament, right?


daz

unread,
Oct 13, 2005, 9:30:34 AM10/13/05
to

Ruby Quiz wrote

>
> So, what's the first step? I tried http://www.ruby-doc.org/ because I'm a wimp.
> Yep, there's "generator" but we're in trouble with "enumerator". No
> documentation! I clicked the link anyway, to see what I could learn.
>

http://www.ruby-lang.org/cgi-bin/cvsweb.cgi/ruby/ext/enumerator/Attic/enumerator.txt?rev=1.2;content-type=text%2Fplain
http://www.ruby-lang.org/cgi-bin/cvsweb.cgi/~checkout~/ruby/enumerator.c?content-type=text%2Fplain


> Perfect! That was all we need to see. It allows you to take off chunks of an
> Array, it seems. Dang that's cool! Why didn't any of you tell me that was
> there?!

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/154552
(my source for the two links above)


daz

James Edward Gray II

unread,
Oct 13, 2005, 9:43:10 AM10/13/05
to
Attn: Gavin Sinclair

On Oct 13, 2005, at 8:31 AM, daz wrote:

>
> Ruby Quiz wrote
>
>>
>> So, what's the first step? I tried http://www.ruby-doc.org/
>> because I'm a wimp.
>> Yep, there's "generator" but we're in trouble with "enumerator". No
>> documentation! I clicked the link anyway, to see what I could learn.
>>
>>
>
> http://www.ruby-lang.org/cgi-bin/cvsweb.cgi/ruby/ext/enumerator/
> Attic/enumerator.txt?rev=1.2;content-type=text%2Fplain
> http://www.ruby-lang.org/cgi-bin/cvsweb.cgi/~checkout~/ruby/
> enumerator.c?content-type=text%2Fplain

Thank you for leading me to the docs!

Gavin, that second link looks like ready to put online RDoc. Any
reason we don't have it on ruby-doc.org?

James Edward Gray II

daz

unread,
Oct 13, 2005, 10:09:38 AM10/13/05
to

James Edward Gray II wrote:
>
> Gavin, that second link looks like ready to put online RDoc. Any
> reason we don't have it on ruby-doc.org?
>

Possibly because James Britt at ruby-doc.org doesn't
want to conflate 1.9 and 1.8.2 docs (?)

In 1.8.2, Enumerator is an extension;
in 1.9, it's a built-in class with (I think) added functionality.

It would be nice if 1.9 rdoc could be hosted somewhere, though.


daz

Gavin Sinclair

unread,
Oct 13, 2005, 10:48:20 AM10/13/05
to
James Edward Gray II wrote:
> >
> > http://www.ruby-lang.org/cgi-bin/cvsweb.cgi/ruby/ext/enumerator/
> > Attic/enumerator.txt?rev=1.2;content-type=text%2Fplain
> > http://www.ruby-lang.org/cgi-bin/cvsweb.cgi/~checkout~/ruby/
> > enumerator.c?content-type=text%2Fplain
>
> Thank you for leading me to the docs!
>
> Gavin, that second link looks like ready to put online RDoc. Any
> reason we don't have it on ruby-doc.org?

Wow! Them's some pretty long links! I take it there are some docs for
enumerator in CVS and they could do with some translation into RDoc.
OK, I'll make that my next target.

Thanks,
Gavin

James Edward Gray II

unread,
Oct 13, 2005, 11:06:43 AM10/13/05
to

Gavin, those are 1.9 docs. If that's a problem, just let me know and
I'll build a patch to add them to Ruby 1.8 and see if I can get it
committed.

James Edward Gray II

James Britt

unread,
Oct 13, 2005, 7:39:16 PM10/13/05
to

There had been an older version on them on ruby-doc, though I think they
got lost in the server shuffle.

I can add them again, if there is sufficient interest. But it seems
that they're a bit avant garde for most people who wouldn't already have
them locally anyway.


James

--

http://www.ruby-doc.org - The Ruby Documentation Site
http://www.rubyxml.com - News, Articles, and Listings for Ruby & XML
http://www.rubystuff.com - The Ruby Store for Ruby Stuff
http://www.jamesbritt.com - Playing with Better Toys


James Edward Gray II

unread,
Oct 13, 2005, 8:10:33 PM10/13/05
to
On Oct 13, 2005, at 6:39 PM, James Britt wrote:

> daz wrote:
>
>> James Edward Gray II wrote:
>>
>>> Gavin, that second link looks like ready to put online RDoc. Any
>>> reason we don't have it on ruby-doc.org?
>>>
>>>
>> Possibly because James Britt at ruby-doc.org doesn't
>> want to conflate 1.9 and 1.8.2 docs (?)
>> In 1.8.2, Enumerator is an extension;
>> in 1.9, it's a built-in class with (I think) added functionality.
>> It would be nice if 1.9 rdoc could be hosted somewhere, though.
>>
>
> There had been an older version on them on ruby-doc, though I think
> they got lost in the server shuffle.
>
> I can add them again, if there is sufficient interest. But it
> seems that they're a bit avant garde for most people who wouldn't
> already have them locally anyway.

I see absolutely no reason not to make every last shred of
documentation available to the users of Ruby. I say we document,
document, document until there is no library left to cover.

Enumerator is a handy tool, soon to be a part of the core. Why would
we *choose* not to educate people about it?!

If that line of thinking is wrong, I don't want to be right! Call me
radical.

James Edward Gray II

James Britt

unread,
Oct 13, 2005, 8:27:27 PM10/13/05
to
James Edward Gray II wrote:
> On Oct 13, 2005, at 6:39 PM, James Britt wrote:
>
>> daz wrote:
>>
>>> James Edward Gray II wrote:
>>>
>>>> Gavin, that second link looks like ready to put online RDoc. Any
>>>> reason we don't have it on ruby-doc.org?
>>>>
>>>>
>>> Possibly because James Britt at ruby-doc.org doesn't
>>> want to conflate 1.9 and 1.8.2 docs (?)
>>> In 1.8.2, Enumerator is an extension;
>>> in 1.9, it's a built-in class with (I think) added functionality.
>>> It would be nice if 1.9 rdoc could be hosted somewhere, though.
>>>
>>
>> There had been an older version on them on ruby-doc, though I think
>> they got lost in the server shuffle.
>>
>> I can add them again, if there is sufficient interest. But it seems
>> that they're a bit avant garde for most people who wouldn't already
>> have them locally anyway.
>
>
> I see absolutely no reason not to make every last shred of
> documentation available to the users of Ruby. I say we document,
> document, document until there is no library left to cover.

Hosting documentation is trivial. Getting people to write documentation
is not. At least not, it seems, without offering financial renumeration.

James Edward Gray II

unread,
Oct 13, 2005, 9:13:11 PM10/13/05
to
On Oct 13, 2005, at 7:27 PM, James Britt wrote:

> Hosting documentation is trivial. Getting people to write
> documentation is not. At least not, it seems, without offering
> financial renumeration.

Why write documentation when we don't put up what we have?

James Edward Gray II

James Edward Gray II

unread,
Oct 13, 2005, 11:46:39 PM10/13/05
to
I'm sorry, but the more I think about this, the more it bugs me...

On Oct 13, 2005, at 7:27 PM, James Britt wrote:

> Hosting documentation is trivial.

You do this part, right?

You run ads on the site and this (http://www.rubystuff.com/
about.html) also generates income for that service.

> Getting people to write documentation is not.

I do this part. I've documented three standard libraries in the last
year. ERb, Delegate, and Forwardable.

> At least not, it seems, without offering financial renumeration.

I haven't seen my check yet?!

James Edward Gray II

Kev Jackson

unread,
Oct 14, 2005, 12:21:18 AM10/14/05
to

>> Getting people to write documentation is not.
>
>
> I do this part. I've documented three standard libraries in the last
> year. ERb, Delegate, and Forwardable.
>
>> At least not, it seems, without offering financial renumeration.
>
If I could get paid to write docs I'd be all over it like something all
over something else. But if someone could point to some area that needs
documenting I'd be more than happy to help - I was a technical author a
few years ago, and I have contributed code and docs to several open
source projects (mainly Java), I just didn't realise that more hands
were needed - please push links etc to me I'll help out with doco

Kev


James Britt

unread,
Oct 14, 2005, 2:09:52 AM10/14/05
to

Perhaps the best place to start is here:

http://ruby-doc.org/stdlib/

Items listed to the left in italics do not have sufficient docs.

(That may be true as well of some of the other items, but I believe the
italics are meant to indicate 'needs work.')

James

at RubyConf05

James Britt

unread,
Oct 14, 2005, 2:16:28 AM10/14/05
to
James Edward Gray II wrote:
> I'm sorry, but the more I think about this, the more it bugs me...

Why?

>
> On Oct 13, 2005, at 7:27 PM, James Britt wrote:
>
>> Hosting documentation is trivial.
>
>
> You do this part, right?

Indeed.

>
> You run ads on the site and this (http://www.rubystuff.com/ about.html)
> also generates income for that service.

Yes. Helps pay some of the bills.

>
>> Getting people to write documentation is not.
>
>
> I do this part. I've documented three standard libraries in the last
> year. ERb, Delegate, and Forwardable.

There are a few people writing core and stdlib docs on a semi-regular
basis, but by and large, there are large parts of Ruby insufficiently
documented.

>
>> At least not, it seems, without offering financial renumeration.
>
>
> I haven't seen my check yet?!

Maybe after the bills get paid.

:)

Kev Jackson

unread,
Oct 14, 2005, 2:53:19 AM10/14/05
to

> Perhaps the best place to start is here:
>
> http://ruby-doc.org/stdlib/
>
> Items listed to the left in italics do not have sufficient docs.
>
Ok so I've looked at the site (and at the status report). I presume
that to get working on them I'd need to get the actual ruby sources and
start adding comments/doc/examples into the source files, but then how
do I push changes in the original sources back so that the RDoc can be
extracted/published? If the sources are part of the stdlib (which ships
with ruby itself), is there a bugzilla/issu tracker or something to
attach patches to?

Or is the preferred way to download a doc bundle and manually edit that?

I'll keep searching around myself (this afternoon I'm completely free
which is nice) but any pointers in the preferred way of contributing
would be appreciated

Kev


Gavin Sinclair

unread,
Oct 14, 2005, 6:14:49 AM10/14/05
to

The stuff on ruby-doc.org/{stdlib,core} is generated from 1.8 source
and library code, so 1.9 stuff won't appear there. There's no reason,
however, that other, arbitrary, documentation can't be hosted on
ruby-doc.org.

So if it's appropriate that it should appear in 1.8, I'd certainly
encourage you to make a patch and get it committed.

Cheers,
Gavin

Gavin Sinclair

unread,
Oct 14, 2005, 6:24:29 AM10/14/05
to
Kev Jackson wrote:

> If I could get paid to write docs I'd be all over it like something all
> over something else. But if someone could point to some area that needs
> documenting I'd be more than happy to help - I was a technical author a
> few years ago, and I have contributed code and docs to several open
> source projects (mainly Java), I just didn't realise that more hands
> were needed - please push links etc to me I'll help out with doco

Hi Kev,

Documentation of core and library classs is written in the source code
(be it C or Ruby). Thus to contribute, you want to avail yourself of
(anonymous) CVS access (it's easy). Then you edit files, create a
patch, and get it committed. That last step has been the problem in
the past; mea culpa. Suggested process: email the patch to me and/or
James Gray (sorry, James!). Bug me mercilessly if nothing's being
done.

Anyway, then it ends up in Ruby itself and appears on ruby-doc.org
hopefully soon after.

Any questions, just email.

Cheers,
Gavin

Gavin Sinclair

unread,
Oct 14, 2005, 8:29:03 AM10/14/05
to
Kev Jackson wrote:

> Ok so I've looked at the site (and at the status report). I presume
> that to get working on them I'd need to get the actual ruby sources and
> start adding comments/doc/examples into the source files, but then how
> do I push changes in the original sources back so that the RDoc can be
> extracted/published? If the sources are part of the stdlib (which ships
> with ruby itself), is there a bugzilla/issu tracker or something to
> attach patches to?

Damn, thought I replied earlier but ... it hasn't appeared, so I guess
I didn't. Sorry if this is a duplicate.

You do need to get the Ruby sources (anonymous CVS is best) and work
with those. There's no bugzilla or similar for doc patches. This part
of the process has been a failure and I take responsibility for that.
If you have a patch, email it to me and/or James Gray. One of us will
massage it through ruby-core back into CVS, and James Britt and I will
regenerate the bits and pieces on ruby-doc.org/{core,stdlib}.

Thanks for any help, and feel free to ask any questions.

Cheers,
Gavin

daz

unread,
Oct 14, 2005, 7:44:39 PM10/14/05
to

Gavin Sinclair wrote:
> Kev Jackson wrote:
>
> > [...] to get working on them I'd need to [...]

>
> If you have a patch, email it to me and/or James Gray.
>
> Thanks for any help, and feel free to ask any questions.
>
> Cheers,
> Gavin
>


/**********************************************************************

iconv.c -

$Author: nobu $
$Date: 2005/03/27 23:40:02 $
created at: Wed Dec 1 20:28:09 JST 1999

All the files in this distribution are covered under the Ruby's
license (see the file COPYING).

Documentation by Yukihiro Matsumoto and Gavin Sinclair.

**********************************************************************/


Is there a case for having a $Doc: Name $ in the header ?

Perhaps also a standard way to add contributors names to doc updates
which doesn't wipe out the original documentor's name (as seems to
happen with $Author: Name $ in many patched core files).

The main reason for asking is that, if anyone is going to spend a deal
of time and effort on documentation, recognition should be recorded.
(I hope that's only obvious.)

It would give us somewhere to direct our appreciation, spiritually
(or, perhaps, e-mail-ially).

$Doc: Dave Thomas; gs; jeg; kj $ - which magically expands to HTML
links for each member on ruby-doc-squad-wiki.org might be possible
(later?).


daz

Gavin Sinclair

unread,
Oct 16, 2005, 12:04:54 PM10/16/05
to
daz wrote:
>
> /**********************************************************************
>
> iconv.c -
>
> $Author: nobu $
> $Date: 2005/03/27 23:40:02 $
> created at: Wed Dec 1 20:28:09 JST 1999
>
> All the files in this distribution are covered under the Ruby's
> license (see the file COPYING).
>
> Documentation by Yukihiro Matsumoto and Gavin Sinclair.
>
> **********************************************************************/
>
> Is there a case for having a $Doc: Name $ in the header ?

What would that do?

> Perhaps also a standard way to add contributors names to doc updates
> which doesn't wipe out the original documentor's name (as seems to
> happen with $Author: Name $ in many patched core files).

But author and documenter are often different people. Recording the
credits in plain text (as above) ensures they are not lost as CVS tags
come and go.

> $Doc: Dave Thomas; gs; jeg; kj $ - which magically expands to HTML
> links for each member on ruby-doc-squad-wiki.org might be possible
> (later?).

Are you suggesting that every person who cvs commits a file is
recorded?

Cheers,
Gavin

daz

unread,
Oct 18, 2005, 12:07:37 PM10/18/05
to

Gavin Sinclair wrote:

> daz wrote:
> >
> >
> > Is there a case for having a $Doc: Name $ in the header ?
>
> What would that do?
>

Probably nothing but harm :/
It's a searchable string, isn't it?
Documentation is part of authorship, isn't it?

> > Perhaps also a standard way to add contributors names to doc updates
> > which doesn't wipe out the original documentor's name (as seems to
> > happen with $Author: Name $ in many patched core files).
>
> But author and documenter are often different people.

Hence the separation into $Doc: Name $

> Documentation by Yukihiro Matsumoto and Gavin Sinclair.

> [...]


> Recording the credits in plain text (as above) ensures they are
> not lost as CVS tags come and go.
>

Nothing wrong with that unless someone wants to parse it.
I assume that's the purpose of the $ $ delimeters in header fields.

> > $Doc: Dave Thomas; gs; jeg; kj $ - which magically expands to HTML
> > links for each member on ruby-doc-squad-wiki.org might be possible
> > (later?).
>
> Are you suggesting that every person who cvs commits a file is
> recorded?
>

Not commits; - significantly contributes to documentation.
Yeah - whatever could I have been thinking?

> Cheers,
> Gavin
>

Thanks,

Dazza


James Britt

unread,
Oct 18, 2005, 1:08:01 PM10/18/05
to
Gavin Sinclair wrote:
> Kev Jackson wrote:
>
>
>>If I could get paid to write docs I'd be all over it like something all
>>over something else. But if someone could point to some area that needs
>>documenting I'd be more than happy to help - I was a technical author a
>>few years ago, and I have contributed code and docs to several open
>>source projects (mainly Java), I just didn't realise that more hands
>>were needed - please push links etc to me I'll help out with doco
>
>
> Hi Kev,
>
> Documentation of core and library classs is written in the source code
> (be it C or Ruby). Thus to contribute, you want to avail yourself of
> (anonymous) CVS access (it's easy). Then you edit files, create a
> patch, and get it committed. That last step has been the problem in
> the past; mea culpa. Suggested process: email the patch to me and/or
> James Gray (sorry, James!). Bug me mercilessly if nothing's being
> done.
>


BTW, what is the copyright on submitted docs, and is it made clear to
those submitting documentation?


James Britt

Gavin Sinclair

unread,
Oct 18, 2005, 11:10:52 PM10/18/05
to

James Britt wrote:
>
> > Documentation of core and library classs is written in the source code
> > (be it C or Ruby). Thus to contribute, you want to avail yourself of
> > (anonymous) CVS access (it's easy). Then you edit files, create a
> > patch, and get it committed. That last step has been the problem in
> > the past; mea culpa. Suggested process: email the patch to me and/or
> > James Gray (sorry, James!). Bug me mercilessly if nothing's being
> > done.
>
> BTW, what is the copyright on submitted docs, and is it made clear to
> those submitting documentation?

I have no idea what the copyright situation is. Of the few people who
have submitted documentation, none have raised the question of
copyright.

Is there a reason to believe the documentation is treated differently
from the source code of which it's part? If the documentation is "Ruby
Licence", which I expect it is, what implication does that have for
copyright?

Gavin

Gavin Sinclair

unread,
Oct 18, 2005, 11:15:33 PM10/18/05
to

daz wrote:

> > Documentation by Yukihiro Matsumoto and Gavin Sinclair.
> > [...]
> > Recording the credits in plain text (as above) ensures they are
> > not lost as CVS tags come and go.
> >
>
> Nothing wrong with that unless someone wants to parse it.
> I assume that's the purpose of the $ $ delimeters in header fields.

The purpose of those delimiters is so CVS treats them specially. The
content between the delimiters is created by CVS.

I.e. you put $Author$ in the source code, and CVS later expands that to
$Author: whoever$. To the best of my knowledge, CVS doesn't recognise
$Doc$, and you can't make it do so.

Cheers,
Gavin

Gavin Kistner

unread,
Oct 19, 2005, 9:26:36 AM10/19/05
to
On Oct 14, 2005, at 12:09 AM, James Britt wrote:
> http://ruby-doc.org/stdlib/
>
> Items listed to the left in italics do not have sufficient docs.

Ahh...I always wondered what was up with the italics. Good to know :)


Gavin Sinclair

unread,
Oct 20, 2005, 8:43:11 AM10/20/05
to

I guess I should put an explanatory note in! :)

Gavin

James Britt

unread,
Oct 20, 2005, 10:13:56 AM10/20/05
to


Could someone sell a book that included the text from the std-lib docs?
Would the book have to be released under Ruby's license (whatever that
means)?

Gavin Sinclair

unread,
Oct 20, 2005, 7:06:55 PM10/20/05
to
James Britt wrote:

> > Is there a reason to believe the documentation is treated differently
> > from the source code of which it's part? If the documentation is "Ruby
> > Licence", which I expect it is, what implication does that have for
> > copyright?
>
> Could someone sell a book that included the text from the std-lib docs?
> Would the book have to be released under Ruby's license (whatever that
> means)?

I don't know. Good way of looking at it.

Gavin

Brian Schröder

unread,
Oct 21, 2005, 4:12:51 AM10/21/05
to
On 20/10/05, James Britt <jam...@neurogami.com> wrote:
> Gavin Sinclair wrote:
> > James Britt wrote:
> >
> >>>Documentation of core and library classs is written in the source code
> >>>(be it C or Ruby). Thus to contribute, you want to avail yourself of
> >>>(anonymous) CVS access (it's easy). Then you edit files, create a
> >>>patch, and get it committed. That last step has been the problem in
> >>>the past; mea culpa. Suggested process: email the patch to me and/or
> >>>James Gray (sorry, James!). Bug me mercilessly if nothing's being
> >>>done.
> >>
> >>BTW, what is the copyright on submitted docs, and is it made clear to
> >>those submitting documentation?
> >
> >
> > I have no idea what the copyright situation is. Of the few people who
> > have submitted documentation, none have raised the question of
> > copyright.
> >
> > Is there a reason to believe the documentation is treated differently
> > from the source code of which it's part? If the documentation is "Ruby
> > Licence", which I expect it is, what implication does that have for
> > copyright?
> >
>
>
> Could someone sell a book that included the text from the std-lib docs?
> Would the book have to be released under Ruby's license (whatever that
> means)?
>

The Pickaxe pp. 653-759

brian

>
> James Britt
>
> --
>
> http://www.ruby-doc.org - The Ruby Documentation Site
> http://www.rubyxml.com - News, Articles, and Listings for Ruby & XML
> http://www.rubystuff.com - The Ruby Store for Ruby Stuff
> http://www.jamesbritt.com - Playing with Better Toys
>
>


--
http://ruby.brian-schroeder.de/

Stringed instrument chords: http://chordlist.brian-schroeder.de/


James Britt

unread,
Oct 21, 2005, 11:26:53 AM10/21/05
to
Brian Schröder wrote:
> On 20/10/05, James Britt <jam...@neurogami.com> wrote:
>
>>Gavin Sinclair wrote:
>>
>>>James Britt wrote:
>>>
>>>
>>>>>Documentation of core and library classs is written in the source code
>>>>>(be it C or Ruby). Thus to contribute, you want to avail yourself of
>>>>>(anonymous) CVS access (it's easy). Then you edit files, create a
>>>>>patch, and get it committed. That last step has been the problem in
>>>>>the past; mea culpa. Suggested process: email the patch to me and/or
>>>>>James Gray (sorry, James!). Bug me mercilessly if nothing's being
>>>>>done.
>>>>
>>>>BTW, what is the copyright on submitted docs, and is it made clear to
>>>>those submitting documentation?
>>>
>>>
>>>I have no idea what the copyright situation is. Of the few people who
>>>have submitted documentation, none have raised the question of
>>>copyright.
>>>
>>>Is there a reason to believe the documentation is treated differently
>>>from the source code of which it's part? If the documentation is "Ruby
>>>Licence", which I expect it is, what implication does that have for
>>>copyright?
>>>
>>
>>
>>Could someone sell a book that included the text from the std-lib docs?
>> Would the book have to be released under Ruby's license (whatever that
>>means)?
>>
>
>
> The Pickaxe pp. 653-759


They do not appear to be part of the Ruby source distro.

For example, look at the rdoc for the rss library in 1.8.3. Then look
at page 728 of pickaxe 2nd Ed.


James

0 new messages