# Draw Parcly Taxel's cutie mark

I've been a fan of My Little Pony: Friendship Is Magic for a long time, and very early on I created a pony version of myself also called Parcly Taxel (warning: may be too cute to look at). The symbol below is her "cutie mark", which you will be drawing here:

## Construction

All key points of the cutie mark lie on a rotated hexagonal grid, which will be given coordinates such that (1,0) is 15 degrees counterclockwise from the $$\+x\$$-axis and (0,1) is likewise 75 degrees counterclockwise.

• One of the six "arms" of the galaxy is bounded by the following segments in order:
• A cubic Bézier curve with control points (3,-2), (4,0), (5,4), (-2,12)
• A straight line from (-2,12) to (3,9)
• A cubic Bézier curve with control points (3,9), (7,3), (6,-1), (4,-3)
• A straight line from (4,-3) to (3,-2)
• The other five arms are obtained by successive 60-degree rotations of the initial arm about the origin. Their fill alternates between RGB #5A318E/(90,49,142) (dark blue) and #54A2E1/(84,162,225) (light blue), with the initial arm getting #5A318E. They have no stroke.
• Layered above the arms is a regular hexagon centred on the origin and with one vertex at (5,1). It has no fill and is instead stroked with RGB colour #576AB7/(87,106,183) (medium blue), has stroke width 0.5 and has miter join. Parts of the arms should show inside the hexagon.

Draw Parcly Taxel's cutie mark, with the following stipulations:

• The image can be saved to a file or piped raw to stdout in any common image file format, or it can be displayed in a window.
• The image must have (to best effort) the same orientation and aspect ratio as those implied by the construction. Raster images must be at least 400 pixels wide, and an error of 2 pixels/1% is allowed.
• If your language does not support arbitrary RGB colours you may use any colours reasonably recognisable as light, medium and dark blue.

This is ; fewest bytes wins.

I made this challenge to celebrate reaching 100,000 reputation on the Mathematics Stack Exchange, which I achieved on 27 February 2023.

• Congrats on the 100k on Math SE!!! Mar 1 at 17:51
• mlpvector.club/users/332/cg/v/241-Parcly-Taxel has a reference SVG that's trivially minified to 752 bytes. I can see right off the bat that it's manually drawing each of the swooshes though (instead of e.g. using <use>). Are shape description languages like SVG and TikZ allowed? Mar 1 at 20:53
• @JakobLovern Yes and yes (the latter even has a tips question) Mar 1 at 23:56
• @JakobLovern The provided file over there has quantised coordinates (in accordance with some conventions of the cutie mark section of my Selwyn Git repo). I redrew it exactly according to my own construction for this challenge, just to be sure. Mar 2 at 2:57

## SVG (HTML5), 361357 337 bytes

<svg viewBox=-25,-25,50,50><g fill=#54A2E1><g id=g><path id=a d=M.4,5.3C5.7,5.7,14.8,5,20.4,-9L21.6,-.4C15.7,8.3,6.6,9,-.1,7.2 /><use href=#a transform=rotate(120) /><use href=#a transform=rotate(240)></g><use href=#g transform=rotate(60) fill=#5A318E /><path d=M1.2,-11.1l9,6.6L9,6.6L-1.2,11.1l-9,-6.6L-9,-6.6Z fill=none stroke=#576AB7>

Edit: Saved 4* 24 bytes thanks to @ccprog. (I would have done it myself but the old version used a lot of integers and I was expecting to have to waste many bytes on the accuracy, but it turns out that e.g. √2(7-4√3) is amazingly close to 5!)

*crossed out 4 is still regular 4

• Oh wow, optimizations galore! Love how this builds off the same <use> concept as my answer without being nearly as unwieldy Mar 2 at 1:26
• -4 bytes if you leave off the  / in the last tag before the closing group tag and the  /> at the end. Also, I don't see a rule for the final orientation of the axes, so you could remove the initial transform=rotate(45) for -20 bytes. Mar 2 at 2:40
• Further, the arm path command can be written as M4,3.46C8,0,14-6.93,8-20.8L15-15.6C17-5.2,11,1.73,5,5.2, and the hexagon as M11-1.73,7,8.66-4,10.4-11,1.73-7-8.66,4-10.4Z (-10 bytes) Mar 2 at 3:00
• @ccprog "The image must have ... the same orientation ..."
– Neil
Mar 2 at 8:46
• (I don't like the coordinate optimisation, even though it is valid according to a Stack Overflow answer I just read.)
– Neil
Mar 2 at 8:58

# PaperScript, 358 355 bytes

H=(a,b)=>new Point(a+b/2,-b*.75**.5)
for(i=0;6>i++;){
p=new Path(H(4,-3))
p.C=p.cubicCurveTo
p.C(H(6,-1),H(7,3),H(3,9))
p.lineTo(H(-2,12))
p.C(H(5,4),H(4,0),H(3,-2))
p.closed=1
p.fillColor=i%2?"#54A2E1":"#5A318E"
p.pivot=0
p.rotation=60*i-15
}
v=H(5,1)
h=Path.RegularPolygon([0,0],6,v.length)
h.strokeColor="#576AB7"
h.strokeWidth=.5
h.rotation=v.angle+15


You can try it here. Paste in the code, and either add the following lines at the end:

view.center = [0,0]
view.zoom = 10


or use the magnifying glass tool to zoom in on the drawing (which will be partially visible in the top left corner of the canvas).

### Ungolfed and commented

// Convert hex coordinates (a, b) to Cartesian coordinates (x, y)
// (without the extra 15-degree rotation, which we will apply later)
function hexPoint(a, b) {
// a and b represent the magnitudes of two vectors whose
// sum is the point we want. The a vector points to the right
// (angle = 0 degrees) and the b vector points up and right
// (angle = -60 degrees, due to positive y being down in
// this coordinate system).
// Thus, x is a * cos(0) + b * cos(-60), which simplifies to:
var x = a + b / 2;
// and y is a * sin(0) + b * sin(-60):
var y = b * -Math.sqrt(3 / 4);
return new Point(x, y);
}

// Draw the six arms
// We draw each arm using the reference points given, then set its
// fill color and rotate it according to the loop index
for (var i = 0; i < 6; i++) {
// Start at (4, -3)
var arm = new Path(hexPoint(4, -3));
// Draw a cubic Bézier curve from there to (3, 9), with handles
// at (6, -1) and (7, 3)
arm.cubicCurveTo(hexPoint(6, -1), hexPoint(7, 3), hexPoint(3, 9));
// Draw a line from there to (-2, 12)
arm.lineTo(hexPoint(-2, 12));
// Draw a cubic Bézier curve from there to (3, -2), with handles
// at (5, 4) and (4, 0)
arm.cubicCurveTo(hexPoint(5, 4), hexPoint(4, 0), hexPoint(3, -2));
// Close the path
arm.closed = true;
// Fill with dark blue if i is even, light blue if i is odd
arm.fillColor = i % 2 ? "#54A2E1" : "#5A318E";
// Set the pivot point to the origin
arm.pivot = new Point(0, 0);
// Pivot the arm around that point 15 degrees counterclockwise
// and 60 * i degrees clockwise
arm.rotation = -15 + 60 * i;
}

// Draw the hexagon
// We know one of its vertices is at (5, 1), so we'll use that to
// calculate the right numbers for Paper.js's polygon-drawing
// function and a subsequent rotation
var vertex = hexPoint(5, 1);
// Draw a polygon:
hexagon = Path.RegularPolygon({
// Centered at the origin
center: new Point(0, 0),
// With six sides
sides: 6,
// With radius equal to the length of a vector from the origin
// to the vertex
// Medium blue
strokeColor: "#576AB7",
// With 0.5 stroke width
strokeWidth: 0.5,
// With these rotations:
//  30 degrees clockwise because the default orientation is
//   pointy-topped but we want flat-topped
//  15 degrees counterclockwise because of the rotation of
//   the whole figure
//  An angle equal to the angle a vector from the origin to
//   the previously calculated vertex makes with the x-axis
rotation: 30 - 15 + vertex.angle,
// With miter join style (default, don't need to specify)
});

// Put the origin in the center of the view and zoom in so the
// figure is more visible
view.center = new Point(0, 0);
view.zoom = 10;


# JavaScript (ES6), 273 bytes

Thanks to @Shaggy for suggesting to switch from HTML+JS to JS and saving 2 bytes in the new version

Generates a SVG similar to this one from Parcly Taxel.

f=(s=<svg viewBox="-22 -23 44 4${a=6}">)=>a--?f(s+(p='<path fill=')+#5${a&1?'a318e':'4a2e1'} d="M20.3-9C14.8 5 5.8 5.6.4 5.3L-.1 7.2C6.5 9 15.6 8.4 21.6-.4" transform="rotate(${a*60})"/>):s+p+none stroke=#576ab7 d="M-10.2 4.5-9-6.6 1.2-11.1 10.2-4.5 9 6.6-1.2 11.1Z">  Try it online! ### Graphical output f=(s=<svg viewBox="-22 -23 44 4${a=6}">)=>a--?f(s+(p='<path fill=')+#5${a&1?'a318e':'4a2e1'} d="M20.3-9C14.8 5 5.8 5.6.4 5.3L-.1 7.2C6.5 9 15.6 8.4 21.6-.4" transform="rotate(${a*60})"/>):s+p+none stroke=#576ab7 d="M-10.2 4.5-9-6.6 1.2-11.1 10.2-4.5 9 6.6-1.2 11.1Z">

document.write(f())

• Why not simply JavaScript for 289 bytes? Mar 2 at 10:54
• @Shaggy Good question... (Thank you!) Mar 2 at 11:04
• 276 bytes Mar 2 at 18:28
• Also, if you replace the spaces in the viewBox and d attributes with commas then you should, I believe be able to remove their enclosing quotation marks. Mar 2 at 19:08
• Oh, and you should also be able to drop the first closing Z. Mar 3 at 12:29

## SVG: 737 bytes

<svg xmlns="http://www.w3.org/2000/svg" viewBox="-22 -23 44 46"><defs><path id="s" d="M6 28C4 24 2 16 16 0L6 6C-2 18 0 26 4 30z"/></defs><g transform="rotate(-15)"><use transform="matrix(1,0,.5,-.87,-24,21)" href="#s" fill="#54A2E1"/><use transform="rotate(60) matrix(1,0,.5,-.87,-24,21)" href="#s" fill="#5A318E"/><use transform="rotate(120)matrix(1,0,.5,-.87,-24,21)" href="#s" fill="#54A2E1"/><use transform="rotate(180)matrix(1,0,.5,-.87,-24,21)" href="#s" fill="#5A318E"/><use transform="rotate(240)matrix(1,0,.5,-.87,-24,21)" href="#s" fill="#54A2E1"/><use transform="rotate(300)matrix(1,0,.5,-.87,-24,21)" href="#s" fill="#5A318E"/><path style="fill:none;stroke:#576AB7" d="M-11 1.7-7-8.7l11-1.7 7 8.7L7 8.7l-11 1.7z4"/></g></svg>


Using Parcly Taxel's SVG code here we can get a diff (mine in full color, original in red behind):

I could get it closer with a better transform matrix but that would require a larger file size.

## Pug (HTML), 266 267 bytes

- i=0
svg(viewBox='-25,-25,50,50')
while i<6
path(d='M3-2C4,0,5,4-2,12L3,9C7,3,6-1,4-3' fill=#5${i&1?'4a2e1':'a318e'}transform=rotate(${60*i++})matrix(1.93-.52.52-1.93,0,0))
path(d='M1.2-11.1l9,6.6L9,6.5-1.2,11.1l-9-6.6L-9-6.5Z' fill='none' stroke='#576AB7')


Try it online

The coordinate system transformation can be written as a matrix

$$\begin{matrix}\\ 1 & tan(15°) & 0\\ -tan(15°) & -1 & 0\\ 0 & 0 & 1\\ \end{matrix}$$

For a unit length of 2, (so the stroke width of the hexagon is 1), this has to be scaled with $$\\frac{2}{cos(15°)}\$$.

The path data remain in their original coordinate system as defined for the arms, but to ensure the constant stroke width of the hexagon, pretransformed path data are used.

• You have L9,6.5 and L-9-6.6 so your hexagon might be slightly more asymmetric then it needs to be.
– Neil
Mar 3 at 8:32
• @Neil corrected. I had inconsistencies anyway because I forgot to correct for the slight increase in unit length from the transform matrix. +6 bytes, unfortunately. Mar 3 at 13:01

# JShell 17: 490 488 + 6 bytes

Requires the JAVASE arg to be passed to JShell. I'm unsure how that's scored, hence the "+6" in the title. Shows the image in a window that might be hard to find at first.

new Frame(){public void paint(Graphics h){var g=(Graphics2D)h;g.rotate(-Math.PI/12);var p=new GeneralPath();for(var i=0;i<6;i++,g.rotate(Math.PI/3,169,304),g.setColor(new Color(5910926-i%2*364205)),p.moveTo(249,96),p.curveTo(309,235,249,304,209,338),p.lineTo(219,355),p.curveTo(279,321,339,252,319,148),p.closePath(),g.fill(p));int[]a={279,238,129,59,99,209},b={287,390,407,320,217,200};g.setColor(new Color(5728951));g.setStroke(new BasicStroke(10));g.draw(new Polygon(a,b,6));}}.show();


A surprising topic for a code golf challenge, nice!
What isn't surprising however is the not very competitive score... but as long as my Java answer isn't twice as long as its competitors, I'm happy with it.

This entry uses precalculated values whereever possible. I lose some precision doing this, but as I'm outputting a raster image I still get a fairly accurate result as this overlayed image shows:

In the end, I was able to gain two bytes by reducing the image size from 900x900 to 450x450, making the accuracy loss more noticeable. Here is the original post in case the error is too big:

new Frame(){public void paint(Graphics h){var g=(Graphics2D)h;g.rotate(-Math.PI/12);var p=new GeneralPath();for(var i=0;i<6;i++,g.setColor(new Color(5910926-i%2*364205)),p.moveTo(465,160),p.curveTo(585,437,465,575,385,644),p.lineTo(405,678),p.curveTo(525,609,645,472,605,264),p.closePath(),g.fill(p),g.rotate(Math.PI/3,305,575));int[]a={525,444,225,85,166,385},b={542,748,782,608,402,368};g.setColor(new Color(5728951));g.setStroke(new BasicStroke(20));g.draw(new Polygon(a,b,6));}}.show();