Text utility

Based on reinder's utility, with a few enhancements:
* differentiated horizontal and vertical scaling
* lowered position for some punctuation signs
* alignement option: left|middle|right

Log in to post a comment.

const turtle = new Turtle()

// Text utility function, based on https://turtletoy.net/turtle/1713ddbe99
const print = (() => {
const raw = '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>fnkrjsirjqkrktiv>]wajsj>fnjqirjskrjq>_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>fnjjikjlkkjj<jqirjskrjq>fnjjikjlkkjj<krjsirjqkrktiv>^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'
    const decode = (char) => char.charCodeAt(0)-106
    const decodePos = ([x, y]) => [decode(x), decode(y)]
    const decodePath = (str) => str.match(/.{2}/g).map(decodePos)
    const decodePaths = (str) => str ? str.split('<').map(decodePath) : []
    const decodeGlyph = ([lt, rt, ...rest], i) => [decode(lt), decode(rt), decodePaths(rest.join(''))]
    const font = raw.split('>').map(decodeGlyph)

    const add = ([x1, y1], [x2, y2]) => [x1 + x2, y1 + y2]
    const rot = ([x, y], [cos, sin]) => [x*cos - y*sin, y*cos + x*sin]
    //console.log(font.map((g, i) => [String.fromCharCode(i+32), ...g]))

    return (t, str, options = {}) => {
        const defaults = { scale: [1, 1], italic: 0, kerning: 1, align: 'left' }
        const { scale, italic, kerning, align } = Object.assign(defaults, options)
        const [sx, sy] = scale ? isNaN(scale) ? scale : [scale, scale] : [1, 1]
        const strWidth = (str) => [...str].reduce((w, char) => {
            const code = char.charCodeAt(0)-32
            const [lt, rt] = font[code] || [0, 0]
            return w+(rt-lt)
        }, 0)
        const width = Math.max(...str.split('\n').map(strWidth))*sx*kerning
        let pos = [t.x(), t.y()], o = pos
        const h = t.h(), [ch, sh] = [Math.cos(h), Math.sin(h)]
        const printPaths = (paths) => {
            const pos = [t.x(), t.y()]
            paths.forEach((path) => {
                path.forEach(([x, y], i) => {
                    const abs = add(pos, rot([(x-y*italic)*sx, y*sy], [ch, sh]))
                    i == 0 ? t.jump(abs) : t.goto(abs)
                })
            })
        }
        const start = [t.x(), t.y()]
        str.split('\n').map((line) => {
            const start = [t.x(), t.y()]
            const lineWidth = strWidth(line)*sx*kerning
            const dx = ({ left: 0, middle: lineWidth/2, right: lineWidth })[align]
            t.up(); t.backward(dx)
            line.split('').forEach((char) => {
                const start = [t.x(), t.y()]
                const code = char.charCodeAt(0)-32
                const [lt, rt, paths] = font[code] || [0, 0, []]
                t.up(); t.backward(lt*sx)
                t.down(); printPaths(paths)
                t.up(); t.jump(start); t.forward((rt-lt)*sx*kerning)
            })
            t.jump(start)
            t.up(); t.right(90); t.forward(38*sy); t.left(90);
        })
        t.jump(start)
    }
})()

const fit = .2
const round = (number, n) => {
  const d = Math.pow(10, n);
  return Math.round((number + Number.EPSILON) * d) / d;
}
const roundScale = (scale, n) => {
    return Array.isArray(scale) ? `[${round(scale[0], n)}, ${round(scale[1], n)}]` : round(scale, n)
}
const printWithOptions = (options = {}) => {
    const defaults = { scale: [1, 1], italic: 0, kerning: 1, align: 'left' }
    const { scale, italic, kerning, align } = Object.assign(defaults, options)
    const optStr = `scale: ${roundScale(scale, 2)}, italic: ${italic}, kerning: ${kerning}, align: ${align}`
    const str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\n0123456789 &"\'(-_)=+^$%*,?;.:/!~#{[|^@]}<>'
    print(turtle, `${optStr}\n${str}`, options)
}
function walk(i) {
    turtle.jump(0, -90); printWithOptions({ scale: fit, italic: .2, align: 'middle' })
    turtle.jump(0, -60); printWithOptions({ scale: fit, kerning: .95, align: 'middle' })
    turtle.jump(0, -30); printWithOptions({ scale: [fit*.9, fit], kerning: .95, align: 'middle' })
    turtle.jump(0,   0); printWithOptions({ scale: [fit*.8, fit], kerning: .95, align: 'middle' })
    turtle.jump(0,  30); printWithOptions({ scale: [fit*.7, fit], kerning: .95, align: 'middle' })
    turtle.jump(0,  55); printWithOptions({ scale: [fit*.6, fit*.6], kerning: .95, align: 'left' })
    turtle.jump(0,  85); printWithOptions({ scale: [fit*.6, fit*.6], kerning: .95, align: 'right' })
    turtle.jump(0,  70); turtle.radians(); turtle.setheading(Math.PI/10)
    printWithOptions({ scale: fit*.6, kerning: .95, align: 'middle' })
}