)
}
}
)
(
}
{
)
)
(
)
(
(
{
}
)
(
)
}
)
)
{
(
(
)
)
}
)
(
}

Catmull Rom to Bezier SVG

  1. // from: 
  2. //   https://github.com/DmitryBaranovskiy/raphael/blob/b5fdbdc9850827eb49622cd0bc81b26025f8128a/dev/raphael.core.js#L1019
  3. // and: 
  4. //   http://schepers.cc/getting-to-the-point
  5.  
  6. function catmullRom2bezier(crp, close) {
  7.   const d = [];
  8.   for (let i = 0, iLen = crp.length; iLen - 2 * !close > i; i += 2) {
  9.     const p = [
  10.       { x: crp[i - 2], y: crp[i - 1] },
  11.       { x: crp[i], y: crp[i + 1] },
  12.       { x: crp[i + 2], y: crp[i + 3] },
  13.       { x: crp[i + 4], y: crp[i + 5] }
  14.     ];
  15.     if (close) {
  16.       if (!i) {
  17.         p[0] = { x: crp[iLen - 2], y: crp[iLen - 1] };
  18.       } else if (iLen - 4 == i) {
  19.         p[3] = { x: crp[0], y: crp[1] };
  20.       } else if (iLen - 2 == i) {
  21.         p[2] = { x: crp[0], y: crp[1] };
  22.         p[3] = { x: crp[2], y: crp[3] };
  23.       }
  24.     } else {
  25.       if (iLen - 4 == i) {
  26.         p[3] = p[2];
  27.       } else if (!i) {
  28.         p[0] = { x: crp[i], y: crp[i + 1] };
  29.       }
  30.     }
  31.     d.push([
  32.       'C',
  33.       (-p[0].x + 6 * p[1].x + p[2].x) / 6,
  34.       (-p[0].y + 6 * p[1].y + p[2].y) / 6,
  35.       (p[1].x + 6 * p[2].x - p[3].x) / 6,
  36.       (p[1].y + 6 * p[2].y - p[3].y) / 6,
  37.       p[2].x,
  38.       p[2].y
  39.     ]);
  40.   }
  41.  
  42.   // really for demo purposes:
  43.  
  44.   // draw a tiny rect if less than 2 points
  45.   console.log(d.length);
  46.   if (d.length < 2) {
  47.     const x = crp[0];
  48.     const y = crp[1];
  49.     return `M ${x} ${y} h 1 v 1 h -1 v-1`;
  50.  
  51.     // draw a line if 2 points
  52.   } else if (d.length === 2) {
  53.     return `M ${crp[0]} ${crp[1]} L ` + crp.flat().join` `;
  54.   }
  55.   // draw curves if 3 or more (raphael only ever did this, other numbers of points
  56.   // were considered invalid)
  57.   return  `M ${crp[0]} ${crp[1]}` + d.flat().join` `;
  58. }
  59.  
  60. // try it out:
  61.  
  62. const pathCommands = catmullRom2bezier([
  63.   100, 100, 200, 100, 200, 300, 170, 150, 120, 180, 120, 120
  64.   ], true)
  65.  
  66. const svg = `
  67.   click 3 or more times anywhere<br>
  68.   <button>clear</button>
  69.   <svg>
  70.     <path d="M 100 100 ${pathCommands}" stroke="blue" fill="none" />
  71.   </svg>
  72.   <style>
  73.   svg {
  74.     position:absolute;
  75.     left:0;top:0;
  76.     overflow:visible;
  77.     pointer-events:none;
  78.   }
  79.   </style>
  80. `;
  81.  
  82. const div = document.body.appendChild(
  83.   document.createElement('div')
  84. );
  85. div.innerHTML = svg;
  86. const path = div.querySelector('path')
  87.  
  88. let pnts = [];
  89.  
  90. function isButton(e) {
  91.   if (e.target.tagName === 'BUTTON') {
  92.     pnts = []
  93.     path.setAttribute('d', 'M 0 0L0 0');
  94.     return true;
  95.   }
  96. }
  97.  
  98. document.addEventListener('click', e => {
  99.   if (isButton(e)) return;
  100.   pnts.push(e.clientX, e.clientY);
  101.   path.setAttribute('d', catmullRom2bezier(pnts, true))
  102. });
  103.  
  104. document.addEventListener('touchend', e => {
  105.   if (isButton(e)) return;
  106.   pnts.push(e.touchs[0].clientX, e.touchs[0].e.clientY);
  107.   path.setAttribute('d', catmullRom2bezier(pnts, true))
  108. });

Raphaël was my first deep dive into SVG. Back then, only VML was supported in older IE versions, so using Raphaël was key for tons of commercial work I did (Raphaël generated VML as well as SVG). This snippet is the Catmull Rom code from Raphaël. I’m pretty sure I didn’t know what a Catmull Rom spline was until I found out that using `R` in a path wasn’t supported natively in SVG… and was actually some magic thing that existed in Raphaël alone.

// curves // javascript // math // svg

John Wallis Pi (Wallis Product)

  1. /*
  2. It was always my affectation even from a child, in all pieces
  3. of Learning or Knowledge, not merely to learn by rote, which 
  4. is soon forgotten, but to know the grounds or reasons of what 
  5. I learn; to inform my Judgement, as well as furnish my Memory; 
  6. and thereby, make a better Impression on both.
  7.  
  8. - John Wallis
  9.  
  10. */
  11.  
  12. i = c = 1
  13. setInterval(_ => {
  14.   for (j = 0; j < 1000; j++, i += .5)
  15.     c *= (~~i * 2) / (~~(i - .5) * 2 + 1)
  16.  
  17.   console.log(c * 2 + '\n' + 3.141592653589793)
  18. }, 16);
π = 2
2 / 1
·
2 / 3
·
4 / 3
·
4 / 5
·
6 / 5
·
6 / 7
·
8 / 7
·
8 / 9
·   · · ·

I was at an old book store a few days ago and randomly picked up History of Mathematics Vol. I by David Eugene Smith 1923 for $7.50. After enjoying flipping around and reading different parts, I pulled up Wikipedia to read more about the author… From there I found some interesting quotes from talking about John Wallis – when I saw the Wallis Product I was inspired to write a quick snippet.

// golfed // javascript // math // pi

Extremely Golfed Canvas

  1. d = document
  2. b = d.body
  3. with(
  4.   b.appendChild(Object.assign(
  5.   d.createElement`canvas`, { width: 100, height: 100 })
  6.   ).getContext`2d`) {
  7.  
  8.   fillStyle = 'red'
  9.   fillRect(5, 5, 20, 20)
  10. }
  11.  
  12. // slightly more complex example
  13. with(
  14.   b.appendChild(Object.assign(
  15.   d.createElement`canvas`, { width: 200, height: 200 })
  16.   ).getContext`2d`) {
  17.  
  18.   fillStyle = '#555', strokeStyle = '#fff', fillRect(0, 0, 200, 200)
  19.   for (i = 0; i < 10; i++) 
  20.     beginPath(), moveTo(20, 20), lineTo(i * 20 + 10, 190), stroke()
  21. }

Easy way to write canvas stuff with very little boilerplate code. with doesn’t work in strict mode.

Snippet Zone Code Highlighter

I wrote a naive syntax highlighter a few weeks ago. Here it is rendering the code for itself. :

This is not a perfect highlighter, but it was fun to create and I will definitely use it in the Snippet Zone Github repository once I get that set up.

There is some css that goes with the highlighter, it’s pretty boring, but here it is anyway:

  1. .hh {
  2.   white-space: pre-wrap;
  3.   font-family: monaco, monospace;
  4.   line-height: 1.5;
  5.   font-size: .8em;
  6.   background: rgb(3, 21, 36);
  7.   color: rgba(156, 221, 254, 1);
  8.   padding: 1em;
  9. }
  10.  
  11. .hh b {
  12.   font-weight: normal;
  13. }
  14.  
  15. .hh i {
  16.   color: #1ad6ae;
  17. }
  18.  
  19. .hh u {
  20.   color: rgb(255, 195, 252);
  21.   text-decoration: none;
  22. }
  23.  
  24. .num {
  25.   color: #b5cea8;
  26. }
  27.  
  28. .str, .str b  {
  29.   color: #ce9178;
  30. }
  31.  
  32. .par {
  33.   color: white;
  34.   font-weight: bold;
  35. }
  36.  
  37. .brk {
  38.   color: white;
  39.   font-weight: bold;
  40. }
  41.  
  42. .o {
  43.   color: rgb(0, 151, 221);
  44. }
  45.  
  46. .obj {
  47.   color: rgb(52, 206, 47);
  48.   font-weight: bold !important;
  49. }
  50.  
  51. .w {
  52.   color: #1ad6ae;
  53.   font-style: italic;
  54. }
  55.  
  56. .k {
  57.   color: aqua;
  58. }
  59.  
  60. .cmt {
  61.   color: gray !important;
  62. }
  63. .cmt b {
  64.   color: gray !important;
  65. }

I have no idea why I used such bad class names there 😛

// css // dom // javascript // meta // strings // ui

Mini-Markdown Alternate Version

  1. const tagRules = {
  2.   b: '\\*\\*',
  3.   i: '_',
  4.   strike: '~~',
  5.   code: '`'
  6. };
  7. tagRules.keys = Object.keys(tagRules);
  8.  
  9. const toTag = (str, delim, tag) => str.replace(
  10.   new RegExp(
  11.     `${delim}(.+)${delim}`, 'gm'),`<${tag}>$1</${tag}>`)
  12.  
  13. const makeTags = str => 
  14.   tagRules.keys.reduce((accum, val, i) => 
  15.     toTag(accum, tagRules[val], val), str)
  16.  
  17. const markConfig = { 
  18.   emptyLine: {
  19.     check: val => val.trim() === '',
  20.     process: () => '<br>'
  21.   },
  22.   header: {
  23.     calc: val => val.split`#`.length - 1,
  24.     check: (val, result) => result > 0,
  25.     process: (val, result) => `<h${result} style="margin-bottom:0">
  26.       ${val.replace(/#/g, '')}</h${result}>`
  27.   },
  28.   hr: {
  29.     check: val => val.trim() === '***',
  30.     process: () => '<hr>'
  31.   },
  32.   li: {
  33.     check: val => val.startsWith('- '), 
  34.     process: (val, result, lastMethod) => {
  35.       const open = lastMethod != 'li' ? '<ul>' : '';
  36.       return `${open}<li>${val}</li>`;
  37.     }
  38.   }
  39. };
  40.  
  41. markConfig.keys = Object.keys(markConfig);
  42. markConfig.tail = {
  43.   process: (val, result, lastMethod) => {
  44.     const close = lastMethod == 'li' ? '</ul>' : '';
  45.     return val + close;
  46.   }
  47. };
  48.  
  49. const mark = str => {
  50.   const lines = str.split`\n`;
  51.   let lastMethod;
  52.   const newStr = lines.map((line, i) => {
  53.     const method = markConfig.keys.find(key => {
  54.       const { calc = () => {}, check } = markConfig[key];
  55.       return check(line, calc(line))
  56.     }) || 'tail'
  57.  
  58.     const { calc = () => {} } = markConfig[method];
  59.     const newLine = markConfig[method].process(line, calc(line), lastMethod);
  60.     lastMethod = method;
  61.     return newLine;
  62.   })
  63.   return newStr.join`\n`;
  64. }
  65.  
  66. // try it out
  67.  
  68. const md = document.body.appendChild(document.createElement('div'));
  69. md.style.fontFamily = 'sans-serif';
  70. md.innerHTML = `# Mini Markdown Subset
  71.  
  72. This is a subset of markdown stuff
  73.  
  74. ## It includes
  75.  
  76. - headers
  77. - **bold styles**
  78. - _italic styles, <br> multiline fine..._
  79. - \`code style\`
  80.  
  81. Other than ~~strikethrough~~ that is pretty much it... oh and **hr** tags
  82. ***
  83. ***
  84. _here is some italic text with some bold text **nested** <br>within it etc..._
  85. `;
  86. md.innerHTML = makeTags(mark(md.innerHTML));

This is another version of a post from awhile back. I was curious about readability. This has no for loops and no if statements. Personally I find it less readable than the other version…

Here is the simpler original version for comparison… this one also has nested list support.

  1.   const isLi = val => val.match(/(^\s+)?- /)
  2.  
  3.   function mark(str) {
  4.     const lines = str.split`\n`;
  5.     let wasLi = false
  6.     let lastDepth = 0;
  7.     let depth;
  8.  
  9.   for (let i = 0; i < lines.length; i++) {
  10.     let curr = lines[i];
  11.     const line = curr.trim();
  12.     const hdr = line.split`#`.length - 1;
  13.  
  14.     if (line === '') {
  15.       lines[i] = '<br>';
  16.     } else if (hdr > 0) {
  17.       lines[i] = `<h${hdr} style="margin-bottom:0">
  18.         ${curr.replace(/#/g, '')}</h${hdr}>`;
  19.     } else if (line === '***') {
  20.       lines[i] = '<hr>';
  21.     } else if (isLi(curr)) {
  22.       depth = curr.split('-')[0].length + 1;
  23.  
  24.       lines[i] = '';
  25.       if (depth < lastDepth) {
  26.         const diff = (lastDepth - depth) / 2
  27.  
  28.         lines[i] += '</ul>'.repeat(diff);
  29.         lastDepth = depth 
  30.       }
  31.  
  32.       lines[i] += `${depth > lastDepth ? '<ul>' : ''}
  33.         <li>${curr.replace(/-\s+/, '')}</li>`;
  34.  
  35.       lastDepth = depth;
  36.  
  37.       wasLi = true;
  38.     } else if (wasLi) {
  39.       lines[i - 1] = '</ul>\n'.repeat(lastDepth)
  40.       wasLi = false
  41.     }
  42.   }
  43.  
  44.   return lines.join`\n`
  45.     .replace(/\*\*(.+)\*\*/gm, '<b>$1</b>')
  46.     .replace(/_(.+)_/gm, '<i>$1</i>')
  47.     .replace(/~~(.+)~~/gm, '<strike>$1</strike>')
  48.     .replace(/`(.+)`/gm, '<code>$1</code>');
  49. }
  50.  
  51. // try it out
  52.  
  53. const md = document.body.appendChild(document.createElement('div'));
  54. md.style.fontFamily = 'sans-serif';
  55. md.innerHTML = `
  56. #Snippet Zone
  57.  
  58. This snippet renders a subset of markdown. 
  59.  
  60. - **bold** and _italic text_
  61. - lists 
  62.   - this particulate
  63.     - snippet can render 
  64.     - nested
  65.   - lists
  66.  
  67. - _**~~strikethrough text~~**_
  68.  
  69. In a real project you will probably want to use
  70. something more complete like the great <a href="https://github.com/markedjs/marked" target="blank" rel="noopener">marked.js</a> library.
  71. `;
  72. md.innerHTML = mark(md.innerHTML);
snippet.zone ~ 2021-24 /// {s/z}