Euro construction 💶

Official Euro sign design: en.wikipedia.org/wiki/euro_sign#design

Log in to post a comment.

// You can find the Turtle API reference here: https://turtletoy.net/syntax
Canvas.setpenopacity(1);

const helperLines = 1; // min=0, max=1, step=1
const penWidth = 11;  // min=2, max=13, step=1
const openAngle = 40; // min=20, max=80, step=10


const radsPerDeg = Math.PI / 180;

// Global code will be evaluated once.
const turtle = new Turtle();
const text = new Text();

// The walk function will be called until it returns false.
function walk(i) {
    let outerRadius = penWidth * 6;
    let innerRadius = penWidth * 5;

    //inner circle
    let innerCircleStops = [openAngle];
    let bigBeta = Math.asin((penWidth * 1.5) / innerRadius) / radsPerDeg;
    let smallBeta = Math.asin((penWidth * .5) / innerRadius) / radsPerDeg;
    innerCircleStops.push(180 - openAngle - bigBeta);
    innerCircleStops.push(bigBeta - smallBeta);
    innerCircleStops.push(2 * smallBeta);
    innerCircleStops.push(bigBeta - smallBeta);
    innerCircleStops.push(180 - bigBeta - openAngle);
    
    turtle.jump(innerRadius, 0);
    turtle.setheading(90);
    turtle.pendown();
    let innerPoints = [turtle.position()];
    for(let i = 0; i < innerCircleStops.length; i++) {
        turtle.isdown()? turtle.penup(): turtle.pendown();
        turtle.circle(innerRadius, innerCircleStops[i]);
        innerPoints.push(turtle.position());
    }

    //outer circle
    let outerCircleStartAngle = Math.acos(innerRadius * Math.cos(openAngle * radsPerDeg) / outerRadius) / radsPerDeg;
    let outerCircleStops = [outerCircleStartAngle];
    
    bigBeta = Math.asin((penWidth * 1.5) / outerRadius) / radsPerDeg;
    smallBeta = Math.asin((penWidth * .5) / outerRadius) / radsPerDeg;
    outerCircleStops.push(180 - outerCircleStartAngle - bigBeta);
    outerCircleStops.push(bigBeta - smallBeta);
    outerCircleStops.push(2 * smallBeta);
    outerCircleStops.push(bigBeta - smallBeta);
    
    let lowerBetaRad = Math.atan(innerPoints[innerPoints.length - 1][0] / (outerRadius - innerPoints[innerPoints.length - 1][1]));
    let b = 2 * outerRadius * Math.cos(lowerBetaRad);
    let c = Math.cos(lowerBetaRad) * b;
    let upperBeta = Math.acos((c - outerRadius) / outerRadius) / radsPerDeg;
    outerCircleStops.push(180 - bigBeta - (90 - upperBeta));
    
    turtle.jump(outerRadius, 0);
    turtle.setheading(90);
    turtle.pendown();
    let outerPoints = [turtle.position()];
    for(let i = 0; i < outerCircleStops.length; i++) {
        turtle.isdown()? turtle.penup(): turtle.pendown();
        turtle.circle(outerRadius, outerCircleStops[i]);
        outerPoints.push(turtle.position());
    }
    
    let slopeYoverX = (outerRadius - outerPoints[outerPoints.length - 1][1]) / outerPoints[outerPoints.length - 1][0];
    let slopeXoverY = outerPoints[outerPoints.length - 1][0] / (outerRadius - outerPoints[outerPoints.length - 1][1]);
    
    //bottom left line
    turtle.jump(outerPoints[2]);
    turtle.goto(-penWidth * 7.5, innerPoints[2][1]);
    turtle.goto((-penWidth * 7.5) + (slopeXoverY * penWidth), outerPoints[3][1])
    turtle.goto(outerPoints[3]);
    
    //top left line
    turtle.jump(outerPoints[4]);
    turtle.goto(-penWidth * 7.5, innerPoints[4][1]);
    turtle.goto((-penWidth * 7.5) + (slopeXoverY * penWidth), outerPoints[5][1])
    turtle.goto(outerPoints[5]);
    
    //bottom right line
    turtle.jump(innerPoints[2]);
    turtle.goto(slopeXoverY * (outerRadius - (1.5 * penWidth)), innerPoints[2][1]);
    turtle.goto(slopeXoverY * (outerRadius - (.5  * penWidth)), innerPoints[3][1]);
    turtle.goto(innerPoints[3]);

    //top right line
    turtle.jump(innerPoints[4]);
    turtle.goto(slopeXoverY * (outerRadius + (.5 *  penWidth)), innerPoints[4][1]);
    turtle.goto(slopeXoverY * (outerRadius + (1.5 * penWidth)), innerPoints[5][1]);
    turtle.goto(innerPoints[5]);
    
    //bottom circle-connector
    turtle.jump(innerPoints[1]);
    turtle.goto(outerPoints[1]);
    
    //top circle-connector
    turtle.jump(innerPoints[6]);
    turtle.goto(outerPoints[6]);
    
    if(helperLines == 1) {
        //draw help
        //horizontal-center-line
        dashedLine(-95,0,-outerRadius,0, [1, 2, 3, 2]);
        dashedLine(-innerRadius,0,95,0, [1, 2, 3, 2]);
        //vertical-center-line
        dashedLine(0,-95,0,-outerRadius, [1, 2, 3, 2]);
        dashedLine(0,-innerRadius,0,-1.5*penWidth, [1, 2, 3, 2]);
        dashedLine(0,-.5*penWidth,0,.5*penWidth, [1, 2, 3, 2]);
        dashedLine(0,1.5*penWidth,0,innerRadius, [1, 2, 3, 2]);
        dashedLine(0,outerRadius,0,95, [1, 2, 3, 2]);
        
        //upper 40deg from center line
        dashedLine(0,0,.5*penWidth/Math.tan(openAngle*radsPerDeg) ,-.5*penWidth, [1,1]);
        dashedLine(1.5*penWidth/Math.tan(openAngle*radsPerDeg) ,-1.5*penWidth,95, -Math.tan(openAngle*radsPerDeg)*95, [1,1]);
        //lower 40deg from center line
        dashedLine(0,0,.5*penWidth/Math.tan(openAngle*radsPerDeg) ,.5*penWidth, [1,1]);
        dashedLine(1.5*penWidth/Math.tan(openAngle*radsPerDeg) ,1.5*penWidth,95, Math.tan(openAngle*radsPerDeg)*95, [1,1]);
        
        //vertical through upper innercircle 40deg crossing
        dashedLine(innerPoints[6][0], innerPoints[6][1], innerPoints[6][0], 95, [1,1])
        dashedLine(outerPoints[1][0], -outerPoints[1][1], innerPoints[1][0], -95, [1,1])
        
        //upper circle-border
        dashedLine(0, -outerRadius, 95, -outerRadius, [1,1])
        //lower circle-border
        dashedLine(0, outerRadius, 95, outerRadius, [1,1])
        
        //midbottom to upperright circle crossing
        dashedLine(-(95 - outerRadius)*slopeXoverY, 95, 0, outerRadius, [3, 1]);
        dashedLine(0 + (outerPoints[6][0] - innerPoints[6][0]), outerRadius - (innerPoints[6][1] - outerPoints[6][1]), (outerRadius + 95) * slopeXoverY, -95, [3, 1])
        
        //left vertical
        dashedLine(-7.5 * penWidth, -95, -7.5 * penWidth, 95, [1,1]);
        
        //upper diagonal-left
        dashedLine(-95, (95 - (7.5 * penWidth)) * slopeYoverX + (1.5 * penWidth), (-7.5 * penWidth) + (2 * penWidth * slopeXoverY), -.5 * penWidth, [3, 1]);
        dashedLine((-7.5 * penWidth) + (3 * penWidth * slopeXoverY), -1.5 * penWidth, (-7.5 * penWidth) + (((1.5 * penWidth) + 95) * slopeXoverY), -95, [3, 1]);
        //lower diagonal-left
        dashedLine(-95, (95 - (7.5 * penWidth)) * slopeYoverX + (-.5 * penWidth), (-7.5 * penWidth) + (((-.5 * penWidth) + 95) * slopeXoverY), -95, [3, 1]);
        
        //upper 6-length line
        lineArrow(90, -outerRadius, 90, 0);

        //lower 6-length line
        lineArrow(90, 0, 90, outerRadius);

        //circle penwidth indicator
        lineArrow(0, -outerRadius, 0, -innerRadius);

        //vertical lines length indicator
        lineArrow(-penWidth * 7.5, -outerRadius - 5, 0, -outerRadius - 5);

        //vertical lines penwidth indicator
        lineArrow(-innerRadius / 2, -penWidth / 2, -innerRadius / 2, penWidth * -1.5);
        lineArrow(-innerRadius / 3, -penWidth / 2, -innerRadius / 3, penWidth / 2);
        lineArrow(-innerRadius / 2, penWidth / 2, -innerRadius / 2, penWidth * 1.5);

        //lower angle indidactor
        turtle.setheading(90);
        arcArrow((outerRadius + innerRadius) / 2, 0, (outerRadius + innerRadius) / 2, openAngle);
        
        //upper angle indidactor
        turtle.setheading(270);
        arcArrow((outerRadius + innerRadius) / 2, 0, -(outerRadius + innerRadius) / 2, openAngle);
        
        //upper 40deg text
        turtle.jump(Math.cos(openAngle * radsPerDeg / 2) * outerRadius *.93, -Math.sin(openAngle * radsPerDeg / 2) * outerRadius *.93);
        turtle.setheading(0);
        text.print(turtle, '' + openAngle, .2);
        turtle.setheading(270);
        turtle.penup();
        turtle.forward(2);
        text.print(turtle, 'o', .1);

        //lower 40deg text
        turtle.jump(Math.cos(openAngle * radsPerDeg / 2) * outerRadius *.93, Math.sin(openAngle * radsPerDeg / 2) * outerRadius *.93);
        turtle.setheading(0);
        text.print(turtle, '' + openAngle, .2);
        turtle.setheading(270);
        turtle.penup();
        turtle.forward(2);
        text.print(turtle, 'o', .1);
        
        //lower 6 length text
        turtle.jump(92, outerRadius / 2);
        turtle.setheading(0);
        text.print(turtle, '' + 6, .2);
        //upper 6 length text
        turtle.jump(92, -outerRadius / 2);
        turtle.setheading(0);
        text.print(turtle, '' + 6, .2);
        
        //circle penwidth text
        turtle.jump(2, -innerRadius - (penWidth / 2));
        turtle.setheading(0);
        text.print(turtle, '' + 1, .2);

        //vertical lines length text
        turtle.jump(-penWidth * 3.75, -outerRadius);
        turtle.setheading(0);
        text.print(turtle, '7.5', .2);

        //vertical lines penwidth text
        turtle.jump((-innerRadius / 2) + 2, -penWidth);
        turtle.setheading(0);
        text.print(turtle, '' + 1, .2);

        turtle.jump((-innerRadius / 3) + 2, 0);
        turtle.setheading(0);
        text.print(turtle, '' + 1, .2);

        turtle.jump((-innerRadius / 2) + 2, penWidth);
        turtle.setheading(0);
        text.print(turtle, '' + 1, .2);
    }
    
    return false;
}

function lineArrow(x1, y1, x2, y2, arrowSize, arrowAngle) {
    let heading = y2 > y1? 90: 270;
    if(x1 != x2) {
        heading = (x2-x1 < 0? 180: 0) + Math.atan((y2 - y1) / (x2 - x1)) / radsPerDeg;
    }
    arrow(x1, y1, heading, Math.sqrt((x2-x1)**2 + (y2-y1)**2), null, null, arrowSize, arrowAngle);
}

function arcArrow(x, y, radius, extent, arrowSize, arrowAngle) {
    arrow(x, y, turtle.heading(), null, radius, extent, arrowSize, arrowAngle);
}

function arrow(x1, y1, heading, length, radius, extent, arrowSize = 3, arrowAngle = 30) {
        let isArc = length == null;
        
        //lower 6-length line
        turtle.jump(x1, y1);
        turtle.setheading(heading);
        turtle.left(arrowAngle);
        turtle.forward(arrowSize);
        turtle.penup();
        turtle.backward(arrowSize);
        turtle.pendown();
        turtle.right(2*arrowAngle);
        turtle.forward(arrowSize);
        turtle.penup();
        turtle.backward(arrowSize);
        turtle.pendown();
        turtle.setheading(heading);
        
        if(isArc) {
            turtle.circle(radius, extent);
        } else {
            turtle.forward(length);
        }
        
        turtle.left(180 - arrowAngle);
        turtle.forward(arrowSize);
        turtle.penup();
        turtle.backward(arrowSize);
        turtle.pendown();
        turtle.left(2 * arrowAngle);
        turtle.forward(arrowSize);
}

function dashedLine(x1, y1, x2, y2, pattern = [500]) {
    turtle.jump(x1, y1);
    turtle.setheading(y2 > y1? 90: 270);
    let len = Math.sqrt((x2-x1)**2 + (y2-y1)**2);
    if(x1 != x2) {
        turtle.setheading((x2-x1 < 0? 180: 0) + Math.atan((y2 - y1) / (x2 - x1)) / radsPerDeg);
    }
    let tr = 0;
    let i = 0;
    turtle.penup();
    while(tr < len) {
        let inc = pattern[i % pattern.length];
        turtle.isdown()? turtle.penup(): turtle.pendown();
        turtle.forward(inc + tr > len? len - tr: inc);
        i++;
        tr+=inc;
    }
}

////////////////////////////////////////////////////////////////
// Text utility code. Created by Reinder Nijhoff 2019
// https://turtletoy.net/turtle/1713ddbe99
////////////////////////////////////////////////////////////////

function Text() {
    class Text {
        print (t, str, scale = 1, italic = 0, kerning = 1) {
            t.radians();
            let pos = [t.x(), t.y()], h = t.h(), o = pos;
            str.split('').map(c => {
                const i = c.charCodeAt(0) - 32;
                if (i < 0 ) {
                    pos = o = this.rotAdd([0, 48*scale], o, h);
                } else if (i > 96 ) {
                    pos = this.rotAdd([16*scale, 0], o, h);
                } else {
                    const d = dat[i], lt = d[0]*scale, rt = d[1]*scale, paths = d[2];
                    paths.map( p => {
                        t.up();
                    	p.map( s=> {
                        	t.goto(this.rotAdd([(s[0]-s[1]*italic)*scale - lt, s[1]*scale], pos, h));
                        	t.down();
                        });
                    });
                    pos = this.rotAdd([(rt - lt)*kerning, 0], pos, h);
                }
            });
        }
        rotAdd (a, b, h) {
            return [Math.cos(h)*a[0] - Math.sin(h)*a[1] + b[0], 
                    Math.cos(h)*a[1] + Math.sin(h)*a[0] + b[1]];
        }
    }
    
const dat = ('br>eoj^jl<jqirjskrjq>brf^fe<n^ne>`ukZdz<qZjz<dgrg<cmqm>`thZhw<lZlw<qao_l^h^e_caccdeefg'+
'gmiojpkqmqporlshsercp>^vs^as<f^h`hbgdeeceacaab_d^f^h_k`n`q_s^<olmmlolqnspsrrspsnqlol>]wtgtfsereqfph'+
'nmlpjrhsdsbraq`o`makbjifjekckaj_h^f_eaecffhimporqssstrtq>eoj`i_j^k_kajcid>cqnZl\\j_hcghglhqjulxnz>c'+
'qfZh\\j_lcmhmllqjuhxfz>brjdjp<egom<ogem>]wjajs<ajsj>fnkojpiojnkokqis>]wajsj>fnjniojpkojn>_usZaz>`ti'+
'^f_dbcgcjdofrisksnrpoqjqgpbn_k^i^>`tfbhak^ks>`tdcdbe`f_h^l^n_o`pbpdofmicsqs>`te^p^jfmfogphqkqmppnrk'+
'shserdqco>`tm^clrl<m^ms>`to^e^dgefhekenfphqkqmppnrkshserdqco>`tpao_l^j^g_ebdgdlepgrjsksnrppqmqlping'+
'kfjfggeidl>`tq^gs<c^q^>`th^e_dadceegfkgnhpjqlqopqorlshserdqcocldjfhigmfoepcpao_l^h^>`tpeohmjjkikfjd'+
'hcecddaf_i^j^m_oapepjoomrjshserdp>fnjgihjikhjg<jniojpkojn>fnjgihjikhjg<kojpiojnkokqis>^vrabjrs>]wag'+
'sg<amsm>^vbarjbs>asdcdbe`f_h^l^n_o`pbpdofngjijl<jqirjskrjq>]xofndlcicgdfeehekfmhnknmmnk<icgefhfkgmh'+
'n<ocnknmpnrntluiugtdsbq`o_l^i^f_d`bbad`g`jambodqfrislsorqqrp<pcokompn>asj^bs<j^rs<elol>_tc^cs<c^l^o'+
'_p`qbqdpfoglh<chlhoipjqlqopqorlscs>`urcqao_m^i^g_eadccfckdnepgrismsorqprn>_tc^cs<c^j^m_oapcqfqkpnop'+
'mrjscs>`sd^ds<d^q^<dhlh<dsqs>`rd^ds<d^q^<dhlh>`urcqao_m^i^g_eadccfckdnepgrismsorqprnrk<mkrk>_uc^cs<'+
'q^qs<chqh>fnj^js>brn^nnmqlrjshsfreqdndl>_tc^cs<q^cl<hgqs>`qd^ds<dsps>^vb^bs<b^js<r^js<r^rs>_uc^cs<c'+
'^qs<q^qs>_uh^f_daccbfbkcndpfrhslsnrppqnrkrfqcpan_l^h^>_tc^cs<c^l^o_p`qbqepgohlici>_uh^f_daccbfbkcnd'+
'pfrhslsnrppqnrkrfqcpan_l^h^<koqu>_tc^cs<c^l^o_p`qbqdpfoglhch<jhqs>`tqao_l^h^e_caccdeefggmiojpkqmqpo'+
'rlshsercp>brj^js<c^q^>_uc^cmdpfrisksnrppqmq^>asb^js<r^js>^v`^es<j^es<j^os<t^os>`tc^qs<q^cs>asb^jhjs'+
'<r^jh>`tq^cs<c^q^<csqs>cqgZgz<hZhz<gZnZ<gznz>cqc^qv>cqlZlz<mZmz<fZmZ<fzmz>brj\\bj<j\\rj>asazsz>fnkc'+
'ieigjhkgjfig>atpeps<phnfleiegfehdkdmepgrislsnrpp>`sd^ds<dhffhekemfohpkpmopmrkshsfrdp>asphnfleiegfeh'+
'dkdmepgrislsnrpp>atp^ps<phnfleiegfehdkdmepgrislsnrpp>asdkpkpiognfleiegfehdkdmepgrislsnrpp>eqo^m^k_j'+
'bjs<gene>atpepuoxnylzizgy<phnfleiegfehdkdmepgrislsnrpp>ate^es<eihfjemeofpips>fni^j_k^j]i^<jejs>eoj^'+
'k_l^k]j^<kekvjyhzfz>are^es<oeeo<ikps>fnj^js>[y_e_s<_ibfdegeifjijs<jimfoeretfuius>ateees<eihfjemeofp'+
'ips>atiegfehdkdmepgrislsnrppqmqkphnfleie>`sdedz<dhffhekemfohpkpmopmrkshsfrdp>atpepz<phnfleiegfehdkd'+
'mepgrislsnrpp>cpgegs<gkhhjfleoe>bsphofleieffehfjhkmlompopporlsisfrep>eqj^jokrmsos<gene>ateeeofrhsks'+
'mrpo<peps>brdejs<pejs>_ubefs<jefs<jens<rens>bseeps<pees>brdejs<pejshwfydzcz>bspees<eepe<esps>cqlZj['+
'i\\h^h`ibjckekgii<j[i]i_jakbldlfkhgjkllnlpkrjsiuiwjy<ikkmkojqirhthvixjylz>fnjZjz>cqhZj[k\\l^l`kbjci'+
'eigki<j[k]k_jaibhdhfihmjilhnhpirjskukwjy<kkimiojqkrltlvkxjyhz>^vamakbhdgfghhlknlplrksi<akbidhfhhill'+
'nmpmrlsisg>brb^bscsc^d^dsese^f^fsgsg^h^hsisi^j^jsksk^l^lsmsm^n^nsoso^p^psqsq^r^rs').split('>').map(
    r=> { return [r.charCodeAt(0)-106,r.charCodeAt(1)-106, r.substr(2).split('<').map(a => {const ret 
    = []; for (let i=0; i<a.length; i+=2) {ret.push(a.substr(i, 2).split('').map(b => b.charCodeAt(0)
    -106));} return ret; })]; });

    return new Text();
}