<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Troy Ni</title>
        <link>https://troyni.com</link>
        <description>Troy's blog</description>
        <lastBuildDate>Tue, 09 Jun 2026 01:42:38 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Troy Ni</title>
            <url>https://troyni.com/favicon.ico</url>
            <link>https://troyni.com</link>
        </image>
        <copyright>All rights reserved 2026</copyright>
        <item>
            <title><![CDATA[Book List]]></title>
            <link>https://troyni.com/articles/book-list</link>
            <guid>https://troyni.com/articles/book-list</guid>
            <pubDate>Tue, 05 Mar 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<div class="my-6 flex gap-2.5 rounded-2xl border border-zinc-500/20 bg-zinc-50/50 p-4 leading-6 text-zinc-900 dark:border-zinc-500/30 dark:bg-zinc-500/5 dark:text-zinc-200 dark:[--tw-prose-links-hover:theme(colors.zinc.300)] dark:[--tw-prose-links:theme(colors.white)]"><svg viewBox="0 0 16 16" aria-hidden="true" class="mt-1 h-4 w-4 flex-none fill-zinc-500 stroke-white dark:fill-zinc-200/20 dark:stroke-zinc-200"><circle cx="8" cy="8" r="8" stroke-width="0"></circle><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M6.75 7.75h1.5v3.5"></path><circle cx="8" cy="4" r=".5" fill="none"></circle></svg><div class="[&amp;>:first-child]:mt-0 [&amp;>:last-child]:mb-0"><p>Last updated: 2021</p></div></div>
<h2 id="design">Design</h2>
<div class="not-prose grid grid-cols-2 md:grid-cols-3 gap-x-6 gap-y-6"><div class="not-prose text-center"><div class="relative"><div class="absolute top-0 left-0 w-full h-full flex flex-col items-center justify-center"><img class="max-h-full max-w-full" src="/assets/book-list/image5.jpeg" alt=""></div><div style="position:relative;width:100%;padding-bottom:100%" data-radix-aspect-ratio-wrapper=""><div style="position:absolute;top:0;right:0;bottom:0;left:0"></div></div></div><h4 class="text-foreground font-semibold mt-3 text-sm leading-5">基本的基本</h4><p class="text-xs leading-5">版面设计的基本原理</p></div><div class="not-prose text-center"><div class="relative"><div class="absolute top-0 left-0 w-full h-full flex flex-col items-center justify-center"><img class="max-h-full max-w-full" src="/assets/book-list/image6.jpeg" alt=""></div><div style="position:relative;width:100%;padding-bottom:100%" data-radix-aspect-ratio-wrapper=""><div style="position:absolute;top:0;right:0;bottom:0;left:0"></div></div></div><h4 class="text-foreground font-semibold mt-3 text-sm leading-5">认知与设计</h4><p class="text-xs leading-5">理解 UI 设计准则</p></div><div class="not-prose text-center"><div class="relative"><div class="absolute top-0 left-0 w-full h-full flex flex-col items-center justify-center"><img class="max-h-full max-w-full" src="/assets/book-list/image7.jpeg" alt=""></div><div style="position:relative;width:100%;padding-bottom:100%" data-radix-aspect-ratio-wrapper=""><div style="position:absolute;top:0;right:0;bottom:0;left:0"></div></div></div><h4 class="text-foreground font-semibold mt-3 text-sm leading-5">About Face</h4><p class="text-xs leading-5">The Essentials of Interaction Design</p></div></div>
<h2 id="math">Math</h2>
<div class="not-prose grid grid-cols-2 md:grid-cols-3 gap-x-6 gap-y-6"><div class="not-prose text-center"><div class="relative"><div class="absolute top-0 left-0 w-full h-full flex flex-col items-center justify-center"><img class="max-h-full max-w-full" src="/assets/book-list/image8.jpeg" alt=""></div><div style="position:relative;width:100%;padding-bottom:100%" data-radix-aspect-ratio-wrapper=""><div style="position:absolute;top:0;right:0;bottom:0;left:0"></div></div></div><h4 class="text-foreground font-semibold mt-3 text-sm leading-5">Calculus</h4><p class="text-xs leading-5">Early Transcendentals</p></div><div class="not-prose text-center"><div class="relative"><div class="absolute top-0 left-0 w-full h-full flex flex-col items-center justify-center"><img class="max-h-full max-w-full" src="/assets/book-list/image9.png" alt=""></div><div style="position:relative;width:100%;padding-bottom:100%" data-radix-aspect-ratio-wrapper=""><div style="position:absolute;top:0;right:0;bottom:0;left:0"></div></div></div><h4 class="text-foreground font-semibold mt-3 text-sm leading-5">Linear Algebra and its Applications</h4></div><div class="not-prose text-center"><div class="relative"><div class="absolute top-0 left-0 w-full h-full flex flex-col items-center justify-center"><img class="max-h-full max-w-full" src="/assets/book-list/image10.jpeg" alt=""></div><div style="position:relative;width:100%;padding-bottom:100%" data-radix-aspect-ratio-wrapper=""><div style="position:absolute;top:0;right:0;bottom:0;left:0"></div></div></div><h4 class="text-foreground font-semibold mt-3 text-sm leading-5">Infinite Powers</h4></div><div class="not-prose text-center"><div class="relative"><div class="absolute top-0 left-0 w-full h-full flex flex-col items-center justify-center"><img class="max-h-full max-w-full" src="/assets/book-list/image11.jpeg" alt=""></div><div style="position:relative;width:100%;padding-bottom:100%" data-radix-aspect-ratio-wrapper=""><div style="position:absolute;top:0;right:0;bottom:0;left:0"></div></div></div><h4 class="text-foreground font-semibold mt-3 text-sm leading-5">Nonlinear Dynamics and Chaos</h4></div><div class="not-prose text-center"><div class="relative"><div class="absolute top-0 left-0 w-full h-full flex flex-col items-center justify-center"><img class="max-h-full max-w-full" src="/assets/book-list/image12.png" alt=""></div><div style="position:relative;width:100%;padding-bottom:100%" data-radix-aspect-ratio-wrapper=""><div style="position:absolute;top:0;right:0;bottom:0;left:0"></div></div></div><h4 class="text-foreground font-semibold mt-3 text-sm leading-5">Vector Calculus, Linear Algebra, and Differential Forms</h4></div></div>
<h2 id="rendering">Rendering</h2>
<div class="not-prose grid grid-cols-2 md:grid-cols-3 gap-x-6 gap-y-6"><div class="not-prose text-center"><div class="relative"><div class="absolute top-0 left-0 w-full h-full flex flex-col items-center justify-center"><img class="max-h-full max-w-full" src="/assets/book-list/image13.png" alt=""></div><div style="position:relative;width:100%;padding-bottom:100%" data-radix-aspect-ratio-wrapper=""><div style="position:absolute;top:0;right:0;bottom:0;left:0"></div></div></div><h4 class="text-foreground font-semibold mt-3 text-sm leading-5">Physically Based Rendering</h4></div><div class="not-prose text-center"><div class="relative"><div class="absolute top-0 left-0 w-full h-full flex flex-col items-center justify-center"><img class="max-h-full max-w-full" src="/assets/book-list/image14.png" alt=""></div><div style="position:relative;width:100%;padding-bottom:100%" data-radix-aspect-ratio-wrapper=""><div style="position:absolute;top:0;right:0;bottom:0;left:0"></div></div></div><h4 class="text-foreground font-semibold mt-3 text-sm leading-5">Real-Time Rendering</h4></div></div>
<h2 id="coding">Coding</h2>
<div class="not-prose grid grid-cols-2 md:grid-cols-3 gap-x-6 gap-y-6"><div class="not-prose text-center"><div class="relative"><div class="absolute top-0 left-0 w-full h-full flex flex-col items-center justify-center"><img class="max-h-full max-w-full" src="/assets/book-list/image15.jpeg" alt=""></div><div style="position:relative;width:100%;padding-bottom:100%" data-radix-aspect-ratio-wrapper=""><div style="position:absolute;top:0;right:0;bottom:0;left:0"></div></div></div><h4 class="text-foreground font-semibold mt-3 text-sm leading-5">HTML &amp; CSS</h4><p class="text-xs leading-5">设计与构建网站</p></div><div class="not-prose text-center"><div class="relative"><div class="absolute top-0 left-0 w-full h-full flex flex-col items-center justify-center"><img class="max-h-full max-w-full" src="/assets/book-list/image16.jpeg" alt=""></div><div style="position:relative;width:100%;padding-bottom:100%" data-radix-aspect-ratio-wrapper=""><div style="position:absolute;top:0;right:0;bottom:0;left:0"></div></div></div><h4 class="text-foreground font-semibold mt-3 text-sm leading-5">Wolfram 语言入门</h4></div></div>
<h2 id="generative-art">Generative Art</h2>
<div class="not-prose grid grid-cols-2 md:grid-cols-3 gap-x-6 gap-y-6"><div class="not-prose text-center"><div class="relative"><div class="absolute top-0 left-0 w-full h-full flex flex-col items-center justify-center"><img class="max-h-full max-w-full" src="/assets/book-list/image17.jpeg" alt=""></div><div style="position:relative;width:100%;padding-bottom:100%" data-radix-aspect-ratio-wrapper=""><div style="position:absolute;top:0;right:0;bottom:0;left:0"></div></div></div><h4 class="text-foreground font-semibold mt-3 text-sm leading-5">The Nature of Code</h4></div><div class="not-prose text-center"><div class="relative"><div class="absolute top-0 left-0 w-full h-full flex flex-col items-center justify-center"><img class="max-h-full max-w-full" src="/assets/book-list/image18.png" alt=""></div><div style="position:relative;width:100%;padding-bottom:100%" data-radix-aspect-ratio-wrapper=""><div style="position:absolute;top:0;right:0;bottom:0;left:0"></div></div></div><h4 class="text-foreground font-semibold mt-3 text-sm leading-5">Generative Art</h4></div></div>]]></content:encoded>
            <author>1272777550@qq.com (Troy Ni)</author>
        </item>
        <item>
            <title><![CDATA[如何在公众号中插入公式]]></title>
            <link>https://troyni.com/articles/building-mpmath-zh</link>
            <guid>https://troyni.com/articles/building-mpmath-zh</guid>
            <pubDate>Sun, 16 Feb 2020 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h1>如何在公众号中插入公式</h1>
<blockquote>
<p>直到今天，我终于有办法在微信公众号中插入公式了。</p>
</blockquote>
<h2 id="">缘起</h2>
<p>为什么微信公众号就没有插入数学公式的功能呢？</p>
<p>这个问题困扰了我很久。看看知乎，看看语雀，人家早就已经支持公式插入了，唯独这保守的微信，永远听不到用户的声音。不加就不加吧，大不了我自己做！一！个！</p>
<h2 id="">现有方法</h2>
<p>方法有吗？有，还不少。</p>
<h3>1. 插入公式图片</h3>
<p>给公式截图，插入图片，比如这篇极其优秀的图文：<a target="_blank" href="https://mp.weixin.qq.com/s?__biz=MzI0OTAyNTI0Ng==&amp;mid=2651265867&amp;idx=1&amp;sn=77e25b1ac2261d40161bf6b20ef1d51c&amp;scene=21#wechat_redirect">理想披萨方程式</a>。</p>
<p>这种方法有什么问题呢？</p>
<ol>
<li>
<p>不清晰</p>
</li>
<li>
<p>加载慢</p>
</li>
<li>
<p>插入麻烦</p>
</li>
<li>
<p>不支持公众号内编辑</p>
</li>
</ol>
<p>可这可能偏偏是别人不说我们唯一能想到的一种方法了，所以许多人都是这么用过来的。大家都妥协了。</p>
<p>我偏不，盗版书都不会这么做。</p>
<h3>2. 在知乎上输入公式并复制</h3>
<p>不知道是哪个鬼才第一个想到这个神奇的方法，最有毒的是，知乎有自己的公式后端，在复制微信的时候会自动迁移图床。同样是矢量的格式，清晰无比，速度也够快。</p>
<p>但是吧，就有那么一点小问题，比如：</p>
<ol>
<li>
<p>公式以图像标签插入，难控制字号</p>
</li>
<li>
<p>不支持 Darkmode</p>
</li>
<li>
<p>不支持公众号内编辑</p>
</li>
</ol>
<p>OK，我又拒绝了。</p>
<h3>3. Markdown Nice</h3>
<p>可能这个时候，有人会来告诉你，有那么一个神器可以生成插入微信的公式：</p>
<p><a target="_blank" href="https://mdnice.com">mdnice.com</a></p>
<blockquote>
<p>喜欢 MathJax 的同理</p>
</blockquote>
<p>这真的是个非常完美的编辑器，大概是全网第一个实现公式插入的编辑器吧（这篇文章就是使用 mdnice 上传的）。可是微信公众号自己的编辑器又是一个很能令人愤怒的东西，总会出一些奇怪的问题，比如：</p>
<ol>
<li>公式复制到微信后 SVG 被加上一层 embed 标签，自动传至微信图床，失去 currentColor 的继承，不支持 Darkmode</li>
</ol>
<p>针对这个问题，我的一位师哥（公众号 <code>Cigaret</code>）给出了十分好的解决方案，可以一键去除被微信加上的 embed 标签：</p>
<p><a target="_blank" href="https://github.com/kongxiangyan/bookmarklet">github.com/kongxiangyan/bookmarklet</a></p>
<h3 language="math">4.<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>TeX</mtext></mrow><annotation encoding="application/x-tex">\TeX</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8988em;vertical-align:-0.2155em"></span><span class="mord text"><span class="mord textrm">T</span><span class="mspace" style="margin-right:-0.1667em"></span><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.4678em"><span style="top:-2.7845em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord textrm">E</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2155em"><span></span></span></span></span><span class="mspace" style="margin-right:-0.125em"></span><span class="mord textrm">X</span></span></span></span></span> <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo>→</mo></mrow><annotation encoding="application/x-tex">\rightarrow</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.3669em"></span><span class="mrel">→</span></span></span></span> SVG</h3>
<p>公式这一行，一直存在一个鄙视链：</p>
<p language="math">Word 公式 &lt; MathType &lt; <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>LaTeX</mtext></mrow><annotation encoding="application/x-tex">\LaTeX</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8988em;vertical-align:-0.2155em"></span><span class="mord text"><span class="mord textrm">L</span><span class="mspace" style="margin-right:-0.36em"></span><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6833em"><span style="top:-2.905em"><span class="pstrut" style="height:2.7em"></span><span class="mord"><span class="mord textrm mtight sizing reset-size6 size3">A</span></span></span></span></span></span><span class="mspace" style="margin-right:-0.15em"></span><span class="mord text"><span class="mord textrm">T</span><span class="mspace" style="margin-right:-0.1667em"></span><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.4678em"><span style="top:-2.7845em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord textrm">E</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2155em"><span></span></span></span></span><span class="mspace" style="margin-right:-0.125em"></span><span class="mord textrm">X</span></span></span></span></span></span></p>
<p language="math">其中，Word 公式是公认的最难看，但是方便且普及；MathType 是个还算过的去的商业软件，但它就只是个桌面端程序；而 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>LaTeX</mtext></mrow><annotation encoding="application/x-tex">\LaTeX</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8988em;vertical-align:-0.2155em"></span><span class="mord text"><span class="mord textrm">L</span><span class="mspace" style="margin-right:-0.36em"></span><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.6833em"><span style="top:-2.905em"><span class="pstrut" style="height:2.7em"></span><span class="mord"><span class="mord textrm mtight sizing reset-size6 size3">A</span></span></span></span></span></span><span class="mspace" style="margin-right:-0.15em"></span><span class="mord text"><span class="mord textrm">T</span><span class="mspace" style="margin-right:-0.1667em"></span><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.4678em"><span style="top:-2.7845em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord textrm">E</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2155em"><span></span></span></span></span><span class="mspace" style="margin-right:-0.125em"></span><span class="mord textrm">X</span></span></span></span></span></span>，不仅最好看，而且适用范围最广，前有 MathJax 适配网页，后有 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext mathvariant="monospace">\</mtext><mtext>blah</mtext><mo stretchy="false">{</mo><mtext>TeX</mtext><mo stretchy="false">}</mo></mrow><annotation encoding="application/x-tex">\verb|\| \text{blah} \{ \TeX \}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em"></span><span class="mord text"><span class="mord texttt">\</span></span><span class="mord text"><span class="mord">blah</span></span><span class="mopen">{</span><span class="mord text"><span class="mord textrm">T</span><span class="mspace" style="margin-right:-0.1667em"></span><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.4678em"><span style="top:-2.7845em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord textrm">E</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2155em"><span></span></span></span></span><span class="mspace" style="margin-right:-0.125em"></span><span class="mord textrm">X</span></span><span class="mclose">}</span></span></span></span> 特供 Pages，唯一的问题就是，我们不知道也不会用。</p>
<p language="math">所以我选择排出了大概是全微信第一篇 <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mtext>TeX</mtext></mrow><annotation encoding="application/x-tex">\TeX</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8988em;vertical-align:-0.2155em"></span><span class="mord text"><span class="mord textrm">T</span><span class="mspace" style="margin-right:-0.1667em"></span><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.4678em"><span style="top:-2.7845em"><span class="pstrut" style="height:3em"></span><span class="mord"><span class="mord textrm">E</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.2155em"><span></span></span></span></span><span class="mspace" style="margin-right:-0.125em"></span><span class="mord textrm">X</span></span></span></span></span> SVG 的图文：<a target="_blank" href="https://mp.weixin.qq.com/s?__biz=MzU1Njg1NTU2Mw==&amp;mid=2247484276&amp;idx=1&amp;sn=574f78743cc80cd527198cf51efbf759&amp;scene=21#wechat_redirect">一道从初中做到大学的数学题</a>。但是这样的流程对于普通用户来说太复杂，学习成本太高，听说过也放弃了。</p>
<h2 id="">自己开发插件</h2>
<p>说了这么多方法，为什么最后还是选择了自己开发插件呢？</p>
<p>原因很简单，就是这个我们又爱又恨的公众号原生编辑器。</p>
<p>你说它有多令人头疼吧，很多人一上来根本排不出好看的文章，打击强烈。</p>
<p>但为什么我们还要选择用它编辑呢？因为微信强迫你，没有它你连音视频和小程序都插入不了。所以，我至今选择在内部编辑而不是第三方网站。</p>
<p>在这样的情况下，也是权当学习，我和 @CPunisher，两个刚上大一的前端小白开始做起了微信公众号公式编辑 Chrome 插件。几番波折，我们已经发出了一个还算稳定的版本，并且在 Github 上开源：</p>
<p><a target="_blank" href="https://github.com/ciaochaos/mpMath">github.com/ciaochaos/mpMath</a></p>
<p><img src="/assets/building-mpmath/1fa9214e84c1455ee3278fb11b1e391a_MD5.jpg" alt=""></p>
<p>公式编辑插件</p>
<p><img src="/assets/building-mpmath/87a3986c8465e725790882ba860faf1d_MD5.gif" alt=""></p>
<p>公式插入演示</p>
<p>没想到 mdnice 社区的大佬们立马响应，为这个插件做好了适配。接下来，在 mdnice 中生成好的公式也能在公众号编辑器中通过插件进行二次编辑了！</p>
<p>但是细数开发过程中碰到的那些坑，我们都有些哭笑不得，比如：</p>
<ol>
<li>
<p>不知道怎样做插件</p>
</li>
<li>
<p>我们一开始连 JavaScript 都不懂</p>
</li>
<li>
<p>微信编辑器在命令插入的时候会遇到各种各样的问题</p>
</li>
<li>
<p>公式莫名其妙地消失、变化、出错</p>
</li>
<li>
<p>在原生编辑器里点保存的那一刻，一切都消失了</p>
</li>
</ol>
<p>好在，通过不懈的努力，我们已经解决了大部分的问题。剩下的，还望大家能来 Github 提供一些帮助……</p>
<p>在这个过程中，特别感谢 mdnice、idx 和 Cigaret 提供的帮助。</p>
<p>就这样，我们两个不满微信的「愤青」，也摸爬滚打地拿出了一个看上去很不错的方案，也是第一次，接触了开源的世界。</p>
<h3>作者</h3>
<ul>
<li>
<p>ciaochaos - CUC</p>
</li>
<li>
<p>CPunisher - BUAA</p>
</li>
</ul>]]></content:encoded>
            <author>1272777550@qq.com (Troy Ni)</author>
        </item>
        <item>
            <title><![CDATA[如何制作一个漂亮的二维码]]></title>
            <link>https://troyni.com/articles/building-qrbtf-zh</link>
            <guid>https://troyni.com/articles/building-qrbtf-zh</guid>
            <pubDate>Sat, 09 May 2020 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h1>如何制作一个漂亮的二维码</h1>
<blockquote>
<p>置顶：我们的二维码生成网站 qrbtf.com</p>
</blockquote>
<blockquote>
<p>有人说，二维码是&nbsp;线下向线上转化最重要的入口，无论是对于线下商家、自媒体运营者、策展人还是纸媒编辑，二维码都是非常重要的一环。而标准二维码形式单一、对比浓重，常常在一张海报中「拉低审美」。</p>
</blockquote>
<h2 id="">缘起</h2>
<p>二维码是什么？</p>
<p>从大学开始，大范围地接触生成艺术——利用计算机算法制作的视听艺术作品，有那么一刻，我心中也会想，二维码不也可以是一种生成艺术？</p>
<p><img src="/assets/building-qrbtf/b1c48b55bc9ba8930967eec3bfde14f9_MD5.png" alt=""></p>
<p>《Generative Art》插图</p>
<p>还记得几年前的一个下午，我打开了初代 Apple Watch，我永远忘不了配对的时候，Apple Watch 上出现的动画。我看了足足五分钟，才忍心完成配对——失去观赏动画的机会。后来，我还是没忍住重装了手表，就为了再看一次这段动画。当时的我为什么这么着迷？不是因为这个动画有多「好看」，而是惊叹于我居然能通过扫描一个混沌的、肉眼看不出规律的动态图形，完成信息的识别、手表的配对。</p>
<p>这种体验，一下子把我震撼了。</p>
<p>原来信息的传递，可以如此的艺术化，原来生成艺术的背后，还有「还原的艺术」。后来，网络上能找到这段视频了，我也得知，苹果在五年前「Patented!」这项叫做「Invisible optical label for transmitting information between computing devices」的技术，也就是&nbsp;设备间隐式光标记信息交换技术。</p>
<p>这项技术起源于光标记，在我看来，真正有趣的是其中的「隐形」二字。这也恰恰是今天，我们想在二维码上看到的东西。</p>
<p>二维码的默认样式对比强烈，像是一种椒盐噪声。我们想尽办法，让他变得低调、融入背景、「隐形」。这是大多数人心目中，对二维码美化、艺术二维码的共识。</p>
<h2 id="">不满现有美化方案，自己做一个二维码生成网站</h2>
<p>二维码美化，方案还是有很多的。</p>
<p>徒手设计是一招，商业报价千元起步；找淘宝是一招，六块钱一次，自选模版，不但丑还改不了；网上现成的编辑器是一招，要素过多，完全学不会。</p>
<p>这些方案可以大致分为三类。一是纯手工打造，甲方爸爸换一个网址你掉一桌头发；二是模版选择，质量不高也定制不了，大多数模版还只是背景图、颜色的更换；三是专门的编辑器，太难学。</p>
<p>于是一周前，我明白了，我无非是想要一个&nbsp;满足自己的需求、满足自己的质量要求、丰富自己选择、匹配自己的工作流程&nbsp;的二维码生成工具。</p>
<p>好一个「以自我为中心」的设计。</p>
<p><img src="/assets/building-qrbtf/5d1b654a28aa91b54b7f1e565bdc8dd6_MD5.jpg" alt=""></p>
<p>push pull push</p>
<p>今天，在和一位天赋异禀同学一起学习、设计、开发一周百余次 push 后，我拿出了第一个还算能用的版本。从零学习了前端框架，也终于涉猎了后端知识。上才艺：</p>
<p>网址：qrbtf.com</p>
<p><img src="/assets/building-qrbtf/509ab09a5a55fa3595fca835fb713b43_MD5.png" alt=""></p>
<p>与同学一起制作的网页，能做出来，<br>
<!-- -->实属「初生牛犊不怕虎」</p>
<p><img src="/assets/building-qrbtf/31f575b291df6d315d68888f7785cd36_MD5.jpg" alt=""></p>
<p>我在测试阶段制作的样式</p>
<p>复习一下所学，这个页面中我将二维码列表横向排布在一个溢出的盒子里。在移动端，我借鉴了 Apple 一个非常出色的设计。即每页放置两个二维码，而第三个二维码露出一截。这样的设计在保证对称性的同时暗示了用户这个区域是可以滑动的。这个暗示是怎么做到的呢？前有格式塔封闭性原理，用户会认为露出来的那一截背后有一个完整的二维码，后有设计心理，这一小节充当了意符的作用，是一种很棒的隐喻，远胜于文字提示、切换按钮、滑动条。</p>
<p>由于，我只需要满足我自己的需求，就将网站的重点放在了「参数化设计」上。</p>
<h2 id="">参数化设计</h2>
<p>什么是参数化设计？</p>
<p>在过去，传统的艺术创作流程通常会带有「线性设计」的痕迹，比如画一幅水彩画、写一篇作文，不可撤销、一旦初始条件发生更改只能重做。这种模式逼迫、鼓励艺术家全神贯注地投入创作，一旦出错，一幅作品就毁了。到了视频剪辑领域，大家发现传统的线性编辑不再能适应浩大的影视制作工程，于是非编——非线性编辑的概念被提出。到了工业设计领域，工程量对设计流程又提出了更高的要求，程序化、流程化、参数化的方法接踵而至，提供便利的同时也降低了门槛、提高了上限。</p>
<p>小时候我们玩过 Scratch——模块化编程。实质上，他就是一种流程化的设计，流程化不仅体现在入门级应用上，行业级应用如 Grasshopper、Houdini、UE4、Blender 等也都吸收了这种方法，打造了十分引人的「连连看系统」，通过给节点连线的方式设计，无疑是一种对创意、艺术的解放。</p>
<p>无论是程序化（写代码）还是流程化（连连看、搭积木），无疑是一种参数化、计算机辅助的设计。在制作这样一个二维码工具之前，我花了几分钟时间，以自身体验为中心，思考了学习成本、创作体验与创作限制等维度，设计了这样一个预设样式、可调参数的生成器。提供二维码样式而不是提供编辑器，降低了学习的成本、创作的时间成本，同时可以对样式的审美感受进行统一把关，提供可调参数，满足了我以及与我需求类似的用户对更自由、更规范的统一调整方案的需要，提供 SVG 的导出，为设计师进行二次创作提供了可能。</p>
<h2 id="">我的愿景：借力现代化设计流程，</h2>
<p>启发设计更多有趣的二维码</p>
<p><img src="/assets/building-qrbtf/d43ab3f18952d04d589ea5f0a483caba_MD5.jpg" alt=""></p>
<p>同学的原创纯手工设计</p>
<p><img src="/assets/building-qrbtf/578227bc9d6c549a1ccb5aeb35632ed4_MD5.jpg" alt=""></p>
<p>我的数字化复刻</p>
<p>线性与非线性设计，都有有趣、有价值的一面，也都有各自的问题。我希望我的工具能提供我需要的 I/O，能接入我的工作流程。即便是看上去无懈可击的非线性设计，有时候也需要那样一个「神来之笔」，比如生成完二维码我可以导入我的设计工具，在中间删去一些信息点，加个头像，这一步，你认为是写程序好还是自己摆弄好？</p>
<p>借力现代化设计流程，可以节省很多的人力成本，比如过去我们要改变二维码信息点的尺寸，只能依靠软件深处的特殊功能，或是修改图形代码，而现在，就是网站上一个参数的事情。设计、搭建这个网站，也有想要做一个二维码发布平台的企图。如果你也有兴趣，欢迎联系，加入设计、开发。</p>
<p>最后要感谢 JZ Creatice 的一篇文章&nbsp;<a target="_blank" href="https://mp.weixin.qq.com/s?__biz=MzU3MDIyODE4NQ==&amp;mid=2247490058&amp;idx=1&amp;sn=ec50a4c166e1b92345cd53082cfe90f5&amp;scene=21#wechat_redirect">「九分之八」二维码美化设计法</a>，给了我一些很棒的启发。</p>
<p>另外，我正在设法把二维码生成的工具集成到 mdnice.com，一个支持 Markdown、数学公式、CSS 样式编辑的微信公众号编辑器上面。一起期待吧！</p>
<p>完。</p>]]></content:encoded>
            <author>1272777550@qq.com (Troy Ni)</author>
        </item>
        <item>
            <title><![CDATA[前端环境配置]]></title>
            <link>https://troyni.com/articles/front-end-setup-zh</link>
            <guid>https://troyni.com/articles/front-end-setup-zh</guid>
            <pubDate>Wed, 06 Mar 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h3>所需程序</h3>
<ul>
<li>WebStorm</li>
<li>Node.js / npm</li>
<li>Yarn</li>
</ul>
<h2 id="">安装步骤</h2>
<h3>一、安装 Node.js / npm</h3>
<p><a target="_blank" href="https://nodejs.org/">nodejs.org</a></p>
<h3>二、配置 npm</h3>
<div class="my-6 overflow-hidden rounded-2xl bg-zinc-900 shadow-md dark:ring-1 dark:ring-white/10"><div class="not-prose"><div class="group dark:bg-white/2.5"><div class="relative"><pre class="overflow-x-auto p-4 text-xs text-white"><code class="language-shell"><span><span style="color: var(--shiki-token-function)">npm</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">config</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">set</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">registry</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">https://registry.npmmirror.com</span></span>
<span></span></code></pre><button type="button" class="group/button absolute right-4 top-3.5 overflow-hidden rounded-full py-1 pl-2 pr-3 text-2xs font-medium opacity-0 backdrop-blur transition focus:opacity-100 group-hover:opacity-100 bg-white/5 hover:bg-white/7.5 dark:bg-white/2.5 dark:hover:bg-white/5"><span aria-hidden="false" class="pointer-events-none flex items-center gap-0.5 text-zinc-400 transition duration-300"><svg viewBox="0 0 20 20" aria-hidden="true" class="h-5 w-5 fill-zinc-500/20 stroke-zinc-500 transition-colors group-hover/button:stroke-zinc-400"><path stroke-width="0" d="M5.5 13.5v-5a2 2 0 0 1 2-2l.447-.894A2 2 0 0 1 9.737 4.5h.527a2 2 0 0 1 1.789 1.106l.447.894a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-5a2 2 0 0 1-2-2Z"></path><path fill="none" stroke-linejoin="round" d="M12.5 6.5a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-5a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2m5 0-.447-.894a2 2 0 0 0-1.79-1.106h-.527a2 2 0 0 0-1.789 1.106L7.5 6.5m5 0-1 1h-3l-1-1"></path></svg>Copy</span><span aria-hidden="true" class="pointer-events-none absolute inset-0 flex items-center justify-center text-emerald-400 transition duration-300 translate-y-1.5 opacity-0">Copied!</span></button></div></div></div></div>
<p>打开 <strong>终端</strong>，输入后回车。</p>
<p>作用：将 npm（Node.js 默认包管理软件）软件源替换为淘宝镜像。</p>
<h3>三、安装 Yarn</h3>
<h4>安装</h4>
<div class="my-6 overflow-hidden rounded-2xl bg-zinc-900 shadow-md dark:ring-1 dark:ring-white/10"><div class="not-prose"><div class="group dark:bg-white/2.5"><div class="relative"><pre class="overflow-x-auto p-4 text-xs text-white"><code class="language-shell"><span><span style="color: var(--shiki-token-function)">npm</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">install</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">-g</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">yarn</span></span>
<span></span></code></pre><button type="button" class="group/button absolute right-4 top-3.5 overflow-hidden rounded-full py-1 pl-2 pr-3 text-2xs font-medium opacity-0 backdrop-blur transition focus:opacity-100 group-hover:opacity-100 bg-white/5 hover:bg-white/7.5 dark:bg-white/2.5 dark:hover:bg-white/5"><span aria-hidden="false" class="pointer-events-none flex items-center gap-0.5 text-zinc-400 transition duration-300"><svg viewBox="0 0 20 20" aria-hidden="true" class="h-5 w-5 fill-zinc-500/20 stroke-zinc-500 transition-colors group-hover/button:stroke-zinc-400"><path stroke-width="0" d="M5.5 13.5v-5a2 2 0 0 1 2-2l.447-.894A2 2 0 0 1 9.737 4.5h.527a2 2 0 0 1 1.789 1.106l.447.894a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-5a2 2 0 0 1-2-2Z"></path><path fill="none" stroke-linejoin="round" d="M12.5 6.5a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-5a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2m5 0-.447-.894a2 2 0 0 0-1.79-1.106h-.527a2 2 0 0 0-1.789 1.106L7.5 6.5m5 0-1 1h-3l-1-1"></path></svg>Copy</span><span aria-hidden="true" class="pointer-events-none absolute inset-0 flex items-center justify-center text-emerald-400 transition duration-300 translate-y-1.5 opacity-0">Copied!</span></button></div></div></div></div>
<p>作用：使用更好的软件源 Yarn。</p>
<h4>配置</h4>
<div class="my-6 overflow-hidden rounded-2xl bg-zinc-900 shadow-md dark:ring-1 dark:ring-white/10"><div class="not-prose"><div class="group dark:bg-white/2.5"><div class="relative"><pre class="overflow-x-auto p-4 text-xs text-white"><code class="language-shell"><span><span style="color: var(--shiki-token-function)">yarn</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">config</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">set</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">registry</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">https://registry.npmmirror.com</span></span>
<span></span></code></pre><button type="button" class="group/button absolute right-4 top-3.5 overflow-hidden rounded-full py-1 pl-2 pr-3 text-2xs font-medium opacity-0 backdrop-blur transition focus:opacity-100 group-hover:opacity-100 bg-white/5 hover:bg-white/7.5 dark:bg-white/2.5 dark:hover:bg-white/5"><span aria-hidden="false" class="pointer-events-none flex items-center gap-0.5 text-zinc-400 transition duration-300"><svg viewBox="0 0 20 20" aria-hidden="true" class="h-5 w-5 fill-zinc-500/20 stroke-zinc-500 transition-colors group-hover/button:stroke-zinc-400"><path stroke-width="0" d="M5.5 13.5v-5a2 2 0 0 1 2-2l.447-.894A2 2 0 0 1 9.737 4.5h.527a2 2 0 0 1 1.789 1.106l.447.894a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-5a2 2 0 0 1-2-2Z"></path><path fill="none" stroke-linejoin="round" d="M12.5 6.5a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-5a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2m5 0-.447-.894a2 2 0 0 0-1.79-1.106h-.527a2 2 0 0 0-1.789 1.106L7.5 6.5m5 0-1 1h-3l-1-1"></path></svg>Copy</span><span aria-hidden="true" class="pointer-events-none absolute inset-0 flex items-center justify-center text-emerald-400 transition duration-300 translate-y-1.5 opacity-0">Copied!</span></button></div></div></div></div>
<p>作用：将 Yarn 软件源替换为淘宝镜像。</p>
<h3>五、安装 WebStorm</h3>
<ol>
<li>下载：<a target="_blank" href="https://www.jetbrains.com/webstorm/">https://www.jetbrains.com/webstorm/</a></li>
<li>学生优惠申请：<a target="_blank" href="https://www.jetbrains.com/community/education/#students">https://www.jetbrains.com/community/education/#students</a></li>
</ol>
<h4>完成</h4>]]></content:encoded>
            <author>1272777550@qq.com (Troy Ni)</author>
        </item>
        <item>
            <title><![CDATA[十万只鸟儿在 GPU 上飞行：一次关于算法与自然的探索]]></title>
            <link>https://troyni.com/articles/parametric-creature-zh</link>
            <guid>https://troyni.com/articles/parametric-creature-zh</guid>
            <pubDate>Tue, 23 Nov 2021 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>曾经有一个问题困扰了我很久，为什么这个世界上的人和物质，都会在不同尺度下呈现不同的聚团和排斥。人与人相互靠近，却会形成人群与人群的竞争与敌对，原子和分子相互靠近，却能产生相互分离的物体，乃至星球和星系。</p>
<p>怀着这份朴素的思考，我克服了艺术生的专业壁垒，开始研究起了复杂系统（Complex System）、群体智能（Swarm Intelligence）、元启发式（Metaheuristics）、自组织（Self-Organization）、涌现（Emergence）、ABM（Agent-Based Modeling）这些曾经离我十分遥远的概念，将这些研究与发现做成了艺术作品，发布了人生第一个 App Store / Steam 应用。</p>
<p>如果你也有兴趣，不妨了解一下这个故事，和我们一起探索……</p>
<h2 id="">最初的启发</h2>
<p>我是一个很少玩游戏的人，但《Frost》和他的续作《Lifelike》却给我留下了异常深刻的印象，游戏的官网告诉我，目前没有任何 CPU 能提供如此高性能的集群模拟，而该游戏基于 Metal 开发了 GPU 算法。</p>
<p>后来学习到了 Unity Visual Effect Graph，一个先进且易用的 GPU 粒子系统，但这个系统没有办法模拟粒子间作用，这加深了我的困惑。Frost 是怎么实现的？</p>
<p><img src="/assets/parametric-creature/fe27c6b577a21975333d86912dae640f_MD5.png" alt=""></p>
<p>和吕老师分享了我的困惑</p>
<h2 id="">从鸟群开始</h2>
<p>在学术界，群集系统的开山之作是一个 1986 年的仿生程序 Boids，它将鸟群的运动归纳成了个体运动的三个基本法则，排斥（Separation）、对齐（Alignment）、靠近（Cohesion），且每个个体只能观察到一定范围甚至一定视域的其他个体。</p>
<p>，时长00:27</p>
<p>经典的鸟群算法 Boids</p>
<p><img src="/assets/parametric-creature/2c6691977280ca0430bb7e6299117901_MD5.png" alt=""></p>
<p>Processing 官网的 Boids 算法案例，<a target="_blank" href="https://processing.org/examples/flocking.html">https://processing.org/examples/flocking.html</a></p>
<h2 id="">两群鸟的相遇</h2>
<p>项目网站：<a target="_blank" href="https://bingweb.binghamton.edu/~sayama/SwarmChemistry/">https://bingweb.binghamton.edu/~sayama/SwarmChemistry/</a></p>
<p>当我发现这一研究时，瞬间兴奋了起来，粒子系统继承了鸟群模型的三个基本法则，添加了更多的行为法则，同时在一个场景里，添加了不同的鸟群。多样性在一瞬间涌现了出来。</p>
<p><img src="/assets/parametric-creature/18150a2763971fe042ede50cc868b796_MD5.png" alt=""></p>
<p>项目官网上可以下载 Java 程序，同时也开源了代码。</p>
<p>但是，程序把粒子的个数限制在了 300，而我们都清楚的一点是，CPU 的（单线程、遍历）找邻居算法，复杂度是 O(N2)，即粒子数量多一倍，计算的耗时是原来的四倍，模拟的瓶颈一下子就出现了。</p>
<h2 id="gpu">GPU：一万倍性能提升</h2>
<p>英伟达的老黄曾经说过一句话，根据摩尔定律，GPU 相比 CPU 的提升相当于把十年后的计算资源交到现在的研究人员手中，这也是 GPU 计算感知明显的原因吧。</p>
<p>YouTube 上的一位博主，游戏 Headmaster 的制作人，实现了 50w 粒子的 Boids 模拟，原理和 Processing 的 Pixel Flow 库例子程序一样，并不是真正在查找邻居，而是将粒子的信息写入一张向量材质，从而改变每一个粒子的位置。</p>
<p><img src="/assets/parametric-creature/611d0961e4d9c8a5923b13338fe43398_MD5.png" alt=""></p>
<p>基于向量场材质绘制的模拟，可以轻松实现十万级别的 Boids 近似模拟，但这种方法不适用我想实现的复杂模型</p>
<h3>第一次尝试<br>Python + Numba</h3>
<p>我的第一次实验是在 Python 上使用 Numba 库绑定 CUDA 进行加速，借助 Python 极低的门槛和 Numba 神奇的语法，我们很快就将 CPU 算法移植到了 GPU 平台，并用 OpenGL 绘制出来。</p>
<p><img src="/assets/parametric-creature/6c1ab034a754f03f28741ed27ca4d536_MD5.png" alt=""></p>
<p>Numba 库，只需要添加几行代码就可以让一个函数在 GPU 上并行</p>
<p><img src="/assets/parametric-creature/6c82a80c8f9cb5c62453743174a3a9d1_MD5.jpg" alt=""></p>
<p>第一次跑通算法，当时的画面还是黑白的</p>
<p>，时长00:27</p>
<p>使用 OpenGL 渲染的程序</p>
<p><img src="/assets/parametric-creature/c7503011ae7c3d6a36750843ba35d65e_MD5.jpg" alt=""></p>
<p>在教室里测试运行</p>
<p><img src="/assets/parametric-creature/609567ac2b4c5d4d4c9e7b2aaca98a4e_MD5.jpg" alt=""></p>
<p>展览效果图</p>
<h3>第二次尝试<br>TouchDesigner</h3>
<p><img src="/assets/parametric-creature/0877634596637e9e0a64bee71d8bf9ef_MD5.jpg" alt=""></p>
<p>第一次用 TouchDesigner，很多效果都不知道该怎么做，但至少是跑通了</p>
<p><img src="/assets/parametric-creature/d481a36a039d47e852aad122168c081e_MD5.jpg" alt=""></p>
<p>结课忽悠人的时候，已经有了 60 倍的性能提升</p>
<p>遗憾的是，我们并没有在 TouchDesigner 中直接调用 OpenGL 计算着色器（因为 macOS 不支持），所有的运算仍然是在 Python 的 Numba 中完成，只是将绘图部分从 OpenGL 转移到 TouchDesigner 中。</p>
<p>除此之外，Python（Numba）没有结构体和向量类型，所有的数据都需要存在数组中，x、y 坐标的运算就都得写两次，非常麻烦。</p>
<h3>第三次尝试<br>Houdini</h3>
<p>在学习了 Houdini 之后，我打开了新的世界，对 OpenCL 的支持让我有了编写底层 GPU 程序的机会（CUDA C++ 对我来说真的太困难了），便捷的数据绑定、超高的性能，只用几个节点几次连接就把算法全部移植成功了。</p>
<p><img src="/assets/parametric-creature/d6db8cf7f7a8a26d6f7a242b15954c93_MD5.jpg" alt=""></p>
<p>在 Houdini 中运用 OpenCL 节点加速模拟，借助向量语法轻松改写三维</p>
<p><img src="/assets/parametric-creature/2ffa4899bc081c1c82ae8765a9359d07_MD5.jpg" alt=""></p>
<p>用 Redshift 渲染（没对上焦就很艺术）</p>
<p>Houdini OpenCL 的表现让我有了进一步优化的动力，甚至也有了想做跨平台应用的念头。明明是一个离线特效软件，却能拥有异常流畅的实时性能，比 Python 的 Numba 不知道好多少……</p>
<h3>最后的集大成</h3>
<p>Unity</p>
<p>在踩遍无数坑后，我终于找到了最最合适的平台。</p>
<h4>计算着色器：解决互操作问题，多平台编译</h4>
<p>在此前的 Python / Houdini 程序中，所有粒子数据的计算和渲染都需要在 CPU、GPU 之间来回拷贝，由于传统架构的内存不共享，这将严重影响计算性能，而事实上，GPU 的计算程序和绘图程序是可以互操作的，即索引同一段数据（Buffer）。</p>
<p>而 Unity 恰好提供了一个完美的解决方案，HLSL Compute Shader。Unity 会将写好的 HLSL 程序编译成 Metal、DirectX、Vulkan 和 OpenGL，从而省去多平台重写代码的麻烦，同普通的 Shader 程序一样，都支持读取 GPU 中的 Buffer。</p>
<p>即便什么优化都没有做，也能轻松实现数倍的性能提升，在粒子数量大的情况下尤为明显。</p>
<h4>追求极限的优化：空间分割算法</h4>
<p>直到现在，所有粒子的找邻居计算都是一整个暴力遍历，而有几种奇妙的方法，可以让程序对每个粒子只查找周围的粒子，大大省去检索的时间。其中最好理解也最适合这个场景的方法，就是空间分割。</p>
<p>由于每个粒子都有最大的观察半径 40，我便可以将 1280 × 720 的活动区域分割成 32 × 18 个长宽 40 的正方形网格，这样，每个粒子只需要遍历所在网格周围的九个网格。理由是，间隔了一个网格的粒子，相互距离肯定超过了 40。</p>
<p>在经过空间分割优化后，性能相比未优化平均提升三倍。</p>
<p>除此之外，还有排序算法、K-D Tree 分割，值得探索。</p>
<p><img src="/assets/parametric-creature/870e7e50d508a109e9e58097abca0796_MD5.jpg" alt=""></p>
<p>经过基本的网格空间分割后，性能达到了 20w 粒子 60FPS，3070Ti，对比 Processing 的 CPU 程序性能提升达一万倍</p>
<p>经过基本的网格空间分割后，性能达到了 20w 粒子 60FPS，3070Ti，对比 Processing 的 CPU 程序性能提升达一万倍</p>
<h4>新的发现：粒子系统生成斑马纹</h4>
<p>一次偶然的尝试，我发现只要给粒子加入一个小小的机制：敌我识别，即根据类型区别对待近邻粒子，就可以产生非常奇妙的自组织形态。可能是一朵小花，也可能是一个斑马纹样。</p>
<p>以往斑马纹只能通过 Reaction-Diffusion 算法或是 Multi-Neighborhood Cellular Automata 算法实现，而现在，基于粒子的模型也能做到了，这或许能打开生命科学的新思路，因为，生物的基础是细胞，而不是像素。</p>
<p>，时长01:16</p>
<p><img src="/assets/parametric-creature/39c3e28ee1ae92b7632af301782fe65a_MD5.png" alt=""></p>
<p>粒子运动时开出的一朵小花，也是现在的应用图标</p>
<p><img src="/assets/parametric-creature/ca58faa764285e60c88c3de68e643ade_MD5.jpg" alt=""></p>
<p>类似蠕虫的效果，完全由粒子自组织，破坏后依旧可以恢复</p>
<p><img src="/assets/parametric-creature/3b346b06fa12ba7780b725e89d19ba5b_MD5.jpg" alt=""></p>
<p>独特的斑马纹路</p>
<p><img src="/assets/parametric-creature/3ebaee3d367488b6df5ab156a2dd3e8c_MD5.jpg" alt=""></p>
<p>连接 MIDI 键盘后的效果，24 个参数正好对应了 AKAI MPD218 上的二十四个旋钮，体验极佳</p>
<p><img src="/assets/parametric-creature/93cdb9016b402e58b4255e4fca327217_MD5.jpg" alt=""></p>
<p>在学院里测试时拍摄的照片</p>
<p><img src="/assets/parametric-creature/4d92c4e0a333911fa8c3d87ef6cb2e37_MD5.jpg" alt=""></p>
<p>我为作品制作的移动端 UI，可以选择有趣的预设，拍摄截图、复制粘贴参数。</p>
<p><img src="/assets/parametric-creature/0f98c77f04dd52d2e9cf272f4629464f_MD5.jpg" alt=""></p>
<p>VR 版本，正在开发中～</p>
<h2 id="">从想法到上架</h2>
<p>一些小体会</p>
<h3>迈出第一步，坚持尝试</h3>
<p>还记得头一回和老师分享这个想法，是在今年的三月，那个时候我是有一些畏惧，毕竟我不是科班出身，却想去研究大部分计算机本科都不会教的 GPU 计算。</p>
<p>突然有一天，我被丹琪姐拉到学院问，环保部要办一个 COP15 生物多样性大会的展览，你们有没有兴趣做一些作品？</p>
<p>我才再一次想起这个话题，心想，要不要挑战一下？</p>
<p>我找来了有着同样追求的朋友们：我的高中死党，跑通了第一个 Python 程序的陈同学（Github：CPunisher），过着美国时间、每晚 24 点交接工作的 Unity 队友时辰（抖音@时辰不死于背锅），以及一起做创意、策划和展览的金希、杜西大师哥。</p>
<p>一路上遇到了不少高人，给了我们成吨成吨的帮助。</p>
<p><img src="/assets/parametric-creature/cfcaeb9e98e439f3d64ac85442ef3b55_MD5.png" alt=""></p>
<p>Github 好♂友，懂得都懂</p>
<p><img src="/assets/parametric-creature/e30d55156738dcf7e5d3cbb0ce538b22_MD5.png" alt=""></p>
<p>连肝了三天三夜，才把 iOS 程序的黑屏点亮了，这时队友带上来的 0.72（楼下一串串香店名）让我顿时心虚激荡，双厨狂喜</p>
<p><img src="/assets/parametric-creature/dbdfbf331019be56d6ac2e1f1ae8ebd3_MD5.jpg" alt=""></p>
<p>班主任杨老师，我们的显卡爸爸，承包了一整个专业的算力，带领全班秒杀 R9000P</p>
<p><img src="/assets/parametric-creature/7174f7714f4585459609aec5803641bf_MD5.png" alt=""></p>
<p>沈浩老师向我引荐的新媒体艺术家任远，《Processing 创意编程》一书的作者，给我提的一个 “小目标”，成为了我不断追求极限的动力</p>
<p><img src="/assets/parametric-creature/62c6d894aa55a2ea168fff40f0f91bab_MD5.png" alt=""></p>
<p>游戏系的萌神呼风唤雨，请费老师、郑师哥教我空间分割等算法</p>
<p><img src="/assets/parametric-creature/13d8ffc791dd28530fbaf8e960c8ac15_MD5.png" alt=""></p>
<p><img src="/assets/parametric-creature/55c9633c1b6adb3ca1c69b49eafbe369_MD5.png" alt=""></p>
<p>这位研究群集化学的可爱的日本教授回复了我的邮件，并问我能不能做出带进化算法的版本</p>
<p><img src="/assets/parametric-creature/ae823f725a7c3cb0791c6f27e13d8466_MD5.png" alt=""></p>
<p>和上面提到的游戏开发者分享了我的程序</p>
<p><img src="/assets/parametric-creature/5daa62069f2ad06068e7def9634c18ba_MD5.png" alt=""></p>
<p>系主任：你们这是雁过拔毛，我要收管理费</p>
<p>高手，都是高手</p>
<p><img src="/assets/parametric-creature/fb6cc524b650ccae7101349048712c99_MD5.png" alt=""></p>
<p>课程海报，感谢环保部赏脸！</p>
<p><img src="/assets/parametric-creature/3de30df8f89039e7e7e35bf9f41916ca_MD5.jpg" alt=""></p>
<p>聆听 WWF 官方介绍生物多样性</p>
<p><img src="/assets/parametric-creature/7c92e59518237ce5a4be8e4144cba042_MD5.jpg" alt=""></p>
<p>特别感谢孙老师、丹琪姐、博哥以及黑弓的大佬们，鸥哥、老曹、小鹿姐，挤出时间亲临指导，带我们混吃混喝，希望速速再来玩</p>
<p>（都说我们这届是最惨的，因为疫情一众小学期出国访学的项目都取消了，但也还是成功拥有了一个个精彩的小学期，只能说，中传数媒，入股不亏 👍）</p>
<h3>发布、提交审核</h3>
<p>终于过审了！</p>
<p>一直听说苹果的审核很严格，经历了一次后觉得，是真的很麻烦，但踩这些坑特别值。</p>
<p>曾经做过的很多东西，都停在了原型、Demo，从未想过要发布出来，让更多的人看到。这一次我选择放下了心里的完美主义，不等功能 100% 完善，先发了再说。</p>
<p>然后迎接我的，是开发者账号注册，出口合规，美国报税单，外汇账户，分级评定，打包上传，这些完全没接触过的东西。</p>
<p><img src="/assets/parametric-creature/cf24a07c5f0e14a8dc2e333281359afc_MD5.png" alt=""></p>
<p>App Store 必须提交的废话文学作品：隐私政策（没有别的意思，只想说对于这种不收集任何隐私数据的程序还要专门开一个网页说明没有收集真的很费事）</p>
<p><img src="/assets/parametric-creature/487f86b79979cc7776710e8ce150f58b_MD5.png" alt=""></p>
<p>第一次打包上传成功</p>
<p><img src="/assets/parametric-creature/3a58ac2c0da8060c60902ccf34712c7d_MD5.jpg" alt=""></p>
<p>一直在打包，一直在测试</p>
<p><img src="/assets/parametric-creature/3e5502b01e4dd26f1f32b226cdf1939c_MD5.png" alt=""></p>
<p>上传了几十张不同尺寸的截图、宣传图，终于见到了自己的商店页面</p>
<p><img src="/assets/parametric-creature/d850083319528733bf9a3f4a56b32ccf_MD5.png" alt=""></p>
<p>Steam 过审！</p>
<p><img src="/assets/parametric-creature/b0f07da3a71374e99997fa22027aa6ff_MD5.jpg" alt=""></p>
<p>App Store 过审！</p>
<h2 id="">写在最后</h2>
<p>就在不久前，2021 年诺贝尔物理力学奖颁发给了三位对复杂物理系统作出重要贡献的科学家，复杂性思维正变得越来越重要。天气在确定性中无端变化，生命在无序世界中积累秩序。每一个个体都有独立的目标，在有限的连结中传递信息，却能产生令人震惊的群体智慧。这大概也是我如此痴迷于此的原因吧。</p>
<p><img src="/assets/parametric-creature/b605387341c1af8a8b2b02840f1a8443_MD5.png" alt=""></p>
<p>正在构筑东京铁路的黏菌</p>
<p><img src="/assets/parametric-creature/6be43434278b616973b9574b35079871_MD5.jpg" alt=""></p>
<p>大一时绘制的朋友圈点赞关系网，根据图关系进行聚集分区</p>
<p><img src="/assets/parametric-creature/9bcd6872247ab37cf47bde11cf2ad803_MD5.jpg" alt=""></p>
<p>我的奇异吸引子的生成艺术作品</p>
<p><img src="/assets/parametric-creature/2fbede2bb0a0230ff3bf33fde113081d_MD5.png" alt=""></p>
<p>多近邻感知细胞自动机（Multi-Neighborhood Cellular Automata）</p>
<p><img src="/assets/parametric-creature/88afb7a1f6d626ed831c0f447859f1c0_MD5.png" alt=""></p>
<p>反应—扩散（Reaction-Diffusion）</p>
<p>完。</p>
<p>点击阅读原文或在 App Store 中搜索 “参数生命” 即可购买应用（Steam 版本 2021.12.3 上线）。如果你有任何疑问，欢迎添加我的微信（nhciao，请备注名字），一起交流！</p>]]></content:encoded>
            <author>1272777550@qq.com (Troy Ni)</author>
        </item>
        <item>
            <title><![CDATA[Migration of QRBTF]]></title>
            <link>https://troyni.com/articles/qrbtf-migration</link>
            <guid>https://troyni.com/articles/qrbtf-migration</guid>
            <pubDate>Mon, 26 Feb 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Running a generative AI website requires a lot of time and server resources. Right now, I’m devoting most of my time to building <a target="_blank" href="https://midreal.ai">MidReal.ai</a>, an AI story writing platform.</p>
<p>Because of this, I plan to move <a target="_blank" href="https://qrbtf.com">QRBTF</a> into <a target="_blank" href="https://midreal.ai/lab">MidReal AI Lab</a> and no longer maintain its independent user system, GPU cluster and front-end and back-end servers.</p>
<ul>
<li>
<p>The new QR code generation page will be moved from <a target="_blank" href="https://qrbtf.com/ai">qrbtf.com/ai</a> to <a target="_blank" href="https://midreal.ai/qrcode">midreal.ai/qrcode</a>. During the beta test, you can generate a QR code for <strong>any URL</strong> by just logging in to your Discord account.</p>
</li>
<li>
<p>QRBTF’s <a target="_blank" href="https://discord.gg/V9CNuqYfte">Discord server</a> will also be renamed <strong><em>Latent Cat</em></strong> and will continue to bring you a variety of AI experimental applications.</p>
</li>
</ul>
<p>Also feel free to try out my new product <a target="_blank" href="https://midreal.ai">MidReal</a> and join its <a target="_blank" href="https://discord.gg/rwQSeEK8bY">Discord server</a>, it's still in its very early stages and any feedback and ideas are welcome.</p>
<p><img src="/assets/qrbtf-migration/qrbtf-midreal.jpg" alt="QRBTF &amp; MidReal"></p>]]></content:encoded>
            <author>1272777550@qq.com (Troy Ni)</author>
        </item>
        <item>
            <title><![CDATA[AI 生成可扫码图像 — 新 ControlNet 模型展示]]></title>
            <link>https://troyni.com/articles/qrcode-controlnet-zh</link>
            <guid>https://troyni.com/articles/qrcode-controlnet-zh</guid>
            <pubDate>Sun, 04 Jun 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2 id="">引</h2>
<p>许久没有更新公众号，来分享一个新作：
<strong>ControlNet for QR Code</strong></p>
<p>什么效果？有什么用处？来看下面的例子。</p>
<p>这是一张平平无奇，看上去略有些错乱的 Stable Diffusion 生成的风格化图像：</p>
<p><img src="/assets/qrcode-controlnet/11ff26bdcc5993416dc7966312e726db_MD5.jpg" alt=""></p>
<p>但给它加上三个定位点后，这张图就变成一个可以扫描识别的二维码了：</p>
<p><img src="/assets/qrcode-controlnet/b76da83a26cdd638c513c1a66c9cfc6f_MD5.jpg" alt=""></p>
<p>长按识别二维码，跳转至 qrbtf.com</p>
<p>很神奇吧！下面是这个项目的缘起、训练过程和更多生图结果……</p>
<h2 id="">缘起</h2>
<p>大二的时候，我和同学做了一个参数化二维码生成器，qrbtf.com（<a target="_blank" href="https://mp.weixin.qq.com/s?__biz=MzU1Njg1NTU2Mw==&amp;mid=2247484719&amp;idx=1&amp;sn=6188194d879096a04a2be4771f397bdd&amp;scene=21#wechat_redirect">如何制作一个漂亮的二维码</a>），出于各种（摆烂）原因，至今没有继续更新。记得一次和创新工场的咏刚老师交流时，突然聊到，能不能在任何肉眼观察完全正常的图像中编码隐藏的信息。在那个 GAN 的年代，机器学习的生态远没有如今活跃，不说有 Gradio Web UI、Diffusers 这样好用的框架，但配环境就足够劝退我了，这个想法就此搁置。</p>
<p><img src="/assets/qrcode-controlnet/e7cc2341335a334bc5eae26c1968ac53_MD5.png" alt=""></p>
<p>直到 Stable Diffusion 横空出世、ControlNet 席卷各大行业，经历了很长一段时间的摸索，我才重新开了这个坑，能不能用扩散模型生成一个看上去很像一张图片的二维码？</p>
<p><img src="/assets/qrcode-controlnet/cdde06472975213d25f86862550734e4_MD5.jpg" alt=""></p>
<p>最初的 ControlNet 尝试</p>
<p><img src="/assets/qrcode-controlnet/07f74ba76bdc51d8b0ab4fbbe2c5875c_MD5.jpg" alt=""></p>
<p>训练的中国传统纹样 LoRA</p>
<p><img src="/assets/qrcode-controlnet/f579b1aee40b71a85c25b5785bee6f86_MD5.png" alt=""></p>
<p>AIGC All in One 文档，持续更新中</p>
<p><img src="/assets/qrcode-controlnet/5e5171a86c9f0ea2d3928999e22b594f_MD5.jpg" alt=""></p>
<p>HuggingFace JAX/Diffusers Sprint</p>
<h2 id="">训练</h2>
<p>ControlNet 训练的数据结构十分简单，仅为一张输入图（conditioning image）、一张输出图（image）和一段标注（caption）。官方给出了非常多预训练模型，包括 1.0 版本中的 Depth、HED、OpenPose 和 1.1 中非常有创意的 Shuffle、Tile 和 Instruct Pix2Pix 等。</p>
<p>ControlNet 的训练对数据量和算力均有较高要求，论文中记录的训练数据量从 8 万到 300 万不等，训练时间可达 600 个 A100 GPU 小时。好在作者提供了基础的训练脚本，HuggingFace 也做了 Diffusers 实现。</p>
<p>在此前的 JAX Sprint 中，我们有幸使用 Google TPU v4，非常快地完成了 300 万张图的训练。可惜活动结束，我们回到了实验室的 A6000 / 4090，训练了一个 10 万张图的版本，且学习率非常大，只为尽早出现“突变拟合”（Sudden Convergence）。</p>
<p><img src="/assets/qrcode-controlnet/d1970d415a04776e4d66fab1ed8ad49d_MD5.png" alt=""></p>
<p>GPU / TPU 训练参数</p>
<p><img src="/assets/qrcode-controlnet/5850563792bff453d68c4f2bd1e75052_MD5.jpg" alt=""></p>
<p>灰度控制 ControlNet，训练流程见 aigc.ioclab.com/sd-showcase/brightness-controlnet</p>
<p><img src="/assets/qrcode-controlnet/460f12bcd08f2384c7328a902c510f4a_MD5.jpg" alt=""></p>
<p>光影控制 ControlNet，训练流程见 aigc.ioclab.com/sd-showcase/light_controlnet</p>
<h2 id="">推理</h2>
<p>测试模型训练完毕后，我们尝试了多种 Checkpoint + LoRA + QR Code ControlNet 的组合。得到了下面各式各样的可识别二维码。</p>
<h3>中国传统纹样</h3>
<p>LoRA 训练流程：aigc.ioclab.com/sd-showcase/chinese-ornament&nbsp;
LoRA 模型下载：civitai.com/models/29858/chinese-traditional-pattern</p>
<p><img src="/assets/qrcode-controlnet/c12ac165cd6e33257c39048fd55225dd_MD5.png" alt=""></p>
<p><img src="/assets/qrcode-controlnet/f0519ed057b583a72836c1da628ea787_MD5.png" alt=""></p>
<p><img src="/assets/qrcode-controlnet/93f87a90ecc6d1168e1d951f8317949f_MD5.png" alt=""></p>
<p><img src="/assets/qrcode-controlnet/f1092d38d6dbdc8370d1e6e5048dbfec_MD5.png" alt=""></p>
<h3>浮世绘风格</h3>
<p>LoRA 训练流程：aigc.ioclab.com/sd-showcase/fuyue&nbsp;
LoRA 模型下载：civitai.com/models/25222/ukiyo-e-fuyue-style-background-mix</p>
<p><img src="/assets/qrcode-controlnet/7fcaeff50b632c3f38094c5f984d4414_MD5.jpg" alt=""></p>
<p><img src="/assets/qrcode-controlnet/c908d480a3d2711f4b201241a726067a_MD5.jpg" alt=""></p>
<p><img src="/assets/qrcode-controlnet/9934cdc1f01135af19af183de965de35_MD5.jpg" alt=""></p>
<h3>二次元和插画风格</h3>
<p><img src="/assets/qrcode-controlnet/d81aaf05d139f7cbd3b938a860d3b902_MD5.jpg" alt=""></p>
<p><img src="/assets/qrcode-controlnet/158c17543563c49aed279b68729516b1_MD5.jpg" alt=""></p>
<p><img src="/assets/qrcode-controlnet/82251aafe93defa212378b55fc136a02_MD5.jpg" alt=""></p>
<p><img src="/assets/qrcode-controlnet/d0b3201a64e968c25e6779235358ac40_MD5.png" alt=""></p>
<p><img src="/assets/qrcode-controlnet/a931837f5be7ef038984616d9f76ea47_MD5.png" alt=""></p>
<p><img src="/assets/qrcode-controlnet/93756b57b228dae45e5c7aa3f62a271d_MD5.jpg" alt=""></p>
<p><img src="/assets/qrcode-controlnet/af32f7438bf6dc7adfb603a2e7502e41_MD5.jpg" alt=""></p>
<p><img src="/assets/qrcode-controlnet/12163ba377639cb5c43469907fcd49d6_MD5.jpg" alt=""></p>
<h3>水墨风格（MoXin）</h3>
<p><img src="/assets/qrcode-controlnet/8bc55d33e12e9007b663741027cff181_MD5.png" alt=""></p>
<h3>水彩风格</h3>
<p><img src="/assets/qrcode-controlnet/4f3afe6ece0de12d1cd20bb4195e117e_MD5.png" alt=""></p>
<h3>立体风格</h3>
<p><img src="/assets/qrcode-controlnet/1afa51983eaa46c46b2a2328c65750ba_MD5.png" alt=""></p>
<p><img src="/assets/qrcode-controlnet/f4b9159b0e8e27948fe68fd7b255dbf8_MD5.png" alt=""></p>
<p><img src="/assets/qrcode-controlnet/89aa4967cdaaf68076ec0f81f8065b4c_MD5.png" alt=""></p>
<p><img src="/assets/qrcode-controlnet/f9fdaffc0bc9203ac1cc103fc8dd2291_MD5.png" alt=""></p>
<h3>抽象风格</h3>
<p><img src="/assets/qrcode-controlnet/9b0164bf91ce5c607a9560c5270cb919_MD5.png" alt=""></p>
<p><img src="/assets/qrcode-controlnet/74e93d940e5f0d424009cafbe3e8f860_MD5.png" alt=""></p>
<h3>PCB 风格</h3>
<p><img src="/assets/qrcode-controlnet/ca882a4a5ff15e26ebcc9c3d1a7f44b3_MD5.png" alt=""></p>
<p><img src="/assets/qrcode-controlnet/0750a9129d544a998175550acbef2e60_MD5.png" alt=""></p>
<h3>Bonus：Ps 重绘</h3>
<p><img src="/assets/qrcode-controlnet/8b482ae9d017bdb1e1facf22fecc6a9e_MD5.jpg" alt=""></p>
<p><img src="/assets/qrcode-controlnet/c1c998403ea3c559af401f86aa33c568_MD5.jpg" alt=""></p>
<h2 id="">后记</h2>
<p>本科毕业之际，疫情退散，看到了生成式 AI 如此蓬勃发展，不禁感叹，真想重读一次本科。</p>
<p>这次的 QR Code ControlNet 离不开和 @陈柏宇(时辰) @王照涵 @陈智勇 几位同学的通力合作，一起在三天内完成了数据集准备、训练和推理测试，以及来自吕欣老师、孙国玉老师实验室的 GPU 资源支持。也要隆重感谢 Google、HuggingFace 此前慷慨提供的 TPU 服务器，属实是爽了一把。</p>
<p>模型发布、技术文档请留意公众号后续更新和文档更新（aigc.ioclab.com），欢迎点击阅读原文给文档留言！</p>]]></content:encoded>
            <author>1272777550@qq.com (Troy Ni)</author>
        </item>
        <item>
            <title><![CDATA[Test Article]]></title>
            <link>https://troyni.com/articles/test</link>
            <guid>https://troyni.com/articles/test</guid>
            <pubDate>Mon, 26 Feb 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Welcome to my website!</p>
<div class="my-6 flex gap-2.5 rounded-2xl border border-zinc-500/20 bg-zinc-50/50 p-4 leading-6 text-zinc-900 dark:border-zinc-500/30 dark:bg-zinc-500/5 dark:text-zinc-200 dark:[--tw-prose-links-hover:theme(colors.zinc.300)] dark:[--tw-prose-links:theme(colors.white)]"><svg viewBox="0 0 16 16" aria-hidden="true" class="mt-1 h-4 w-4 flex-none fill-zinc-500 stroke-white dark:fill-zinc-200/20 dark:stroke-zinc-200"><circle cx="8" cy="8" r="8" stroke-width="0"></circle><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M6.75 7.75h1.5v3.5"></path><circle cx="8" cy="4" r=".5" fill="none"></circle></svg><div class="[&amp;>:first-child]:mt-0 [&amp;>:last-child]:mb-0"><p>Before you can make requests to the Protocol API, you will need to grab your
API key from your dashboard. You find it under <a target="" href="#">Settings » API</a>.</p></div></div>
<p>Before making your first API request, you need to pick which API client you will use. In addition to good ol' cURL HTTP requests, Protocol offers clients for JavaScript, Python, and PHP. In the following example, you can see how to install each client.</p>
<button type="button" aria-hidden="true" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0"></button><div class="my-6 overflow-hidden rounded-2xl bg-zinc-900 shadow-md dark:ring-1 dark:ring-white/10"><div class="not-prose"><div class="flex min-h-[calc(theme(spacing.12)+1px)] flex-wrap items-start gap-x-4 border-b border-zinc-700 bg-zinc-800 px-4 dark:border-zinc-800 dark:bg-transparent"><div class="-mb-px flex gap-4 text-xs font-medium" role="tablist" aria-orientation="horizontal"><button class="border-b py-3 transition ui-not-focus-visible:outline-none border-emerald-500 text-emerald-400" id="headlessui-tabs-tab-:Rcofauuulqnla:" role="tab" type="button" aria-selected="true" tabindex="0" data-headlessui-state="selected">cURL</button><button class="border-b py-3 transition ui-not-focus-visible:outline-none border-transparent text-zinc-400 hover:text-zinc-300" id="headlessui-tabs-tab-:Rkofauuulqnla:" role="tab" type="button" aria-selected="false" tabindex="-1" data-headlessui-state="">JavaScript</button><button class="border-b py-3 transition ui-not-focus-visible:outline-none border-transparent text-zinc-400 hover:text-zinc-300" id="headlessui-tabs-tab-:Rsofauuulqnla:" role="tab" type="button" aria-selected="false" tabindex="-1" data-headlessui-state="">Python</button><button class="border-b py-3 transition ui-not-focus-visible:outline-none border-transparent text-zinc-400 hover:text-zinc-300" id="headlessui-tabs-tab-:R14ofauuulqnla:" role="tab" type="button" aria-selected="false" tabindex="-1" data-headlessui-state="">PHP</button></div></div><div><div id="headlessui-tabs-panel-:R38fauuulqnla:" role="tabpanel" tabindex="0" data-headlessui-state="selected"><div class="group dark:bg-white/2.5"><div class="relative"><pre class="overflow-x-auto p-4 text-xs text-white"><code class="language-bash"><span><span style="color: var(--shiki-token-comment)"># cURL is most likely already installed on your machine</span></span>
<span><span style="color: var(--shiki-token-function)">curl</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">--version</span></span>
<span></span></code></pre><button type="button" class="group/button absolute right-4 top-3.5 overflow-hidden rounded-full py-1 pl-2 pr-3 text-2xs font-medium opacity-0 backdrop-blur transition focus:opacity-100 group-hover:opacity-100 bg-white/5 hover:bg-white/7.5 dark:bg-white/2.5 dark:hover:bg-white/5"><span aria-hidden="false" class="pointer-events-none flex items-center gap-0.5 text-zinc-400 transition duration-300"><svg viewBox="0 0 20 20" aria-hidden="true" class="h-5 w-5 fill-zinc-500/20 stroke-zinc-500 transition-colors group-hover/button:stroke-zinc-400"><path stroke-width="0" d="M5.5 13.5v-5a2 2 0 0 1 2-2l.447-.894A2 2 0 0 1 9.737 4.5h.527a2 2 0 0 1 1.789 1.106l.447.894a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-5a2 2 0 0 1-2-2Z"></path><path fill="none" stroke-linejoin="round" d="M12.5 6.5a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-5a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2m5 0-.447-.894a2 2 0 0 0-1.79-1.106h-.527a2 2 0 0 0-1.789 1.106L7.5 6.5m5 0-1 1h-3l-1-1"></path></svg>Copy</span><span aria-hidden="true" class="pointer-events-none absolute inset-0 flex items-center justify-center text-emerald-400 transition duration-300 translate-y-1.5 opacity-0">Copied!</span></button></div></div></div><span id="headlessui-tabs-panel-:R58fauuulqnla:" role="tabpanel" tabindex="-1" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0"></span><span id="headlessui-tabs-panel-:R78fauuulqnla:" role="tabpanel" tabindex="-1" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0"></span><span id="headlessui-tabs-panel-:R98fauuulqnla:" role="tabpanel" tabindex="-1" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0"></span></div></div></div>
<h2 id="choose-your-client">Choose your client</h2>
<p>123</p>
<h1>GFM</h1>
<h2 id="autolink-literals">Autolink literals</h2>
<p><a target="_blank" href="http://www.example.com">www.example.com</a>, <a target="_blank" href="https://example.com">https://example.com</a>, and <a target="_blank" href="/cdn-cgi/l/email-protection#d9bab6b7adb8baad99bca1b8b4a9b5bcf7bab6b4"><span class="__cf_email__" data-cfemail="86e5e9e8f2e7e5f2c6e3fee7ebf6eae3a8e5e9eb">[email&nbsp;protected]</span></a>.</p>
<h2 id="footnote">Footnote</h2>
<p>A note<sup><a id="user-content-fnref-1" data-footnote-ref="true" aria-describedby="footnote-label" target="" href="#user-content-fn-1">1</a></sup></p>
<h2 id="strikethrough">Strikethrough</h2>
<p><del>one</del> or <del>two</del> tildes.</p>
<h2 id="table">Table</h2>
<table><thead><tr><th>a</th><th align="left">b</th><th align="right">c</th><th align="center">d</th></tr></thead></table>
<h2 id="tasklist">Tasklist</h2>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" disabled=""> <!-- -->to do</li>
<li class="task-list-item"><input type="checkbox" disabled="" checked=""> <!-- -->done</li>
</ul>
<section data-footnotes="true" class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Big note. <a data-footnote-backref="true" class="data-footnote-backref" aria-label="Back to content" target="" href="#user-content-fnref-1">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
            <author>1272777550@qq.com (Troy Ni)</author>
        </item>
        <item>
            <title><![CDATA[网页技术栈 2024]]></title>
            <link>https://troyni.com/articles/web-stack-2024</link>
            <guid>https://troyni.com/articles/web-stack-2024</guid>
            <pubDate>Wed, 06 Mar 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<div class="my-6 flex gap-2.5 rounded-2xl border border-zinc-500/20 bg-zinc-50/50 p-4 leading-6 text-zinc-900 dark:border-zinc-500/30 dark:bg-zinc-500/5 dark:text-zinc-200 dark:[--tw-prose-links-hover:theme(colors.zinc.300)] dark:[--tw-prose-links:theme(colors.white)]"><svg viewBox="0 0 16 16" aria-hidden="true" class="mt-1 h-4 w-4 flex-none fill-zinc-500 stroke-white dark:fill-zinc-200/20 dark:stroke-zinc-200"><circle cx="8" cy="8" r="8" stroke-width="0"></circle><path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M6.75 7.75h1.5v3.5"></path><circle cx="8" cy="4" r=".5" fill="none"></circle></svg><div class="[&amp;>:first-child]:mt-0 [&amp;>:last-child]:mb-0"><p>仅限个人路线选择，可能并非最佳实践</p></div></div>
<blockquote>
<p>又：面向非科班程序员的前端指南</p>
</blockquote>
<blockquote>
<p>还没开写，列了一下大纲</p>
</blockquote>
<h2 id="">前端</h2>
<ul>
<li>Vercel</li>
<li>Next.js</li>
<li>Tailwind CSS</li>
<li>shadcn/ui</li>
<li>Framer</li>
</ul>
<h2 id="">后端</h2>
<ul>
<li>FastAPI</li>
<li>Railway</li>
</ul>
<h2 id="ai">AI</h2>
<ul>
<li>Vercel AI SDK</li>
<li>Gradio</li>
</ul>]]></content:encoded>
            <author>1272777550@qq.com (Troy Ni)</author>
        </item>
        <item>
            <title><![CDATA[微商福利：什么时候发朋友圈最合适？]]></title>
            <link>https://troyni.com/articles/wechat-memory-insights-zh</link>
            <guid>https://troyni.com/articles/wechat-memory-insights-zh</guid>
            <pubDate>Sun, 17 Nov 2019 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>在网络传播理论与实践课上，吕欣老师提到了 “媒体黄金时间的重新定义”。交通广播的黄金时间是 6:00 - 7:00 和 17:30 - 18:30 的早晚交通高峰，电视的黄金时间是 19:00 - 21:00，到了移动互联网时代，手机和我们互相成为延伸，不再像广播、电视那样那么受限于使用的场景。那么这个时代的黄金时间是什么？</p>
<p>大家不妨思考一下这个问题：</p>
<p><strong>你认为什么时候发朋友圈最合适？</strong></p>
<p>有人说，晚饭后发朋友圈最合适，因为那个时候大部分人都会闲下来刷朋友圈；也有人说，晚上发朋友圈的人太多了，自己发的内容太容易被淹没，反而更难取得关注；还有人说，早起发朋友圈更好、正午发朋友圈更好，因为大家有起床后工作前或是午饭后刷朋友圈的习惯，而且那时朋友圈更新的频率比较小……</p>
<p>以上都是大家对朋友圈发布的基于经验的思考，经验会不会出错呢？我想研究一下。</p>
<p>首先，我们发朋友圈最期待的就是两个反馈：点赞和评论，因为它们是由我们所发布的内容转化而来的，所以我们称之为 “<strong>转化量</strong>”。来看看我朋友圈一天中点赞量和评论量在 24 小时时间上的分布，其中图中的每一个点代表一条朋友圈，纵坐标是其收获的点赞量 / 评论量，横坐标是朋友圈发布的时间。</p>
<p><img src="/assets/wechat-memory-insights/922709deff15de5be7ce41b7f377157d_MD5.jpg" alt=""></p>
<p>朋友圈点赞数与时间散点图</p>
<p><img src="/assets/wechat-memory-insights/2ba79ab6b2079cd3f70149bf27de7c59_MD5.jpg" alt=""></p>
<p>朋友圈评论数与时间散点图</p>
<p>看到这里，很多人就想下结论了，显然朋友圈晚上发最合适嘛，晚上的点又高又密。真的是这样吗？这其中有许多的问题。</p>
<p>首先是样本，图中的 7418 个点代表我获取我的朋友圈的 7418 条数据，局限性很大，不能代表所有人朋友圈的发布情况。其次，点的密度大只能说明该时段朋友圈的发布量大，这没什么。还有，晚上点赞量高的朋友圈多，但点赞量低的朋友圈也多，<strong>点赞量的期望</strong>真的会高于其他时段吗？</p>
<p>抛开样本的局限性不说，我们先看一看这些朋友圈都是在什么时候发的：</p>
<p><img src="/assets/wechat-memory-insights/be855a649b5a291f0699e5005700bbbc_MD5.jpg" alt=""></p>
<p>朋友圈发布时间直方图</p>
<p>工作日和周末各时段的平均发布量对比图：</p>
<p><img src="/assets/wechat-memory-insights/a6114ed5d18317d537fe16cd9e10d563_MD5.png" alt=""></p>
<p>工作日与周末朋友圈平均发布量对比图</p>
<p>可以看出，我的朋友喜欢在正午、傍晚、和晚间发布朋友圈，在每晚 21:00 - 23:00 达到全日的最高峰。同时，周末的高峰比工作日更高。</p>
<p>这就是为什么有人会觉得晚上发朋友圈会被 “淹没” 了。</p>
<p>可是，发朋友圈的人多，看朋友圈的人也会多啊！我把一天拆成 48 个时段，统计每个时段朋友圈的数量、点赞总量、评论总量，绘制成如下散点图。</p>
<p><img src="/assets/wechat-memory-insights/992c292257385dcee75e3b6e82eb0d78_MD5.jpg" alt=""></p>
<p>各时间段点赞量（黑）、评论量（灰）与发布量的散点图与拟合直线</p>
<p>其中，每一个点代表一个时段，横坐标是其间朋友圈的数量，纵坐标是其点赞总量（黑）/ 评论总量（灰）。再进行一波线性拟合，发现……无论什么时候发朋友圈，获得点赞、评论数量的期望几乎是一样的，一样的！</p>
<p>这可真是太尴尬了。</p>
<p>这个时候老师发来了一条公式：<strong>选择或然率公式</strong>。</p>
<p><img src="/assets/wechat-memory-insights/18689f7d6c8069654aba066d0658352f_MD5.jpg" alt=""></p>
<p>将其投影在微信朋友圈上，我也得出了一条类似的公式：（微信朋友圈）转化量公式。</p>
<p><img src="/assets/wechat-memory-insights/b6f9001f0391fad25d3604cdde35b3d3_MD5.png" alt=""></p>
<p>进行简单的转换，可以得出：</p>
<p><img src="/assets/wechat-memory-insights/67ddaf934f8e1d38aed1a155ebd3800d_MD5.png" alt=""></p>
<p>也就是：</p>
<p><img src="/assets/wechat-memory-insights/6cfc125e5bfea7ef2eff1403bdc1281e_MD5.png" alt=""></p>
<p>所以，转化量期望恒定的源头就在于：我们发朋友圈的时间和浏览朋友圈的时间分布几乎是一致的。</p>
<p>等等，回到刚才的问题，期望一样又能怎样呢？<strong>期望恒定了，我们获得更大转化量的机会也恒定了吗？</strong></p>
<p>这时，我们来关注一个新的指标：<strong>注意熵</strong>。</p>
<p>微信朋友圈的发布、浏览、点赞、评论都是<strong>符号互动</strong>的重要组成，它形成了比以往更多的人与人之间的<strong>强弱互惠性服务关系</strong>。这些互动拓展了个体维持关系的能力，同时也挤压了用户的时间，占用了用户的注意力。</p>
<p>假设我们的注意在各条朋友圈之间的分配是完全随机的，那么各条朋友圈获得的曝光机会也就是相等的。但事实上，我们的注意力会集中在某几个位置而忽视其余位置，我们怎样去描述<strong>注意的集中或分散程度</strong>？</p>
<p>基于香农的信息熵理论，用户朋友圈的注意熵可以反应用户<strong>注意的不确定性</strong>。</p>
<p>熵被用来衡量一个随机变量出现的期望值，计算方法是：</p>
<p><img src="/assets/wechat-memory-insights/a544433696c0694cd2fa861d635e4c73_MD5.png" alt=""></p>
<p>我将点赞以及评论数据作为计算注意熵的来源，探究用户注意的分散度。对于一份点赞样本 C，P(ci) =&nbsp;ci&nbsp;/&nbsp;∑&nbsp;C，即某条朋友圈的点赞数可反应用户注意停留在该内容上的概率。</p>
<p>同样把一天分为 48 个时段，对每个时段的所有朋友圈计算注意熵，可以得到总注意熵的趋势图：</p>
<p><img src="/assets/wechat-memory-insights/fd43be994a93585b164bf623fcc46f05_MD5.png" alt=""></p>
<p>各时间段的总注意熵</p>
<p>如果对每个时段取定量的朋友圈样本，可以绘制各时段确定样本数注意熵的趋势图：</p>
<p><img src="/assets/wechat-memory-insights/adf518a5e7dbf8121606f916658056f1_MD5.png" alt=""></p>
<p>各时间段确定样本数的注意熵</p>
<p>由图可知，在一个确定时间段内，用户的注意的随机性会因内容的增多而变强，而在一个确定数量的样本中，用户注意的随机性几乎恒定。</p>
<p>这就说明，用户浏览内容时，注意力出现的概率是恒定的，即用户出现对某条朋友圈关注度提升的概率是恒定的，但是用户的注意总量会被有限时间的内容数量所稀释，信息量越大，用户随时间分配注意的随机性越强。</p>
<p>太玄学了。</p>
<p>紧接着，我的神仙老师又发来了一本书——<strong>巴拉巴西的《链接》</strong>，读完之后，我发现其中<strong>无尺度网络</strong>的<strong>幂律分布</strong>同样适用于微信朋友圈。</p>
<p>首先，我们把我朋友圈的每一个用户视作一个<strong>节点</strong>，每一个次点赞都形成两个节点之间的<strong>有向边</strong>，这样一来，一个<strong>有向图</strong>就形成了：</p>
<p><img src="/assets/wechat-memory-insights/7c1b4ac3d2c8147115e348b630218eb4_MD5.jpg" alt=""></p>
<p>一个相同阶数和边数的<strong>随机图</strong>：</p>
<p><img src="/assets/wechat-memory-insights/146fae3d4bf0b60e15a3dc047b73f42c_MD5.jpg" alt=""></p>
<p>统计两个图中各个节点的<strong>度</strong>（节点连接的边数），可以发现，朋友圈的图的度的分布是<strong>幂律分布</strong>，而随机图的分布是<strong>二项式分布</strong>。对于幂律分布来说，研究其<strong>度的期望</strong>的意义不很大。</p>
<p><img src="/assets/wechat-memory-insights/2ad95b1874f36638780d1a66d25fcdf0_MD5.jpg" alt=""></p>
<p>二图度分布对比图</p>
<blockquote>
<p>平均没有意义，多少不是关键。</p>
</blockquote>
<p>综上所述，如果朋友圈的质量足够高，那么我们可以一直维持在自己朋友圈这个幂律分布的顶端节点——那个拥有连接数最多的节点，这个时候，朋友圈转化率的期望值没有意义——它几乎是恒定的，朋友圈发布的数量不是关键——大部分朋友圈会被 “淹没”，起决定作用的就是我们获得的曝光量。所以，质量足够高，什么时候 “人多” 什么时候发，这，就是互联网时代的 “黄金时间”。</p>
<p>用《链接》里的另一句话作为此次小思考的收尾：<strong>节点永远在为链接而竞争</strong>。</p>
<p>One more thing……</p>
<p><img src="/assets/wechat-memory-insights/f9f83073a7794fe8fa3dd1117999561f_MD5.jpg" alt=""></p>
<p>根据点赞关系绘制的社区图，可以非常精准的分类我的各个好友群体</p>
<hr>
<p>非常感谢吕欣老师、沈浩老师、孙国玉老师、张辉老师的悉心教导！</p>
<p>完。</p>]]></content:encoded>
            <author>1272777550@qq.com (Troy Ni)</author>
        </item>
        <item>
            <title><![CDATA[New Start & Welcome!]]></title>
            <link>https://troyni.com/articles/welcome</link>
            <guid>https://troyni.com/articles/welcome</guid>
            <pubDate>Tue, 19 Dec 2023 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Welcome to my new personal website!</p>
<p>After struggling for a long time with domain names (formerly nhciao.com / ciaochaos.com), front-end architecture, etc., I finally set up <a target="_blank" href="https://troyni.com">troyni.com</a> as my personal website.</p>
<h3>Tech stack</h3>
<ul>
<li>Next.js + React.js + Tailwind CSS + shacd/ui for front-end</li>
<li>MDX + KaTeX for article content</li>
<li>Mixpanel for analysis</li>
<li>Vercel for CI/CD</li>
</ul>
<h3>Plans</h3>
<p>In the future, I will continue to write English/Chinese articles on the <a target="" href="/articles">/articles</a> page, share projects on the <a target="" href="/projects">/projects</a> page, and add some cool stuff to <a target="" href="/playground">/playground</a>.</p>]]></content:encoded>
            <author>1272777550@qq.com (Troy Ni)</author>
        </item>
    </channel>
</rss>