Numbers as Circular Graphics

First, study this puzzle to get a feel for what you will be producing.

Your challenge is to write a program or function which will output a circular graphic like the ones from the puzzle, given a (base 10) number between 1 and 100 (inclusive). This is similar to this challenge, except that you will be producing a graphic rather than roman numerals. The following circles represent the numbers 1-10, from left to right:

As the answer to the puzzle states, your graphic should read like a roman numeral from the inside-out, where the line thickness represents the roman numeral symbols and the entire graphic represents the number. For your reference, here are the line thicknesses which you will need. Each line should have a 3px padding between it and the next.

Number  Roman Numeral   Line Width
1       I               1px
5       V               3px
10      X               5px
50      L               7px
100     C               9px


Please post a sample or two of your output. Assume input is correct, standard loopholes, etc and so forth. This is code golf, so fewest bytes win. In the event of a tie, most votes win. Good luck!

• Is the correct absolute size of the image necessary, or is it sufficient to have the right relative sizes? Nov 26 '14 at 7:43
• @DavidZhang Yes, please stick to the line and padding sizes that I listed, for fairness sake. Nov 26 '14 at 12:38

Mathematica - 166 181 bytes

A bit more concise than the other Mathematica answer, thanks in part to a more point-free style.

c = Characters; r = Riffle;
Graphics[r[{0, 0}~Disk~# & /@ Reverse@Accumulate[
l = {3} ~Join~ r[2 Position[c@"IVXLC", #][[1, 1]] - 1 & /@
c@IntegerString[#, "Roman"], 3]], {White, Black}],
ImageSize -> 2 Total@l] &


All whitespace is for clarity only. This defines an anonymous function which returns the desired graphic.

Animation

Generating an animated GIF of the number circles is trivial in Mathematica, which has built-in functions for animating and exporting sequences of arbitrary objects. Assuming the above code has just been executed,

Table[Show[%@n, PlotRange -> {{-100, 100}, {-100, 100}},
ImageSize -> 200], {n, 1, 399, 1}];
Export["animcircles.gif", %]


Example Output

• Please post a few results. Sorry for not asking this is the first place. I've also changed the question to accept functions. Nov 26 '14 at 12:40
• Thanks for the suggestions @MartinBüttner. The code has been fixed to output correctly-sized images, and example output has been added. Nov 26 '14 at 17:17
• Your animation wiggles. Not that I could do better. Nov 27 '14 at 20:35
• Hmm, you're right. I'm really not sure why it does that, considering I specified the plot range explicitly for Mathematica. Nov 28 '14 at 19:24
• Maybe related to the wiggling: mathematica.stackexchange.com/q/134272 Dec 26 '16 at 15:42

Common Lisp - 376331 304 bytes

(use-package(car(ql:quickload'vecto)))(lambda(n o &aux(r 3)l p)(map()(lambda(c)(setf l(position c" I V X L C D M")p(append((set-line-width,l)(centered-circle-path 0 0,(+(/ l 2)r))(stroke))p)r(+ r l 3)))(format()"~@R"n))(with-canvas(:width(* 2 r):height(* 2 r))(translate r r)(map()'eval p)(save-png o)))


Examples

(1) (24)

(104) (1903) (3999)

Animation

For numbers from 1 to 400:

NB: For the record, this animation is done as follows:

I have a modified version of the code, named rings which returns the width of the produced image. Hence, the result of the the following loop is the maximum size, here 182:

 (loop for x from 1 to 400
maximize (rings x (format nil "/tmp/rings/ring~3,'0d.png" x)))


The whole loop takes 9.573 seconds. That gives approximately 24ms for each integer. Then, in a shell:

 convert -delay 5 -loop 0 -gravity center -extent 182x182 ring*png anim.gif


Ungolfed

(ql:quickload :vecto)
(use-package :vecto)

(lambda (n o)
(loop with r = 3
for c across (format nil "~@R" n)
for l = (1+ (* 2(position c"IVXLCDM")))
for h = (/ l 2)
collect (,(incf r h),l) into p
do (incf r (+ h 3))
finally (with-canvas(:width (* 2 r) :height (* 2 r))
(loop for (x y) in p
do (set-line-width y)
(centered-circle-path r r x)
(stroke))
(save-png o))))


Explanations

• The function takes an integer N between 1 and 3999 and a filename

• I use (format nil "~@R" N) to convert from decimal to roman. For example:

 (format nil "~@R" 34) => "XXXIV"


The ~@R format control string is specified to work for integers between 1 and 3999. That's why there is a limitation for the range of allowed inputs.

• I iterate over the resulting string to build a list P containing (radius width) couples, for each numeral C.

• The width is a simple linear mapping: I use the constant string "IVXLCDM" to compute the position of C in it. Multiplying by two and adding one, we obtain the desired value:

       (1+ (* 2 (position c "IVXLCDM")))


This is however done slightly differently in the golfed version:

       (position c " I V X L C D M")

• The computation of each radius takes into account the width of each ring as well as empty spaces between rings. Without any speed optimization, computations remain precise because they are not based on floats, but rational numbers.

Edit: I changed the parameters to comply with the padding rules.

• Once this is done, I know the required size of the resulting canvas (twice the latest computed radius).

• Finally, I draw a circle for each element of P and save the canvas.
• "This code supports all roman numerals (IVXLCDM)". Does that mean your program takes roman numerals as input? That's not what I intended, but pretty cool. Props for the animation too. Nov 26 '14 at 12:45
• No, no, sorry if this is unclear: it works for any integer between 1 and 3999. In your question, you only required inputs from 1 to 100, and your table does not mention D or M... I'll edit that part. Nov 26 '14 at 12:51

HTML+JQuery, 288

HTML

<canvas>


JS

    r=3;w=9;c=$('canvas').get(0).getContext('2d') for(i=prompt(),e=100;e-.1;e/=10){ if((x=Math.floor(i/e)%10)==4)d(w)+d(w+2) else if(x==9)d(w)+d(w+4) else{if(x>4)d(w+2) for(j=x%5;j;j--)d(w)} w-=4} function d(R){c.lineWidth=R c.beginPath() c.arc(150,75,r+=R/2,0,7) c.stroke() r+=R/2+3}  r=3;w=9;c=$('canvas').get(0).getContext('2d')
for(i=prompt(),e=100;e-.1;e/=10){
if((x=Math.floor(i/e)%10)==4)d(w)+d(w+2)
else if(x==9)d(w)+d(w+4)
else{if(x>4)d(w+2)
for(j=x%5;j;j--)d(w)}
w-=4}
function d(R){c.lineWidth=R
c.beginPath()
c.arc(150,75,r+=R/2,0,7)
c.stroke()
r+=R/2+3}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<canvas>

Fiddle

• No stack snippet ? Nov 26 '14 at 13:27
• @Optimizer Totally forgot we have that now Nov 26 '14 at 13:38

Java, 565

import java.awt.*;class Z{public static void main(String[]s){int i=new Byte(s[0]),j=i/10,k=i%10;String t="",u;if(j>8)t="59";if(j>9)t="9";if(j==4)t="57";else if(j<9){t=j>4?"7":"";j-=j>4?5:0;if(j>0)t+="5";if(j>1)t+="5";if(j>2)t+="5";}if(k>8)t+="15";if(k==4)t+="13";else if(k<9){t+=k>4?"3":"";k-=k>4?5:0;if(k>0)t+="1";if(k>1)t+="1";if(k>2)t+="1";}u=t;Frame f=new Frame(){public void paint(Graphics g){g.setColor(Color.BLACK);int x=0;for(char c:u.toCharArray()){int z=c-48,q=x;for(;x<q+z;)g.drawOval(99-x,99-x,x*2,x++*2);x+=3;}}};f.setSize(200,200);f.setVisible(1>0);}}


Examples

93

Formatted nicely:

import java.awt.*;
class Z {
public static void main(String[] s) {
int i = new Byte(s[0]), j = i / 10, k = i % 10;
String t = "", u;
if (j > 8)
t = "59";
if (j > 9)
t = "9";
if (j == 4) {
t = "57";
} else if (j < 9) {
t = j > 4 ? "7" : "";
j -= j > 4 ? 5 : 0;
if (j > 0)
t += "5";
if (j > 1)
t += "5";
if (j > 2)
t += "5";
}
if (k > 8)
t += "15";
if (k == 4) {
t += "13";
} else if (k < 9) {
t += k > 4 ? "3" : "";
k -= k > 4 ? 5 : 0;
if (k > 0)
t += "1";
if (k > 1)
t += "1";
if (k > 2)
t += "1";
}
u = t;
Frame f = new Frame() {
public void paint(Graphics g) {
g.setColor(Color.BLACK);
int x = 0;
for (char c : u.toCharArray()) {
int z = c - 48, q = x;
for (; x < q + z;) {
g.drawOval(99 - x, 99 - x, x * 2, x++ * 2);
}
x += 3;
}
}
};
f.setSize(200, 200);
f.setVisible(1 > 0);
}
}

• Please post a few results. Sorry for not asking this is the first place. Nov 26 '14 at 12:39

Mathematica 9 - 301 249 bytes

:D It feels cheaty to use the built-in converting to Roman numerals, but hey.

l=Length;k=Characters;r@n_:=(w=Flatten[Position[k@"IVXLC",#]*2-1&/@k@IntegerString[n,"Roman"]];Show[Table[Graphics@{AbsoluteThickness@w[[i]],Circle[{0,0},(Join[{0},Accumulate[3+w]]+3)[[i]]+w[[i]]/2]},{i,Range@l@w}],ImageSize->{(Total@w+(l@w)*3)*2}])


(When I did this last night I didn't have much time, but I realized it could be golfed down way more. And I also took some hints from David Zhang... :D Thanks!)

A bit more clearly:

l=Length;
k=Characters;
r@n_:=
(
w=Flatten[Position[k@"IVXLC",#]*2-1&/@k@IntegerString[n,"Roman"]];
Show[Table[Graphics@{AbsoluteThickness@w[[i]],Circle[{0,0},(Join[{0},Accumulate[3+w]]+3)[[i]]+w[[i]]/2]},{i,Range@l@w}],ImageSize->{(Total@w+(l@w)*3)*2}]
)


This is a function that you can call like this:

r[144]


Or, you can show the results from values a to b with: Table[r[i],{i,a,b}]

Note: This only works for values up to 399.

Python 2, 322 296

The script reads the number to be converted from stdin, and outputs the image as SVG markup.

.. I use 'red' instead of 'black', because it saves 2 chars :)

Here are some samples : for 23 : http://jsfiddle.net/39xmpq49/ for 42 : http://jsfiddle.net/7Ls24q9e/1/

i=input();r=9
def R(n,p):
global r,i;i-=n;print '<circle r="{0}" stroke-width="{1}"/>'.format(r,p);r+=p+3
print '<svg viewBox="-50 -50 99 99" fill="none" stroke="red">',[[R(n,int(p)) for p in s*int(i/n)] for n,s in zip([100,90,50,40,10,9,5,4,1],'9/59/7/57/5/15/3/13/1'.split('/'))]and'','</svg>'


JavaScript 342334 308

function R(n){var v=document,o=[],x=1,c=v.body.appendChild(v.createElement('canvas')).getContext('2d')
while(n)v=n%10,y=x+2,o=[[],[x],[x,x],[x,x,x],[x,y],[y],[y,x],[y,x,x],[y,x,x,x],[x,x+=4]][v].concat(o),n=(n-v)/10
v=3
while(x=o.shift())c.lineWidth=x,c.beginPath(),c.arc(150,75,v+x/2,0,7),c.stroke(),v+=x+3}

for (var i = 1; i <= 100; i++) {
R(i);
}