Diagram of the Utmost Extremes

The Mathematica SE question Creating the Yin Yang symbol with minimal code seems to be an out-of-the-box ready-to-go Code Golf SE question!

The following code creates the Yin Yang symbol

Graphics[{Black, Circle[{0, 0}, 1], White,
DiskSegment[{0, 0}, 1, {1/2 \[Pi], 3/2 \[Pi]}],
Black, DiskSegment[{0, 0}, 1, {3/2 \[Pi], 5/2 \[Pi]}], White,
Disk[{0, 0.5}, 0.5],
Black, Disk[{0, -0.5}, 0.5], Black, Disk[{0, 0.5}, 0.125],
White, Disk[{0, -0.5}, 0.125]
}] // Show


Knowing that 'there is always someone who can do things with less code', I wondered what the optimal way is, in Mathematica, to create the Yin Yang symbol.

Not really an urgent question to a real problem, but a challenge, a puzzle, if you like. I hope these kind of questions can still be asked here.

From my current understanding what's actually sought in this question is not the symbol for Yin and Yang which is just the complementary black and white halves of a circle, but the Taijitu or "Diagram of the Utmost Extremes" which is rotated by 90 degrees so that the circles halves are roughly left and right, and with two small circular spots are added along the vertical axis at half radius.

Answers to the meta question Default acceptable image I/O methods for image related challenges already show a high level of community acceptance; I wouldn't want to specify further.

Regular code golf; fewest bytes to generate Code Golf SE-acceptable graphics output per the meta link above.

• border thickness must be between 1 pixel and 4% of the overall diameter
• spot diameter must be between 1/10 and 1/7 of the overall diameter
• overall diameter at least 200 pixels
• error 2 pixels / 1%.
• Should resemble black ink on white paper as the original symbol is defined. However since some anti-aliasing uses color, use of color is not excluded as long as the image looks monochrome.
• same orientation as the image specimen left side white, small dots along central vertical axis.

This is the Taijitu (太極圖), with black representing yin and white representing yang. It is a symbol that reflects the inescapably intertwined duality of all things in nature, a common theme in Taoism. No quality is independent of its opposite, nor so pure that it does not contain its opposite in a diminished form: these concepts are depicted by the vague division between black and white, the flowing boundary between the two, and the smaller circles within the large regions.

• I think you should specify: Accuracy in terms of resolution, or minimum number of pixels where applicable; border thickness; spot size; colour (does it have to be black/white or would any 2 colours do); rotation (does it have to be background on left/foreground on right, or would any 90 deg / 180 deg / other rotation do?) Dec 26, 2021 at 22:42
• @LevelRiverSt Good point, I see what you mean. I've done my best, how does that look?
– uhoh
Dec 26, 2021 at 22:53
• @LevelRiverSt Has your comment been sufficiently addressed? Does it need to remain? My concern is that folks will see it and think there are outstanding issues and then not answer.
– uhoh
Dec 26, 2021 at 23:18
• colour & rotation are clear. The Q&A format is a bit cluttered. It's better to make the answers stand on their own, then you can delete the questions and the text will be more concise. The other points I'm not sure you understood - you should specify something objective. Normally we specify something objective like border thickness must be between 1 pixel and 4% of the overall diameter, spot diameter must be between 1/10 and 1/7 of the overall diameter, overall diameter at least 200 pixels, and error 2 pixels / 1%. Ive suggested these as I think they don't invalidate the existing answers Dec 27, 2021 at 0:52
• @LevelRiverSt done, I'll leave the whole antialiasing to another day/question. Thanks!
– uhoh
Dec 27, 2021 at 1:08

R, 153152145 144 bytes

Edit: -8 bytes thanks to pajonk

plot(NA,,,l<-c(-8,8),l,an=F,ax=F)
Map(function(r,c,y,h)polygon(r*1i^(1+1:(h*99)/99)+y*1i,b=c,c=c),c(8.2,8,4,4,1,1),1:0,c(0,0,l/2,4,-4),-.75:5*8)


Try it at rdrr.io

plot(NA,,,l<-c(-8,8),l,an=F,ax=F) # initializes a new plot area
# from x,y = -8 to 8
# without annotation, without axes

Map(                              # apply function...
function(r,c,y,h)                # with arguments r=radius, c=colour, y=y position, h=half-circle?
polygon(r*1i^(1+1:(h*99)/99)+y*1i,
# draw polygon using complex coordinates
b=c,c=c),                      # border=c, colour=c
# ...to these values:
c(8.2,8,4,4,1,1),                 # radii for 6 circles
1:0,                             # colours (recycled; 0=white, 1=black)
c(0,0,-4,4,4,-4),                # y positions
-.75:5*8                         # abs≥4:full circle, 2:half circle (only 2nd)
)

• @uhoh - I think it's a display issue: the R graphics window is resizeable (and the output can get distorted to fit). On my laptop it's a circle (unless I resize it). In any case, the coordinates of the outer circle are from -8.2 to 8.2 across, and from -8.2i to 8.2i up-&-down, so it's mathematically a circle! Dec 27, 2021 at 0:38
• You can replace -4,4 with l/2 for -1 byte. Dec 27, 2021 at 19:55
• @pajonk - Well spotted! Thanks! That's a happy coincidence... Dec 27, 2021 at 20:06
• Map saves some bytes (Map(function(r,c,y,h)polygon(r*1i^(1+1:(h*99)/99)+y*1i,b=c,c=c),c(8.2,8,4,4,1,1),1:0,c(0,0,l/2,4,-4),4-2*!-1:4)). Dec 28, 2021 at 6:42
• @pajonk - Much nicer. Thanks. Dec 28, 2021 at 10:57

JavaScript (ES6), 141110 96 bytes

X=>Y=>(e=y=>((R=X-210)**2+(Y-y*100-10)**2)**.5/5)(2)<42&&e(2)>40|R>0^e(R>0?3:1)<20^e(3)<5^e(1)<5


A pixel-shader function taking x and y coordinates between 0 and 420 and returning a boolean value, as allowed by this consensus. The below snippet creates a 420x420 image.

-14 bytes thanks to tsh

f=

X=>Y=>(e=y=>((R=X-210)**2+(Y-y*100-10)**2)**.5/5)(2)<42&&e(2)>40|R>0^e(R>0?3:1)<20^e(3)<5^e(1)<5

let canvas = document.getElementById('x').getContext2d

for(let i = 0; i < 420; i++){
for(let j = 0; j < 420; j++){
canvas.fillStyle = f(i)(j) ? 'black' : 'white'
canvas.fillRect(i,j,1,1);

}
}
<canvas id=x width=420 height=420 ></canvas>

Explanation (old)

X=>Y=>                 // Curry function taking x and y parameters
(e = y => (          // Euclidean distance function named e
(X-210)**2+      // To (x = 200,
(Y-y*100-10)**2  // y = input * 100 + 10
) ** .5
)(2) < 210           // Is the distance to the center less than 210?
&& e(2) > 200        // If not, shortcircuit, else is the distance to the center greater than 200 (border)?
| (                  // Else...
Y > 210            // Is Y > 210 (Bottom half)
& e(3) < 100       // And distance to center of lower circle less than 100 (Black circle on bottom)
| X > 210          // Or X > 210 (Area on left)
& e(1) > 100       // And distance to center of upper circle > 100 (Excluding white top circle)
) & e(3) > 25        // And distance to center of lower circle > 25 (Excluding white bottom circle)
| e(1) < 25          // Or distance to center of upper circle < 25 (Including black top circle)

• X=>Y=>(e=y=>((R=X-210)**2+(Y-y*100-10)**2)**.5/5)(2)<42&&e(2)>40|R>0^e(R>0?3:1)<20^e(3)<5^e(1)<5
– tsh
Dec 27, 2021 at 9:27

SVG (HTML 5) 172 bytes

This is just an optimization of Neil's post, but he was warry of path minification. I think that just a bit silly (I hope you can excuse the expression), as there are at least two libraries in millionfold use that do just that. One is a default part of Adobe Illustrator's SVG export, the other is part of the SVGO library. – Both would not fair well if used unaltered here, because they assume to work for standalone SVG files which need to be wellformed XML.

The pivotal part of path optimization is that the grammar expects a greedy parser and takes separation as optional: everything that can be legally interpreted as part of a token is read as a part of it, and the next token can start immediately.

So

M 0,-20 a 40,40 0 0 1 0,80


can be shortened to

M0-20a40,40,0,010,80

• because the - cannot be interpreted as part of a preceding number
• because command letters need no separation from numbers
• because the fourth and fifth parameters to an A/a command, commonly called large_arc and sweep, are actually flags that can only take the values 0 or 1.

<svg viewBox=-41,-21,82,82><path d=M0-20a40,40,0,010,80a20,20,0,010-40a20,20,0,000-40a40,40,0,000,80a40,40,0,010-80 stroke=#000 /><circle r=5 /><circle cy=40 r=5 fill=#fff>

PostScript, 139 86 bytes

00000000: 5b28 5b29 7b92 967d 2f63 7b30 8768 0192  [([){..}/c{0.h..
00000010: 057d 285d 297b 6392 427d 3e3e 920d 2e30  .}(]){c.B}>>...0
00000020: 3492 9b88 4837 3292 8b39 7b34 7d92 8332  4...H72..9{4}..2
00000030: 2e30 3220 6392 a732 870e 0139 3092 0592  .02 c..2...90...
00000040: 4233 2031 5d5b 3520 315d 3320 2e32 5d30  B3 1][5 1]3 .2]0
00000050: 5b35 202e 325d                           [5 .2]


Before tokenization:

[([){setgray}/c{0 360 arc}(]){c fill}>>begin
.04 setlinewidth 72 72 scale
9{4}repeat 2.02 c stroke
2 270 90 arc fill
3 1][5 1]3 .2]0[5 .2]


As rendered by Preview in OS X:

Explanation:

% first some abbreviations
[([){setgray}/c{0 360 arc}(]){c fill}>>begin
.04 setlinewidth 72 72 scale
9{4}repeat            % push 4s onto stack
2.02 c stroke         % main stroked circle (r=2.02)
2 270 90 arc fill     % black semicircle (r=2)
3 1]                  % black semicircle (r=1)
[                     % choose white
5 1]                  % white semicircle (r=1)
3 .2]                 % white semicircle (r=.2)
0[                    % choose black
5 .2]                 % black semicircle (r=.2)


HTML, 267 bytes

<c style="border:1vw solid;background:linear-gradient(90deg,#fff 50%,#000 50%)"><c w style=top:0%><c></c></c><c style=top:50%><c w><style>c{position:absolute;padding:25%;border-radius:50%;top:25%;left:25%;background:#000}c c c{transform:scale(.5)}[w]{background:#fff}

Size of large ball is 52vw (50vw + border). Width of border is 1vw (1.92%). Size of small ball is 6.25vw (12.0%).

SVG (HTML5), 199 183 bytes

<svg viewBox=-41,0,82,82><path d=M0,1A40,40,0,0,1,0,81A20,20,0,0,1,0,41A20,20,0,0,0,0,1A40,40,0,0,0,0,81A40,40,0,0,1,0,1 stroke=#000 /><circle cy=21 r=5 /><circle cy=61 r=5 fill=#fff>

The path draws the main black body but also the outer border (it's golfier than a separate circle for some reason), but the two dots are separate circles. The border thickness is 1.25% of the overall diameter. The spot diameter is 12.5% of the overall diameter. The image scales to the viewing area. Edit: Saved 16 bytes thanks to @ccprog.

• The last two parameters to the A command are flags, which means you can leave out the commas: M41,1A40,40,0,0141,81A20,20,0,0141,41A20,20,0,0041,1A40,40,0,0041,81A40,40,0,0141,1. Dec 28, 2021 at 16:30
• ..and three more through the use of the a command: M41,1a40,40,0,010,80a20,20,0,010-40a20,20,0,000-40a40,40,0,000,80a40,40,0,010-80 Dec 28, 2021 at 16:42
• Setting viewBox=-41,-21,82,82 lets you leave out the cx cy attributes for the first circle, and cx for the second. While using relative path commands, only the first coordinate changes: M0-20a.... Final tally: 172 bytes. Dec 28, 2021 at 16:58
• @ccprog The last two parameters are the destination point, not flags, so I don't understand what you mean there; in fact, none of the examples where you removed commas work for me, so I only ended up setting viewBox=-41,0,82,82, which still saved 16 bytes, thanks.
– Neil
Dec 28, 2021 at 18:11
• Sorry for the confusion. Its the two commands before the destination coordinates, commonly called large_arc and sweep, and the commas after them. I was thinking in terms of my own parser, which treats the dest coordinates differently from the other parameters. You should be able to copy the following code verbatim: <svg viewBox=-41,-21,82,82><path d=M0,60a20,20,0,010-40a20,20,0,000-40a40,40,0,010,80 stroke=#000 /><circle r=5 /><circle cy=40 r=5 fill=#fff /><path d=M0,60a40,40,0,110-80 fill=none stroke=#000> Dec 28, 2021 at 18:45