14
\$\begingroup\$

This happy individual is known in folklore as the Easter Bunny.

Easter Bunny

Colours

It is created using 4 colours:

  • Light pink
  • Dark pink
  • White
  • Black

(I'm happy for these colours to be approximated in your answer. It does not need to be this precise shade)

Shapes

It is constructed of these shapes:

  • Two tall ellipses in light pink (outer ears). They have the same height and width.
  • Two tall ellipses in dark pink (inner ears). They are smaller than the outer ears and one is drawn on top of each of the outer ears. They are the same height and width.
  • One large circle in light pink (head). It intersects with the bottom third of both outer ears (but no higher).
  • Two circles in white (outer eyes). They are drawn on the head. Their diameter is less than the width of the outer ears. They have the same horizontal position as the outer ears.
  • Two circles in black (inner eyes). They have a smaller diameter than the outer eyes. One is drawn on each of the outer eyes.
  • One vertical line in black (upper mouth). It is drawn on the head and is lower than the bottom of the outer eyes. It is roughly equidistant from each eye.
  • Two arcs in black (lower mouth). They are drawn on the head and arc downwards from a horizontal line. Both intersect with the bottom of the upper mouth, but one goes right and the other goes left.

The Rules

  • Use any language and tools you like.
  • Output can be an image, html, svg, or other markup.
  • It's code golf, so aim to do it in the smallest number of bytes.
  • Please include a screenshot of the result.
  • Please feel free to approximate the colours defined.

Happy Easter!

\$\endgroup\$
8
  • \$\begingroup\$ What size image would you like? \$\endgroup\$
    – Neil
    Apr 21, 2019 at 10:46
  • \$\begingroup\$ @Neil size doesn’t matter, Neil. As long as it meets the requirements for shape and colour. \$\endgroup\$
    – AJFaraday
    Apr 21, 2019 at 11:43
  • \$\begingroup\$ I've got a desmos answer in the works. Is having no white in the eyes acceptable? \$\endgroup\$ Apr 21, 2019 at 15:47
  • \$\begingroup\$ @BenjaminUrquhart not really. That considerably simplifies the process. You could make it a grey, if that helps. \$\endgroup\$
    – AJFaraday
    Apr 21, 2019 at 15:55
  • 1
    \$\begingroup\$ @BenjaminUrquhart works for me \$\endgroup\$
    – AJFaraday
    Apr 21, 2019 at 16:07

14 Answers 14

19
\$\begingroup\$

T-SQL, 445 439 bytes

DECLARE @ GEOMETRY='MULTIPOINT((3 3),(7 3))',
@l GEOMETRY='CIRCULARSTRING(3 6,3.3 9,3 12,2.7 9,3 6)',@r GEOMETRY
SET @[email protected](.6)SET @[email protected]('CIRCULARSTRING(7 6,7.3 9,7 12,6.7 9,7 6)')
SELECT*FROM(VALUES(@),(@),(@),(@r.STBuffer(.3)),(@),(@),(@),(@),(@),(@),(@),(@),(@),
(@),(@),('CIRCULARSTRING(7 0,6 -1,5 0,5 1,5 1,5 0,5 0,4 -1,3 0)'),
(GEOMETRY::Point(5,1,0).STBuffer(5).STUnion(@r.STBuffer(1.3)).STDifference(@.STBuffer(.4)))
)a(g)

This site could use more T-SQL-based drawings!

Run on SQL 2017, but uses SQL geo-spatial storage features added back in SQL 2008. Line breaks are for readability only.

Output:

enter image description here

So, this was a pain to do in T-SQL, since spatial objects aren't exactly designed for drawing (no "ellipse" object, for example). Even more, getting the colors even close to right required a bit of trial and error.

Basically I'm constructing the following geometric objects:

  1. The eyes (@), which are points expanded into disks using STBuffer(.6) (the set of all points within 0.6 of those starting points)
  2. The ears (@r), which are generated as pointy curves, but are "puffed out" using STBuffer into either the inner or outer ears
  3. The face, which is a disk plus the ears minus the eyes. I have to construct this and display it as a single object, or SQL would display it in different colors.
  4. The mouth, which is a curve created using CIRCULARSTRING

To get the colors right, I have to SELECT these in the proper order. SSMS has a built-in sequence of colors for objects displayed in the spatial results pane, so the dark pink inner ears had to come 4th, and the light pink face had to come 16th. This required putting in a whole bunch of extra copies of the eyes, which is ok since we want them as close to black as possible (colors are somewhat transparent, so stacking them makes them darker).

Help and inspiration from the following resources:

EDIT: Moved the bunny down by 4 units, which changes some coordinates to a single digit, saving 6 bytes. No change in displayed output.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Building a rabbit with geometry objects, brilliant! xD \$\endgroup\$ Apr 24, 2019 at 6:57
11
\$\begingroup\$

Red, 375 340 329 bytes

Red[needs 'View]f: 'fill-pen p: 'pen e: 'ellipse c: 'circle
t:[5x5 0 180]view[base 200x200 #FFF draw
compose[(p)pink(f)pink(c)100x100 30(e)75x25
20x60(e)105x25 20x60(p)#E28(f)#E28(e)79x35 12x35(e)109x35
12x35(p)#FFF(f)#FFF(c)88x92 8(c)112x92 8(p)#000(f)#000(c)88x92
5(c)112x92 5 line 100x108 100x115 arc 95x115(t)arc 105x115(t)]]

enter image description here

\$\endgroup\$
7
\$\begingroup\$

Ruby with Shoes, 240 characters

Shoes.app{['fcc',[0,40,60],[5,0,20,50],[35,0,20,50],'f99',[t=10,t,t,h=30],[40,t,t,h],'fff',[t,55,15],[35,55,15],'000',[14,58,7],[38,58,7]].map{|v|stroke fill v rescue oval *v}
nofill
line h,75,h,80
arc 25,80,t,t,0,3.14
arc 35,80,t,t,0,3.14}

Sample output:

Shoes window screenshot with easter bunny

\$\endgroup\$
7
\$\begingroup\$

Python, 368 bytes

Using matplotlib.

from matplotlib import pyplot as l,patches as p,transforms as t;z=0,;[l.gca().add_patch(p.Wedge(z*2,R,s,360,width=w,color=(r,o,o),transform=t.Affine2D.from_values(X,0,0,9,350+x*n,y*9)))for R,s,w,r,o,X,x,y in zip([11,7,15,4,2,2,99],z*5+(180,359),[None]*5+[.2,.4],(1,)*4+z*3,(.8,.6,.8,1)+z*3,[4,4]+[9]*5,[7,7,0,6,6,2,98.8],[51,51,30,35,35,24,26])for n in[-9,9]];l.show()

Result: enter image description here

Ungolfed:

from matplotlib import pyplot, patches, transforms
z = 0, # store zero as a tuple for later repetition
for radius, startAngle, width, red, other, xStretch, x, y in \
    zip([11 ,7  ,15 ,4  ,2  ,2  ,99   ],  # radius
        z * 5 +             (180,359  ),  # start angle
        [None] * 5 +        [.2 ,.4   ],  # wedge width (None = full)
        (1,) * 4        +z * 3         ,  # red channel
        (.8 ,.6 ,.8 ,1) +z * 3         ,  # other color channels
        [4]*2 + [9]*5                  ,  # x stretch factor
        [ 7 ,7  ,0  ,6  ,6  ,2  ,98.8 ],  # x
        [51 ,51 ,30 ,35 ,35 ,24 ,26   ]): # y
#        |   |   |   |   |   |   |
#        |   |   |   |   |   |   "straight" line for upper mouth
#        |   |   |   |   |   |   approximated by large radius arc
#        |   |   |   |   |   |
#        |   |   |   |   |   Arc for lower mouth
#        |   |   |   |   |
#        |   |   |   |   Inner eye circle
#        |   |   |   |
#        |   |   |   Outer eye circle
#        |   |   |
#        |   |   Circle for head
#        |   |
#        |   Inner ear ellipse
#        |
#        Outer ear ellipse

    for n in [-9, 9]:        # draw left and right side mirrored
        pyplot.gca().add_patch( patches.Wedge(
            z*2,       # center = (0, 0), actual location set by the transform below
            radius,
            startAngle,
            360,       # end angle
            width = width,
            color = (red, other, other), # only red channel varies from the others
            transform = transforms.Affine2D.from_values( # affine transform matrix
                xStretch,    # x stretch factor
                0, 0,        # unused cross-axis coefficients for skew/rotation
                9,           # y stretch factor
                x * n + 350, # x value reflected by n, centered at 350
                y * 9 )))    # y value

pyplot.show()
\$\endgroup\$
5
\$\begingroup\$

JavaScript + P5.js, 291 276 273 bytes

A lot of small changes this time around.. that don't change the byte size at all.

setup=_=>{createCanvas(u=400,u);(e=ellipse,f=fill)`#fcc`;e(u/=2,u,x=150,x);e(x+=10,z=99,50,z);e(w=240,z,50,z);f`#f77`;e(x,z,y=30,80);e(w,z,y,80);f``;e(w,v=180,y,y);e(x,v,y,y);f(0);e(w,v,y=9,y);e(x,v,y,y);noFill(line(u,225,u,250));arc(195,...a=[245,y,y,0,PI]);arc(205,...a)}

Try it online!

Explanation:

setup = _ => { 
    createCanvas(u=400, u);                 // Create basic canvas.
    (e = ellipse, f = fill)`#fcc`;          // Light pink
    e(u /= 2, u, 150, 150);                 // These first few lines end up defining short-hand names for functions.
    e(x += 10, z = 99, 50, z);              // Throughout the code, you will see
    e(w = 240, z, 50, z);                   // lots of variable definitions.
    f`#f77`;                                // Dark pink
    e(x, z, y = 30, 80);                    // Another variable declaration
    e(w, z, y, 80);
    f``;                                    // Empty fill argument = white, apparently? (Eyes start here)
    e(w, v = 180, y, y);                    // I'll just stop commenting on declarations now
    e(x, v, y, y);
    f(0);                                   // fill(x) = fill(x, x, x)
    e(w, v, y = 9, y);
    e(x, v, y, y);
    noFill(line(u, 225, u, 250));           // Last part of bunny starts here.
                                            // Get rid of fill so the bunny doesn't look bad
    arc(195, ...a= [245, y, y, 0, PI]);
    arc(205, ...a)                          // arc() creates something similar to a semi-circle.
}
\$\endgroup\$
2
  • \$\begingroup\$ You can use #fcc and #f77 for the colors. \$\endgroup\$
    – ovs
    Apr 22, 2019 at 7:21
  • \$\begingroup\$ Well... I haven't found any new bytes since then. Safe to say I've optimized this too much? \$\endgroup\$
    – Epicness
    Apr 27, 2019 at 18:32
5
\$\begingroup\$

Javascript, 381 326 bytes

Thanks Arnold and Epicness.

(d=document).body.appendChild(a=d.createElement`canvas`);b=a.getContext`2d`;'A707|A7,/|Z707|Z7,/|MZAA|CR--|UR--|CR**|UR**|Id**|Nd**|La(+'.split`|`.map(x=>x.split``.map(c=>c.charCodeAt()-40)).map((x,i)=>b[b.beginPath(b.fillStyle='#'+'fccf77fff000'.substr('030306699'[i],3)),b.ellipse(...x,0,0,3*-~(i<9)),i>8?'stroke':'fill']())

\$\endgroup\$
5
  • 1
    \$\begingroup\$ 389 bytes \$\endgroup\$
    – Arnauld
    Apr 22, 2019 at 12:38
  • 1
    \$\begingroup\$ Recently submited an edit for -6. \$\endgroup\$
    – Epicness
    Apr 22, 2019 at 17:42
  • \$\begingroup\$ If you are willing to change your answer HTML+JavaScript you can make the HTML part <canvas id=A> and the first part of the JS b=A.getContext... - I used this in my Flag of Iceland some time ago. This post just saved that that answer 2 bytes :) codegolf.stackexchange.com/a/176852/8340 \$\endgroup\$
    – dana
    Apr 23, 2019 at 10:05
  • \$\begingroup\$ As a bonus you can embed a "stack snippet" :) stackoverflow.blog/2014/09/16/… \$\endgroup\$
    – dana
    Apr 23, 2019 at 10:08
  • 1
    \$\begingroup\$ You can save 3 more bytes with (D=document).body.appendChild(a=D.createElement`canvas`). \$\endgroup\$
    – Arnauld
    Apr 23, 2019 at 16:23
5
\$\begingroup\$

HTML, 280 278 bytes

a{color:#FFC8C8;}b{color:#FF7F7F;font-size:6px;margin-left:-10px;}m,n,j,d{display:block;}m{margin:-15px -3px;font-size:40px;}n{margin:-35px 5px;color:#FFF;font-size:15px;}j{margin:-14px 1px;color:#000;font-size:10px;}
<a>⬮<b>⬮</b><a>⬮<b>⬮</b><m>⬤<n>● ●<j>●‌‌ ‌‌ ‌‌ ●<d>‌‌ ‌‌ ‌‌ w

Here is a screenshot:

enter image description here

Citations

\$\endgroup\$
0
4
\$\begingroup\$

Lua + LÖVE/Love2D, 328 bytes

l=love g=l.graphics c=g.setColor e=g.ellipse a=g.arc f="fill" k="line" o="open"function l.draw()c(1,.7,.7)e(f,50,82,40,40)e(f,30,28,10,25)e(f,70,28,10,25)c(1,.4,.4)e(f,30,28,5,18)e(f,70,28,5,18)c(1,1,1)e(f,35,73,8,8)e(f,65,73,8,8)c(0,0,0)g[k](49,90,49,99)a(k,o,45,96,5,.5,2.7)a(k,o,53,96,5,.5,2.7)e(f,35,73,4,4)e(f,65,73,4,4)end

Try it online!

enter image description here

\$\endgroup\$
4
\$\begingroup\$

Processing, 388 343 319 chars/bytes

Not very elegant, but here it is. Saved bytes by making the image smaller.

int b=50,c=60,g=70;
noStroke();
//Face
fill(#FFCCCC);
ellipse(b,g,c,c);
//Outer ears
ellipse(40,25,15,b);
ellipse(c,25,15,b);
//Inner ears
fill(#FF9999);
ellipse(40,25,7,30);
ellipse(c,25,7,30);
//Outer eyes
fill(-1);
ellipse(40,g,10,10);
ellipse(c,g,10,10);
//Inner eyes
fill(0);
ellipse(40,g,5,5);
ellipse(c,g,5,5);
//Mouth
stroke(0);
line(b,80,b,85);
noFill();
arc(53,85,5,5,0,PI);
arc(48,85,5,5,0,PI);

enter image description here

\$\endgroup\$
6
  • \$\begingroup\$ I saved a few bytes here and there: 332 bytes \$\endgroup\$
    – Zylviij
    Apr 23, 2019 at 18:41
  • \$\begingroup\$ 279 bytes \$\endgroup\$
    – Zylviij
    Apr 23, 2019 at 18:49
  • \$\begingroup\$ If strokes are allowed, 258 bytes \$\endgroup\$
    – Zylviij
    Apr 23, 2019 at 19:02
  • \$\begingroup\$ @Zylviij Used some of the ideas you had and got it down to 343 chars. I don't think you can assign functions to variables in Processing. \$\endgroup\$
    – Robert S.
    Apr 23, 2019 at 21:15
  • \$\begingroup\$ I haven't ever personally used or downloaded processing, but I did test my scripts here and they worked without producing errors in the image or console \$\endgroup\$
    – Zylviij
    Apr 23, 2019 at 21:19
4
\$\begingroup\$

PostScript, 688 484 468 439 359 bytes

Golfed version:

<</F{closepath fill}/C{0 360 arc}/c{curveto}/s{setrgbcolor}/M{moveto}/h{7 15 M 7 25 1 25 1 15 c 1 5 7 5 7 15 c F 1 .5 .5 s 6 15 M 6 22 2 22 2 15 c 2 8 6 8 6 15 c F 1 1 1 s 4 3 2 C F 0 0 0 s 4 3 1 C F 0 -3 M 0 -5 lineto stroke 1 -5 1 180 0 arc stroke}>>begin 80 60 translate 5 5 scale .2 setlinewidth 1 .7 .7 s 0 0 10 C F gsave h grestore -1 1 scale h showpage

Ungolfed version:

% define some short-named procedures for later use
<<
  /F {closepath fill}
  /C {0 360 arc}
  /c {curveto}
  /s {setrgbcolor}
  /M {moveto}
  /h {                           % procedure for drawing one half
    7 15 M 7 25 1 25 1 15 c      % ellipse for outer ear
           1  5 7  5 7 15 c F
    1 .5 .5 s                    % dark pink
    6 15 M 6 22 2 22 2 15 c      % ellipse for inner ear
           2  8 6  8 6 15 c F
    1 1 1 s                      % white
    4 3 2 C F                    % circle for outer eye
    0 0 0 s                      % black
    4 3 1 C F                    % circle for inner eye
    0 -3 M 0 -5 lineto stroke    % line for upper mouth
    1 -5 1 180 0 arc stroke      % arc for lower mouth
  }
>> begin

80 60 translate                  % over-all shift
5 5 scale                        % over-all scale
.2 setlinewidth
1 .7 .7 s                        % light pink
0 0 10 C F                       % large circle for head
gsave h grestore                 % right half
-1 1 scale h                     % left half
showpage

Result:

bunny

\$\endgroup\$
3
\$\begingroup\$

SVG (HTML5), 415 bytes

<svg width=48 height=80><g fill=#fdd><circle cx=24 cy=52 r=24 /><ellipse cx=12 cy=16 rx=8 ry=16 /><ellipse cx=36 cy=16 rx=8 ry=16 /></g><g fill=#f99><ellipse cx=12 cy=16 rx=4 ry=12 /><ellipse cx=36 cy=16 rx=4 ry=12 /></g><g fill=#fff><circle cx=16 cy=44 r=6 /><circle cx=32 cy=44 r=6 /></g><circle cx=16 cy=44 r=3 /><circle cx=32 cy=44 r=3 /><path stroke=#000 fill=none d=M18,60a3,3,180,0,0,6,0v-6v6a3,3,180,0,0,6,0

Keeping the height below 100 to help save precious bytes, but still the longest...

\$\endgroup\$
3
\$\begingroup\$

Java, 508 472 bytes

import java.awt.*;v->new Frame(){Graphics2D G;Color C;void d(int...d){G.fillOval(d[0],d[1],d[2],d[3]);}{add(new Panel(){public void paint(Graphics g){G=(Graphics2D)g;G.setPaint(C.PINK);d(0,65,99,99);d(22,0,24,75);d(58,0,24,75);G.setPaint(C.MAGENTA);d(27,5,14,65);d(63,5,14,65);G.setPaint(C.WHITE);d(24,85,20,20);d(60,85,20,20);G.setPaint(C.BLACK);d(30,91,8,8);d(66,91,8,8);G.drawArc(41,124,9,11,0,-180);G.drawArc(50,124,9,11,0,-180);G.drawLine(50,119,50,130);}});show();}}

This is the resulting bunny:
enter image description here

Explanation:

import java.awt.*;              // Required imports for almost everything
v->                             // Method with empty unused parameter and no return-type
  new Frame(){                  //  Create a new Frame
    Graphics2D G;               //   Graphics2D-object on class-level
    Color C;                    //   Color variable to save bytes with static calls
    void d(int...d){            //   Create an inner method with integer-varargs as parameter
      G.fillOval(               //    Draw a circle/oval, filled with the current color:
        d[0],d[1],              //     With the first two integers as starting x,y position
        d[2],                   //     Third as width
        d[3]));}                //     And fourth as height
    {                           //   Then open an initializer-block inside the Frame-class
     add(new Panel(){           //    And add a Panel to the Frame we can draw on
       public void paint(Graphics g){
                                //     Override the Panel's paint-method
         G=(Graphics2D)g;       //      Set the Graphics2D-object with the parameter
         G.setPaint(C.PINK);    //      Set the color to pink (255,175,175)
         d(0,65,99,99);         //      Draw the head circle
         d(22,0,24,75);         //      Draw the left ear
         d(58,0,24,75);         //      Draw the right ear
         G.setPaint(C.MAGENTA); //      Change the color to magenta (255,0,255)
         d(27,5,14,65);         //      Draw the inner part of the left ear
         d(63,5,14,65);         //      Draw the inner part of the right ear
         G.setPaint(C.WHITE);   //      Change the color to white (255,255,255)
         d(24,85,20,20);        //      Draw the left eye
         d(60,85,20,20);        //      Draw the right eye
         G.setPaint(C.BLACK);   //      Change the color to black (0,0,0)
         d(30,91,8,8);          //      Draw the left pupil
         d(66,91,8,8);          //      Draw the right pupil
         G.drawArc(41,124,9,11,0,-180);
                                //      Draw the left mouth elipse
         G.drawArc(50,124,9,11,0,-180);
                                //      Draw the right mouth elipse
         G.drawLine(50,119,50,130);}});
                                //      Draw the line of the mouth
    show();}}                   //    And finally show the Frame on the screen
\$\endgroup\$
2
\$\begingroup\$

Racket – 597 bytes

#!racket/gui
(require pict)(show-pict(let*([c cc-superimpose][f filled-ellipse][h hc-append][v vc-append][r rotate][p"pink"][E(c(f 85 140 #:color p)(f 50 90 #:color"maroon"))][I(c(f 60 60 #:color"white")(f 30 30 #:color"brown"))][M(dc(λ(D X Y)(define-syntax-rule(S a ...)(send D a ...))(let([P(S get-pen)][B(S get-brush)])(S set-brush(new brush%[style 'transparent]))(S set-pen(new pen%[width 5]))(S draw-arc(+ 1 X)Y 48 48(- pi)0)(S set-brush B)(S set-pen P)))50 50)])(v -40(h 20(r E .2)(r E -.2))(c(f 220 220 #:color p)(v 20(h 40 I I)(v -25(filled-rectangle 8 30 #:color"darkbrown")(h M M)))))))

Here's the TIO link. Note that TIO can not display the pict, hence why it shows an error. Feel free to use the link as a means to verify byte count and/or copy code into a local file.


Screenshot from DrRacket

Screenshot of the Easter Bunny drawn in a new window; ran in DrRacket.

Yeah, I'm using Light Theme at 12am xD

Explanation of the code

This explanation uses the ungolfed version of the program, but I will explain what I did to golf it up along the way.

On line one, we set the language to racket/gui. This us to create GUIs and use some pieces of Racket's canvas API (we use the brush% and pen% classes to make new styles for them). Right underneath the language statement, we import the pict package. This package sort of works like JavaScript's Canvas API.

#lang racket/gui
(require pict)

(show-pict ...)

We then start the program by calling show-pict which renders a picture to the screen.

While Racket has already made picts for fishes and files, it doesn't have a pict for an easter bunny. So let's make one! To make the eyes and the ears, we can use filled ellipses. In pict, they are defined simply as filled-ellipse. The function receives the following input:

(filled-ellipse width height #:color [fill-color])

Now we want to center an ellipse inside of another (the outer one for the outer ear/eyeball and the inner ellipse for the inner ear/pupil). To that, we simply use the function cc-superimpose (standing for "align center x, align center y, on top of another pict").

(let ([ear (cc-superimpose (filled-ellipse 85 140 #:color "pink")
                           (filled-ellipse 50 90 #:color "maroon"))]  ;; Inner ear
      [eye (cc-superimpose (filled-ellipse 60 60 #:color "white")
                           (filled-ellipse 30 30 #:color "brown"))])  ;; Eye pupil
  ...))

Now comes a trickier part: the mouth. I say it is tricky because it involves using the canvas context API to draw arcs. pict provides a neat little function called dc which allows use to access the drawing context for the canvas. It accepts three arguments: a function that draws the pict using the context, width and height. The function that it receives must also receive three arguments, but this time, they are: the drawing context and the current x and y positions for the pen and brush.

(dc (lambda (dc dx dy)
      ...) width height)

To tell the context to draw something or to configure the pen and brush, we use the send function to call the one of the object's methods. In our case, we set the brush to 'transparent.

(send dc set-brush (new brush% [style 'transparent]))

Now that the basic functionality is mostly explained, let's create the mouth once and for all.

(let ([ear (cc-superimpose (filled-ellipse 85 140 #:color "pink")
                           (filled-ellipse 50 90 #:color "maroon"))]  ;; Inner ear
      [eye (cc-superimpose (filled-ellipse 60 60 #:color "white")
                           (filled-ellipse 30 30 #:color "brown"))]  ;; Eye pupil
      [mouth (dc (λ (dc dx dy)
                   ;; Save old pen/brush state
                   (define old-pen (send dc get-pen))
                   (define old-brush (send dc get-brush))
                   ;; Set new styles for pen/brush
                   (send dc set-brush (new brush% [style 'transparent]))
                   (send dc set-pen (new pen% [width 5]))
                   ;; Draw an arc of width/height 48 at (dx+1,dy) counter-clockwise
                   (send dc draw-arc (+ 1 dx) dy 48 48 (- pi) 0)
                   ;; Reset to default pen/brush for `dc` function
                   (send dc set-brush old-brush)
                   (send dc set-pen old-pen)) 50 50)])
  ...))

One thing to note is (+ 1 dx). I had a rendering issue where the arc would be one pixel to the left of the nose, and this seemed to fix it. I think it might be caused from putting the arcs side-by-side using hc-append.

Let's add these facial features to the actual face.

(let ([ear (cc-superimpose (filled-ellipse 85 140 #:color "pink")
                           (filled-ellipse 50 90 #:color "maroon"))]  ;; Inner ear
      [eye (cc-superimpose (filled-ellipse 60 60 #:color "white")
                           (filled-ellipse 30 30 #:color "brown"))]  ;; Eye pupil
      [mouth (dc (λ (dc dx dy)
                    ;; Save old pen/brush state
                    (define old-pen (send dc get-pen))
                    (define old-brush (send dc get-brush))
                    ;; Set new styles for pen/brush
                    (send dc set-brush (new brush% [style 'transparent]))
                    (send dc set-pen (new pen% [width 5]))
                    ;; Draw an arc of width/height 48 at (dx+1,dy) counter-clockwise
                    (send dc draw-arc (+ 1 dx) dy 48 48 (- pi) 0)
                    ;; Reset to default pen/brush for `dc` function
                    (send dc set-brush old-brush)
                    (send dc set-pen old-pen))
                  50 50)])
  ;; Ears + Face
  (vc-append -40
             ;; Ears
             (hc-append 20
                        (rotate ear .2)
                        (rotate ear -.2))
             ;; Face
             (cc-superimpose (filled-ellipse 220 220 #:color "pink")
                             (vc-append 20
                                        ;; Eyes
                                        (hc-append 40 eye eye)
                                        (vc-append -25
                                                   ;; Nose
                                                   (filled-rectangle 8 30 #:color "darkbrown")
                                                   ;; Mouth
                                                   (hc-append mouth mouth))))))

The vc-append appends picts vertically, and hc-append appends picts horizontally. Both functions receive an optional spacing parameter as it's first argument. So the arcs for the mouth have no space between them, but the eyes have 40 pixels between them. To fit the nose and the mouth snuggly together, I used -25 as the spacing.

Putting the program together is as simple as placing the let block inside of show-pict.

A cleaner more declarative way

#lang racket/gui
(require pict)

(define ear (cc-superimpose (filled-ellipse 85 140 #:color "pink")
                            (filled-ellipse 50 90 #:color "maroon")))

(define ears (hc-append 20 (rotate ear .2) (rotate ear -.2)))

(define eye (cc-superimpose (filled-ellipse 60 60 #:color "white")
                            (filled-ellipse 30 30 #:color "brown")))

(define eyes (hc-append 40 eye eye))

(define nose (filled-rectangle 8 30 #:color "darkbrown"))

(define mouth (dc (λ(dc dx dy)
                    (define old-pen (send dc get-pen))
                    (define old-brush (send dc get-brush))
                    (send dc set-brush (new brush% [style 'transparent]))
                    (send dc set-pen (new pen% [width 5]))
                    (send dc draw-arc (+ 1 dx) dy 48 48 (- pi) 0)
                    (send dc set-brush old-brush)
                    (send dc set-pen old-pen))
                  50 50))

(define nose+mouth (vc-append -25 nose (hc-append mouth mouth)))

(define eyes+nose+mouth (vc-append 20 eyes nose+mouth))

(define face (cc-superimpose (filled-ellipse 220 220 #:color "pink")
                             eyes+nose+mouth))

(define easter-bunny (vc-append -40 ears face))
(show-pict easter-bunny)

How I golfed the code

While the let statement is cool, its brother let* is cooler. A normal let statement doesn't allow other variables inside of its binding block to see each other. but this isn't the case with let*:

;; Normal let - Can't use `f` in let binding
(let ([f filled-ellipse]
      [red-circle (filled-ellipse 50 50 #:color "red")])
  (cc-superimpose (f 50 50 #:color "yellow") red-circle))

;; Cooler let*
(let ([f filled-ellipse]
      [red-circle (f 50 50 #:color "red")])
  (cc-superimpose (f 50 50 #:color "yellow") red-circle))

Because I use cc-superimpose, vc-append, hc-append, etc. a lot, I used let* so that I could bind them to shorter names:

(let* ([c cc-superimpose]
       [f filled-ellipse]
       [h hc-append]
       [v vc-append]
       [r rotate]
       [p "pink"])
  ...)

I then proceeded to rename all variables to shorter names, ear becomes E, eye becomes I, etc. After that was done, I started to ponder what I could do with the dreaded mouth sends. I ended up creating a macro :)

(let* (...
       [M (dc (λ (D X Y)
                 ;; This macro will replace (S get-pen) with (send D get-pen)
                 (define-syntax-rule (S a ...)
                   (send D a ...))
                 (let ([P (S get-pen)] [B (S get-brush)])
                   (S set-brush (new brush% [style 'transparent]))
                   (S set-pen (new pen% [width 5]))
                   (S draw-arc (+ 1 X) Y 48 48 (- pi) 0)
                   (S set-brush B)
                   (S set-pen P)))
                50 50)])
  ...)

Once that was done, I deleted all unnecessary spaces.

The golfed code and the code I used in the explanation are basically identical. If you don't want to open a new window, simply remove the call to show-pict. Pict only displays well in DrRacket. If you try to print a pict to a console like CMD or terminal, you'd see #<pict> which is Racket's way of telling you "Yes the statement returned something, but I can only tell you what type of object it returned.

Conclusion

It's 2am now as I write this conclusion, hope everyone enjoyed this little tutorial-ish explanation!

Have an amazing week!

\$\endgroup\$
1
\$\begingroup\$

Scala, with Java AWT(Abstract Window Toolkit). 714 bytes

Port of @Kevin Cruijssen's Java answer in Scala.


Golfed Scala code.

import java.awt._;import java.awt.event._;object M extends App{new Frame{def d(g:Graphics2D,x:Int,y:Int,w:Int,h:Int)=g.fillOval(x,y,w,h);add(new Panel{override def paint(g:Graphics){val g2=g.asInstanceOf[Graphics2D];g2.setPaint(Color.PINK);d(g2,0,65,99,99);d(g2,22,0,24,75);d(g2,58,0,24,75);g2.setPaint(Color.MAGENTA);d(g2,27,5,14,65);d(g2,63,5,14,65);g2.setPaint(Color.WHITE);d(g2,24,85,20,20);d(g2,60,85,20,20);g2.setPaint(Color.BLACK);d(g2,30,91,8,8);d(g2,66,91,8,8);g2.drawArc(41,124,9,11,0,-180);g2.drawArc(50,124,9,11,0,-180);g2.drawLine(50,119,50,130);}});setSize(new Dimension(200,200));setVisible(true);addWindowListener(new WindowAdapter{override def windowClosing(e:WindowEvent):Unit=System.exit(0);})}}

This is the resulting bunny.

enter image description here

Ungolfed Scala code.

import java.awt._
import java.awt.event._

object ScalaMain extends App {
  new Frame {
    def d(g: Graphics2D, x: Int, y: Int, w: Int, h: Int): Unit = g.fillOval(x, y, w, h)

    add(new Panel {
      override def paint(g: Graphics): Unit = {
        val g2d = g.asInstanceOf[Graphics2D]
        g2d.setPaint(Color.PINK)
        d(g2d, 0, 65, 99, 99)
        d(g2d, 22, 0, 24, 75)
        d(g2d, 58, 0, 24, 75)
        g2d.setPaint(Color.MAGENTA)
        d(g2d, 27, 5, 14, 65)
        d(g2d, 63, 5, 14, 65)
        g2d.setPaint(Color.WHITE)
        d(g2d, 24, 85, 20, 20)
        d(g2d, 60, 85, 20, 20)
        g2d.setPaint(Color.BLACK)
        d(g2d, 30, 91, 8, 8)
        d(g2d, 66, 91, 8, 8)
        g2d.drawArc(41, 124, 9, 11, 0, -180)
        g2d.drawArc(50, 124, 9, 11, 0, -180)
        g2d.drawLine(50, 119, 50, 130)
      }
    })
    setSize(new Dimension(200, 200))
    setVisible(true)
    addWindowListener(new WindowAdapter {
      override def windowClosing(e: WindowEvent): Unit = System.exit(0)
    })
  }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.